From daf3d0972286f43d706be64b6929cbbf288f964a Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sat, 21 Dec 2024 23:36:41 -0700 Subject: [PATCH 01/24] git subrepo clone --branch=d1ff06cc503a74dca0150d5e988f2c93158b0cdf https://github.com/ivmai/bdwgc subrepo: subdir: "bdwgc" merged: "d1ff06cc5" upstream: origin: "https://github.com/ivmai/bdwgc" branch: "d1ff06cc503a74dca0150d5e988f2c93158b0cdf" commit: "d1ff06cc5" git-subrepo: version: "0.4.9" origin: "https://github.com/ingydotnet/git-subrepo" commit: "cce3d93" --- bdwgc/.appveyor.yml | 106 + bdwgc/.gitattributes | 16 + bdwgc/.gitignore | 175 + bdwgc/.gitrepo | 12 + bdwgc/.travis.yml | 787 ++ bdwgc/AUTHORS | 462 + bdwgc/CMakeLists.txt | 842 ++ bdwgc/ChangeLog | 10771 ++++++++++++++++ bdwgc/Config.cmake.in | 5 + bdwgc/Makefile.am | 249 + bdwgc/Makefile.direct | 430 + bdwgc/NT_MAKEFILE | 186 + bdwgc/OS2_MAKEFILE | 59 + bdwgc/PCR-Makefile | 47 + bdwgc/README.QUICK | 85 + bdwgc/README.md | 575 + bdwgc/SMakefile.amiga | 172 + bdwgc/WCC_MAKEFILE | 286 + bdwgc/allchblk.c | 993 ++ bdwgc/alloc.c | 1817 +++ bdwgc/autogen.sh | 18 + bdwgc/backgraph.c | 558 + bdwgc/bdw-gc.pc.in | 10 + bdwgc/blacklst.c | 301 + bdwgc/build/s60v3/bld.inf | 11 + bdwgc/build/s60v3/libgc.mmp | 78 + bdwgc/checksums.c | 178 + bdwgc/configure.ac | 1235 ++ bdwgc/cord/cord.am | 40 + bdwgc/cord/cordbscs.c | 942 ++ bdwgc/cord/cordprnt.c | 455 + bdwgc/cord/cordxtra.c | 643 + bdwgc/cord/tests/cordtest.c | 349 + bdwgc/cord/tests/de.c | 640 + bdwgc/cord/tests/de_cmds.h | 31 + bdwgc/cord/tests/de_win.c | 385 + bdwgc/cord/tests/de_win.h | 97 + bdwgc/cord/tests/de_win.rc | 70 + bdwgc/darwin_stop_world.c | 780 ++ bdwgc/dbg_mlc.c | 1221 ++ bdwgc/digimars.mak | 102 + bdwgc/doc/README.DGUX386 | 39 + bdwgc/doc/README.Mac | 332 + bdwgc/doc/README.OS2 | 6 + bdwgc/doc/README.amiga | 284 + bdwgc/doc/README.arm.cross | 62 + bdwgc/doc/README.autoconf | 59 + bdwgc/doc/README.cmake | 62 + bdwgc/doc/README.cords | 45 + bdwgc/doc/README.darwin | 81 + bdwgc/doc/README.emscripten | 15 + bdwgc/doc/README.environment | 185 + bdwgc/doc/README.ews4800 | 81 + bdwgc/doc/README.hp | 18 + bdwgc/doc/README.linux | 127 + bdwgc/doc/README.macros | 609 + bdwgc/doc/README.rs6000 | 9 + bdwgc/doc/README.sgi | 39 + bdwgc/doc/README.solaris2 | 68 + bdwgc/doc/README.symbian | 11 + bdwgc/doc/README.uts | 2 + bdwgc/doc/README.win32 | 215 + bdwgc/doc/README.win64 | 27 + bdwgc/doc/debugging.md | 273 + bdwgc/doc/doc.am | 52 + bdwgc/doc/finalization.md | 158 + bdwgc/doc/gc.man | 155 + bdwgc/doc/gcdescr.md | 546 + bdwgc/doc/gcinterface.md | 216 + bdwgc/doc/leak.md | 160 + bdwgc/doc/overview.md | 311 + bdwgc/doc/porting.md | 263 + bdwgc/doc/scale.md | 173 + bdwgc/doc/simple_example.md | 182 + bdwgc/doc/tree.md | 177 + bdwgc/dyn_load.c | 1574 +++ bdwgc/extra/AmigaOS.c | 544 + bdwgc/extra/MacOS.c | 169 + bdwgc/extra/Mac_files/MacOS_config.h | 27 + bdwgc/extra/Mac_files/dataend.c | 9 + bdwgc/extra/Mac_files/datastart.c | 9 + bdwgc/extra/gc.c | 93 + bdwgc/extra/msvc_dbg.c | 406 + bdwgc/extra/pcr_interface.c | 181 + bdwgc/extra/real_malloc.c | 39 + bdwgc/extra/symbian.cpp | 52 + bdwgc/extra/symbian/global_end.cpp | 10 + bdwgc/extra/symbian/global_start.cpp | 10 + .../symbian/init_global_static_roots.cpp | 28 + bdwgc/finalize.c | 1386 ++ bdwgc/fnlz_mlc.c | 118 + bdwgc/gc_badalc.cc | 39 + bdwgc/gc_badalc.cpp | 2 + bdwgc/gc_cpp.cc | 114 + bdwgc/gc_cpp.cpp | 2 + bdwgc/gc_dlopen.c | 102 + bdwgc/gcj_mlc.c | 266 + bdwgc/headers.c | 422 + bdwgc/ia64_save_regs_in_stack.s | 11 + bdwgc/include/cord.h | 378 + bdwgc/include/cord_pos.h | 128 + bdwgc/include/ec.h | 88 + bdwgc/include/extra/gc.h | 2 + bdwgc/include/extra/gc_cpp.h | 2 + bdwgc/include/gc.h | 2195 ++++ bdwgc/include/gc_allocator.h | 341 + bdwgc/include/gc_backptr.h | 96 + bdwgc/include/gc_config_macros.h | 458 + bdwgc/include/gc_cpp.h | 603 + bdwgc/include/gc_disclaim.h | 75 + bdwgc/include/gc_gcj.h | 111 + bdwgc/include/gc_inline.h | 209 + bdwgc/include/gc_mark.h | 333 + bdwgc/include/gc_pthread_redirects.h | 126 + bdwgc/include/gc_tiny_fl.h | 90 + bdwgc/include/gc_typed.h | 122 + bdwgc/include/gc_version.h | 47 + bdwgc/include/include.am | 47 + bdwgc/include/javaxfc.h | 65 + bdwgc/include/leak_detector.h | 68 + bdwgc/include/private/darwin_semaphore.h | 87 + bdwgc/include/private/darwin_stop_world.h | 53 + bdwgc/include/private/dbg_mlc.h | 182 + bdwgc/include/private/gc_alloc_ptrs.h | 60 + bdwgc/include/private/gc_atomic_ops.h | 124 + bdwgc/include/private/gc_hdrs.h | 215 + bdwgc/include/private/gc_locks.h | 282 + bdwgc/include/private/gc_pmark.h | 485 + bdwgc/include/private/gc_priv.h | 3126 +++++ bdwgc/include/private/gcconfig.h | 3382 +++++ bdwgc/include/private/msvc_dbg.h | 70 + bdwgc/include/private/pthread_stop_world.h | 64 + bdwgc/include/private/pthread_support.h | 196 + bdwgc/include/private/specific.h | 133 + bdwgc/include/private/thread_local_alloc.h | 201 + bdwgc/m4/gc_set_version.m4 | 40 + bdwgc/mach_dep.c | 428 + bdwgc/malloc.c | 722 ++ bdwgc/mallocx.c | 638 + bdwgc/mark.c | 1952 +++ bdwgc/mark_rts.c | 961 ++ bdwgc/misc.c | 2709 ++++ bdwgc/new_hblk.c | 192 + bdwgc/obj_map.c | 90 + bdwgc/os_dep.c | 5408 ++++++++ bdwgc/pthread_start.c | 73 + bdwgc/pthread_stop_world.c | 1449 +++ bdwgc/pthread_support.c | 2646 ++++ bdwgc/ptr_chck.c | 282 + bdwgc/reclaim.c | 853 ++ bdwgc/sparc_mach_dep.S | 61 + bdwgc/sparc_netbsd_mach_dep.s | 34 + bdwgc/specific.c | 189 + bdwgc/tests/disclaim_bench.c | 164 + bdwgc/tests/disclaim_test.c | 280 + bdwgc/tests/disclaim_weakmap_test.c | 473 + bdwgc/tests/huge_test.c | 67 + bdwgc/tests/initsecondarythread.c | 115 + bdwgc/tests/leak_test.c | 25 + bdwgc/tests/middle.c | 31 + bdwgc/tests/realloc_test.c | 36 + bdwgc/tests/smash_test.c | 41 + bdwgc/tests/staticrootslib.c | 72 + bdwgc/tests/staticrootstest.c | 77 + bdwgc/tests/subthread_create.c | 179 + bdwgc/tests/test.c | 2525 ++++ bdwgc/tests/test_atomic_ops.c | 98 + bdwgc/tests/test_cpp.cc | 425 + bdwgc/tests/tests.am | 179 + bdwgc/tests/thread_leak_test.c | 103 + bdwgc/tests/threadkey_test.c | 119 + bdwgc/tests/trace_test.c | 54 + bdwgc/thread_local_alloc.c | 310 + bdwgc/tools/callprocs.sh | 4 + bdwgc/tools/if_mach.c | 32 + bdwgc/tools/if_not_there.c | 58 + bdwgc/tools/setjmp_t.c | 167 + bdwgc/tools/threadlibs.c | 82 + bdwgc/typd_mlc.c | 734 ++ bdwgc/win32_threads.c | 3365 +++++ 180 files changed, 79660 insertions(+) create mode 100644 bdwgc/.appveyor.yml create mode 100644 bdwgc/.gitattributes create mode 100644 bdwgc/.gitignore create mode 100644 bdwgc/.gitrepo create mode 100644 bdwgc/.travis.yml create mode 100644 bdwgc/AUTHORS create mode 100644 bdwgc/CMakeLists.txt create mode 100644 bdwgc/ChangeLog create mode 100644 bdwgc/Config.cmake.in create mode 100644 bdwgc/Makefile.am create mode 100644 bdwgc/Makefile.direct create mode 100644 bdwgc/NT_MAKEFILE create mode 100644 bdwgc/OS2_MAKEFILE create mode 100644 bdwgc/PCR-Makefile create mode 100644 bdwgc/README.QUICK create mode 100644 bdwgc/README.md create mode 100644 bdwgc/SMakefile.amiga create mode 100644 bdwgc/WCC_MAKEFILE create mode 100644 bdwgc/allchblk.c create mode 100644 bdwgc/alloc.c create mode 100755 bdwgc/autogen.sh create mode 100644 bdwgc/backgraph.c create mode 100644 bdwgc/bdw-gc.pc.in create mode 100644 bdwgc/blacklst.c create mode 100644 bdwgc/build/s60v3/bld.inf create mode 100644 bdwgc/build/s60v3/libgc.mmp create mode 100644 bdwgc/checksums.c create mode 100644 bdwgc/configure.ac create mode 100644 bdwgc/cord/cord.am create mode 100644 bdwgc/cord/cordbscs.c create mode 100644 bdwgc/cord/cordprnt.c create mode 100644 bdwgc/cord/cordxtra.c create mode 100644 bdwgc/cord/tests/cordtest.c create mode 100644 bdwgc/cord/tests/de.c create mode 100644 bdwgc/cord/tests/de_cmds.h create mode 100644 bdwgc/cord/tests/de_win.c create mode 100644 bdwgc/cord/tests/de_win.h create mode 100644 bdwgc/cord/tests/de_win.rc create mode 100644 bdwgc/darwin_stop_world.c create mode 100644 bdwgc/dbg_mlc.c create mode 100644 bdwgc/digimars.mak create mode 100644 bdwgc/doc/README.DGUX386 create mode 100644 bdwgc/doc/README.Mac create mode 100644 bdwgc/doc/README.OS2 create mode 100644 bdwgc/doc/README.amiga create mode 100644 bdwgc/doc/README.arm.cross create mode 100644 bdwgc/doc/README.autoconf create mode 100644 bdwgc/doc/README.cmake create mode 100644 bdwgc/doc/README.cords create mode 100644 bdwgc/doc/README.darwin create mode 100644 bdwgc/doc/README.emscripten create mode 100644 bdwgc/doc/README.environment create mode 100644 bdwgc/doc/README.ews4800 create mode 100644 bdwgc/doc/README.hp create mode 100644 bdwgc/doc/README.linux create mode 100644 bdwgc/doc/README.macros create mode 100644 bdwgc/doc/README.rs6000 create mode 100644 bdwgc/doc/README.sgi create mode 100644 bdwgc/doc/README.solaris2 create mode 100644 bdwgc/doc/README.symbian create mode 100644 bdwgc/doc/README.uts create mode 100644 bdwgc/doc/README.win32 create mode 100644 bdwgc/doc/README.win64 create mode 100644 bdwgc/doc/debugging.md create mode 100644 bdwgc/doc/doc.am create mode 100644 bdwgc/doc/finalization.md create mode 100644 bdwgc/doc/gc.man create mode 100644 bdwgc/doc/gcdescr.md create mode 100644 bdwgc/doc/gcinterface.md create mode 100644 bdwgc/doc/leak.md create mode 100644 bdwgc/doc/overview.md create mode 100644 bdwgc/doc/porting.md create mode 100644 bdwgc/doc/scale.md create mode 100644 bdwgc/doc/simple_example.md create mode 100644 bdwgc/doc/tree.md create mode 100644 bdwgc/dyn_load.c create mode 100644 bdwgc/extra/AmigaOS.c create mode 100644 bdwgc/extra/MacOS.c create mode 100644 bdwgc/extra/Mac_files/MacOS_config.h create mode 100644 bdwgc/extra/Mac_files/dataend.c create mode 100644 bdwgc/extra/Mac_files/datastart.c create mode 100644 bdwgc/extra/gc.c create mode 100644 bdwgc/extra/msvc_dbg.c create mode 100644 bdwgc/extra/pcr_interface.c create mode 100644 bdwgc/extra/real_malloc.c create mode 100644 bdwgc/extra/symbian.cpp create mode 100644 bdwgc/extra/symbian/global_end.cpp create mode 100644 bdwgc/extra/symbian/global_start.cpp create mode 100644 bdwgc/extra/symbian/init_global_static_roots.cpp create mode 100644 bdwgc/finalize.c create mode 100644 bdwgc/fnlz_mlc.c create mode 100644 bdwgc/gc_badalc.cc create mode 100644 bdwgc/gc_badalc.cpp create mode 100644 bdwgc/gc_cpp.cc create mode 100644 bdwgc/gc_cpp.cpp create mode 100644 bdwgc/gc_dlopen.c create mode 100644 bdwgc/gcj_mlc.c create mode 100644 bdwgc/headers.c create mode 100644 bdwgc/ia64_save_regs_in_stack.s create mode 100644 bdwgc/include/cord.h create mode 100644 bdwgc/include/cord_pos.h create mode 100644 bdwgc/include/ec.h create mode 100644 bdwgc/include/extra/gc.h create mode 100644 bdwgc/include/extra/gc_cpp.h create mode 100644 bdwgc/include/gc.h create mode 100644 bdwgc/include/gc_allocator.h create mode 100644 bdwgc/include/gc_backptr.h create mode 100644 bdwgc/include/gc_config_macros.h create mode 100644 bdwgc/include/gc_cpp.h create mode 100644 bdwgc/include/gc_disclaim.h create mode 100644 bdwgc/include/gc_gcj.h create mode 100644 bdwgc/include/gc_inline.h create mode 100644 bdwgc/include/gc_mark.h create mode 100644 bdwgc/include/gc_pthread_redirects.h create mode 100644 bdwgc/include/gc_tiny_fl.h create mode 100644 bdwgc/include/gc_typed.h create mode 100644 bdwgc/include/gc_version.h create mode 100644 bdwgc/include/include.am create mode 100644 bdwgc/include/javaxfc.h create mode 100644 bdwgc/include/leak_detector.h create mode 100644 bdwgc/include/private/darwin_semaphore.h create mode 100644 bdwgc/include/private/darwin_stop_world.h create mode 100644 bdwgc/include/private/dbg_mlc.h create mode 100644 bdwgc/include/private/gc_alloc_ptrs.h create mode 100644 bdwgc/include/private/gc_atomic_ops.h create mode 100644 bdwgc/include/private/gc_hdrs.h create mode 100644 bdwgc/include/private/gc_locks.h create mode 100644 bdwgc/include/private/gc_pmark.h create mode 100644 bdwgc/include/private/gc_priv.h create mode 100644 bdwgc/include/private/gcconfig.h create mode 100644 bdwgc/include/private/msvc_dbg.h create mode 100644 bdwgc/include/private/pthread_stop_world.h create mode 100644 bdwgc/include/private/pthread_support.h create mode 100644 bdwgc/include/private/specific.h create mode 100644 bdwgc/include/private/thread_local_alloc.h create mode 100644 bdwgc/m4/gc_set_version.m4 create mode 100644 bdwgc/mach_dep.c create mode 100644 bdwgc/malloc.c create mode 100644 bdwgc/mallocx.c create mode 100644 bdwgc/mark.c create mode 100644 bdwgc/mark_rts.c create mode 100644 bdwgc/misc.c create mode 100644 bdwgc/new_hblk.c create mode 100644 bdwgc/obj_map.c create mode 100644 bdwgc/os_dep.c create mode 100644 bdwgc/pthread_start.c create mode 100644 bdwgc/pthread_stop_world.c create mode 100644 bdwgc/pthread_support.c create mode 100644 bdwgc/ptr_chck.c create mode 100644 bdwgc/reclaim.c create mode 100644 bdwgc/sparc_mach_dep.S create mode 100644 bdwgc/sparc_netbsd_mach_dep.s create mode 100644 bdwgc/specific.c create mode 100644 bdwgc/tests/disclaim_bench.c create mode 100644 bdwgc/tests/disclaim_test.c create mode 100644 bdwgc/tests/disclaim_weakmap_test.c create mode 100644 bdwgc/tests/huge_test.c create mode 100644 bdwgc/tests/initsecondarythread.c create mode 100644 bdwgc/tests/leak_test.c create mode 100644 bdwgc/tests/middle.c create mode 100644 bdwgc/tests/realloc_test.c create mode 100644 bdwgc/tests/smash_test.c create mode 100644 bdwgc/tests/staticrootslib.c create mode 100644 bdwgc/tests/staticrootstest.c create mode 100644 bdwgc/tests/subthread_create.c create mode 100644 bdwgc/tests/test.c create mode 100644 bdwgc/tests/test_atomic_ops.c create mode 100644 bdwgc/tests/test_cpp.cc create mode 100644 bdwgc/tests/tests.am create mode 100644 bdwgc/tests/thread_leak_test.c create mode 100644 bdwgc/tests/threadkey_test.c create mode 100644 bdwgc/tests/trace_test.c create mode 100644 bdwgc/thread_local_alloc.c create mode 100755 bdwgc/tools/callprocs.sh create mode 100644 bdwgc/tools/if_mach.c create mode 100644 bdwgc/tools/if_not_there.c create mode 100644 bdwgc/tools/setjmp_t.c create mode 100644 bdwgc/tools/threadlibs.c create mode 100644 bdwgc/typd_mlc.c create mode 100644 bdwgc/win32_threads.c diff --git a/bdwgc/.appveyor.yml b/bdwgc/.appveyor.yml new file mode 100644 index 000000000..d0b4284ef --- /dev/null +++ b/bdwgc/.appveyor.yml @@ -0,0 +1,106 @@ +version: 8.2.x-{build} + +image: +- Visual Studio 2015 + +environment: + matrix: + - TARGET: cmake + CMAKE_CONFIG: Debug + CMAKE_OPTIONS: -DBUILD_SHARED_LIBS=OFF -Dbuild_tests=ON -Denable_cplusplus=ON -Denable_gc_assertions=ON -Werror=deprecated + - TARGET: cmake + CMAKE_CONFIG: Debug + CMAKE_OPTIONS: -Dbuild_tests=ON -Denable_cplusplus=ON -Denable_gc_assertions=ON -Denable_gc_debug=ON -Denable_threads=OFF + - TARGET: cmake + CFLAGS_EXTRA: -DNO_MSGBOX_ON_ERROR -DNO_MPROTECT_VDB + CMAKE_CONFIG: Release + CMAKE_OPTIONS: -Dbuild_tests=ON -Denable_cplusplus=ON -Denable_large_config=ON -Ddisable_gc_debug=ON + - TARGET: cmake + CMAKE_CONFIG: Release + CMAKE_OPTIONS: -Denable_parallel_mark=OFF + - TARGET: nmake + BLD: debug + CPU: x86 + MACRO_DEFS: enable_static=1 + MS_SDK_VER: v7.1 + - TARGET: nmake + BLD: debug + CPU: x64 + MS_SDK_VER: v7.1 + - TARGET: nmake + BLD: release + CPU: x86 + MACRO_DEFS: nodebug=1 + MS_SDK_VER: v7.1 + - TARGET: cygwin + CONF_OPTIONS: --enable-cplusplus + CFLAGS_EXTRA: -D GCTEST_PRINT_VERBOSE + - TARGET: cygwin + CONF_OPTIONS: --enable-cplusplus --disable-munmap --enable-gc-assertions + - TARGET: cygwin64 + CONF_OPTIONS: --enable-cplusplus + CFLAGS_EXTRA: -D GCTEST_PRINT_VERBOSE + - TARGET: cygwin64 + CFLAGS_EXTRA: -D GC_ALWAYS_MULTITHREADED -D LINT2 -D TEST_MANUAL_VDB + CONF_OPTIONS: --enable-cplusplus --enable-gc-assertions --disable-shared + - TARGET: cygwin64 + CONF_OPTIONS: --disable-threads + CFLAGS_EXTRA: -std=c11 -D USE_WINALLOC + - TARGET: mingw + CFLAGS_EXTRA: -Werror -Wall -Wextra -Wpedantic + - TARGET: mingw + CFLAGS_EXTRA: -Werror -Wall -Wextra -Wpedantic -D GC_THREADS -D THREAD_LOCAL_ALLOC -D PARALLEL_MARK -D GC_ASSERTIONS -D EMPTY_GETENV_RESULTS -D GC_GCJ_SUPPORT -D USE_MUNMAP -D LARGE_CONFIG -D NO_MSGBOX_ON_ERROR + - TARGET: mingw + CFLAGS_EXTRA: -Werror -Wall -Wextra -Wpedantic -O3 -march=native -D GC_THREADS -D GC_GCJ_SUPPORT -D GC_TIME_LIMIT=10 -D WINXP_USE_PERF_COUNTER -D NO_MSGBOX_ON_ERROR + - TARGET: mingw-shared-no-make + CFLAGS_EXTRA: -Werror -Wall -Wextra -Wpedantic -D GC_THREADS -D GC_ASSERTIONS -D ENABLE_DISCLAIM -D GC_GCJ_SUPPORT -D GC_PREFER_MPROTECT_VDB -D GC_CALL=__stdcall -D GC_CALLBACK=__fastcall -D CONSOLE_LOG -D NO_MSGBOX_ON_ERROR + - TARGET: mingw-w64 + CFLAGS_EXTRA: -Werror -Wall -Wextra -Wpedantic -D NO_MSGBOX_ON_ERROR + - TARGET: mingw-w64 + CFLAGS_EXTRA: -Werror -Wall -Wextra -Wpedantic -D GC_THREADS -D THREAD_LOCAL_ALLOC -D PARALLEL_MARK -D GC_ASSERTIONS -D GC_GCJ_SUPPORT -D NO_RETRY_GET_THREAD_CONTEXT + +clone_depth: 50 + +install: +- cmd: git clone --depth=50 https://github.com/ivmai/libatomic_ops.git -b release-7_6 + +build_script: +- cmd: if [%TARGET%]==[cmake] ( + cmake %CMAKE_OPTIONS% -Denable_werror=ON -DCFLAGS_EXTRA="%CFLAGS_EXTRA%" . && cmake --build . --config %CMAKE_CONFIG% ) +- cmd: if [%TARGET%]==[nmake] ( + "C:\Program Files\Microsoft SDKs\Windows\%MS_SDK_VER%\Bin\SetEnv.cmd" /%CPU% /%BLD% && nmake /f NT_MAKEFILE %MACRO_DEFS% ) +- cmd: if [%TARGET%]==[cygwin] ( + C:\cygwin\bin\bash -e -l -c + "cd /cygdrive/c/projects/bdwgc && ./autogen.sh && ./configure %CONF_OPTIONS% --enable-werror && cat include/config.h && make -j CFLAGS_EXTRA='%CFLAGS_EXTRA%'" ) +- cmd: if [%TARGET%]==[cygwin64] ( + C:\cygwin64\bin\bash -e -l -c + "cd /cygdrive/c/projects/bdwgc && ./autogen.sh && ./configure %CONF_OPTIONS% --enable-werror && cat include/config.h && make -j CFLAGS_EXTRA='%CFLAGS_EXTRA%'" ) +- cmd: if [%TARGET%]==[mingw] ( + C:\MinGW\msys\1.0\bin\bash -e -l -c + "cd /c/projects/bdwgc && make -j -f Makefile.direct CC=gcc CFLAGS_EXTRA='%CFLAGS_EXTRA%'" ) +- cmd: if [%TARGET%]==[mingw-shared-no-make] ( + C:\MinGW\msys\1.0\bin\bash -e -l -c + "cd /c/projects/bdwgc && gcc -I include -D GC_BUILTIN_ATOMIC -D GC_DLL %CFLAGS_EXTRA% -shared -o gc.dll extra/gc.c" ) +- cmd: if [%TARGET%]==[mingw-w64] ( + C:\msys64\usr\bin\bash -e -l -c + "cd /c/projects/bdwgc && make -j -f Makefile.direct CC=gcc CFLAGS_EXTRA='%CFLAGS_EXTRA%'" ) + +test_script: +- cmd: if [%TARGET%]==[cmake] ( ctest --build-config %CMAKE_CONFIG% -V ) +- cmd: if [%TARGET%]==[nmake] ( + "C:\Program Files\Microsoft SDKs\Windows\%MS_SDK_VER%\Bin\SetEnv.cmd" /%CPU% /%BLD% && nmake /f NT_MAKEFILE check %MACRO_DEFS% && nmake /f NT_MAKEFILE clean ) +- cmd: if [%TARGET%]==[cygwin] ( + C:\cygwin\bin\bash -e -l -c + "cd /cygdrive/c/projects/bdwgc && make -j check-without-test-driver CFLAGS_EXTRA='%CFLAGS_EXTRA%'" ) +- cmd: if [%TARGET%]==[cygwin64] ( + C:\cygwin64\bin\bash -e -l -c + "cd /cygdrive/c/projects/bdwgc && make -j check-without-test-driver CFLAGS_EXTRA='%CFLAGS_EXTRA%'" ) +- cmd: if [%TARGET%]==[mingw] ( + C:\MinGW\msys\1.0\bin\bash -e -l -c + "cd /c/projects/bdwgc && make -j -f Makefile.direct check CC=gcc CFLAGS_EXTRA='-DGC_NOT_DLL %CFLAGS_EXTRA%'" ) +- cmd: if [%TARGET%]==[mingw-shared-no-make] ( + C:\MinGW\msys\1.0\bin\bash -e -l -c + "cd /c/projects/bdwgc && gcc -I include -D GC_BUILTIN_ATOMIC -D GC_DLL %CFLAGS_EXTRA% -o gctest.exe tests/test.c gc.dll && gctest.exe" ) +- cmd: if [%TARGET%]==[mingw-w64] ( + C:\msys64\usr\bin\bash -e -l -c + "cd /c/projects/bdwgc && make -j -f Makefile.direct check CC=gcc CFLAGS_EXTRA='%CFLAGS_EXTRA%'" ) diff --git a/bdwgc/.gitattributes b/bdwgc/.gitattributes new file mode 100644 index 000000000..1c7bc7f50 --- /dev/null +++ b/bdwgc/.gitattributes @@ -0,0 +1,16 @@ +# Git repo attributes. + +# Ensure shell and configure-related files have LF enforced. +*.ac text eol=lf +*.am text eol=lf +*.m4 text eol=lf +*.sh text eol=lf + +# Ensure all text files have normalized line endings in the repository. +* text=auto + +# These files should use CR/LF line ending: +/digimars.mak -text + +# Note: "core.eol" configuration variable controls which line endings to use +# for the normalized files in the working directory (the default is native). diff --git a/bdwgc/.gitignore b/bdwgc/.gitignore new file mode 100644 index 000000000..a38ed0411 --- /dev/null +++ b/bdwgc/.gitignore @@ -0,0 +1,175 @@ +# Ignored files in bdwgc Git repo. + +# Binary files (in root dir, cord, tests): +*.dll +*.exe +*.gcda +*.gch +*.gcno +*.la +*.lib +*.lo +*.o +*.obj + +*.gc.log +.dirstamp +/*.a +/*_bench.log +/*_bench.trs +/*_test +/*test.log +/*test.trs +/.libs/ +/Makefile +/add_gc_prefix +/atomicopstest +/base_lib +/bdw-gc.pc +/c++ +/config.cache +/config.log +/config.status +/cord/cordtest +/cord/de +/cord/de_win.rbj +/cord/de_win.res +/cord/tests/de_win.rbj +/cord/tests/de_win.res +/cords +/cordtest +/core +/cpptest +/de +/de.dir/ +/disclaim_bench +/disclaimtest +/dont_ar_1 +/dont_ar_3 +/dont_ar_4 +/gc-* +/gc.log +/gcname +/gctest +/gctest_dyn_link +/gctest_irix_dyn_link +/hugetest +/if_mach +/if_not_there +/initfromthreadtest +/leaktest +/lib*.so +/libtool +/middletest +/realloctest +/smashtest +/staticrootstest +/subthreadcreatetest +/sunos5gc.so +/test-suite.log +/test_atomic_ops +/test_atomic_ops.log +/test_atomic_ops.trs +/test_cpp +/test_cpp.cpp +/test_cpp.log +/test_cpp.trs +/threadkeytest +/threadleaktest +/threadlibs +/tracetest +/weakmaptest + +/out/ + +# Config, dependency and stamp files generated by configure: +.deps/ +/include/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 +/aclocal.m4 +/autom4te.cache/ +/compile +/config.guess +/config.sub +/configure +/configure~ +/depcomp +/include/config.h.in +/install-sh +/ltmain.sh +/m4/libtool.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +/missing +/mkinstalldirs +/test-driver + +# These files are generated by CMake: +*.tlog +/*.vcxproj +/*.vcxproj.filters +/CMakeCache.txt +/CMakeFiles/ +/DartConfiguration.tcl +/Testing/Temporary/ +/cord/CMakeFiles/ +/cord/Makefile +/gc.sln +/tests/*.vcxproj +/tests/*.vcxproj.filters +/tests/*test +/tests/CMakeFiles/ +/tests/Makefile +/tests/test_cpp +CTestTestfile.cmake +cmake_install.cmake + +# Rarely generated files (mostly by some Win/DOS compilers): +/*.copied.c +/*.csm +/*.err +/*.i +/*.lb1 +/*.lnk +/*.map +/*.out +/*.rbj +/*.res +/*.stackdump +/*.sym +/*.tmp +*.bsc +*.dll.manifest +*.exp +*.idb +*.ilk +*.pdb +*.sbr +*.tds +gc.def + +# Stuff from VS build system and IDE +*.vcproj.*.user +.vs/ + +# Code analysis tools: +*.c.gcov +*.cc.gcov +*.h.gcov +*.sancov +/.sv*-dir +/cov-int +/coverage.info +/pvs-project.log +/pvs-project.tasks +/strace_out diff --git a/bdwgc/.gitrepo b/bdwgc/.gitrepo new file mode 100644 index 000000000..a44eceed9 --- /dev/null +++ b/bdwgc/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme +; +[subrepo] + remote = https://github.com/ivmai/bdwgc + branch = d1ff06cc503a74dca0150d5e988f2c93158b0cdf + commit = d1ff06cc503a74dca0150d5e988f2c93158b0cdf + parent = 5cf1ed4457f398b1d0c71d6b4b7b12599b685f86 + method = merge + cmdver = 0.4.9 diff --git a/bdwgc/.travis.yml b/bdwgc/.travis.yml new file mode 100644 index 000000000..39c686f8c --- /dev/null +++ b/bdwgc/.travis.yml @@ -0,0 +1,787 @@ +dist: xenial +language: cpp +os: linux + +jobs: + include: + - compiler: clang + env: + - CONF_OPTIONS="--enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CONF_OPTIONS="--enable-cplusplus" + - os: osx + env: + - CONF_OPTIONS="--enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - lcov + compiler: gcc + env: + - CONF_OPTIONS="--enable-gcov --enable-single-obj-compilation --enable-cplusplus --disable-shared --enable-gc-assertions" + - CFLAGS_EXTRA="-D USE_CUSTOM_SPECIFIC" + - CC_FOR_CHECK=g++ + - MAKEFILE_TARGETS="all" + - MAKEFILE_TARGETS_CHECK="check" + - NO_CLONE_LIBATOMIC_OPS=true + - REPORT_COVERAGE=true + - TESTS_CUSTOM_RUN=true + - env: + - MAKEFILE_TARGETS="dist" + - os: osx + env: + - CSA_CHECK=true + - CFLAGS_EXTRA="-m32" + - compiler: clang + language: c + env: + - CSA_CHECK=true + - CFLAGS_EXTRA="-D ALL_INTERIOR_POINTERS -D CHECKSUMS -D DBG_HDRS_ALL -D DEBUG_THREADS -D ENABLE_TRACE -D GC_ALWAYS_MULTITHREADED -D GC_ASSERTIONS -D GC_ATOMIC_UNCOLLECTABLE -D GC_ENABLE_SUSPEND_THREAD -D GC_GCJ_SUPPORT -D GC_PRINT_BACK_HEIGHT -D GC_THREADS -D HANDLE_FORK -D JAVA_FINALIZATION -D KEEP_BACK_PTRS -D MAKE_BACK_GRAPH -D PARALLEL_MARK -D PRINT_BLACK_LIST -D THREAD_LOCAL_ALLOC -D USE_MMAP -D USE_MUNMAP" + - env: + - CPPCHECK_ENABLE="--enable=unusedFunction -I libatomic_ops/src extra/gc.c tests/*.c" + - env: + - CPPCHECK_ENABLE="--enable=unusedFunction --force -D GC_BUILTIN_ATOMIC *.cc cord/*.c cord/tests/*.c tests/*.c tests/*.cc" + - CPPCHECK_OUT_FILTER="Z" + - NO_CLONE_LIBATOMIC_OPS=true + - env: + - CPPCHECK_ENABLE="-j4 --enable=information,performance,portability,style,warning --force -U GC_PRIVATE_H -I libatomic_ops/src *.c" + - env: + - CPPCHECK_ENABLE="-j4 --enable=information,performance,portability,style,warning --force -U GC_PRIVATE_H -I libatomic_ops/src *.cc cord/*.c cord/tests/*.c extra/AmigaOS.c extra/MacOS.c extra/msvc_dbg.c extra/symbian.cpp tests/*.c tests/*.cc tools/*.c" + - arch: arm64 + compiler: clang + - arch: arm64 + compiler: gcc + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: arm64 + compiler: clang-12 + dist: focal + env: + - CFLAGS_EXTRA="-O3" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --disable-shared" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-11 + sources: + - ubuntu-toolchain-r-test + arch: arm64 + compiler: gcc-11 + dist: bionic + env: + - CFLAGS_EXTRA="-O3 -march=native" + - CONF_OPTIONS="--enable-cplusplus --disable-gcj-support" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - musl-tools + arch: arm64 + compiler: musl-gcc + language: c + env: + - CFLAGS_EXTRA="-O3 -D SOFT_VDB" + - CONF_OPTIONS="--enable-gc-assertions" + - NO_CLONE_LIBATOMIC_OPS=true + - arch: arm64 + compiler: gcc + env: + - CONF_OPTIONS="--disable-threads" + - NO_CLONE_LIBATOMIC_OPS=true + - arch: arm64 + addons: + apt: + packages: + - libatomic-ops-dev + compiler: gcc + env: + - CONF_OPTIONS="--with-libatomic-ops=yes --enable-gc-assertions --enable-cplusplus --disable-munmap" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: arm64 + compiler: clang-12 + dist: focal + language: c + env: + - CFLAGS_EXTRA="-fsanitize=memory,undefined -fno-omit-frame-pointer" + - CONF_OPTIONS="--enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - TESTS_CUSTOM_RUN=true + - arch: arm64 + compiler: clang + env: + - CMAKE_CONFIG="Release" + - CMAKE_OPTIONS="-Dbuild_tests=ON -DBUILD_SHARED_LIBS=OFF -Denable_cplusplus=ON -Denable_gc_assertions=ON" + - NO_CLONE_LIBATOMIC_OPS=true + - arch: arm64 + compiler: gcc + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check cord/de" + - arch: ppc64le + compiler: clang + - arch: ppc64le + compiler: gcc + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: ppc64le + compiler: clang-12 + dist: focal + env: + - CFLAGS_EXTRA="-O3" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-11 + sources: + - ubuntu-toolchain-r-test + arch: ppc64le + compiler: gcc-11 + dist: bionic + env: + - CFLAGS_EXTRA="-O3 -D NO_MPROTECT_VDB" + - CONF_OPTIONS="--enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: ppc64le + compiler: clang-12 + dist: focal + language: c + env: + - CFLAGS_EXTRA="-fsanitize=memory,undefined -fno-omit-frame-pointer" + - CONF_OPTIONS="--disable-shared" + - NO_CLONE_LIBATOMIC_OPS=true + - TESTS_CUSTOM_RUN=true + - arch: ppc64le + compiler: clang + env: + - CMAKE_CONFIG="Release" + - CMAKE_OPTIONS="-Dbuild_tests=ON -Denable_cplusplus=ON -Denable_gc_assertions=ON" + - NO_CLONE_LIBATOMIC_OPS=true + - arch: ppc64le + compiler: clang + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check cord/de" + - arch: s390x + compiler: clang + - arch: s390x + compiler: gcc + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: s390x + compiler: clang-12 + dist: focal + env: + - CFLAGS_EXTRA="-O3 -D NO_RETRY_SIGNALS" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus" + - addons: + apt: + packages: + - gcc-10 + sources: + - ubuntu-toolchain-r-test + arch: s390x + compiler: gcc-10 + dist: focal + env: + - CFLAGS_EXTRA="-O3" + - CONF_OPTIONS="--enable-cplusplus --disable-shared" + - NO_CLONE_LIBATOMIC_OPS=true + - arch: s390x + compiler: gcc + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check cord/de" + - os: freebsd + compiler: gcc + env: + - CONF_OPTIONS="--enable-cplusplus --enable-gc-assertions" + - MAKE_NPROC=8 + - NO_CLONE_LIBATOMIC_OPS=true + - os: freebsd + env: + - CONF_OPTIONS="--enable-cplusplus" + - MAKE_NPROC=8 + - NO_CLONE_LIBATOMIC_OPS=true + - os: freebsd + env: + - CONF_OPTIONS="--enable-gc-assertions --disable-shared" + - MAKE_NPROC=8 + - NO_CLONE_LIBATOMIC_OPS=true + - os: freebsd + env: + - CMAKE_CONFIG="Release" + - CMAKE_OPTIONS="-Denable_cplusplus=ON" + - NO_CLONE_LIBATOMIC_OPS=true + - os: freebsd + compiler: clang + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check cord/de" + - MAKE_NPROC=8 + - addons: + apt: + packages: + - gcc-11 + sources: + - ubuntu-toolchain-r-test + compiler: gcc-11 + dist: bionic + env: + - CFLAGS_EXTRA="-O3 -march=native" + - CONF_OPTIONS="--enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-multilib + compiler: clang + env: + - CFLAGS_EXTRA="-m32" + - CONF_OPTIONS="--enable-gc-assertions" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-multilib + compiler: gcc + env: + - CFLAGS_EXTRA="-m32" + - CONF_OPTIONS="--enable-gc-assertions --enable-mmap" + - os: osx + env: + - CFLAGS_EXTRA="-m32" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CONF_OPTIONS="--disable-threads --enable-checksums --disable-munmap --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CFLAGS_EXTRA="-D DBG_HDRS_ALL -D SHORT_DBG_HDRS" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CFLAGS_EXTRA="-D DBG_HDRS_ALL -D SHORT_DBG_HDRS -D LINT2" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-handle-fork=manual --disable-throw-bad-alloc-library" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CFLAGS_EXTRA="-D DEBUG_ADD_DEL_ROOTS -D DEBUG_THREADS -D GC_DEBUG -D GC_LOG_TO_FILE_ALWAYS" + - CONF_OPTIONS="--enable-cplusplus" + - compiler: gcc + env: + - CFLAGS_EXTRA="-D BSD_TIME -D DEFAULT_STACK_MAYBE_SMALL -D ENABLE_TRACE -D EMPTY_GETENV_RESULTS -D GC_ALWAYS_MULTITHREADED -D GC_NETBSD_THREADS_WORKAROUND -D CPPCHECK" + - CONF_OPTIONS="--enable-cplusplus" + - compiler: clang + env: + - CFLAGS_EXTRA="-march=native -D _FORTIFY_SOURCE=2 -D DEFAULT_VDB -D TEST_WITH_SYSTEM_MALLOC" + - CONF_OPTIONS="--with-libatomic-ops=no --enable-cplusplus --disable-handle-fork" + - addons: + apt: + packages: + - libatomic-ops-dev + compiler: gcc + env: + - CONF_OPTIONS="--with-libatomic-ops=yes --enable-gc-assertions --enable-cplusplus --disable-throw-bad-alloc-library" + - CFLAGS_EXTRA="-D TEST_PAGES_EXECUTABLE" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CFLAGS_EXTRA="-march=native -D _FORTIFY_SOURCE=2 -D AO_DISABLE_GCC_ATOMICS" + - CONF_OPTIONS="--with-libatomic-ops=no --enable-munmap --enable-cplusplus --enable-static" + - compiler: gcc + env: + - CONF_CFLAGS="-D AO_USE_PTHREAD_DEFS" + - CONF_OPTIONS="--with-libatomic-ops=no --enable-gc-assertions --enable-cplusplus --enable-static" + - addons: + apt: + packages: + - libatomic-ops-dev + compiler: clang + env: + - CONF_CFLAGS="-D AO_USE_PTHREAD_DEFS" + - CONF_OPTIONS="--with-libatomic-ops=yes --enable-gc-assertions --enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CFLAGS_EXTRA="-D DONT_ADD_BYTE_AT_END -D GC_TIME_LIMIT=10" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus" + - addons: + apt: + packages: + - gcc-multilib + compiler: gcc + env: + - CFLAGS_EXTRA="-m32 -D MARK_BIT_PER_OBJ -D USE_CUSTOM_SPECIFIC" + - CONF_OPTIONS="--enable-gc-assertions" + - compiler: clang + env: + - CFLAGS_EXTRA="-D MARK_BIT_PER_OBJ" + - CONF_OPTIONS="--enable-cplusplus" + - compiler: gcc + env: + - CFLAGS_EXTRA="-D NO_CLOCK -D POINTER_MASK=~0xf" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-handle-fork=manual" + - compiler: gcc + env: + - CFLAGS_EXTRA="-D PROC_VDB -D GC_NO_SYS_FAULT_H -D NO_INCREMENTAL -D DEBUG_DIRTY_BITS" + - CONF_OPTIONS="--enable-cplusplus --disable-docs" + - compiler: clang + env: + - CFLAGS_EXTRA="-D TEST_MANUAL_VDB" + - CONF_OPTIONS="--enable-gc-assertions --disable-parallel-mark" + - compiler: gcc + env: + - CFLAGS_EXTRA="-D TEST_MANUAL_VDB" + - CONF_OPTIONS="--enable-gc-assertions --disable-munmap" + - compiler: gcc + env: + - CFLAGS_EXTRA="-D FIND_LEAK -D SKIP_LEAKED_OBJECTS_PRINTING" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus" + - compiler: gcc + env: + - CFLAGS_EXTRA="-D SMALL_CONFIG -D NO_GETENV" + - CONF_OPTIONS="--enable-cplusplus" + - compiler: gcc + env: + - CFLAGS_EXTRA="-std=c11 -D GC_NO_SIGSETJMP" + - CONF_OPTIONS="--disable-threads --enable-gc-assertions" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CONF_OPTIONS="--disable-thread-local-alloc --enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CONF_OPTIONS="--disable-parallel-mark --disable-thread-local-alloc --enable-gc-assertions --enable-cplusplus" + - compiler: gcc + env: + - CONF_OPTIONS="--enable-gc-debug --enable-cplusplus" + - compiler: gcc + env: + - CONF_OPTIONS="--disable-gc-debug --enable-cplusplus" + - compiler: clang + env: + - CONF_OPTIONS="--enable-large-config --enable-cplusplus --disable-shared --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CFLAGS_EXTRA="-D TEST_HANDLE_FORK" + - CONF_OPTIONS="--enable-cplusplus --disable-shared --enable-static" + - os: osx + env: + - CONF_OPTIONS="--enable-large-config --enable-cplusplus --disable-handle-fork" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-multilib + compiler: gcc + env: + - CONF_OPTIONS="--enable-large-config --disable-munmap" + - CFLAGS_EXTRA="-m32" + - compiler: gcc + env: + - CONF_OPTIONS="--enable-large-config --enable-cplusplus --enable-gc-assertions --enable-static" + - CFLAGS_EXTRA="-D LINT2 -D NO_VDB_FOR_STATIC_ROOTS" + - addons: + apt: + packages: + - gcc-multilib + compiler: clang + env: + - CONF_OPTIONS="--enable-redirect-malloc --enable-static --disable-threads" + - CFLAGS_EXTRA="-m32" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CONF_OPTIONS="--enable-redirect-malloc --enable-cplusplus --enable-static --disable-threads" + - CFLAGS_EXTRA="-m32" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CONF_OPTIONS="--enable-redirect-malloc --enable-gc-debug --enable-cplusplus --enable-gc-assertions" + - compiler: clang + env: + - CONF_OPTIONS="--disable-threads --enable-cplusplus" + - CFLAGS_EXTRA="-O3 -march=native" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CONF_OPTIONS="--disable-static --disable-threads --enable-cplusplus" + - CFLAGS_EXTRA="-O3 -march=native -D GC_PREFER_MPROTECT_VDB" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CONF_OPTIONS="--disable-threads --enable-cplusplus" + - CFLAGS_EXTRA="-O3 -march=native" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CMAKE_CONFIG="Debug" + - CMAKE_OPTIONS="-Denable_cplusplus=ON -Denable_gc_assertions=ON" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CMAKE_CONFIG="Release" + - CMAKE_OPTIONS="-Denable_cplusplus=ON -Denable_large_config=ON" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CMAKE_CONFIG="Debug" + - CMAKE_OPTIONS="-DBUILD_SHARED_LIBS=OFF -Denable_gc_debug=ON" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CMAKE_CONFIG="Release" + - CMAKE_OPTIONS="-DBUILD_SHARED_LIBS=OFF -Denable_threads=OFF" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CMAKE_CONFIG="Debug" + - CMAKE_OPTIONS="-Denable_cplusplus=ON" + - NO_CLONE_LIBATOMIC_OPS=true + - os: osx + env: + - CMAKE_CONFIG="Release" + - CMAKE_OPTIONS="-DBUILD_SHARED_LIBS=OFF -Denable_cplusplus=ON -Denable_gc_assertions=ON -Denable_large_config=ON" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check cord/de" + - compiler: gcc + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check cord/de" + - os: osx + env: + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="check cord/de" + - addons: + apt: + packages: + - musl-tools + compiler: musl-gcc + language: c + env: + - CONF_OPTIONS="--disable-parallel-mark --enable-gc-assertions" + - compiler: clang + env: + - CFLAGS_EXTRA="-fsanitize=address -fno-common -fno-omit-frame-pointer" + - CONF_OPTIONS="--enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - TESTS_CUSTOM_RUN=true + - addons: + apt: + packages: + - gcc-5 + sources: + - ubuntu-toolchain-r-test + compiler: gcc-5 + language: c + env: + - CFLAGS_EXTRA="-fsanitize=address -O0" + - CONF_OPTIONS="--enable-gc-assertions" + - LDFLAGS="-fuse-ld=gold" + - os: osx + env: + - CFLAGS_EXTRA="-fsanitize=address -m32 -fno-omit-frame-pointer" + - CONF_OPTIONS="--disable-shared --enable-cplusplus" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + language: c + env: + - CFLAGS_EXTRA="-fsanitize=memory -fno-omit-frame-pointer -std=gnu11" + - CONF_OPTIONS="--enable-static" + - TESTS_CUSTOM_RUN=true + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CFLAGS_EXTRA="-fsanitize=undefined -fno-common -fno-omit-frame-pointer" + - TESTS_CUSTOM_RUN=true + - CONF_OPTIONS="--enable-cplusplus --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CFLAGS_EXTRA="-fsanitize=thread -D NO_CANCEL_SAFE -D NO_INCREMENTAL -D USE_SPIN_LOCK -fno-omit-frame-pointer -D TEST_FORK_WITHOUT_ATFORK" + - CONF_OPTIONS="--enable-gc-assertions --enable-handle-fork=manual" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CFLAGS_EXTRA="-fsanitize=thread -D NO_CANCEL_SAFE -D NO_INCREMENTAL -D USE_SPIN_LOCK -fno-omit-frame-pointer -D TEST_FORK_WITHOUT_ATFORK" + - CONF_OPTIONS="--enable-gc-assertions --enable-gc-debug --enable-handle-fork=manual --enable-large-config --with-libatomic-ops=no" + - compiler: clang + env: + - CFLAGS_EXTRA="-fsanitize=thread -D NO_CANCEL_SAFE -D NO_INCREMENTAL -D USE_SPIN_LOCK -fno-omit-frame-pointer -D NTHREADS=15" + - CONF_OPTIONS="--disable-parallel-mark" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CFLAGS_EXTRA="-fsanitize=thread -D NO_CANCEL_SAFE -D NO_INCREMENTAL -fno-omit-frame-pointer" + - CONF_OPTIONS="--disable-parallel-mark --disable-thread-local-alloc --disable-shared --enable-gc-assertions --with-libatomic-ops=no" + - compiler: clang + env: + - CFLAGS_EXTRA="-O3 -march=native" + - CONF_OPTIONS="--enable-cplusplus --enable-static --enable-single-obj-compilation" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-multilib + compiler: clang + env: + - CFLAGS_EXTRA="-m32 -D _FORTIFY_SOURCE=2 -D GC_DISABLE_INCREMENTAL -std=gnu11" + - CONF_OPTIONS="--enable-gc-assertions --enable-gc-debug" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-8 + - g++-8 + sources: + - ubuntu-toolchain-r-test + compiler: gcc-8 + language: c + env: + - CXX=g++-8 + - CONF_OPTIONS="--enable-cplusplus --enable-gc-assertions --enable-static" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-8 + - gcc-8-multilib + - gcc-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-8 + language: c + env: + - CFLAGS_EXTRA="-m32 -O3 -std=gnu11" + - CONF_OPTIONS="--disable-shared --enable-static --enable-single-obj-compilation" + - NO_CLONE_LIBATOMIC_OPS=true + - addons: + apt: + packages: + - gcc-8 + - gcc-8-multilib + - gcc-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-8 + language: c + env: + - CFLAGS_EXTRA="-mx32 -march=native -D _FORTIFY_SOURCE=2" + - CONF_OPTIONS="--enable-large-config --enable-gc-assertions" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CFLAGS_EXTRA="-x c++" + - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-gc-debug --disable-shared" + - MAKEFILE_TARGETS="all" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: gcc + env: + - CC_FOR_CHECK=g++ + - CONF_OPTIONS="--enable-gc-assertions" + - MAKEFILE_TARGETS="all" + - MAKEFILE_TARGETS_CHECK="check" + - NO_CLONE_LIBATOMIC_OPS=true + - compiler: clang + env: + - CFLAGS_EXTRA="-O3 -Wall -Wextra -Werror -x c++" + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="cords" + - compiler: gcc + env: + - CC_FOR_CHECK=g++ + - CFLAGS_EXTRA="-O3 -Wall -Wextra -Werror -D TEST_MANUAL_VDB" + - MAKEFILE_NAME=Makefile.direct + - MAKEFILE_TARGETS="cords" + - MAKEFILE_TARGETS_CHECK="cord/de check" + - addons: + apt: + packages: + - g++-mingw-w64 + - gcc-mingw-w64 + compiler: x86_64-w64-mingw32-gcc + language: c + env: + - CXX=x86_64-w64-mingw32-g++ + - CONF_OPTIONS="--host=x86_64-w64-mingw32 --enable-cplusplus --enable-static" + - MAKEFILE_TARGETS="all" + - addons: + apt: + packages: + - gcc-mingw-w64 + compiler: i686-w64-mingw32-gcc + language: c + env: + - CONF_OPTIONS="--host=i686-w64-mingw32" + - MAKEFILE_TARGETS="all" + - CFLAGS_EXTRA="-fno-omit-frame-pointer" + - dist: focal + env: + - MAKEFILE_TARGETS="distcheck" + - AUTOCONF_VER=2.71 + - AUTOMAKE_VER=1.16.5 + - LIBTOOL_VER=2.4.7 + - M4_VER=1.4.19 + - NO_CLONE_LIBATOMIC_OPS=true + +before_install: +- if [[ "$CPPCHECK_ENABLE" != "" ]]; then + CPPCHECK_VER=2.4.1; + git clone --depth=3 https://github.com/danmar/cppcheck.git + ~/cppcheck -b $CPPCHECK_VER; + make --directory ~/cppcheck -j8 CXXFLAGS="-O3 -march=native -D NDEBUG"; + fi +- if [[ "$AUTOCONF_VER" != "" || "$AUTOMAKE_VER" != "" + || "$LIBTOOL_VER" != "" || "$M4_VER" != "" ]]; then + GNUTOOLS_ROOT=`pwd`/../gnu-tools; + export PATH=$GNUTOOLS_ROOT/bin:$PATH; + GNU_DOWNLOAD_SITE=https://ftp.gnu.org/gnu; + fi +- if [[ "$M4_VER" != "" ]]; then + M4_XZ_URL=$GNU_DOWNLOAD_SITE/m4/m4-$M4_VER.tar.xz; + wget -O - $M4_XZ_URL | tar xf - --xz --directory ~; + (cd ~/m4-$M4_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$LIBTOOL_VER" != "" ]]; then + LIBTOOL_XZ_URL=$GNU_DOWNLOAD_SITE/libtool/libtool-$LIBTOOL_VER.tar.xz; + wget -O - $LIBTOOL_XZ_URL | tar xf - --xz --directory ~; + (cd ~/libtool-$LIBTOOL_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$AUTOCONF_VER" != "" ]]; then + AUTOCONF_XZ_URL=$GNU_DOWNLOAD_SITE/autoconf/autoconf-$AUTOCONF_VER.tar.xz; + wget -O - $AUTOCONF_XZ_URL | tar xf - --xz --directory ~; + (cd ~/autoconf-$AUTOCONF_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$AUTOMAKE_VER" != "" ]]; then + AUTOMAKE_XZ_URL=$GNU_DOWNLOAD_SITE/automake/automake-$AUTOMAKE_VER.tar.xz; + wget -O - $AUTOMAKE_XZ_URL | tar xf - --xz --directory ~; + (cd ~/automake-$AUTOMAKE_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$MAKEFILE_TARGETS" == *"dist"* ]]; then + autoconf --version; + automake --version; + m4 --version; + libtool --version || true; + fi +- if [[ "$CONF_CFLAGS" == "" ]]; then CONF_CFLAGS="-g -O2"; fi +- if [[ "$MAKEFILE_NAME" == "" ]]; then MAKEFILE_NAME=Makefile; fi +- if [[ "$MAKEFILE_TARGETS" == "" ]]; then MAKEFILE_TARGETS="check"; fi + +install: +- if [[ "$NO_CLONE_LIBATOMIC_OPS" != true ]]; then + git clone --depth=50 https://github.com/ivmai/libatomic_ops.git + -b release-7_6; + fi +- if [[ "$CMAKE_CONFIG" == "" ]]; then + ./autogen.sh; + fi +- if [[ "$GNUTOOLS_ROOT" != "" ]]; then mv $GNUTOOLS_ROOT $GNUTOOLS_ROOT-x; fi +- if [[ "$REPORT_COVERAGE" == true ]]; then gem install coveralls-lcov; fi + +script: +- if [[ "$CSA_CHECK" != true && "$CMAKE_CONFIG" == "" + && "$CPPCHECK_ENABLE" == "" && "$MAKEFILE_NAME" != "Makefile.direct" + && "$COVERITY_SCAN_BRANCH" != 1 ]]; then + CFLAGS="$CONF_CFLAGS" ./configure $CONF_OPTIONS --enable-werror && + cat include/config.h; + fi +- if [[ "$CSA_CHECK" != true && "$CMAKE_CONFIG" == "" + && "$CPPCHECK_ENABLE" == "" && "$COVERITY_SCAN_BRANCH" != 1 ]]; then + make -j$MAKE_NPROC -f $MAKEFILE_NAME $MAKEFILE_TARGETS + CFLAGS_EXTRA="$CFLAGS_EXTRA" LDFLAGS="$LDFLAGS"; + fi +- if [[ "$CMAKE_CONFIG" != "" ]]; then + cmake $CMAKE_OPTIONS -Dbuild_tests=ON -Denable_werror=ON + -Werror=deprecated . && + cmake --build . --config $CMAKE_CONFIG; + fi +- if [[ "$CMAKE_CONFIG" != "" ]]; then + ctest --build-config $CMAKE_CONFIG -V; + fi +- if [[ "$CC_FOR_CHECK" != "" ]]; then + make -f $MAKEFILE_NAME $MAKEFILE_TARGETS_CHECK CC=$CC_FOR_CHECK + CFLAGS_EXTRA="$CFLAGS_EXTRA"; + fi +- if [ -f gctest.log ]; then cat gctest.log; fi +- if [ -f disclaim_bench.log ]; then cat disclaim_bench.log; fi +- if [ -f disclaim_test.log ]; then cat disclaim_test.log; fi +- if [ -f disclaim_weakmap_test.log ]; then cat disclaim_weakmap_test.log; fi +- if [ -f threadkey_test.log ]; then cat threadkey_test.log; fi +- if [ -f threadleaktest.log ]; then cat threadleaktest.log; fi +- if [[ "$CSA_CHECK" == true ]]; then + ${CC} --analyze -Xanalyzer -analyzer-output=text -Werror + -I include -I libatomic_ops/src $CFLAGS_EXTRA + *.c *.cc cord/*.c cord/tests/cordtest.c cord/tests/de.c extra/gc.c + extra/msvc_dbg.c extra/pcr_interface.c extra/real_malloc.c + tests/*.c tests/*.cc tools/*.c; + fi +- if [[ "$CPPCHECK_ENABLE" != "" ]]; then + if [[ "$CPPCHECK_OUT_FILTER" == "" ]]; then CPPCHECK_OUT_FILTER="c "; fi; + set -o pipefail; ~/cppcheck/cppcheck --error-exitcode=2 + -U GC_API -D CPPCHECK -I include $CPPCHECK_ENABLE | + grep --line-buffered "$CPPCHECK_OUT_FILTER"; + fi +- if [[ "$TESTS_CUSTOM_RUN" == true ]]; then + ASAN_OPTIONS="detect_leaks=1" UBSAN_OPTIONS="halt_on_error=1" + make check-without-test-driver; + fi + +after_success: +- if [[ "$REPORT_COVERAGE" == true ]]; then + lcov --capture --base-directory . --directory . --output-file coverage.info; + lcov --remove coverage.info '/usr/*' 'cord/tests/*' 'libatomic_ops/*' 'tests/*' --output-file coverage.info; + lcov --list coverage.info; + coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info; + fi + +deploy: + provider: releases + edge: true + file: gc-*.tar.gz + file_glob: true + on: + condition: $MAKEFILE_TARGETS = distcheck + repo: ivmai/bdwgc + tags: true diff --git a/bdwgc/AUTHORS b/bdwgc/AUTHORS new file mode 100644 index 000000000..35ca61cda --- /dev/null +++ b/bdwgc/AUTHORS @@ -0,0 +1,462 @@ +This is an attempt to acknowledge contributions to the garbage collector. +Early contributions also mentioned (duplicated) in ChangeLog file; details of +later ones should be in "git log". + +Currently maintained by Ivan Maidanski. + +HISTORY - + + Early versions of this collector were developed as a part of research +projects supported in part by the National Science Foundation +and the Defense Advance Research Projects Agency. + +The garbage collector originated as part of the run-time system for +the Russell programming language implementation. The first version of the +garbage collector was written primarily by Alan Demers. It was then refined +and mostly rewritten, primarily by Hans-J. Boehm, at Cornell U., +the University of Washington, Rice University (where it was first used for +C and assembly code), Xerox PARC, SGI, and HP Labs. However, significant +contributions have also been made by many others. + +Other contributors (my apologies for any omissions): + +Adam Megacz +Adnan Ali +Adrian Bunk +Adrian Pop +Akira Tagoh +Alain Novak +Alan Dosser +Alan J. Demers +Alaskan Emily +Aleksey Demakov +Alessandro Bruni +Alex Ronne Petersen +Alexander Belchenko +Alexander Gavrilov +Alexander Herz +Alexandr Petrosian +Alexandr Shadchin +Alexandre Oliva +Alexis Laferriere +Alistair G. Crooks +Allan Hsu +Amaury Sechet +Andre Leiradella +Andreas Jaeger +Andreas Tobler +Andrei Polushin +Andrej Cedilnik +Andrew Begel +Andrew Buss +Andrew Gray +Andrew Haley +Andrew Horton +Andrew McKinlay +Andrew Pinski +Andrew Stitcher +Andrew Stone +Andrew Whatson +Andy Li +Andy Wingo +Anselm Baird-Smith +Anthony Green +Antoine de Maricourt +Ari Huttunen +Arrigo Triulzi +Ashley Bone +Assar Westerlund +Aurelien Larcher +Barry DeFreese +Baruch Siach +Ben A. Mesander +Ben Cottrell +Ben Hutchings +Ben Maurer +Benjamin Lerman +Bernd Edlinger +Bernd Kuhls +Bernie Solomon +Bill Janssen +Bo Thorsen +Bradley D. LaRonde +Bradley Smith +Brent Benson +Brian Alliet +Brian Beuning +Brian Burton +Brian D. Carlstrom +Brian F. Dennis +Brian J. Cardiff +Brian Lewis +Bruce A Henderson +Bruce Hoult +Bruce Mitchener +Bruno Haible +Bryce McKinlay +Burkhard Linke +Calvin Buckley +Carlos J. Puga Medina +Cesar Eduardo Barros +Charles Fiterman +Charles Mills +Chris Dodd +Chris Lingard +Chris Metcalf +Christian Joensson +Christian Limpach +Christian Thalinger +Christian Weisgerber +Christoffe Raffali +Clay Spence +Clement Chigot +Colin LeMahieu +Craig McDaniel +Dai Sato +Dan Bonachea +Dan Fandrich +Dan Sullivan +Daniel R. Grayson +Danny Smith +Darrell Schiebel +Dave Barrett +Dave Detlefs +Dave Korn +Dave Love +David Ayers +David Brownlee +David Butenhof +David Chase +David Daney +David Grove +David Leonard +David Miller +David Mosberger +David Peroutka +David Pickens +David Stes +David Terei +David Van Horn +Davide Angelocola +Davide Beatrici +Demyan Kimitsa +Dick Porter +Dietmar Planitzer +Dima Pasechnik +Dimitris Apostolou +Dimitris Vyzovitis +Dimitry Andric +Djamel Magri +Doug Kaufman +Doug Moen +Douglas Steel +Eli Barzilay +Elijah Taylor +Elvenlord Elrond +Emmanual Stumpf +Eric Benson +Eric Holk +Erik M. Bray +Fabian Thylman +Fabrice Fontaine +Fergus Henderson +Franklin Chen +Fred Gilham +Fred Stearns +Friedrich Dominicus +Gabor Drescher +Gary Leavens +Geoff Norton +George Koehler +George Talbot +Gerard A Allan +Glauco Masotti +Grzegorz Jakacki +Gustavo Giraldez +Gustavo Rodriguez-Rivera +H.J. Lu +Hamayama +Hannes Mehnert +Hanno Boeck +Hans Boehm +Hans-Peter Nilsson +Henning Makholm +Henrik Theiling +Hironori Sakamoto +Hiroshi Kawashima +Hiroshi Yokota +Hubert Garavel +Iain Sandoe +Ian Piumarta +Ian Searle +Igor Khavkine +Ilya Kurdyukov +Ivan Demakov +Ivan Maidanski +Ivan R +Jaap Boender +Jack Andrews +Jacob Navia +Jakub Jelinek +Jakub Wojciech +James Clark +James Dominy +James Moran +Jan Alexander Steffens +Jan Wielemaker +Jani Kajala +Jared McNeill +Jasper Lievisse Adriaanse +Jay Krell +Jean-Baptiste Nivois +Jean-Claude Beaudoin +Jean-Daniel Fekete +Jeff Sturm +Jeffrey Hsu +Jeffrey Mark Siskind +Jeremy Fitzhardinge +Jesper Peterson +Jesse Hull +Jesse Jones +Jesse Rosenstock +Ji-Yong Chung +Jie Liu +Jim Marshall +Jim Meyering +Joao Abecasis +Joerg Sonnenberger +Johannes Schmidt +Johannes Totz +John Bowman +John Clements +John David Anglin +John Ellis +John Ericson +John Merryweather Cooper +Jon Moore +Jonas Echterhoff +Jonas Hahnfeld +Jonathan Bachrach +Jonathan Chambers +Jonathan Clark +Jonathan Pryor +Josh Peterson +Joshua Richardson +Juan Jose Garcia-Ripoll +Jukka Jylanki +Kai Tietz +Kaz Kojima +Kazu Hirata +Kazuhiro Inaoka +Keith Seitz +Kenjiro Taura +Kenneth Schalk +Kevin Kenny +Kevin Tew +Kevin Warne +Kimura Wataru +Kjetil Matheussen +Klaus Treichel +Klemens Zwischenbrugger +Knut Tvedten +Krister Walfridsson +Kristian Kristensen +Kristian Larsson +Kumar Srikantan +Kurt Miller +Lars Farm +Laurent Morichetti +Leonardo Taccari +Linas Vepstas +Loren J. Rittle +Louis Zhuang +Lucas Holt +Lucas Meijer +Ludovic Courtes +Maarten Thibaut +Mahder Gebremedhin +Manuel A. Fernandez Montecelo +Manuel Serrano +Marc Recht +Marco Maggi +Marcos Dione +Marcus Herbert +Marek Vasut +Margaret Fleck +Mark Boulter +Mark Mitchell +Mark Reichert +Mark Sibly +Mark Weiser +Martin Hirzel +Martin Koeppe +Martin Tauchmann +Massimiliano Gubinelli +Matheus Rambo +Matt Austern +Matthew Flatt +Matthias Andree +Matthias Drochner +Matthieu Herrb +Maurizio Vairani +Max Mouratov +Maximilian Downey Twiss +Maya Rashish +Melissa O'Neill +Michael Arnoldus +Michael DeRoy +Michael Fox +Michael Herring +Michael Smith +Michael Spertus +Michel Schinz +Miguel de Icaza +Mikael Djurfeldt +Mike Frysinger +Mike Gran +Mike McGaughey +Mike Stump +Mitch Harris +Mohan Embar +Naoyuki Sawa +Nathanael Nerode +Neale Ferguson +Neil Sharman +Nguyen Thai Ngoc Duy +Nicolas Cannasse +Niibe Yutaka +Nikita Ermakov +Niklas Therning +Noah Lavine +Nobuyuki Hikichi +Oliver Kurth +Ondrej Bilka +Paolo Molaro +Parag Patel +Patrick Bridges +Patrick C. Beard +Patrick Doyle +Paul Bone +Paul Brook +Paul Graham +Paul Nash +Per Bothner +Peter Bigot +Peter Budai +Peter Chubb +Peter Colson +Peter Housel +Peter Monks +Peter Ross +Peter Seebach +Peter Wang +Petr Krajca +Petr Salinger +Petter Urkedal +Philip Brown +Philipp Tomsich +Philippe Queinnec +Phillip Musumeci +Phong Vo +Pierre de Rop +Pontus Rydin +Radek Polak +Rainer Orth +Ranjit Mathew +Rauli Ruohonen +Regis Cridlig +Reimer Behrends +Renaud Blanch +Rene Girard +Rex Dieter +Reza Shahidi +Richard Earnshaw +Richard Henderson +Richard Sandiford +Richard Zidlicky +Rob Haack +Robert Brazile +Rodrigo Kumpera +Roger Sayle +Roland McGrath +Roman Hodek +Romain Naour +Romano Paolo Tenca +Rutger Ovidius +Ryan Gonzalez +Ryan Murray +Salvador Eduardo Tropea +Sam James +Samuel Martin +Samuel Thibault +Scott Ananian +Scott Ferguson +Scott Schwartz +Shawn Wagner +Shea Levy +Shiro Kawai +Simon Gornall +Simon Kainz +Simon Posnjak +Slava Sysoltsev +Sorawee Porncharoenwase +ssrlive +Stefan Ring +Stefano Rivera +Steve Youngs +Sugioka Toshinobu +Suzuki Toshiya +Sven Hartrumpf +Sven Verdoolaege +Takis Psarogiannakopoulos +Tatsuya Bizenn +Tautvydas Zilys +Terrell Russell +Thiemo Seufer +Thomas Funke +Thomas Klausner +Thomas Linder Puls +Thomas Maier +Thomas Schwinge +Thomas Thiriez +Thorsten Glaser +Tilman Vogel +Tim Bingham +Tim Cannell +Tim Gates +Timothy N. Newsham +Tom Tromey +Tommaso Tagliapietra +Toralf Foerster +Toshio Endo +Tsugutomo Enami +Tum Nguyen +Tyson Dowd +Uchiyama Yasushi +Ulrich Drepper +Ulrich Weigand +Uros Bizjak +Vernon Lee +Victor Ivrii +Victor Romero +Vineet Gupta +Vitaly Magerya +Vladimir Tsichevski +Walter Bright +Walter Underwood +Wilson Ho +Wink Saville +Wookey +Xi Wang +Xiaokun Zhu +Yann Dirson +Yannis Bres +Yasuhiro Kimura +Yuki Okumura +Yusuke Suzuki +Yvan Roux +Zach Saw +Zhang Na +Zhiying Chen +Zhong Shao +Zoltan Varga diff --git a/bdwgc/CMakeLists.txt b/bdwgc/CMakeLists.txt new file mode 100644 index 000000000..ddb04eece --- /dev/null +++ b/bdwgc/CMakeLists.txt @@ -0,0 +1,842 @@ +# +# Copyright (c) 1994 by Xerox Corporation. All rights reserved. +# Copyright (c) 1996 by Silicon Graphics. All rights reserved. +# Copyright (c) 1998 by Fergus Henderson. All rights reserved. +# Copyright (c) 2000-2010 by Hewlett-Packard Company. All rights reserved. +# Copyright (c) 2010-2021 Ivan Maidanski +## +# 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. +## + +# +# get cmake and run: +# cmake -G "Visual Studio 8 2005" +# in the same dir as this file +# this will generate gc.sln +# + +cmake_minimum_required(VERSION 3.1) + +set(PACKAGE_VERSION 8.2.4) +# Version must match that in AC_INIT of configure.ac and that in README. +# Version must conform to: [0-9]+[.][0-9]+[.][0-9]+ + +# Info (current:revision:age) for the Libtool versioning system. +# These values should match those in cord/cord.am and Makefile.am. +set(LIBCORD_VER_INFO 6:0:5) +set(LIBGC_VER_INFO 6:2:5) +set(LIBGCCPP_VER_INFO 6:0:5) + +option(enable_cplusplus "C++ support" OFF) +if (enable_cplusplus) + project(gc) +else() + project(gc C) +endif() + +if (POLICY CMP0057) + # Required for CheckLinkerFlag, at least. + cmake_policy(SET CMP0057 NEW) +endif() + +include(CheckCCompilerFlag) +include(CheckCSourceCompiles) +include(CheckFunctionExists) +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CMakePackageConfigHelpers) +include(CTest) +include(GNUInstallDirs) + +if (NOT (${CMAKE_VERSION} VERSION_LESS "3.18.0")) + include(CheckLinkerFlag) +endif() + +# Customize the build by passing "-D=ON|OFF" in the command line. +option(BUILD_SHARED_LIBS "Build shared libraries" ON) +option(build_cord "Build cord library" ON) +option(build_tests "Build tests" OFF) +option(enable_docs "Build and install documentation" ON) +option(enable_threads "Support threads" ON) +option(enable_parallel_mark "Parallelize marking and free list construction" ON) +option(enable_thread_local_alloc "Turn on thread-local allocation optimization" ON) +option(enable_threads_discovery "Enable threads discovery in GC" ON) +option(enable_throw_bad_alloc_library "Turn on C++ gctba library build" ON) +option(enable_gcj_support "Support for gcj" ON) +option(enable_sigrt_signals "Use SIGRTMIN-based signals for thread suspend/resume" OFF) +option(enable_gc_debug "Support for pointer back-tracing" OFF) +option(disable_gc_debug "Disable debugging like GC_dump and its callees" OFF) +option(enable_java_finalization "Support for java finalization" ON) +option(enable_atomic_uncollectable "Support for atomic uncollectible allocation" ON) +option(enable_redirect_malloc "Redirect malloc and friends to GC routines" OFF) +option(enable_disclaim "Support alternative finalization interface" ON) +option(enable_large_config "Optimize for large heap or root set" OFF) +option(enable_gc_assertions "Enable collector-internal assertion checking" OFF) +option(enable_mmap "Use mmap instead of sbrk to expand the heap" OFF) +option(enable_munmap "Return page to the OS if empty for N collections" ON) +option(enable_dynamic_loading "Enable tracing of dynamic library data roots" ON) +option(enable_register_main_static_data "Perform the initial guess of data root sets" ON) +option(enable_checksums "Report erroneously cleared dirty bits" OFF) +option(enable_werror "Pass -Werror to the C compiler (treat warnings as errors)" OFF) +option(enable_single_obj_compilation "Compile all libgc source files into single .o" OFF) +option(enable_handle_fork "Attempt to ensure a usable collector after fork()" ON) +option(disable_handle_fork "Prohibit installation of pthread_atfork() handlers" OFF) +option(enable_emscripten_asyncify "Use Emscripten asyncify feature" OFF) +option(install_headers "Install header and pkg-config metadata files" ON) +option(without_libatomic_ops "Use atomic_ops.h in libatomic_ops/src" OFF) + +# Override the default build type to RelWithDebInfo (this instructs cmake to +# pass -O2 -g -DNDEBUG options to the compiler by default). +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE + STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY + STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel") +endif() + +# Convert VER_INFO values to [SO]VERSION ones. +if (BUILD_SHARED_LIBS) + # cord: + string(REGEX REPLACE "(.+):.+:.+" "\\1" cord_cur ${LIBCORD_VER_INFO}) + string(REGEX REPLACE ".+:(.+):.+" "\\1" cord_rev ${LIBCORD_VER_INFO}) + string(REGEX REPLACE ".+:.+:(.+)$" "\\1" cord_age ${LIBCORD_VER_INFO}) + math(EXPR CORD_SOVERSION "${cord_cur} - ${cord_age}") + set(CORD_VERSION_PROP "${CORD_SOVERSION}.${cord_age}.${cord_rev}") + message(STATUS "CORD_VERSION_PROP = ${CORD_VERSION_PROP}") + # gc: + string(REGEX REPLACE "(.+):.+:.+" "\\1" gc_cur ${LIBGC_VER_INFO}) + string(REGEX REPLACE ".+:(.+):.+" "\\1" gc_rev ${LIBGC_VER_INFO}) + string(REGEX REPLACE ".+:.+:(.+)$" "\\1" gc_age ${LIBGC_VER_INFO}) + math(EXPR GC_SOVERSION "${gc_cur} - ${gc_age}") + set(GC_VERSION_PROP "${GC_SOVERSION}.${gc_age}.${gc_rev}") + message(STATUS "GC_VERSION_PROP = ${GC_VERSION_PROP}") + # gccpp and gctba: + string(REGEX REPLACE "(.+):.+:.+" "\\1" gccpp_cur ${LIBGCCPP_VER_INFO}) + string(REGEX REPLACE ".+:(.+):.+" "\\1" gccpp_rev ${LIBGCCPP_VER_INFO}) + string(REGEX REPLACE ".+:.+:(.+)$" "\\1" gccpp_age ${LIBGCCPP_VER_INFO}) + math(EXPR GCCPP_SOVERSION "${gccpp_cur} - ${gccpp_age}") + set(GCCPP_VERSION_PROP "${GCCPP_SOVERSION}.${gccpp_age}.${gccpp_rev}") + message(STATUS "GCCPP_VERSION_PROP = ${GCCPP_VERSION_PROP}") +endif(BUILD_SHARED_LIBS) + +add_definitions("-DALL_INTERIOR_POINTERS -DNO_EXECUTE_PERMISSION") + +# Set struct packing alignment to word (instead of 1-byte). +if (BORLAND) + add_compile_options(/a4) +elseif (WATCOM) + add_compile_options(/zp4) +endif() + +# Output all warnings. +if (BORLAND) + # All warnings except for particular ones. + add_compile_options(/w /w-pro /w-aus /w-par /w-ccc /w-inl /w-rch) +elseif (MSVC) + # All warnings but ignoring "unreferenced formal parameter" and + # "conditional expression is constant" ones. + add_compile_options(/W4 /wd4100 /wd4127) + # Disable crt security warnings, since unfortunately they warn about all + # sorts of safe uses of strncpy. + add_definitions("-D_CRT_SECURE_NO_DEPRECATE") +elseif (WATCOM) + add_compile_options(/wx) + if (NOT enable_threads) + # Suppress "unreachable code" warning in GC_ASSERT(I_HOLD_LOCK()). + add_compile_options(/wcd=201) + endif() +else() + # TODO add -[W]pedantic -Wno-long-long + add_compile_options(-Wall -Wextra) +endif() + +include_directories(include) + +set(SRC alloc.c reclaim.c allchblk.c misc.c mach_dep.c os_dep.c + mark_rts.c headers.c mark.c obj_map.c blacklst.c finalize.c + new_hblk.c dbg_mlc.c malloc.c dyn_load.c typd_mlc.c ptr_chck.c + mallocx.c) +set(THREADDLLIBS_LIST) +set(NEED_LIB_RT) + +set(_HOST ${CMAKE_SYSTEM_PROCESSOR}-unknown-${CMAKE_SYSTEM}) +string(TOLOWER ${_HOST} HOST) +message(STATUS "TARGET = ${HOST}") + +if (enable_threads) + find_package(Threads REQUIRED) + message(STATUS "Thread library: ${CMAKE_THREAD_LIBS_INIT}") + if (without_libatomic_ops OR BORLAND OR MSVC OR WATCOM) + include_directories(libatomic_ops/src) + # Note: alternatively, use CFLAGS_EXTRA to pass -I<...>/libatomic_ops/src. + else() + # Assume the compiler supports GCC atomic intrinsics. + add_definitions("-DGC_BUILTIN_ATOMIC") + endif() + include_directories(${Threads_INCLUDE_DIR}) + set(THREADDLLIBS_LIST ${CMAKE_THREAD_LIBS_INIT}) + if (${CMAKE_DL_LIBS} MATCHES ^[^-].*) + # Some cmake versions have a broken non-empty CMAKE_DL_LIBS omitting "-l". + # Assume CMAKE_DL_LIBS contains just one library. + set(THREADDLLIBS_LIST ${THREADDLLIBS_LIST} -l${CMAKE_DL_LIBS}) + else() + set(THREADDLLIBS_LIST ${THREADDLLIBS_LIST} ${CMAKE_DL_LIBS}) + endif() +endif(enable_threads) + +set(ATOMIC_OPS_LIBS "") # TODO: Assume libatomic_ops library is not needed. + +# Thread support detection. +if (CMAKE_USE_PTHREADS_INIT) + set(SRC ${SRC} gc_dlopen.c) + if (CYGWIN OR MSYS) + set(SRC ${SRC} win32_threads.c) + else() + set(SRC ${SRC} pthread_start.c pthread_stop_world.c pthread_support.c) + endif() + if (HOST MATCHES .*-.*-hpux10.*) + message(FATAL_ERROR "HP/UX 10 POSIX threads are not supported.") + endif() + # Common defines for POSIX platforms. + add_definitions("-DGC_THREADS -D_REENTRANT") + if (enable_parallel_mark) + add_definitions("-DPARALLEL_MARK") + endif() + if (enable_thread_local_alloc) + add_definitions("-DTHREAD_LOCAL_ALLOC") + set(SRC ${SRC} specific.c thread_local_alloc.c) + endif() + message("Explicit GC_INIT() calls may be required.") + if (HOST MATCHES .*-.*-hpux11.*) + message("Only HP/UX 11 POSIX threads are supported.") + add_definitions("-D_POSIX_C_SOURCE=199506L") + set(NEED_LIB_RT ON) + elseif (HOST MATCHES .*-.*-netbsd.*) + message("Only on NetBSD 2.0 or later.") + add_definitions("-D_PTHREADS") + set(NEED_LIB_RT ON) + endif() + if (MSYS) + # Does not provide process fork functionality. + elseif (APPLE) + # The incremental mode conflicts with fork handling (for now). + # Thus, HANDLE_FORK is not defined. + set(SRC ${SRC} darwin_stop_world.c) + elseif (enable_handle_fork AND NOT disable_handle_fork) + add_definitions("-DHANDLE_FORK") + endif() + if (enable_sigrt_signals) + add_definitions("-DGC_USESIGRT_SIGNALS") + endif() +elseif (CMAKE_USE_WIN32_THREADS_INIT) + add_definitions("-DGC_THREADS") + if (enable_parallel_mark) + add_definitions("-DPARALLEL_MARK") + endif() + if (enable_thread_local_alloc AND (enable_parallel_mark OR NOT BUILD_SHARED_LIBS)) + # Imply THREAD_LOCAL_ALLOC unless GC_DLL. + add_definitions("-DTHREAD_LOCAL_ALLOC") + set(SRC ${SRC} thread_local_alloc.c) + endif() + add_definitions("-DEMPTY_GETENV_RESULTS") + set(SRC ${SRC} win32_threads.c) +elseif (CMAKE_HP_PTHREADS_INIT OR CMAKE_USE_SPROC_INIT) + message(FATAL_ERROR "Unsupported thread package") +endif() + +# Check whether -lrt linker option is needed to use clock_gettime. +if (NOT NEED_LIB_RT) + check_function_exists(clock_gettime HAVE_CLOCK_GETTIME_DIRECTLY) + if (NOT HAVE_CLOCK_GETTIME_DIRECTLY) + # Use of clock_gettime() probably requires linking with "rt" library. + set(NEED_LIB_RT ON) + endif() +endif() + +# Locate and use "rt" library if needed (and the library is available). +if (NEED_LIB_RT) + find_library(LIBRT rt) + if (LIBRT) + set(THREADDLLIBS_LIST ${THREADDLLIBS_LIST} ${LIBRT}) + endif() +endif(NEED_LIB_RT) + +if (disable_handle_fork) + add_definitions("-DNO_HANDLE_FORK") +endif() + +if (enable_gcj_support) + add_definitions("-DGC_GCJ_SUPPORT") + if (enable_threads AND NOT (enable_thread_local_alloc AND HOST MATCHES .*-.*-kfreebsd.*-gnu)) + # FIXME: For a reason, gctest hangs up on kFreeBSD if both of + # THREAD_LOCAL_ALLOC and GC_ENABLE_SUSPEND_THREAD are defined. + add_definitions("-DGC_ENABLE_SUSPEND_THREAD") + endif() + set(SRC ${SRC} gcj_mlc.c) +endif(enable_gcj_support) + +if (enable_disclaim) + add_definitions("-DENABLE_DISCLAIM") + set(SRC ${SRC} fnlz_mlc.c) +endif() + +if (enable_java_finalization) + add_definitions("-DJAVA_FINALIZATION") +endif() + +if (enable_atomic_uncollectable) + add_definitions("-DGC_ATOMIC_UNCOLLECTABLE") +endif() + +if (enable_gc_debug) + add_definitions("-DDBG_HDRS_ALL -DKEEP_BACK_PTRS") + if (HOST MATCHES i.86-.*-dgux.*|ia64-.*-linux.*|i586-.*-linux.*|i686-.*-linux.*|x86-.*-linux.*|x86_64-.*-linux.*) + add_definitions("-DMAKE_BACK_GRAPH") + if (HOST MATCHES .*-.*-.*linux.*) + add_definitions("-DSAVE_CALL_COUNT=8") + endif() + set(SRC ${SRC} backgraph.c) + endif() +endif(enable_gc_debug) + +if (disable_gc_debug) + add_definitions("-DNO_DEBUGGING") +elseif (WINCE) + # Read environment variables from ".gc.env" file. + add_definitions("-DGC_READ_ENV_FILE") +endif() + +if (enable_redirect_malloc) + if (enable_gc_debug) + add_definitions("-DREDIRECT_MALLOC=GC_debug_malloc_replacement") + add_definitions("-DREDIRECT_REALLOC=GC_debug_realloc_replacement") + add_definitions("-DREDIRECT_FREE=GC_debug_free") + else() + add_definitions("-DREDIRECT_MALLOC=GC_malloc") + endif() + add_definitions("-DGC_USE_DLOPEN_WRAP") +endif(enable_redirect_malloc) + +if (enable_munmap) + add_definitions("-DUSE_MMAP -DUSE_MUNMAP") +elseif (enable_mmap) + add_definitions("-DUSE_MMAP") +endif() + +if (NOT enable_dynamic_loading) + add_definitions("-DIGNORE_DYNAMIC_LOADING") +endif() + +if (NOT enable_register_main_static_data) + add_definitions("-DGC_DONT_REGISTER_MAIN_STATIC_DATA") +endif() + +if (enable_large_config) + add_definitions("-DLARGE_CONFIG") +endif() + +if (enable_gc_assertions) + add_definitions("-DGC_ASSERTIONS") +endif() + +if (NOT enable_threads_discovery) + add_definitions("-DGC_NO_THREADS_DISCOVERY") +endif() + +if (enable_checksums) + if (enable_munmap OR enable_threads) + message(FATAL_ERROR "CHECKSUMS not compatible with USE_MUNMAP or threads") + endif() + add_definitions("-DCHECKSUMS") + set(SRC ${SRC} checksums.c) +endif(enable_checksums) + +if (enable_werror) + if (BORLAND) + add_compile_options(/w!) + if (enable_threads) + # Workaround "function should return a value" warning for several + # asm functions in atomic_ops/sysdeps/msftc/x86.h. + add_compile_options(/w-rvl) + endif(enable_threads) + elseif (MSVC) + add_compile_options(/WX) + # Workaround "typedef ignored on left of ..." warning reported in + # imagehlp.h of e.g. Windows Kit 8.1. + add_compile_options(/wd4091) + elseif (WATCOM) + add_compile_options(/we) + else() + add_compile_options(-Werror) + if (APPLE) + # _dyld_bind_fully_image_containing_address is deprecated in OS X 10.5+ + add_compile_options(-Wno-deprecated-declarations) + endif() + endif() +endif(enable_werror) + +if (enable_single_obj_compilation OR BUILD_SHARED_LIBS) + set(SRC extra/gc.c) # override SRC + if (CMAKE_USE_PTHREADS_INIT) + add_definitions("-DGC_PTHREAD_START_STANDALONE") + set(SRC ${SRC} pthread_start.c) + endif(CMAKE_USE_PTHREADS_INIT) +elseif (BORLAND) + # Suppress "GC_push_contents_hdr() is declared but never used" warning. + add_compile_options(/w-use) +endif() + +# Add implementation of backtrace() and backtrace_symbols(). +if (MSVC) + set(SRC ${SRC} extra/msvc_dbg.c) +endif() + +# Instruct check_c_source_compiles to skip linking. +# Alternatively, we could set CMAKE_REQUIRED_LIBRARIES properly. +SET(CMAKE_REQUIRED_FLAGS "-c") + +# Instruct check_c_source_compiles and similar CMake checks not to ignore +# compiler warnings (like "implicit declaration of function"). +if (NOT BORLAND AND NOT MSVC AND NOT WATCOM) + check_c_compiler_flag(-Werror HAVE_FLAG_WERROR) + if (HAVE_FLAG_WERROR) + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") + endif(HAVE_FLAG_WERROR) +endif() + +if (BUILD_SHARED_LIBS) + add_definitions("-DGC_DLL") + # Pass -fvisibility=hidden option if supported. + check_c_compiler_flag(-fvisibility=hidden HAVE_FLAG_F_VISIBILITY_HIDDEN) + if (HAVE_FLAG_F_VISIBILITY_HIDDEN) + add_definitions("-DGC_VISIBILITY_HIDDEN_SET") + add_compile_options(-fvisibility=hidden) + else() + add_definitions("-DGC_NO_VISIBILITY") + endif() + if (${CMAKE_VERSION} VERSION_LESS "3.18.0") + set(WL_NO_UNDEFINED_OPT "-Wl,--no-undefined") + check_c_compiler_flag(${WL_NO_UNDEFINED_OPT} HAVE_FLAG_WL_NO_UNDEFINED) + else() + set(WL_NO_UNDEFINED_OPT "LINKER:--no-undefined") + check_linker_flag(C "${WL_NO_UNDEFINED_OPT}" HAVE_FLAG_WL_NO_UNDEFINED) + endif() +else() + add_definitions("-DGC_NOT_DLL") + if (WIN32) + # Do not require the clients to link with "user32" system library. + add_definitions("-DDONT_USE_USER32_DLL") + endif(WIN32) +endif() + +# Disable strict aliasing optimizations. +# It could re-enabled back by a flag passed in CFLAGS_EXTRA. +check_c_compiler_flag(-fno-strict-aliasing HAVE_FLAG_F_NO_STRICT_ALIASING) +if (HAVE_FLAG_F_NO_STRICT_ALIASING) + add_compile_options(-fno-strict-aliasing) +endif() + +# Extra user-defined flags to pass both to C and C++ compilers. +if (DEFINED CFLAGS_EXTRA) + separate_arguments(CFLAGS_EXTRA_LIST UNIX_COMMAND "${CFLAGS_EXTRA}") + add_compile_options(${CFLAGS_EXTRA_LIST}) +endif() + +# Check whether execinfo.h header file is present. +check_include_file(execinfo.h HAVE_EXECINFO_H) +if (NOT HAVE_EXECINFO_H) + add_definitions("-DGC_MISSING_EXECINFO_H") +endif() + +# Check for getcontext (uClibc can be configured without it, for example). +check_function_exists(getcontext HAVE_GETCONTEXT) +if (NOT HAVE_GETCONTEXT) + add_definitions("-DNO_GETCONTEXT") +endif() + +# Check whether dl_iterate_phdr exists (as a strong symbol). +check_function_exists(dl_iterate_phdr HAVE_DL_ITERATE_PHDR) +if (HAVE_DL_ITERATE_PHDR) + add_definitions("-DHAVE_DL_ITERATE_PHDR") +endif() + +check_symbol_exists(sigsetjmp setjmp.h HAVE_SIGSETJMP) +if (NOT HAVE_SIGSETJMP) + add_definitions("-DGC_NO_SIGSETJMP") +endif() + +# pthread_setname_np, if available, may have 1, 2 or 3 arguments. +if (CMAKE_USE_PTHREADS_INIT) + check_c_source_compiles(" +#define _GNU_SOURCE 1\n +#include \n +int main(void) { (void)pthread_setname_np(\"thread-name\"); return 0; }" + HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) + if (HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) + # Define to use 'pthread_setname_np(const char*)' function. + add_definitions("-DHAVE_PTHREAD_SETNAME_NP_WITHOUT_TID") + else() + check_c_source_compiles(" +#define _GNU_SOURCE 1\n +#include \n +int main(void) {\n + (void)pthread_setname_np(pthread_self(), \"thread-name-%u\", 0); return 0; }" + HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG) + if (HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG) + # Define to use 'pthread_setname_np(pthread_t, const char*, void *)'. + add_definitions("-DHAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG") + else() + check_c_source_compiles(" +#define _GNU_SOURCE 1\n +#include \n +int main(void) {\n + (void)pthread_setname_np(pthread_self(), \"thread-name\"); return 0; }" + HAVE_PTHREAD_SETNAME_NP_WITH_TID) + if (HAVE_PTHREAD_SETNAME_NP_WITH_TID) + # Define to use 'pthread_setname_np(pthread_t, const char*)' function. + add_definitions("-DHAVE_PTHREAD_SETNAME_NP_WITH_TID") + endif() + endif(HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG) + endif (HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) +endif() + +# Check for dladdr (used for debugging). +check_c_source_compiles(" +#define _GNU_SOURCE 1\n +#include \n +int main(void) { Dl_info info; (void)dladdr(\"\", &info); return 0; }" + HAVE_DLADDR) +if (HAVE_DLADDR) + # Define to use 'dladdr' function. + add_definitions("-DHAVE_DLADDR") +endif() + +# Check for emscripten; use asyncify feature if requested. +check_c_source_compiles(" +#ifndef __EMSCRIPTEN__\n +# error This is not Emscripten\n +#endif\n +int main(void) { return 0; }" + EMSCRIPTEN) +if (EMSCRIPTEN AND enable_emscripten_asyncify) + # Use this option if your program is targeting -sASYNCIFY. The latter is + # required to scan the stack, ASYNCIFY_STACK_SIZE is probably needed for + # gctest only. + add_definitions("-DEMSCRIPTEN_ASYNCIFY") + set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -sASYNCIFY -sASYNCIFY_STACK_SIZE=128000") +endif() + +add_library(gc ${SRC}) +target_link_libraries(gc PRIVATE ${THREADDLLIBS_LIST} ${ATOMIC_OPS_LIBS}) +target_include_directories(gc INTERFACE + "$" + "$") + +if (enable_cplusplus) + add_library(gccpp gc_badalc.cc gc_cpp.cc) + target_link_libraries(gccpp PRIVATE gc) + target_include_directories(gccpp INTERFACE + "$" + "$") + if (enable_throw_bad_alloc_library) + # The same as gccpp but contains only gc_badalc. + add_library(gctba gc_badalc.cc) + target_link_libraries(gctba PRIVATE gc) + target_include_directories(gctba INTERFACE + "$" + "$") + endif(enable_throw_bad_alloc_library) +endif() + +if (build_cord) + set(CORD_SRC cord/cordbscs.c cord/cordprnt.c cord/cordxtra.c) + add_library(cord ${CORD_SRC}) + target_link_libraries(cord PRIVATE gc) + target_include_directories(cord INTERFACE + "$" + "$") + if (BUILD_SHARED_LIBS) + set_property(TARGET cord PROPERTY VERSION ${CORD_VERSION_PROP}) + set_property(TARGET cord PROPERTY SOVERSION ${CORD_SOVERSION}) + endif() + install(TARGETS cord EXPORT BDWgcTargets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") +endif(build_cord) + +if (BUILD_SHARED_LIBS AND HAVE_FLAG_WL_NO_UNDEFINED) + # Declare that the libraries do not refer to external symbols. + if (${CMAKE_VERSION} VERSION_LESS "3.13.0") + target_link_libraries(gc PRIVATE ${WL_NO_UNDEFINED_OPT}) + if (enable_cplusplus) + target_link_libraries(gccpp PRIVATE ${WL_NO_UNDEFINED_OPT}) + if (enable_throw_bad_alloc_library) + target_link_libraries(gctba PRIVATE ${WL_NO_UNDEFINED_OPT}) + endif(enable_throw_bad_alloc_library) + endif(enable_cplusplus) + if (build_cord) + target_link_libraries(cord PRIVATE ${WL_NO_UNDEFINED_OPT}) + endif(build_cord) + else() + target_link_options(gc PRIVATE ${WL_NO_UNDEFINED_OPT}) + if (enable_cplusplus) + target_link_options(gccpp PRIVATE ${WL_NO_UNDEFINED_OPT}) + if (enable_throw_bad_alloc_library) + target_link_options(gctba PRIVATE ${WL_NO_UNDEFINED_OPT}) + endif(enable_throw_bad_alloc_library) + endif(enable_cplusplus) + if (build_cord) + target_link_options(cord PRIVATE ${WL_NO_UNDEFINED_OPT}) + endif(build_cord) + endif() +endif() + +if (BUILD_SHARED_LIBS) + set_property(TARGET gc PROPERTY VERSION ${GC_VERSION_PROP}) + set_property(TARGET gc PROPERTY SOVERSION ${GC_SOVERSION}) +endif() +install(TARGETS gc EXPORT BDWgcTargets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + +if (enable_cplusplus) + if (BUILD_SHARED_LIBS) + set_property(TARGET gccpp PROPERTY VERSION ${GCCPP_VERSION_PROP}) + set_property(TARGET gccpp PROPERTY SOVERSION ${GCCPP_SOVERSION}) + endif() + install(TARGETS gccpp EXPORT BDWgcTargets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + if (enable_throw_bad_alloc_library) + if (BUILD_SHARED_LIBS) + set_property(TARGET gctba PROPERTY VERSION ${GCCPP_VERSION_PROP}) + set_property(TARGET gctba PROPERTY SOVERSION ${GCCPP_SOVERSION}) + endif() + install(TARGETS gctba EXPORT BDWgcTargets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + endif(enable_throw_bad_alloc_library) +endif(enable_cplusplus) + +if (install_headers) + install(FILES include/gc.h + include/gc_backptr.h + include/gc_config_macros.h + include/gc_inline.h + include/gc_mark.h + include/gc_tiny_fl.h + include/gc_typed.h + include/gc_version.h + include/javaxfc.h + include/leak_detector.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gc") + install(FILES include/extra/gc.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + if (enable_cplusplus) + install(FILES include/gc_allocator.h + include/gc_cpp.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gc") + install(FILES include/extra/gc_cpp.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + endif() + if (enable_disclaim) + install(FILES include/gc_disclaim.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gc") + endif() + if (enable_gcj_support) + install(FILES include/gc_gcj.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gc") + endif() + if (enable_threads AND CMAKE_USE_PTHREADS_INIT) + install(FILES include/gc_pthread_redirects.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gc") + endif() + if (build_cord) + install(FILES include/cord.h + include/cord_pos.h + include/ec.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gc") + endif() + + # Provide pkg-config metadata. + set(prefix "${CMAKE_INSTALL_PREFIX}") + set(exec_prefix \${prefix}) + set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}") + set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}") + string(REPLACE ";" " " THREADDLLIBS "${THREADDLLIBS_LIST}") + # ATOMIC_OPS_LIBS, PACKAGE_VERSION are defined above. + configure_file(bdw-gc.pc.in bdw-gc.pc @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/bdw-gc.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif(install_headers) + +if (build_tests) + if (build_cord) + add_executable(cordtest cord/tests/cordtest.c) + target_link_libraries(cordtest PRIVATE cord gc) + add_test(NAME cordtest COMMAND cordtest) + + if (WIN32 AND NOT CYGWIN) + add_executable(de cord/tests/de.c cord/tests/de_win.c + cord/tests/de_win.rc) + set_target_properties(de PROPERTIES WIN32_EXECUTABLE TRUE) + target_link_libraries(de PRIVATE cord gc gdi32) + endif() + endif(build_cord) + + # Compile some tests as C++ to test extern "C" in header files. + if (enable_cplusplus) + set_source_files_properties(tests/leak_test.c PROPERTIES LANGUAGE CXX) + if (NOT MSVC) + # WinMain-based test hangs at startup if compiled by VC as C++ code. + set_source_files_properties(tests/test.c PROPERTIES LANGUAGE CXX) + endif() + # To avoid "treating 'c' input as 'c++' when in C++ mode" Clang warning. + if (NOT (BORLAND OR MSVC OR WATCOM)) + add_compile_options(-x c++) + endif() + endif(enable_cplusplus) + + add_executable(gctest WIN32 tests/test.c) + target_link_libraries(gctest PRIVATE gc ${THREADDLLIBS_LIST}) + add_test(NAME gctest COMMAND gctest) + if (WATCOM) + # Suppress "conditional expression in if statement is always true/false" + # and "unreachable code" warnings in GC_MALLOC_[ATOMIC_]WORDS. + target_compile_options(gctest PRIVATE + /wcd=13 /wcd=201 /wcd=367 /wcd=368 /wcd=726) + endif() + + add_executable(hugetest tests/huge_test.c) + target_link_libraries(hugetest PRIVATE gc) + add_test(NAME hugetest COMMAND hugetest) + + add_executable(leaktest tests/leak_test.c) + target_link_libraries(leaktest PRIVATE gc) + add_test(NAME leaktest COMMAND leaktest) + + add_executable(middletest tests/middle.c) + target_link_libraries(middletest PRIVATE gc) + add_test(NAME middletest COMMAND middletest) + + add_executable(realloc_test tests/realloc_test.c) + target_link_libraries(realloc_test PRIVATE gc) + add_test(NAME realloc_test COMMAND realloc_test) + + add_executable(smashtest tests/smash_test.c) + target_link_libraries(smashtest PRIVATE gc) + add_test(NAME smashtest COMMAND smashtest) + + if (NOT (BUILD_SHARED_LIBS AND WIN32)) + add_library(staticrootslib_test tests/staticrootslib.c) + target_link_libraries(staticrootslib_test PRIVATE gc) + add_library(staticrootslib2_test tests/staticrootslib.c) + target_compile_options(staticrootslib2_test PRIVATE "-DSTATICROOTSLIB2") + target_link_libraries(staticrootslib2_test PRIVATE gc) + add_executable(staticrootstest tests/staticrootstest.c) + target_compile_options(staticrootstest PRIVATE "-DSTATICROOTSLIB2") + target_link_libraries(staticrootstest PRIVATE + gc staticrootslib_test staticrootslib2_test) + add_test(NAME staticrootstest COMMAND staticrootstest) + endif() + + if (enable_gc_debug) + add_executable(tracetest tests/trace_test.c) + target_link_libraries(tracetest PRIVATE gc) + add_test(NAME tracetest COMMAND tracetest) + endif() + + if (enable_threads) + add_executable(test_atomic_ops tests/test_atomic_ops.c) + target_link_libraries(test_atomic_ops PRIVATE gc) + add_test(NAME test_atomic_ops COMMAND test_atomic_ops) + + add_executable(threadleaktest tests/thread_leak_test.c) + target_link_libraries(threadleaktest PRIVATE gc ${THREADDLLIBS_LIST}) + add_test(NAME threadleaktest COMMAND threadleaktest) + + if (NOT WIN32) + add_executable(threadkey_test tests/threadkey_test.c) + target_link_libraries(threadkey_test PRIVATE gc ${THREADDLLIBS_LIST}) + add_test(NAME threadkey_test COMMAND threadkey_test) + endif() + + add_executable(subthreadcreate_test tests/subthread_create.c) + target_link_libraries(subthreadcreate_test PRIVATE gc ${THREADDLLIBS_LIST}) + add_test(NAME subthreadcreate_test COMMAND subthreadcreate_test) + + add_executable(initsecondarythread_test tests/initsecondarythread.c) + target_link_libraries(initsecondarythread_test + PRIVATE gc ${THREADDLLIBS_LIST}) + add_test(NAME initsecondarythread_test COMMAND initsecondarythread_test) + endif(enable_threads) + + if (enable_cplusplus) + add_executable(test_cpp WIN32 tests/test_cpp.cc) + target_link_libraries(test_cpp PRIVATE gc gccpp) + add_test(NAME test_cpp COMMAND test_cpp) + endif() + + if (enable_disclaim) + add_executable(disclaim_bench tests/disclaim_bench.c) + target_link_libraries(disclaim_bench PRIVATE gc) + add_test(NAME disclaim_bench COMMAND disclaim_bench) + + add_executable(disclaim_test tests/disclaim_test.c) + target_link_libraries(disclaim_test PRIVATE gc ${THREADDLLIBS_LIST}) + add_test(NAME disclaim_test COMMAND disclaim_test) + + add_executable(disclaim_weakmap_test tests/disclaim_weakmap_test.c) + target_link_libraries(disclaim_weakmap_test + PRIVATE gc ${THREADDLLIBS_LIST}) + add_test(NAME disclaim_weakmap_test COMMAND disclaim_weakmap_test) + endif() +endif(build_tests) + +if (enable_docs) + # Note: documentation which is not installed: README.QUICK + install(FILES AUTHORS README.md + DESTINATION "${CMAKE_INSTALL_DOCDIR}") + install(DIRECTORY doc/ DESTINATION "${CMAKE_INSTALL_DOCDIR}" + FILES_MATCHING + PATTERN "README.*" + PATTERN "*.md") + + install(FILES doc/gc.man DESTINATION "${CMAKE_INSTALL_MANDIR}/man3" + RENAME gc.3) +endif(enable_docs) + +# CMake config/targets files. +install(EXPORT BDWgcTargets FILE BDWgcTargets.cmake + NAMESPACE BDWgc:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bdwgc") + +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/BDWgcConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bdwgc" + NO_SET_AND_CHECK_MACRO) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/BDWgcConfigVersion.cmake" + VERSION "${PACKAGE_VERSION}" COMPATIBILITY AnyNewerVersion) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/BDWgcConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/BDWgcConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bdwgc") + +export(EXPORT BDWgcTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/BDWgcTargets.cmake") diff --git a/bdwgc/ChangeLog b/bdwgc/ChangeLog new file mode 100644 index 000000000..0266d4e6f --- /dev/null +++ b/bdwgc/ChangeLog @@ -0,0 +1,10771 @@ + +== [8.2.4] 2023-05-26 == + +* Abort with appropriate message if first call of mmap fails with EPERM +* Adjust CORD_ec comment placement in ec.h +* Adjust WoW64 workaround to work on UWP/WinRT +* Adjust naming of Win32/64 and x86/64 words in comments and documentation +* Avoid potential race between realloc and GC_block_was_dirty +* Do not double-clear first two words of object in GC_generic_malloc_aligned +* Do not mention FASTLOCK in comment +* Do not mix debug and non-debug allocations in disclaim tests +* Do not prohibit threaded builds with malloc redirection on non-Linux +* Do not prohibit zero proc argument in GC_register_disclaim_proc +* Eliminate '&array may not produce intended result' wcc warnings +* Eliminate 'GC_unmap_end declared but unused' bcc warning in disclaim_bench +* Eliminate 'ISO C++17 does not allow register specifier' gcc warning +* Eliminate 'cast signed to bigger unsigned' CSA warning in WARNs, new_thread +* Eliminate 'n obtained from untrusted source' code defect FP in test_cpp +* Eliminate 'skipping config since MAXSIG/_NSIG is unknown' cppcheck FP +* Eliminate data race FP between remove_protection and write_fault_handler +* Eliminate data race FP reported in need_unreachable_finalization by TSan +* Ensure 'new' system header is included by gc_cpp.h if GC_INCLUDE_NEW +* Ensure GC_NO_PTHREAD_SIGMASK defined if no GC_pthread_sigmask prototype +* Fix 'EMSCRIPTEN macro redefined' compile error +* Fix 'ISO C90 forbids mixed decl and code' warning in SOFT_VDB dirty_init +* Fix 'call to undeclared pthread_setname_np' errors in configure and cmake +* Fix 'failed to create new win32 semaphore' Cygwin fatal error at fork +* Fix 'operator new is missing throw(bad_alloc)' clang warning in gc_cpp.h +* Fix 'overflow in conversion from word' g++ warning in GC_init +* Fix 'unknown option --no-undefined' linker error in cmake script (OS X) +* Fix 'unresolved _end' linker error when targeting Android bitcode +* Fix CORD_next() indent inside loop in test_basics() of cordtest +* Fix DCL_LOCK_STATE placement in GC_set_oom_fn +* Fix GC_excl_table overrun on overflow in GC_exclude_static_roots +* Fix GC_thread_is_registered for finished threads +* Fix GC_unreachable_finalize_mark_proc to ensure its unique address +* Fix GC_unregister_my_thread call before GC functions usage in gctest +* Fix IRIX5 defined wrongly on FreeBSD/mips, WinCE/mips, Tandem S-Series +* Fix allocated objects count increment in alloc8bytes of gctest +* Fix alt-stack handling in GC_push_all_stacks if stack grows up +* Fix comparisons to heap boundary in GC_get_back_ptr_info and GC_mark_from +* Fix data race in GC_heapsize_at_forced_unmap variable +* Fix description of client promise for IGNORE_OFF_PAGE allocated objects +* Fix disabling of automatic dynamic libraries registration +* Fix double initialization of main thread local free lists on Win32 +* Fix gccpp and gctba library names in gcinterface.md +* Fix infinite loop in disable_gc_for_dlopen and GC_wait_for_gc_completion +* Fix infinite wait in pthread_join/detach if thread already finished (Win32) +* Fix joinable threads shutdown on NaCl +* Fix loop condition over dll_thread_table in GC_lookup_pthread (Win32) +* Fix missing GC_CALLBACK for GC_waitForSingleObjectInfinite +* Fix missing extern C for __asan_default_options +* Fix missing libalphagc.so dependency in Makefile.direct +* Fix missing lock while updating GC_in_thread_creation in GC_exit_check +* Fix missing recovery from faults in GC_mark_some on Win64 if MinGW +* Fix missing result check of pthread_attr_getdetachstate in pthread_create +* Fix mistyped function name in documentation of REDIRECT_REALLOC +* Fix negative heap size values reported in WARN +* Fix null pointer dereference in TRACE_TARGET +* Fix of GC_bytes_allocd increment in GC_generic_malloc_inner +* Fix overlapping region assertion in mark_some if malloc redirect on Linux +* Fix potential SIGSEGV on out-of-memory in gctest +* Fix signals delivery fail in find-leak mode if init from non-main thread +* Fix stack top/bottom print order in GC_push_all_stacks if stack grows up +* Fix store-and-dirty call in GC_CONS +* Fix unregistering of thread created by intercepted pthread_create on NaCl +* Fix unused GC_parse_version if no SOFT_VDB and no parallel mark on Linux +* Fix use of unset errno after pthread_create/sigmask calls +* Fix various typos in comments and documentation +* Increment allocated objects count after GC_GCJ_MALLOC() in gctest +* Invoke GC_oom_fn if GC_make_array_descriptor failed because of no memory +* Make Emscripten Asyncify feature optional +* Mention gctba library in README.cmake +* Prevent 'function should return a value' BCC error in CMake script +* Provide meaningful error message in case of Emscripten threaded build +* Reduce local variable scope in resend_lost_signals_retry for cppcheck +* Remove disable-warning options from WCC_MAKEFILE unrecognized by wcc v2.0 +* Remove false warning of missing libpthread.so on Linux +* Remove redundant 'ifdef THREADS' around LOCK/UNLOCK in call_with_alloc_lock +* Remove redundant GC_ATTR_UNUSED for GC_mark_and_push +* Remove unused GC_old_sig_mask from gc_locks.h +* Replace WARN in GC_mark_some wrapper back to GC_COND_LOG_PRINTF +* Specify throw and noexcept for operator new/delete in gc_cpp.h for MS VC +* Support Hurd/x86_64 +* Support client-defined stack pointer adjustment before thread stack push +* Suppress 'unreachable code' wcc warning in I_HOLD_LOCK assertion (CMake) +* Update autotools for release preparation (ac-2.71, am-1.16.5, m4-1.14.19) +* Update dump function name in GC_DUMP_REGULARLY variable documentation +* Use emscripten_stack_get_base instead of emscripten_scan_stack +* Use sbrk() to get OS memory on Emscripten +* Workaround 'writing into region of size 0' gcc warning in suspend_handler +* Workaround CSA null pointer dereference FP in invalidate_map of cord/de +* Workaround a malfunction of soft-dirty bits clearing on Power9 + + +== [8.2.2] 2022-08-26 == + +* Abort if no progress with thread suspend/resume signals resending +* Add CMake option to force libatomic_ops headers usage +* Add _PROP suffix to CORD/GC[CPP]_VERSION variables in CMake script +* Allow not to bypass pthread_cancel hardening in pthread_start +* Allow to start marker threads in child of single-threaded client +* Avoid potential race in GC_init_real_syms after GC_allow_register_threads +* Avoid potential signal loss before sigsuspend in suspend_handler if TSan +* Define SUNOS5SIGS macro for kFreeBSD +* Distribute gc_gcj.h and some other headers in single-obj-compilation +* Do not assert that GC is initialized at DLL_THREAD_DETACH (Win32) +* Do not call SET_HDR() to remove forwarding counts if none exists in hblk +* Do not call mprotect/mmap to GC_unmap/remap (Linux) +* Do not count unmapped regions if GC_unmap is madvise-based (Linux) +* Do not define NEED_FIND_LIMIT in case of OpenBSD user threads +* Do not fail tests if pthread_create returns resource unavailable error +* Do not name GCC intrinsics as C11 ones +* Do not probe to find main data root start if dl_iterate_phdr exists +* Do not send signal to thread which is suspended manually +* Do not use usleep between signals resend if ThreadSanitizer +* Eliminate '-pedantic is not option that controls warnings' GCC-6.3 message +* Eliminate '/GS can not protect parameters' MS VC warning in msvc_dbg +* Eliminate 'R_AARCH64_ABS64 used with TLS symbol' linker warning (clang) +* Eliminate 'buffer overflow detected' FP error in realloc_test +* Eliminate 'extension used' clang warning in sparc_mach_dep.S (configure) +* Eliminate 'function/data pointer conversion in expression' MSVC warning +* Eliminate 'implicit decl of _setjmp' gcc warning if -std=c11 on Cygwin +* Eliminate 'layout of aggregates has changed in GCC 5' warning in test_cpp +* Eliminate 'new_l may be used uninitialized' gcc warning in os_dep (Cygwin) +* Eliminate 'old_gc_no is initialized but not referenced' MS VC false warning +* Eliminate 'possible loss of data' compiler warning in GC_envfile_getenv +* Eliminate 'potentially uninitialized local variable tc' warning (MSVC) +* Eliminate 'skipping config since MAX_HEAP_SECTS is unknown' cppcheck FP +* Eliminate 'unused but set variable' gcc warnings in cpptest +* Eliminate 'value exceeds maximum size' warnings in debug_malloc, huge_test +* Eliminate 'writing into region of size 0' gcc FP warning in realloc +* Eliminate ASan stack-buffer-underflow FP in GC_mark_and_push_stack (E2K) +* Eliminate code defect about incorrect size of allocated object (leaktest) +* Eliminate data race reported by TSan in GC_have_errors +* Eliminate division-by-zero FP warning in GC_ASSERT in reclaim_block +* Eliminate stringop-overflow gcc-12 warning in CORD__next +* Ensure typed objects descriptor is never located in the first word +* Fix 'GC_greatest_stack_base_below is defined but not used' warning (IA64) +* Fix 'GC_text_mapping not used' GCC warning if redirect malloc w/o threads +* Fix 'ISO C forbids conversion of function pointer to object' warning +* Fix 'undeclared getpagesize' compiler warning on AIX and OSF1 +* Fix 'undefined reference to __data_start' linker error on Linux/aarch64 +* Fix 'unresolved __imp__wsprintfA' linker error in msvc_dbg.c (MSVC) +* Fix 'unresolved symbol GetModuleHandle' error in win32_threads.c (UWP) +* Fix (workaround) stack overflow in gctest on Alpine Linux/s390x +* Fix GC_ATTR_NO_SANITIZE_THREAD definition for GCC +* Fix GC_allocate_ml incorrect cleanup in GC_deinit if pthreads (MinGW) +* Fix GC_dirty() argument in GC_malloc_explicitly_typed_ignore_off_page +* Fix GC_make_descriptor for zero length argument +* Fix GC_suspend_thread if called before thread destructor +* Fix GC_unmapped_bytes update in GC_unmap for Sony PS/3 +* Fix SIGSEGV caused by dropped stack access from child process in gctest +* Fix SUNOS5SIGS documentation to match macro definition in gcconfig.h +* Fix abort in Win32 DllMain if PARALLEL_MARK +* Fix abort when GC_repeat_read returns zero +* Fix assertion about built-in AO_test_and_set_acquire on sparc64 (gcc-12) +* Fix assertion violation in GC_allow_register_threads on Windows +* Fix assertion violation of GC_thread_key alignment if pthread-based TLS +* Fix comment in GC_init regarding GC_init_parallel call +* Fix context saving when GC_suspend_thread(self) +* Fix data race in fail_proc1 of gctest +* Fix hang in GC_free if GC_PREFER_MPROTECT_VDB (Mingw64) +* Fix hang in select() called from suspend signal handler if TSan +* Fix hang on sem_wait in GC_suspend_thread if thread was resumed recently +* Fix hb_obj_kind type in documentation (ASCII diagram) describing hblkhdr +* Fix incremental mode enabling in gctest if TEST_MANUAL_VDB +* Fix linking of tests in case of finalization is off +* Fix lock assertion violation in GC_find_limit if always multi-threaded +* Fix memory return to OS in GC_unmap +* Fix missing lock when GC_generate_random_valid_address is called +* Fix missing write() declaration if CONSOLE_LOG (Watcom) +* Fix nodist_libgc_la_SOURCES value in Makefile.am for Solaris/sparc +* Fix oldProc initialization in gc_cleanup and eliminate related warnings +* Fix parallel_initialized assertion violation in initsecondarythread (Win32) +* Fix potential race if start_mark_threads called from threads in child +* Fix propagation of out-of-memory occurred in GC_make_sequence_descriptor +* Fix pthread_setname_np and dladdr detection by CMake +* Fix race between calloc_explicitly_typed and push_complex_descriptor +* Fix typos in comments and debugging.md +* Fix undefined __stack_base__ on UWP/arm64 (llvm-mingw) +* Force GC_with_callee_saves_pushed in suspend_handler if NO_SA_SIGACTION +* Link with rt library to get clock_gettime where necessary +* Make finalizer_closure pointer read/write atomic in malloc and callback +* Move platform-specific sleep call to GC_usleep (refactoring) +* Pass -lrt linker option in CMake script on HP/UX, NetBSD +* Prevent (fix) parallel custom mark procs run in single-threaded clients +* Prevent changing of GC_markers_m1 value while collection in progress +* Refer to Makefile.direct instead of deleted Makefile file in README +* Relax assertion of hb_n_marks in reclaim_block if more than two markers +* Remove IF_IA64 macro in pthread_stop_world (refactoring) +* Remove checking of RS6000 completely +* Remove duplicate check of MSWIN_XBOX1 in os_dep.c +* Remove duplicate include gc_tiny_fl.h in gc_priv.h +* Remove non-working check of M68K in gctest +* Remove useless TSan W/A about read of mark_lock_holder for Windows +* Replace RAISE_SIGNAL macro with a static function (refactoring) +* Replace SSH cloning with HTTPS one in README +* Retry pthread_kill if EAGAIN (Linux) +* Revert "Check real-symbols are already initialized in pthread_join/detach" +* Revert "Remove nested always-false ifdef for HPUX and FREEBSD" +* Revert addition of msvc_dbg.h in include.am +* Set default build type to RelWithDebInfo (CMake) +* Start configure help messages with a lower case letter +* Support 'z' format modifier by CORD_vsprintf +* Support Elbrus 2000 (Linux/e2k) +* Support GCC MCF thread model (mcfgthreads) in configure (MinGW) +* Support GC_remove_roots on Win32 +* Support OpenBSD/riscv64 +* Support build using Makefile.direct on Linux/sparc +* Support space-separated flags in CFLAGS_EXTRA passed to CMake +* Update README.win32 about default build configuration (configure, cmake) +* Update documentation of GC_RATE and MAX_PRIOR_ATTEMPTS +* Use SIGRTMIN+6 as suspend signal if sigrt-signals on OpenBSD +* Use SIGUSR1/2 on FreeBSD/arm64 +* Use compiler TLS on NetBSD only if at least gcc-4.4 or clang-3.9 +* Workaround 'info is not assigned' cppcheck FP if assertions on (OS X) +* Workaround SIG_SUSPEND delivery to thread inside mutex_lock fail if TSan +* Workaround TSan FP about race between generic_malloc and array_mark_proc +* Workaround TSan FP in acquire_mark_lock called from fork_prepare_proc +* Workaround TSan FP warning in finalized_malloc, push_unconditionally +* Workaround TSan FP warning in fork_prepare_proc +* Workaround TSan FP warning in push_marked1/2/4, ptr_store_and_dirty +* Workaround Thread Sanitizer (TSan) FP warning in is_valid_displacement +* Workaround call stack size exceeded in gctest (Wasm) +* Workaround crash in FreeBSD rand() by avoiding its concurrent usage +* Workaround gctest hang if test compiled as C++ code by MSVC (CMake) +* Workaround msvc_dbg.c build failure on arm[64] (MSVC) + + +== [8.2.0] 2021-09-29 == + +* Add API for accessing incremental GC time limit with nanosecond precision +* Add API function to force start of incremental collection +* Add GC_ prefix to scan_ptr and some other static variables (refactoring) +* Add GC_get/set_disable_automatic_collection API +* Add I_HOLD_LOCK assertion to expand_hp_inner and related functions +* Add assertion on free-list argument and result of GC_new_kind +* Add assertion that GC is initialized to base incremental_protection_needs +* Add assertions that GC_page_size is initialized +* Add cordtest, staticrootstest, test_cpp, tracetest, disclaim tests (CMake) +* Add debug messages on thread suspend/resume (Win32) +* Add dummy testing of GC_incr_bytes_allocd/freed +* Add table of contents in gcdescr.md +* Add testing of GC_CALLOC/MALLOC_EXPLICITLY_TYPED (gctest) +* Adjust formatting of numbered lists in README.md to match other .md files +* Adjust highlighting of API prototypes in gcinterface.md +* Adjust macro def/usage for AVR32, CRIS, NETBSD, OPENBSD, SH4 in gcconfig.h +* Adjust printf calls in gctest check_heap_stats so that each has new-line +* Allow incremental GC on Cygwin +* Allow memory unmapping in case of MPROTECT_VDB +* Allow to disable GWW or mprotect-based VDB at build +* Allow to disable Glibc FPU exception mask and TSX workarounds (Linux) +* Allow to disable __builtin_return_address(1) usage (x86 and x64) +* Allow to specify custom value of LOG_PHT_ENTRIES +* Always abort on failure to access /proc/self/maps (Linux) +* Always define default_push_other_roots (code refactoring) +* Avoid gcc stringop-overflow warning for intended overflow in smashtest +* Avoid initial 3ms pause on world stop/start with GC_retry_signals (Linux) +* Build cord.lib by Makefile.direct, NT_MAKEFILE, OS2_MAKEFILE, WCC_MAKEFILE +* Build gc as a shared multi-threaded library by default (CMake) +* Build gccpp library by Makefile.direct, NT_MAKEFILE and WCC_MAKEFILE +* Build gctba library +* Build shared libraries by default (WCC_MAKEFILE) +* Change CLOCK_TYPE to timespec for Nintendo Switch (code refactoring) +* Change EMSCRIPTEN macro for internal use to no-underscore format +* Change log_size fields of finalizer to unsigned type (code refactoring) +* Change type of toggleref_array_size/capacity to size_t (code refactoring) +* Check leak of objects allocated by CRT malloc in gctest (MS VC) +* Check real-symbols are already initialized in pthread_join/detach +* Collapse multiple includes of windows.h (code refactoring) +* Comments reformatting in mark.c to properly delimit sentences +* Compile de test GUI app with resources (CMake) +* Compile gc.c unless building static libraries (NT_MAKEFILE, WCC_MAKEFILE) +* Compile msvc_dbg.c by CMake script (MS VC) +* Declare API function and print amount of memory obtained from OS +* Define GC_win32_free_heap API function for all Windows targets +* Define STATIC macro to static by default +* Depend number of fork_a_thread calls on NTHREADS (gctest) +* Detect dladdr() presence in CMake script +* Detect presence of execinfo.h system header in CMake script +* Detect presence of getcontext and dl_iterate_phdr in CMake script +* Detect sigsetjmp() availability in CMake script +* Disable Clang/GCC aliasing optimization in CMake script by default +* Do not build tests by default (Makefile.direct and other Makefiles) +* Do not build the tests by default (CMake) +* Do not call GC_push_conditional unless PROC_VDB +* Do not call add_to_our_memory with null pointer (refactoring) +* Do not compile pthread_*.c files in Cygwin or MSYS (CMake) +* Do not define GC_write_cs for Xbox One target +* Do not define HAVE_NO_FORK for all Unix-like systems +* Do not hard-code CMAKE_DL_LIBS value and install paths (CMake) +* Do not hard-code finalizable objects limit which triggers GC +* Do not update scratch_last_end_ptr unless used by reg dynamic libraries +* Document GC_incr_bytes_allocd/freed API function +* Eliminate '(long)size<=0 is always false' cppcheck FP +* Eliminate 'Consecutive return is unnecessary' cppcheck style warning +* Eliminate 'accessing GC_dont_gc without lock' in GC_init code defect FP +* Eliminate 'bytes_freed access w/o lock in incr_bytes_free' code defect FP +* Eliminate 'checking if unsigned i < 0' cppcheck FP in is_heap_base +* Eliminate 'hash_val value is never used' cppcheck false positive +* Eliminate 'passing tainted var maps_buf to tainted sink' code defect FP +* Eliminate 'retry_cnt is assigned value but never used' cppcheck FP +* Eliminate 'stop variable is always 0' compiler warning in print_callers +* Eliminate 'struct member os_callback is never used' cppcheck warning +* Eliminate 't->flags not atomically updated' code defect FP +* Eliminate 'tmpl might be accessed at non-zero index' cppcheck error +* Eliminate GCC warning of unsafe __builtin_return_address(1) +* Eliminate code duplication in reclaim_clear and disclaim_and_reclaim +* Eliminate double lock code defect false positive in generic_lock +* Eliminate memory leak reported in add_current_malloc_heap at exit (Win32) +* Emscripten single-threaded support (detect stack base, push registers) +* Enable CMake-based build for Borland and Watcom compilers +* Enable compilation without C runtime (Win32) +* Enable fork testing in single-thread builds (Unix-like) +* Enable mprotect-based incremental GC for Linux/arm and Linux/aarch64 +* Enable true incremental collection even if parallel marker is on +* Enable use of __builtin_unwind_init() if clang-8 or later +* Ensure ELFSIZE is defined in dyn_load.c for OpenBSD (code refactoring) +* Ensure add_to_heap_inner arguments are valid (refactoring) +* Ensure all getters and setters are run at least once by gctest (pthreads) +* Export CMake targets with namespace BDWgc +* Fix 'const obj must be initialized if not extern' error in gc_alloc_ptrs.h +* Fix ./libgc.la dependency on FreeBSD (Automake) +* Fix HOST determination in CMake script +* Fix copyright message in de_win.rc, gc_cpp.cc, ec.h and specific.h +* Fix missing OS_TYPE definition for some targets +* Fix mmap(PROT_NONE) failure if RLIMIT_AS value is low (Linux) +* Generate cordtest and de executable files in GC base folder +* Generate pkg-config metadata file (CMake) +* Get rid of some non-ELF ifdefs (code refactoring) +* Handle potential incomplete buffer read in GC_linux_main_stack_base +* Implement GET_TIME for Nintendo Switch +* Increase NTHREADS value in tests if code coverage analysis +* Install docs and man page if enable_docs (CMake) +* Install gc_gcj.h and gc_pthread_redirects.h only if appropriate +* Log abort message details even if not print_stats (unless SMALL_CONFIG) +* Mark buffer returned by get_maps as const (code refactoring) +* Move C++ GC_ATTR_EXPLICIT and GC_NOEXCEPT definition to gc_config_macros.h +* Move GC state non-pointer variables into GC_arrays (code refactoring) +* Move GC state pointer variables into GC_arrays +* Move GC_scratch_recycle_inner() to alloc.c (refactoring) +* Move GC_throw_bad_alloc definition to new C++ file +* Move QNX and Emscripten macro definitions to proper place in gcconfig.h +* Move definition of GC_n_mark_procs and GC_n_kinds from mark.c to misc.c +* New API (GC_set_markers_count) to control number of parallel markers +* New API function to clear GC exclusion table +* New API function to get size of object debug header +* New API standalone functions to acquire and release the allocator lock +* New CMake option (disable_gc_debug) to remove debugging code +* New CMake option (disable_handle_fork) to disable fork handling completely +* New macro (CONSOLE_LOG) to enable logging to console on Win32 +* New macro (GCTEST_PRINT_VERBOSE) to enable verbose logging in test.c only +* New macro (NO_MSGBOX_ON_ERROR) to avoid message box on GC abort (Win32) +* OpenBSD does not use ELF_CLASS (code refactoring) +* Pass -D GC_DLL -fvisibility=hidden if default configure build is requested +* Pass -no-undefined linker flag if building shared libraries (CMake) +* Print pid of child processes if verbose logging (gctest) +* Read environment variables from a file on WinCE (CMake script) +* Reduce stack-allocated buffer in get_nprocs from 4KB to 1.7KB +* Refine flags field comment in pthread_support.h +* Reflect result of VDB selection at runtime in incremental_protection_needs +* Reformat code of GC_push_roots +* Reformat gc.man (wrap long lines) +* Reformatting and code refactoring of CMake script +* Remove 'current users' section from overview.md +* Remove 'distributed ports', 'scalable versions' sections from overview.md +* Remove AC_MSG_RESULT for THREADDLLIBS (dgux386) +* Remove Borland-specific Makefile and gc.mak script +* Remove GC_eobjfreelist variable in typd_mlc.c (code refactoring) +* Remove GC_gcj_malloc_initialized variable (code refactoring) +* Remove Linux-specific commands for building cord/de from Makefile.direct +* Remove Win32 main_thread static variable if threads discovery is disabled +* Remove code duplication between GC_unmap and GC_unmap_gap (refactoring) +* Remove code duplication between PROTECT and UNPROTECT macros (refactoring) +* Remove commented out assignment of gc_use_mmap in configure (refactoring) +* Remove dash characters comprising prefix of some verbose logs (gctest) +* Remove dependency on user32.dll import library from static libgc (Win32) +* Remove documentation specific to particular old BDWGC releases +* Remove duplicate Linux-related macro definitions in gcconfig.h +* Remove duplicate macro definitions in gcconfig.h except for Linux +* Remove gcmt-dll generation, rename libgc-lib.a to libgc.a (CMake) +* Remove goto statement in print_callers (code refactoring) +* Remove limit on number of heap sections +* Remove new_gc_alloc.h file +* Remove redundant GC_with_callee_saves_pushed call in multi-threaded builds +* Remove redundant check of GC_free argument in register_finalizer +* Remove redundant type casts in backgraph HEIGHT_UNKNOWN/IN_PROGRESS +* Remove unused GC_prev_heap_addr (refactoring) +* Remove unused STACK_GRAN macro definitions (code refactoring) +* Remove unused sparc_sunos4_mach_dep.s file +* Remove useless empty statements after block ones (refactoring) +* Remove weakobj_free_list variable in disclaim_weakmap_test (refactoring) +* Rename READ to PROC_READ in os_dep.c (code refactoring) +* Rename cord/cord test executable to de (CMake) +* Rename ext_descr to typed_ext_descr_t (code refactoring) +* Rename gc64.dll to gc.dll and gc[64]_dll.lib to gc.lib in NT_MAKEFILE +* Rename gc68060.lib to gc.lib, cord/cord68060.lib to cord.lib in SMakefile +* Rename make_as_lib option to enable_static in NT_MAKEFILE and WCC_MAKEFILE +* Rename nothreads option to disable_threads in NT_MAKEFILE +* Repeat run_one_test NTHREADS times by gctest if single-threaded +* Replace "msecs" with "ms" in all comments and messages +* Replace 'stack base' with 'stack bottom' in the documentation +* Replace SN_TARGET_ORBIS to PLATFORM_* and GC_NO_* macros +* Replace _M_AMD64 macro with _M_X64 (code refactoring) +* Replace find_limit_openbsd to find_limit_with_bound (OpenBSD 5.2+) +* Replace obsolete AC_HELP_STRING with AS_HELP_STRING (refactoring) +* Replace push_one calls with push_many_regs one for Win32 thread context +* Report memory region bounds and errno on GC_unmap/remap failure +* Report presence of process fork testing (gctest) +* Report time with a nanosecond precision where available +* Retry suspend/resume signals on all platforms by default +* Run tree and typed tests in child process (gctest) +* Set GC_collecting hint for GC_collect_a_little_inner calls (pthreads) +* Set name of GC marker threads +* Set so-version for installed shared libraries (CMake) +* Simplify logged message in scratch_recycle +* Simplify loops of collect_a_little/stopped_mark invoking mark_some +* Support -fvisibility=hidden option in CMake script +* Support CFLAGS_EXTRA to pass extra user-defined compiler flags (CMake) +* Support FreeBSD/RISC-V, Linux/arc, LoongArch, OpenBSD/powerpc64 +* Support header files installation (CMake) +* Support most configure options in CMake script +* Suppress warnings in test_tinyfl() of gctest reported by Watcom C complier +* Take nanoseconds into account when updating full_gc_total_time +* Turn off C++ API by default, export it in gccpp library (CMake) +* Turn on automatic fork() handling by default on Android +* Update README.cmake regarding Unix, C++ and tests +* Update libgc.so version info to differentiate against v8.0.x +* Update the ASCII diagrams describing the tree structure for pointer lookups +* Update the documentation to match the current GC implementation +* Upgrade cmake_minimum_required(version) to 3.1 +* Use CreateThread without GC_ prefix in gctest (code refactoring) +* Use KB/MB/GB abbreviations uniformly across entire documentation +* Use USE_MMAP_ANON when USE_MMAP is configured on OpenBSD +* Use a specific Emscripten allocator for Tiny +* Use atomic primitives for Sony PlayStation Portable 2 and PS4 +* Use better precision Windows timers +* Use clock_gettime() instead of clock() on Cygwin and Linux +* Use compiler TLS on FreeBSD and NetBSD +* Use mprotect-based VDB on PowerPC and S390 (Linux) +* Use soft dirty bits on Linux (x86, powerpc, s390, x64) +* Workaround 'condition result<=0 is always false' cppcheck FP in get_maps +* Workaround 'push_regs configured incorrectly' error (GCC-11) +* Workaround 'same value in both branches of ternary operator' cppcheck FP +* Workaround various cppcheck false positives + + +== [8.0.10] 2023-05-26 == + +* Abort with appropriate message if first call of mmap fails with EPERM +* Avoid potential race between realloc and GC_block_was_dirty +* Do not prohibit zero proc argument in GC_register_disclaim_proc +* Eliminate '&array may not produce intended result' wcc warnings +* Eliminate 'cast signed to bigger unsigned' CSA warning in GC_new_thread +* Ensure 'new' system header is included by gc_cpp.h if GC_INCLUDE_NEW +* Fix 'overflow in conversion from word' g++ warning in GC_init +* Fix description of client promise for IGNORE_OFF_PAGE allocated objects +* Fix infinite loop in disable_gc_for_dlopen and GC_wait_for_gc_completion +* Fix missing extern C for __asan_default_options +* Fix store-and-dirty call in GC_CONS +* Fix typo in comment of GC_Thread_Rep.dummy +* Fix use of unset errno after pthread_sigmask calls +* Increment allocated objects count after GC_GCJ_MALLOC() in gctest +* Remove redundant GC_ATTR_UNUSED for GC_mark_and_push +* Workaround CSA null pointer dereference FP in invalidate_map of cord/de + +Also, includes 7.6.18 changes + + +== [8.0.8] 2022-08-26 == + +* Avoid potential race in GC_init_real_syms after GC_allow_register_threads +* Define SUNOS5SIGS macro for kFreeBSD +* Distribute gc_disclaim.h in single-obj-compilation +* Do not assert that GC is initialized at DLL_THREAD_DETACH (Win32) +* Do not name GCC intrinsics as C11 ones +* Do not send signal to thread which is suspended manually +* Eliminate 'buffer overflow detected' FP error in realloc_test +* Eliminate 'extension used' clang warning in sparc_mach_dep.S (configure) +* Eliminate 'function/data pointer conversion in expression' MSVC warning +* Eliminate 'implicit decl of _setjmp' gcc warning if -std=c11 on Cygwin +* Eliminate 'new_l may be used uninitialized' gcc warning in os_dep (Cygwin) +* Eliminate 'old_gc_no is initialized but not referenced' MS VC false warning +* Eliminate 'possible loss of data' compiler warning in GC_envfile_getenv +* Eliminate 'value exceeds maximum size' warnings in debug_malloc, huge_test +* Eliminate 'writing into region of size 0' gcc FP warning in realloc +* Eliminate division-by-zero FP warning in GC_ASSERT in reclaim_block +* Eliminate stringop-overflow gcc-12 warning in CORD__next +* Ensure typed objects descriptor is never located in the first word +* Fix 'GC_greatest_stack_base_below is defined but not used' warning (IA64) +* Fix 'GC_text_mapping not used' GCC warning if redirect malloc w/o threads +* Fix 'ISO C forbids conversion of function pointer to object' warning +* Fix 'undeclared getpagesize' compiler warning on AIX and OSF1 +* Fix 'undefined reference to __data_start' linker error on Linux/aarch64 +* Fix GC_allocate_ml incorrect cleanup in GC_deinit if pthreads (MinGW) +* Fix GC_dirty() argument in GC_malloc_explicitly_typed_ignore_off_page +* Fix GC_make_descriptor for zero length argument +* Fix GC_suspend_thread if called before thread destructor +* Fix GC_unmapped_bytes update in GC_unmap for Sony PS/3 +* Fix SIGSEGV caused by dropped stack access from child process in gctest +* Fix SUNOS5SIGS documentation to match macro definition in gcconfig.h +* Fix abort in Win32 DllMain if PARALLEL_MARK +* Fix assertion about built-in AO_test_and_set_acquire on sparc64 (gcc-12) +* Fix assertion violation in GC_allow_register_threads on Windows +* Fix assertion violation of GC_thread_key alignment if pthread-based TLS +* Fix context saving when GC_suspend_thread(self) +* Fix data race in fail_proc1 of gctest +* Fix get_maps failure when GC_repeat_read returns zero +* Fix hang in GC_free if GC_PREFER_MPROTECT_VDB (Mingw64) +* Fix hang in select() called from suspend signal handler if TSan +* Fix hang on sem_wait in GC_suspend_thread if thread was resumed recently +* Fix hb_obj_kind type in documentation (ASCII diagram) describing hblkhdr +* Fix incremental mode enabling in gctest if TEST_MANUAL_VDB +* Fix lock assertion violation in GC_find_limit if always multi-threaded +* Fix missing lock when GC_generate_random_valid_address is called +* Fix nodist_libgc_la_SOURCES value in Makefile.am for Solaris/sparc +* Fix oldProc initialization in gc_cleanup and eliminate related warnings +* Fix parallel_initialized assertion violation in initsecondarythread (Win32) +* Fix potential race if start_mark_threads called from threads in child +* Fix propagation of out-of-memory occurred in GC_make_sequence_descriptor +* Fix race between calloc_explicitly_typed and push_complex_descriptor +* Fix stack overflow in gctest on Alpine Linux/s390x +* Fix typo in debugging.html +* Fix typos in comments of .c files and gc.h +* Fix undefined __stack_base__ on UWP/arm64 (llvm-mingw) +* Make finalizer_closure pointer read/write atomic in malloc and callback +* Prevent (fix) parallel custom mark procs run in single-threaded clients +* Prevent changing of GC_markers_m1 value while collection in progress +* Refer to Makefile.direct instead of deleted Makefile file in README +* Relax assertion of hb_n_marks in reclaim_block if more than two markers +* Remove checking of RS6000 completely +* Remove duplicate check of MSWIN_XBOX1 in os_dep.c +* Remove non-working check of M68K in gctest +* Remove useless TSan W/A about read of mark_lock_holder for Windows +* Replace SSH cloning with HTTPS one in README +* Revert "Remove nested always-false ifdef for HPUX and FREEBSD" +* Revert addition of msvc_dbg.h in include.am +* Support 'z' format modifier by CORD_vsprintf +* Update documentation of GC_RATE and MAX_PRIOR_ATTEMPTS +* Use SIGRTMIN+6 as suspend signal if sigrt-signals on OpenBSD +* Workaround TSan FP about race between generic_malloc and array_mark_proc +* Workaround TSan FP warning in finalized_malloc, push_unconditionally +* Workaround TSan FP warning in push_marked1/2/4, ptr_store_and_dirty +* Workaround Thread Sanitizer (TSan) FP warning in is_valid_displacement +* Workaround crash in FreeBSD rand() by avoiding its concurrent usage (tests) + + +== [8.0.6] 2021-09-28 == + +* Add loop to handle abort error like in suspend logic on Darwin +* Add support of OpenBSD/aarch64 +* Add threading libraries to bdw-gc.pc +* Allocate start_info struct on the stack in GC_pthread_create +* Allow GC_PAUSE_TIME_TARGET environment variable values smaller than 5 ms +* Avoid compiler warning about unused d in GC_CALLOC/MALLOC_EXPLICITLY_TYPED +* Avoid gcc stringop-overflow warning for intended overflow in smashtest +* Check _MSVC_LANG macro in addition to __cplusplus (MS VC) +* Compile C++ code with exception handling enabled in NT_MAKEFILE +* Define OS_TYPE and DATAEND for UWP targets +* Disable mprotect-based incremental GC if /proc roots are used (Linux) +* Do not report 'Incremental GC incompatible' warning more than once +* Do not use Manual VDB mode if C malloc is redirected +* Do not use iOS private symbols +* Eliminate 'GC_non_gc_bytes is deprecated' warning in new_gc_alloc.h +* Eliminate 'GC_old_bus_handler defined but not used' compiler warning +* Eliminate 'cast between incompatible func types' warnings for FARPROC vars +* Eliminate 'comparing signed and unsigned values' BCC warning in cordtest +* Eliminate 'gc_pthread_redirects.h should contain header guard' code defect +* Eliminate 'implicit declaration of sbrk' gcc warning if -std=c11 on Cygwin +* Eliminate 'possible loss of data' BCC and MS VC warnings +* Eliminate 'static GC_sysinfo definition has incomplete type' Clang warning +* Eliminate 'unused function' compiler warnings (GC_add_map_entry, GC_lock) +* Eliminate 'while clause does not guard' GCC warning in GC_parse_map_entry +* Enable sbrk-to-mmap fallback on major supported Unix-like platforms +* Ensure process is running on one CPU core if AO ops are emulated with locks +* Explicitly zero-initialize trace_buf (fix trace_buf initialization) +* Fix 'ACCESS_VIOLATION in marker' GC warning on Win32 async thread start +* Fix 'GC_generic_malloc must be available' GCC error in new_gc_alloc.h +* Fix 'ISO C++17 does not allow dynamic exception spec' clang-8 error +* Fix 'Wrong __data_start/_end pair' if -Bsymbolic-functions used (Linux) +* Fix 'condition pred!=NULL is always true' compiler warning +* Fix 'external linkage required for var because of dllimport' error on MinGW +* Fix 'ulong undefined' compilation error on AIX +* Fix 'undefined reference to __data_start' linker error on RISC-V +* Fix 'use of undeclared BUS_PAGE_FAULT' compilation error on FreeBSD 12 +* Fix 'write to GC log failed' error (Cygwin) +* Fix 'wrong finalization data' gctest failure on Windows +* Fix CMake build on macOS Catalina +* Fix GC_OPENBSD_THREADS definition (OpenBSD/hppa) +* Fix GC_proc_fd value in child process at fork (Solaris) +* Fix GC_with_callee_saves_pushed for Android NDK r23 (clang-12) +* Fix MPROTECT_VDB definition for single-threaded GC builds +* Fix OS_TYPE and USE_MMAP_ANON definitions for Cygwin/x64 +* Fix STACKBOTTOM on 32-bit HP/UX 11.11 +* Fix abort in GC_printf when gctest is built as WinMain executable (Cygwin) +* Fix assertion violation in register_dynlib_callback on Android +* Fix build for OS X (CMake) +* Fix building of shared library with C++ support on MinGW +* Fix compiling by Makefile.direct on OpenBSD/UltraSparc +* Fix configure message about 'AIX gcc optimization fix' +* Fix cordtest build in SMakefile.amiga +* Fix data race regarding *rlh value in generic_malloc_many +* Fix first_thread stack_base initialization if custom GC_stackbottom (Win32) +* Fix gc_allocator.h compilation by Clang +* Fix gc_cflags variable name in configure (HP/UX) +* Fix handling of areas smaller than page size in GC_scratch_recycle +* Fix incorrect markup formatting in documentation +* Fix misaligned tlfs passed to AO_load on m68k +* Fix missing GC_quiet declaration in pcr_interface.c +* Fix missing gc_dlopen.c and specific.c in CMake script +* Fix missing scratch_last_end_ptr update (Irix) +* Fix mmap() failures on AIX, HP/UX and Haiku +* Fix overflow of scratch_free_ptr value +* Fix page_was_[ever_]dirty() for static roots (Solaris) +* Fix printf format specifier in simple_example.md +* Fix save_callers for multi-threaded case if built-in backtrace unavailable +* Fix subexpression widening in memhash() of disclaim_weakmap_test +* Fix test_cpp failure caused by arbitrary link order (Win32) +* Fix test_cpp failure when gc_cpp resides in a dll (Borland, Watcom) +* Fix various typos mostly in documentation files +* Fix word size, data start and alignment for OpenBSD/mips64(el) +* Include when using alloca on AIX +* Limit number of unmapped regions (Linux and DragonFly) +* New macro to avoid system-wide new/delete inlining in gc_cpp.h (Win32) +* Prevent GetThreadContext failure (Windows) +* Prevent WARN of incompatible incremental GC if default or manual VDB +* Reduce a time period between GetExitCodeThread and SuspendThread (Win32) +* Refactoring of WoW64 workaround (Win32) +* Refine GC_INIT documentation about its multiple invocation +* Refine GC_parallel documentation in gc.h +* Refine do_blocking() documentation in gc.h +* Remove a misleading comment about Solaris in gc.h +* Remove cord .h files from list of non-installed headers (Automake) +* Remove dead part of condition to define NEED_FIND_LIMIT in gc_priv.h +* Remove gcmt-lib generation by CMake +* Support MSYS builds by CMake and configure +* Update documentation about the incremental collector support +* Use HEURISTIC2 on OpenBSD when single-threaded +* Use pstat_getprocvm to determine main stack bottom on HP-UX +* Workaround 'expression is only useful for its side effects' WCC warning +* Workaround clang-3.8/s390x bug when processing __builtin_frame_address +* Workaround fread fail after enable_incremental if malloc redirected (Linux) + + +== [8.0.4] 2019-03-02 == + +* Avoid a full GC when growing finalizer tables if in incremental mode +* Avoid potential race in hb_sz access between realloc and reclaim_block +* Avoid test.o rebuild on tests folder timestamp change (Makefile.direct) +* Avoid unexpected heap growth in gctest caused by GC_disable +* Ensure result of every variant of MS_TIME_DIFF has unsigned long type +* Fix 'duplicate symbol' error for tests using multiple static libs (OS X) +* Fix 'undefined reference to __data_start' linker error (Android/aarch64) +* Fix 'unexpected mark stack overflow' abort in push_all_stack +* Fix 'wrong __data_start/_end pair' error on Android +* Fix BSD_TIME variant of MS_TIME_DIFF for the case of a.tv_usec < b.tv_usec +* Fix GetThreadContext stale register values use if WoW64 (Win32) +* Fix invalid initializer of CLOCK_TYPE variables if BSD_TIME +* Fix thread_info() count argument value (OS X) +* Support de_win.c compilation by Makefile.direct (cord/de) + + +== [8.0.2] 2018-12-23 == + +* Abort with appropriate message if executable pages cannot be allocated +* Add initial testing of GC_enable/disable, MALLOC[_ATOMIC]_IGNORE_OFF_PAGE +* Add paths to filenames mentioned in the copyright section in README +* Add test using disclaim notifiers to implement a weak map +* Adjust #error messages format +* Allow to force executable pages allocation in gctest +* Avoid potential 'macro redefinition' errors for config.h macros +* Call real pthread_sigmask instead of its wrapper in start_mark_threads +* Check result of pthread_mutex_unlock in specific.c +* Default to a single-threaded build for Nintendo, Orbis, Sony PSP targets +* Default to non-executable memory allocation across all make scripts +* Define GC_ATOMIC_UNCOLLECTABLE and JAVA_FINALIZATION in all make scripts +* Do not prevent GC from looking at environment variables (BCC_MAKEFILE) +* Do not use 'ifndef AO_CLEAR' in mark, pthread_support and gctest +* Do not use spin locks if AO test-and-set is emulated (pthreads) +* Document HANDLE_FORK macro optional usage in Makefile.direct +* Document assertion in the setters that used to return old value +* Eliminate 'assigned value never used' compiler warning in test_cpp WinMain +* Eliminate 'casting signed to bigger unsigned int' CSA warning +* Eliminate 'different const qualifiers' MS VC warnings in cordbscs +* Eliminate 'function is never used' cppcheck warning for calloc/realloc +* Eliminate 'non-virtual destructor for class with inheritors' CSA warning +* Eliminate 'pointer targets differ in signedness' compiler warning (Win32) +* Eliminate 'struct member is never used' cppcheck warnings in os_dep +* Eliminate 'uninitialized var' cppcheck false positive in mach_dep, os_dep +* Eliminate 'unreferenced formal parameter' compiler warning in msvc_dbg +* Eliminate redundant check in backwards_height +* Fix 'USE_MUNMAP macro redefinition' error for NaCl +* Fix 'collecting from unknown thread' abort in leak-finding mode for Win32 +* Fix 'mprotect remapping failed' abort on NetBSD with PaX enabled +* Fix 'too wide non-owner permissions are set for resource' code defect +* Fix GC_VSNPRINTF in cordprnt for DJGPP and MS VC for WinCE +* Fix GC_register_disclaim_proc for leak-finding mode +* Fix a deadlock in write_fault_handler if AO_or is emulated +* Fix comment typo in CMakeLists.txt +* Fix concurrent bitmap update in GC_dirty +* Fix deadlocks in write and suspend handlers if AO test-and-set is emulated +* Fix executable memory allocation in GC_unix_get_mem +* Fix hbp overflow in GC_install_counts +* Fix linkage with a system libatomic_ops shared library +* Fix lock assertion violation in get_index if GC_ALWAYS_MULTITHREADED +* Fix marking of finalizer closure object +* Fix marks and hb_n_marks consistency when disclaim returns true +* Fix memory allocation on GCF (Linux/x64) +* Fix missing curses.h in cord/de when compiling manually (MS VC, MinGW) +* Fix test_cpp assertion violation in find-leak mode +* Fix tests linkage with internal atomic_ops.o +* Fix unneeded end_stubborn_change/ptr_store_and_dirty in disclaim_test +* Guard against potential buffer overflow in CORD_next and CORD_pos_fetch +* New macro to suppress printing of leaked objects +* Pass -Wall -Wextra -Wpedantic to g++ if supported (configure) +* Prefix internal durango_get_mem symbol with 'GC_' +* Prevent double inclusion of javaxfc.h and private/specific.h +* Print relevant message in tests not appropriate for leak detection mode +* Reduce scope of local variables in GC_remove_all_threads_but_me +* Refine HIDE_POINTER documentation for the case of the leak-finding mode +* Refine documentation in gc_disclaim.h +* Remove extra USE_MMAP definition for Interix +* Remove redundant header double-inclusion checks in the private headers +* Remove strlen calls with a constant string argument in msvc_dbg +* Specify register_disclaim_proc and finalized_malloc argument as non-null +* Support UWP/arm64 target +* Test marking of finalizer closure object in disclaim_test +* Turn off leak detection mode explicitly in cord/de +* Turn off parallel marker, thread-local allocation if used AO ops emulated +* Turn on gcj functionality in BCC, DMC, NT, OS/2, WCC makefiles +* Turn on memory unmapping in BCC/DMC/NT/WCC makefiles and Makefile.direct +* Update NO_EXECUTE_PERMISSION documentation +* Update documentation about arm64 ABI in gcconfig.h +* Use AO_or in async_set_pht_entry_from_index if available +* Use GC_WORD_MAX macro across all C source files +* Use macro to operate on a flag residing in GC_stop_count +* Use standalone private macro to guard against ptr_t redefinition +* Workaround '#error' cppcheck messages in backgraph and private headers +* Workaround 'AST broken' syntax error reported by cppcheck in GC_mark_some +* Workaround 'GC_dump function is never used' cppcheck warning +* Workaround 'local address assignment to a global variable' CSA warning +* Workaround 'local variable end shadows outer symbol' cppcheck warnings +* Workaround 'local variable obj_displ shadows outer symbol' cppcheck warning +* Workaround 'nonlocal var will use ptr to local var' cppcheck false positive +* Workaround 'pointer addition with NULL pointer' cppcheck error in msvc_dbg +* Workaround 'potential non-terminated string' false positive in cordbscs +* Workaround 'value of _MAX_PATH is unknown' cppcheck warning +* Workaround cppcheck warnings regarding CLOCKS_PER_SEC, REDIRECT_REALLOC + + +== [8.0.0] 2018-09-05 == + +* Accept Android platform by both CMake and configure +* Access finalize_now atomically to avoid TSan warning without no-sanitize +* Acknowledge thread restart from suspend_handler (NetBSD) +* Add a sanity check that load_acquire and store_release are available +* Add AO primitives implementation to GC based on GCC atomic intrinsic +* Add assertion for suspend_ack_sem in start_world +* Add assertion to allocobj that live unmarked object cannot be reclaimed +* Add assertions about held lock when accessing all_bottom_indices +* Add assertions to ensure ADD_CALL_CHAIN is called holding the lock +* Add assertions to finalize and threads support for MANUAL_VDB needs +* Add basic calculation of the total full-collection time +* Add check that gc_cpp operator delete is called (test_cpp) +* Add debug logging to new_thread about GC_threads hash table collisions +* Add GC prefix to _MSVC_DBG_H macro +* Add initial RISC-V support +* Add Makefile target to run all tests without test-driver +* Add test_atomic_ops to perform minimal testing of used atomic primitives +* Add two-argument alloc_size attribute to calloc_explicitly_typed (GCC) +* Align IRIX/OSF1_THREADS definition in gc_config_macros.h with gcconfig.h +* Allocate non-executable memory by default (CMake) +* Allow compilation of PROC_VDB code on Linux host (GC_NO_SYS_FAULT_H) +* Allow configure --with-libatomic-ops=none to use GCC atomic intrinsics +* Allow custom N_LOCAL_ITERS and ENTRIES_TO_GET values +* Allow disabling of dynamic loading in CMake script and configure +* Allow disabling of main static data registration in CMake and configure +* Allow disabling of threads discovery in CMake script and configure +* Allow gc_assertions enabling in CMake script +* Allow gc_debug, redirect_malloc, large_config options in CMake script +* Allow GC_NETBSD_THREADS_WORKAROUND macro manual definition +* Allow mmap enabling in CMake script and configure +* Allow passing -D DEFAULT_VDB to CFLAGS +* Allow subthreadcreate_test to be compiled with zero NTHREADS +* Allow to turn on spin locking even if thread-local allocations are used +* Always include gc_atomic_ops.h unless threads are disabled +* Avoid 'Unexpected heap growth' in 64-bit multi-threaded gctest if n_tests=1 +* Avoid duplication of code handling pthreads case in configure +* Avoid potential data race during apply_to_each_object(reset_back_edge) +* Avoid potential data race during GC_dump execution +* Avoid potential race between malloc_kind and mark_thread_local_fls_for +* Avoid potential race between realloc and clear_hdr_marks/reclaim_generic +* Avoid potential race in print_static_roots called by dyld_image_add/remove +* Avoid potential race in SET_MARK_BIT_EXIT_IF_SET if parallel marking +* Avoid potential race when accessing size_map table +* Avoid potential race when storing oh_back_ptr during parallel marking +* Avoid SIGSEGV during GC_INIT on some Android devices +* Build only shared libraries by default (configure) +* Change pointer arguments of push_all[_eager]/conditional API to void* type +* Change type of hb_sz field (of hblkhdr) from size_t to word +* Check consistency of descr, adjust, clear arguments of GC_new_kind +* Check that GC_WIN32_PTHREADS is not specified for Cygwin +* Check thread_local is initialized before accessing thread_key +* Collapse multiple BCOPY_EXISTS macro definitions +* Collapse multiple NT_*_MAKEFILE scripts into a single NT_MAKEFILE +* Collapse multiple page_was_dirty, remove_protection, read_dirty definitions +* Compile checksums.c only if --enable-checksums is given (configure) +* Consistently define WIN32_LEAN_AND_MEAN/NOSERVICE before include windows.h +* Convert .html files to Markdown format +* Convert code of .c files to valid C++ code +* Decide between memory unmapping and mprotect-based dirty bits at runtime +* Declare t local variable in the block where the variable is used +* Define ABORT() using _CrtDbgBreak (if available) on Windows host +* Define CLANG/GNUC_PREREQ macros to check gcc/clang minimum version +* Define DYNAMIC_LOADING for Darwin unless IGNORE_DYNAMIC_LOADING +* Define GC_ASSERT(x) as C assert(x) for external clients of gc_inline.h +* Define GC_PREFETCH_FOR_WRITE to __builtin_prefetch in gc_inline.h (GCC) +* Define GC_THREADS instead of GC_x_THREADS in Makefiles +* Define macro to specify the environment file name extension (Win32/WinCE) +* Define static resend_lost_signals(), restart_all() in pthread_stop_world +* Detect sigsetjmp() availability by configure +* Determine whether to use compiler TLS for kFreeBSD at compile time +* Do not call BCOPY and BZERO if size is zero +* Do not call sem_getvalue in stop_world if one thread exists +* Do not call set_handle_fork(1) in gctest if pthread_atfork not supported +* Do not compile pcr_interface.c and real_malloc.c except by PCR-Makefile +* Do not declare dl_iterate_phdr as weak for kFreeBSD +* Do not include windows.h when compiling gc_cpp.cc +* Do not install gc_allocator.h, gc_disclaim.h unless the features enabled +* Do not merge dynamic root with the existing static one in add_roots_inner +* Do not print n_rescuing_pages value if incremental collections disabled +* Do not push cpsr and frame pointer on Darwin/arm and Darwin/arm64 +* Do not rebuild_root_index unless remove_root_at_pos is called +* Do not specify version info for test libraries (Automake) +* Do not use alternate thread library on Solaris +* Do not use asm in GC_pause +* Do not use PKG_CHECK_MODULES in configure +* Do not use system clock consistently if NO_CLOCK +* Do not use x86 asm in PUSH_CONTENTS_HDR for NaCl +* Document GC_BUILTIN_ATOMIC macro (and gc_atomic_ops private header file) +* Document STACK_NOT_SCANNED macro in gcconfig.h (Emscripten) +* Eliminate 'comparison is always false' code defect in get_maps +* Eliminate 'GC_DEBUG redefined' compiler warning in smashtest +* Eliminate 'potential unsafe sign check of a bitwise operation' code defect +* Enable alternative finalization interface (DISCLAIM) in all makefiles +* Enable compilation for Cygwin with MPROTECT_VDB +* Enable handle-fork and memory unmapping by default +* Enable mprotect-based incremental GC for Win64 (GCC) +* Expose API to control rate and max prior attempts of collect_a_little +* Expose API to control the minimum bytes allocated before a GC occurs +* Fix 'comparison of 255 with expr of type bool' error in gc_atomic_ops.h +* Fix 'doc' files installation folder +* Fix build of cord tests as C++ files (Makefile.direct) +* Fix comment typos in backgraph.c, de.c, gcconfig.h +* Fix delete operator redirection if gc_cpp is built as .dll (Cygwin, MinGW) +* Fix start_world not resuming all threads on Darwin +* Fix test_cpp failure in case GC_DEBUG is defined +* Group common defines for POSIX platforms in configure and CMake scripts +* Guard against USE_PTHREAD_LOCKS and USE_SPIN_LOCK are both defined +* Handle pthread restart signals loss if retry_signals +* Hide value stored to thread-specific entries for a test purpose +* Implement FindTopOfStack(0) for ARM and AArch64 (Darwin) +* Implement memory unmapping for Sony PS/3 +* Imply configure --single-obj-compilation if --disable-static +* Include malloc.c in extra/gc.c after include gc_inline.h +* Increase MAX_HEAP_SECTS (10 times) for large-config +* Initial single-threaded support of Interix subsystem +* Initial support of Nintendo, Orbis, Sony PSP2, WinRT, Xbox One +* Initial support of TIZEN platform +* Install gc.3 man page instead of copying gc.man to doc folder (configure) +* Make extend_size_map() static (code refactoring) +* Make subthreadcreate test compilable even without libatomic_ops +* Match GC_FAST_MALLOC_GRANS formal and actual arguments where possible +* Move de_win compiled resource files to cord/tests +* Move pcr_interface.c, real_malloc.c to 'extra' folder +* New API function (GC_dump_named) to produce named dumps +* New API function (GC_is_incremental_mode) +* New API function (get_expl_freed_bytes_since_gc) +* New API function (get_size_map_at) to get content of size_map table +* New API to stop and start the GC world externally +* New API to turn on manual VDB at runtime +* New field (expl_freed_bytes_since_gc) in public prof_stats_s +* New macro ALWAYS_SMALL_CLEAR_STACK to avoid clearing large stack sections +* New public API (PTR_STORE_AND_DIRTY) to simplify store-and-dirty operation +* Pass CFLAGS_FOR_PIC value to CFLAGS in Makefile.direct +* Print time passed since GC initialization in GC_dump +* Public API (GC_deinit) to allow Win32 critical sections deletion +* Reduce probability of collision in threads hashtable for 64-bit targets +* Reduce the default MUNMAP_THRESHOLD value to 2 for Sony PS/3 +* Refactoring of USE_MMAP/USE_MMAP_ANON pairs definition in gcconfig.h +* Reformat code and comments in gc_allocator.h +* Remove 'dist' target from Makefile.direct +* Remove a redundant check of __cplusplus in Symbian-specific .cpp files +* Remove Android-specific code in gcconfig.h for M68K +* Remove C++ WeakPointer and CleanUp API which lacks implementation +* Remove DGUX_THREADS macro which duplicates GC_DGUX386_THREADS (configure) +* Remove done_init static variable from fnlz_mlc.c +* Remove duplicate definition of ALIGNMENT macro for OpenBSD/arm +* Remove duplicated sample code in leak.md +* Remove EMX_MAKEFILE (add EMX support to Makefile.direct) +* Remove GC code fragment (which already merged) from README.Mac +* Remove GC_GNU_THREADS macro (HURD) +* Remove GENERAL_MALLOC internal macro +* Remove HIGH_BIT macro duplicating SIGNB +* Remove lint-specific code +* Remove Makefile KandRtest target (that supported K&R C compiler) +* Remove MIN_WORDS macro from gc_priv.h +* Remove multi-line macros (FOR_EACH_PRED, ITERATE_DL_HASHTBL_*, PUSH_OBJ) +* Remove name of optional arguments of operator new and new[] in gc_cpp.h +* Remove notes that K&R C compiler is unsupported +* Remove PUSH_CONTENTS_HDR multi-line macro +* Remove redundant check that clear_fl_marks argument is non-null +* Remove redundant THREADS macro checks in alloc.c and gc_priv.h +* Remove stubborn objects allocation code completely, remove stubborn.c +* Remove unnecessary argument casts in add_roots_inner calls +* Remove unnecessary type casts in n_set_marks +* Remove unused USE_GENERIC macro definition and description +* Remove version info in 'de' cord test application +* Replace GC_MALLOC(sizeof T) with GC_NEW(T) in tests +* Replace GC_NO_RETRY_SIGNALS environment variable with GC_RETRY_SIGNALS=0 +* Replace some FIXME items with TODO ones +* Run command passed to if_not_there directly from Makefile.direct +* Same type casts for GC_PTR_STORE arguments regardless of GC_DEBUG +* Skip grungy_pages update when mark state invalid to speedup read_dirty +* Skip typed_test in gctest if NO_TYPED_TEST macro is defined +* Support configure --disable-thread-local-alloc option (similar for CMake) +* Support enable_checksums option in CMake script +* Support Haiku multi-threaded build by CMake +* Support threads for DragonFly in configure +* Turn on 'atomic uncollectable' functionality by default (CMake) +* Turn on GC assertions in NT_MAKEFILE for debug builds +* Turn on gcj, disclaim and java finalization by default (CMake) +* Turn on incremental collection in gctest also if DEFAULT_VDB or MANUAL_VDB +* Turn on incremental mode in cordtest and cord/de +* Turn on incremental mode in disclaim_test, test_cpp and staticroots test +* Turn on parallel marker by default for all multi-threaded builds +* Update GC compilation and usage notes for Win32 +* Update shared libraries version info to differentiate against v7.6 +* Update top_index entry pointer only when the entry is constructed fully +* Use __builtin_expect in SIZET_SAT_ADD macro +* Use __declspec(allocator) for malloc-like prototypes (MS VS 2015+) +* Use __int64 instead of 'long long' in LONG_MULT if appropriate +* Use __thread keyword for Android NDK r12b+ Clang (arm) +* Use atomic allocation for leafs in reverse_test (gctest) +* Use atomic load/store for the concurrently accessed variables in GC_lock +* Use C11 static_assert if available +* Use compiler atomic intrinsics by default if available (configure) +* Use EXPECT FALSE for mark_from code documented as executed rarely +* Use heap-allocated memory for local mark stack of non-marker thread +* Use HOST_ANDROID define instead of PLATFORM_ANDROID +* Use include gc.h with the angle brackets in the man page synopsis +* Use longjmp in fault_handler_openbsd if siglongjmp unavailable (OpenBSD) +* Use MARK_BIT_PER_GRANULE instead of MARK_BIT_PER_OBJ where appropriate +* Use noexcept specifier in gc_allocator and gc_cpp if C++11 +* Use same macro (NTHREADS) across all tests to specify number of threads +* Use sigsetjmp() in setjmp_t tool if available +* Use thread-local allocations for all multi-threaded builds +* Use THREAD_EQUAL consistently to compare pthread_t values +* Workaround 'bad pointer arithmetic' false waring in check_annotated_obj +* Workaround Clang optimizer bug crashing clear_stack_inner on OS X 10.8 +* Workaround Thread Sanitizer (TSan) false positive warnings + + +== [7.6.18] 2023-05-26 == + +* Fix IRIX5 defined wrongly on FreeBSD/mips +* Fix alt-stack handling in GC_push_all_stacks if stack grows up +* Fix data race in GC_heapsize_at_forced_unmap variable + +Also, includes 7.4.24 changes + + +== [7.6.16] 2022-08-26 == + +* Do not send signal to thread which is suspended manually +* Eliminate 'old_gc_no is initialized but not referenced' MS VC false warning +* Fix 'GC_greatest_stack_base_below is defined but not used' warning (IA64) +* Fix context saving when GC_suspend_thread(self) +* Fix data race in fail_proc1 of gctest +* Fix GC_suspend_thread if called before thread destructor +* Fix hang on sem_wait in GC_suspend_thread if thread was resumed recently +* Fix lock assertion violation in GC_find_limit if always multi-threaded +* Fix potential race if start_mark_threads called from threads in child +* Make finalizer_closure pointer read/write atomic in malloc and callback +* Prevent changing of GC_markers_m1 value while collection in progress +* Replace SSH cloning with HTTPS one in README +* Workaround Thread Sanitizer (TSan) FP warning in is_valid_displacement + +Also, includes 7.4.22 changes + + +== [7.6.14] 2021-09-28 == + +* Add loop to handle abort error like in suspend logic on Darwin +* Add support of OpenBSD/aarch64 +* Add threading libraries to bdw-gc.pc +* Disable mprotect-based incremental GC if /proc roots are used (Linux) +* Do not use iOS private symbols +* Eliminate 'GC_old_bus_handler defined but not used' compiler warning +* Eliminate 'comparing signed and unsigned values' BCC warning in cordtest +* Eliminate 'possible loss of data' BCC and MS VC warnings +* Eliminate 'static GC_sysinfo definition has incomplete type' Clang warning +* Eliminate 'unused function GC_add_map_entry' compiler warning +* Eliminate 'while clause does not guard' GCC warning in GC_parse_map_entry +* Explicitly zero-initialize trace_buf (fix trace_buf initialization) +* Fix 'ACCESS_VIOLATION in marker' GC warning on Win32 async thread start +* Fix 'GC_generic_malloc must be available' GCC error in new_gc_alloc.h +* Fix 'ulong undefined' compilation error on AIX +* Fix 'undefined reference to __data_start' linker error on RISC-V +* Fix 'write to GC log failed' error +* Fix GC_proc_fd value in child process at fork (Solaris) +* Fix MPROTECT_VDB definition for single-threaded GC builds +* Fix OS_TYPE and USE_MMAP_ANON definitions for Cygwin/x64 +* Fix STACKBOTTOM on 32-bit HP/UX 11.11 +* Fix abort in GC_printf when gctest is built as WinMain executable (Cygwin) +* Fix assertion violation in register_dynlib_callback on Android +* Fix compiling by Makefile.direct on OpenBSD/UltraSparc +* Fix configure message about 'AIX gcc optimization fix' +* Fix cordtest build in SMakefile.amiga +* Fix data race regarding *rlh value in generic_malloc_many +* Fix first_thread stack_base initialization if custom GC_stackbottom (Win32) +* Fix gc_allocator.h compilation by Clang +* Fix gc_cflags variable name in configure (HP/UX) +* Fix handling of areas smaller than page size in GC_scratch_recycle +* Fix incorrect define GC_OPENBSD_THREADS on sparc64 +* Fix misaligned tlfs passed to AO_load on m68k +* Fix missing GC_quiet declaration in pcr_interface.c +* Fix missing gc_dlopen.c in CMake script +* Fix missing scratch_last_end_ptr update (Irix) +* Fix overflow of scratch_free_ptr value +* Fix page_was_[ever_]dirty() for static roots (Solaris) +* Fix printf format specifier in simple_example.html +* Fix save_callers for multi-threaded case if built-in backtrace unavailable +* Fix test_cpp failure caused by arbitrary link order (Win32) +* Fix test_cpp failure when gc_cpp resides in a dll (Borland, Watcom) +* Fix various typos mostly in documentation files +* Fix word size, data start and alignment for OpenBSD/mips64(el) +* Prevent GetThreadContext failure (Windows) +* Prevent WARN of incompatible incremental GC if default or manual VDB +* Reduce a time period between GetExitCodeThread and SuspendThread (Win32) +* Refactoring of WoW64 workaround (Win32) +* Remove a misleading comment about Solaris in gc.h +* Workaround 'expression is only useful for its side effects' WCC warning +* Workaround fread fail after enable_incremental if malloc redirected (Linux) + + +== [7.6.12] 2019-03-01 == + +* Eliminate 'assigned value never used' compiler warning in test_cpp WinMain +* Fix 'mprotect remapping failed' abort on NetBSD with PaX enabled +* Fix 'undefined reference to __data_start' linker error (Android/aarch64) +* Fix 'unexpected mark stack overflow' abort in push_all_stack +* Fix 'wrong __data_start/_end pair' error on Android +* Fix BSD_TIME variant of MS_TIME_DIFF for the case of a.tv_usec < b.tv_usec +* Fix GetThreadContext stale register values use if WoW64 (Win32) +* Fix executable memory allocation in GC_unix_get_mem +* Fix invalid initializer of CLOCK_TYPE variables if BSD_TIME +* Fix thread_info() count argument value (OS X) +* Update NO_EXECUTE_PERMISSION documentation + + +== [7.6.10] 2018-12-13 == + +* Add paths to filenames mentioned in the copyright section in README +* Call real pthread_sigmask instead of its wrapper in start_mark_threads +* Eliminate 'casting signed to bigger unsigned int' CSA warning +* Eliminate 'non-virtual destructor for class with inheritors' CSA warning +* Fix 'collecting from unknown thread' abort in leak-finding mode for Win32 +* Fix 'too wide non-owner permissions are set for resource' code defect +* Fix 'undefined reference to GC_incremental' linker error in pthread_start +* Fix GC_VSNPRINTF in cordprnt for DJGPP and MS VC for WinCE +* Fix GC_register_disclaim_proc for leak-finding mode +* Fix a deadlock in write_fault_handler if AO_or is emulated +* Fix comment typos in CMakeLists.txt, backgraph.c, de.c, gcconfig.h +* Fix concurrent bitmap update in GC_dirty +* Fix delete operator redirection if gc_cpp is built as .dll (Cygwin, MinGW) +* Fix hbp overflow in GC_install_counts +* Fix linkage with a system libatomic_ops shared library +* Fix lock assertion violation in get_index if GC_ALWAYS_MULTITHREADED +* Fix marking of finalizer closure object +* Fix marks and hb_n_marks consistency when disclaim returns true +* Fix memory allocation on GCF (Linux/x64) +* Fix missing curses.h in cord/de when compiling manually (MS VC, MinGW) +* Fix start_world not resuming all threads on Darwin +* Fix test_cpp assertion violation in find-leak mode +* Fix tests linkage with internal atomic_ops.o +* Fix unneeded end_stubborn_change in disclaim_test +* Guard against potential buffer overflow in CORD_next and CORD_pos_fetch +* New macro to suppress printing of leaked objects +* Prevent double inclusion of javaxfc.h and private/specific.h +* Reduce scope of local variables in GC_remove_all_threads_but_me +* Refine HIDE_POINTER documentation for the case of the leak-finding mode +* Refine documentation in gc_disclaim.h +* Test marking of finalizer closure object in disclaim_test +* Update documentation about arm64 ABI in gcconfig.h +* Use AO_or in async_set_pht_entry_from_index if available +* Use include gc.h with the angle brackets in the man page synopsis + + +== [7.6.8] 2018-08-12 == + +* Add cpu, make_as_lib, nothreads options to NT_MAKEFILE +* Add NetBSD/aarch64 and initial RISC-V support +* Adjust formatting of configure help messages and config.h comments +* Avoid multiple 'getcontext failed' warnings if getcontext is broken +* Cleanup BCC Makefile (remove absolute GC paths, fix del cmd, update clean) +* Collapse multiple NT_*_MAKEFILE scripts into a single NT_MAKEFILE +* Do not call GC_dirty_inner unless GC_incremental +* Do not use NULL in gc_inline.h +* Eliminate 'cast between incompatible function types' compiler warning +* Eliminate 'comparing signed and unsigned values' compiler warnings (bcc) +* Eliminate 'condition is always true' cppcheck warning in init_gcj_malloc +* Eliminate 'declaration of var hides global declaration' compiler warning +* Eliminate 'language extension used' Clang warning in gc.h +* Eliminate 'possibly incorrect assignment in CORD_vsprintf' compiler warning +* Eliminate 'ptr arithmetic with NULL' cppcheck warning in alloc_mark_stack +* Eliminate 'scope of var can be reduced' cppcheck warning in pthread_join +* Eliminate 'switch statement contains no case label' compiler warning +* Eliminate 'variable might be uninitialized' warning in win32_start_inner +* Eliminate duplicate clear_mark_bit call when removing disappearing link +* Fast fail on invalid CPU parameter passed to NT_MAKEFILE +* Fix 'collecting from unknown thread' abort in leak-finding mode +* Fix 'pointer arithmetic with NULL' code defect in print_callers +* Fix Borland version in documentation to match that in BCC_MAKEFILE +* Fix comment about inv_sz computation in setup_header +* Fix comments style in configure.ac and Makefile.am +* Fix compilation by digimars.mak (DMC) +* Fix compilation by WCC makefile +* Fix compilation of darwin_stop_world for iOS 8+ +* Fix cords for MANUAL_VDB +* Fix dependency on gc_cpp source in BCC_MAKEFILE and NT_MAKEFILE +* Fix GC_is_valid_displacement and GC_is_visible for non-small objects +* Fix gctest in leak-finding mode +* Fix infinite restarting of mark_some when a static root disappeared (Linux) +* Fix large object base computation in PUSH_CONTENTS() if MARK_BIT_PER_OBJ +* Fix mark stack overflow checking in push_selected +* Fix missing GC_dirty calls for GC-allocated objects used internally +* Fix missing GC_dirty invocation from debug_end_stubborn_change +* Fix MSWIN32 macro redefinition (WCC) +* Fix multi-threaded gctest for the case of NTHREADS is set to zero +* Fix new and delete operators definition for DigitalMars compiler +* Fix NT_MAKEFILE for VS 2017 +* Fix potential null dereference in GC_CONS +* Fix register_dynamic_libraries on Windows 10 +* Fix result computation in n_set_marks +* Fix return type in GC_set_warn_proc API documentation +* Fix tests for GC compiled with MANUAL_VDB +* Fix the build for Emscripten +* Fix typo in comment for CORD_ec_flush_buf prototype +* Fix typos in ChangeLog and generic_malloc +* Fix UNTESTED for multi-threaded API functions in gctest +* Fix VirtualQuery call in case of malloc failure (Win32) +* Install gc.3 man page instead of copying gc.man to doc folder (configure) +* Keep pointer to the start of previous entry in remove_specific_after_fork +* Move de_win compiled resource files to cord/tests +* Never return null by C++ GC allocators and gc_cpp operator new +* Perform thread_suspend in loop as it may be interrupted (Darwin) +* Really abort if failed to read /proc for library registration (Linux) +* Remove code duplication in gcj_malloc and malloc_explicitly_typed +* Remove duplicate local variable in reclaim_block +* Remove information how to send bugs from README.cords file +* Remove libatomic_ops license information +* Remove unused USE_GENERIC macro definition and description +* Suppress 'functions containing switch are not expanded inline' bcc warning +* Suppress 'non-member operator new/delete may not be inline' VC++ warning +* Turn on incremental collection in gctest also if MANUAL_VDB +* Update copyright information in alloc.c, gc.c/h and the documentation +* Update EXTRA_DIST in Makefile, Win32/64 docs after NT_*_MAKEFILE removal +* Update NT_MAKEFILE usage information in README files for Win32 and Win64 +* Workaround 'class C does not have a copy constructor' cppcheck warning +* Workaround 'function nested_sp is never used' cppcheck style warning +* Workaround 'opposite expression on both sides of &' cppcheck style warning +* Workaround 'template-id not supported in this context' compiler error (WCC) + + +== [7.6.6] 2018-04-20 == + +* Define GC_FREEBSD_THREADS and GC_ADD_CALLER macros for kFreeBSD +* Eliminate 'boolean result used in bitwise operation' cppcheck warning +* Eliminate 'there is pointer arithmetic with NULL' cppcheck warning +* Explicitly unblock GC signals on GC initialization +* Fix 'scope of var can be reduced' cppcheck err in enqueue_all_finalizers +* Fix 'undefined reference to __builtin_unwind_init' linker error (ArmCC) +* Fix arguments delimiter in pcr_interface.c (PCR) +* Fix assertion violation in DllMain of win32_threads +* Fix comment for debug_generic_malloc_inner[_ignore_off_page] +* Fix data race during apply_to_each_object(reset_back_edge) +* Fix dbg_mlc.c/o file name in documentation +* Fix gctest with musl libc on s390x +* Fix include gc_gcj.h in thread_local_alloc.c +* Fix man section number (3) +* Fix missing GC_generic_malloc_words_small implementation in new_gc_alloc.h +* Fix missing new-line in ABORT_ARG definition +* Fix missing SIGBUS handler setup for kFreeBSD +* Fix null dereference in print_callers on backtrace_symbols failure +* Fix null pointer dereference in get_private_path_and_zero_file (Symbian) +* Fix the collector hang when it is configured with --enable-gc-debug +* Fix thread_suspend fail for threads registered from key destructor (OS X) +* Fix type of local variables receiving result of PHT_HASH +* Fix typo in AIX macro name +* Fix typo in comment in specific.h +* Fix unbounded heap growth in case of intensive disappearing links usage +* Remove API symbols renaming in WCC_MAKEFILE +* Support Haiku/x64 and Haiku/x86 hosts +* Support threads for DragonFly in configure +* Workaround 'address of auto-variable returned' cppcheck error +* Workaround gctest hang on kFreeBSD (if thread-local allocations are on) + + +== [7.6.4] 2018-01-26 == + +* Add note of set_free_space_divisor, set_warn_proc ABI change after gc-7.1 +* Change compiler invocation example in gc.man to use dynamic libgc +* Delete dont_ar_* build intermediate files on make clean (Makefile.direct) +* Do not declare dl_iterate_phdr as weak for DragonFly +* Fix 'cords' parallel build in Makefile.direct +* Fix 'undeclared identifier USRSTACK' compiler error on OpenBSD-6.2 +* Fix error code in abort message if sem_wait failed in start_world (NetBSD) +* Fix GC allocation mutex in child after a fork +* Fix global operator delete definition for C++14 in gc_cpp +* Fix last_reclaimed..gc_no interval comparison to threshold in unmap_old +* Fix libgc version which was changed in linkage breaking way +* Fix missing EOLn output in threadlibs tool +* Fix threadlibs tool to output '-lpthread' for DragonFly +* Prevent DATASTART redefinition for NaCl +* Remove obsolete advice about linking with _DYNAMIC=0 (Linux) + + +== [7.6.2] 2017-12-23 == + +* Add assertion that no hb_n_marks underflow occurs +* Add minimal testing of GC_MALLOC_[ATOMIC_]WORDS and GC_CONS (gctest) +* Add minimal testing of GC_set_bit (gctest) +* Add more cases to huge_test to cover sizes close to word-type maximum +* Add testing of new[]/delete[] (test_cpp) +* Adjust AO_HAVE_x check to match AO_fetch_and_add primitive variant used +* Adjust code indentation of calloc_explicitly_typed +* Align local_mark_stack in help_marker explicitly +* Allow custom TRACE_ENTRIES value +* Allow gctest and thread_leak_test with zero NTHREADS +* Avoid data race in finalized_count (gctest) +* Code refactoring of divide-by-HBLKSIZE occurrences +* Code refactoring of huge_test +* Code refactoring of tests/subthread_create regarding AO add primitive +* Compile thread_local_alloc only if multi-threaded build (Makefile.am) +* Delete preprocessor output on make clean (Makefile.direct) +* Disable implicit multi-threaded mode for Win32 to avoid LOCK crash +* Do not disable parallel mark for WRAP_MARK_SOME +* Do not enable mprotect-based incremental mode if unmapping is on (gctest) +* Do not install documentation if configure --disable-docs (new option) +* Do not use tkill (Android) +* Document base and size of objects allocated by finalized_malloc +* Document configure 'syntax error' issue in README +* Eliminate 'address of local variable returned' static analyzer warning +* Eliminate 'array vs singleton' code defect in typed_test (gctest) +* Eliminate 'assigned value never used' CSA warning in min_bytes_allocd +* Eliminate 'boolean result used in bitwise op' cppcheck false warning +* Eliminate 'C-style pointer casting' cppcheck style warnings in test +* Eliminate 'checking if unsigned variable is <0' cppcheck style warning +* Eliminate 'class member var with name also defined in parent' warning +* Eliminate 'comparison is always false' static analyzer warning in finalize +* Eliminate 'Condition 0==datastart always false' cppcheck warning (dyn_load) +* Eliminate 'condition is always true' cppcheck style warning +* Eliminate 'constructor with 1 argument is not explicit' cppcheck warning +* Eliminate 'CORD_*printf is never used' cppcheck style warnings (cordtest) +* Eliminate 'dereference of null' CSA false warning in array_mark_proc +* Eliminate 'function result not used' code defect in GC_mark_local +* Eliminate 'GC_collecting is set but never used' code defect (Win32) +* Eliminate 'GC_record_fault is never used' cppcheck style warning +* Eliminate 'integer shift by a negative amount' code defect in finalize +* Eliminate 'label not used' cppcheck false warnings in GC_mark_X +* Eliminate 'memory leak' code defect for scratch-allocated memory +* Eliminate 'memory leak' code defect in remove_specific +* Eliminate 'non-null arg compared to null' warning in toggleref_add (GCC) +* Eliminate 'non-reentrant function strtok called' cppcheck warning (POSIX) +* Eliminate 'possible integer underflow' code defect (cord-de) +* Eliminate 'potential overflow' static analyzer warning in test +* Eliminate 'printf format specifies type void*' GCC pedantic warnings +* Eliminate 'scope of variable can be reduced' cppcheck warnings +* Eliminate 'suspicious pointer subtraction' cppcheck warning (gc_cpp) +* Eliminate 'this statement may fall through' GCC warnings +* Eliminate 'unnecessary comparison of static strings' cppcheck warning +* Eliminate 'unsafe vsprintf is deprecated' compiler warning +* Eliminate 'unused formal parameter' compiler warnings in C++ code (MS VC) +* Eliminate 'unused variable' compiler warning in remove_all_threads_but_me +* Eliminate 'use of vulnerable sprintf' code defect in de_win test (cord) +* Eliminate 'value exceeds maximum object size' GCC warning in huge_test +* Eliminate 'value of CLOCK_TYPE unknown' cppcheck info message +* Eliminate 'value of DATASTART2 unknown' cppcheck info messages +* Eliminate 'value of GC_PTHREAD_EXIT_ATTRIBUTE unknown' cppcheck messages +* Eliminate 'value of GC_RETURN_ADDR_PARENT unknown' cppcheck info messages +* Eliminate 'value of NEED_FIXUP_POINTER unknown' cppcheck info messages +* Eliminate 'write to memory that was const-qualified' code analyzer warning +* Eliminate all 'scope of variable can be reduced' cppcheck style warnings +* Eliminate CSA warning about incorrect cast applied to HBLK_OBJS +* Eliminate CSA warning about narrowing cast in CleanUp of test_cpp +* Eliminate CSA warning of non-virtual destructor in test_cpp base class +* Eliminate CSA warning of staticroot that can be a local variable (tests) +* Eliminate CSA warning of unmodified non-const static var (disclaim_test) +* Eliminate redundant local variable in register_finalizer +* Eliminate TSan (Thread Sanitizer) warnings in gctest +* Eliminate UBSan warning of overflow during descr subtraction in mark_from +* Eliminate unreachable PROC/DEFAULT_VDB GC_printf calls in gctest main() +* Eliminate unsigned fl_builder_count underflow in mark_thread +* Enable GC_is_tmp_root for all platforms +* Execute more single-threaded GC tests by CMake +* Expand tabs to spaces in de_win.rc (tests) +* Export GC_dump_finalization/regions() +* Export GC_is_tmp_root() and GC_print_trace[_inner]() +* Export GC_print_free_list() +* Fix '32-bit value shift followed by expansion to 64-bit' code defect +* Fix 'GC_written_pages never read' code defect (GWW_VDB) +* Fix 'label cannot be reached' static analyzer warning in disclaim_test +* Fix 'size of tv is unknown' error in brief_async_signal_safe_sleep (musl) +* Fix 'syntax error' reported by cppcheck for mach_dep +* Fix 'unknown type name GC_INNER' compilation error (FreeBSD) +* Fix 'variable assigned a value that is never used' cppcheck style warnings +* Fix 'void pointers in calculations: behavior undefined' cppcheck warning +* Fix assertion violation about disabled cancel in try_to_collect_inner +* Fix atomic_ops build in Makefile.direct for Solaris +* Fix Clang static analyzer warning about not found gc_priv.h in extra files +* Fix compilation error in get_main_stack_base (Emscripten) +* Fix compilation for winpthreads if HANDLE_FORK +* Fix compilation if configured with --enable-werror on OS X +* Fix cord/de build in Makefile.direct (Linux) +* Fix data race in a list referenced by A.aa (gctest) +* Fix data race in collectable_count (gctest) +* Fix data race in do_local_mark when comparing active_count to helper_count +* Fix data race in GC_suspend/resume_thread +* Fix data race in last_stop_count access (suspend_handler_inner) +* Fix data race in make_descriptor when setting explicit_typing_initialized +* Fix data race in mark_thread when updating mark_no +* Fix data race when getting object size in explicitly-typed allocators +* Fix deadlock in GC_suspend_thread +* Fix gctest failure for Darwin if CPPCHECK is defined +* Fix lack of barriers to synchronize memory for suspend_handler +* Fix marking of disclaim-reachable objects in the incremental mode +* Fix message of VDB implementation used if MPROTECT_VDB+GWW_VDB (gctest) +* Fix missing started_thread_while_stopped call from mark_some if GCC/Clang +* Fix null dereference in GC_stack_range_for if not DARWIN_DONT_PARSE_STACK +* Fix page calculation in checksums +* Fix parallel build in Makefile.direct +* Fix test_cpp and c++ parallel build in Makefile.direct +* Fix typo in comment of GC_mark_some +* Fix typos in cdescr.html and README.sgi +* Make GC_INIT optional for clients even if thread-local allocations enabled +* Match uclinux pattern in configure +* Move conditional GC_need_to_lock setting to gc_locks.h (refactoring) +* Move README.QUICK from DOC_FILES to OTHER_FILES in Makefile.direct +* New API function (GC_is_init_called) to check if BDWGC is initialized +* New target (check-cpp) in Makefile.direct +* Prevent abort in register_data_segments for Symbian and Emscripten +* Prevent multiple 'Caught ACCESS_VIOLATION in marker' per collection +* Print realloc_count value in gctest +* Put invariant name in quotes to make mark_state comments clearer +* Refine configure messages when checking for compiler option support +* Remove extraneous semicolons after AC_MSG_WARN (configure) +* Remove page_was_dirty and remove_protection duplicate definitions +* Remove unnecessary type casts of printf arguments to unsigned long +* Remove unused ALIGN_DOUBLE, USE_GENERIC_PUSH_REGS macros (TILE-Gx/Pro) +* Rename 'test' to 'check' target in Makefile.direct +* Replace deprecated rewind to fseek in cordxtra +* Report gcc/clang pedantic warnings (configure) +* Skip thread suspend/resume API testing for Tru64 (OSF1) +* Support AddressSanitizer (Clang/GCC) and MemorySanitizer (Clang) +* Support GC_init (and get_stack_base) from non-main thread on FreeBSD/NetBSD +* Suppress 'tainted string passed to vulnerable operation' false defects +* Suppress 'taking address of label non-standard' GCC/Clang pedantic warning +* Test GC initialization from non-main thread on FreeBSD and NetBSD +* Test GCJ object creation with length-based descriptor (gctest) +* Update comment in finalized_disclaim to match FINALIZER_CLOSURE_FLAG +* Update README regarding make cords with Makefile.direct +* Update README to use autogen.sh on build from the source repository +* Update shared libraries version info to differentiate against v7.4 +* Use mprotect instead of mmap in GC_unmap() on Cygwin +* Use same style of include gc.h in documentation +* Workaround '!GC_page_size is always false' cppcheck style warning +* Workaround '#error' cppcheck error messages +* Workaround '32-bit value shift by >31 bits is undefined' cppcheck warnings +* Workaround 'array compared to 0', 'untrusted loop bound' false defects +* Workaround 'bad address arithmetic' static analysis tool false positive +* Workaround 'checking if unsigned value is negative' cppcheck warning +* Workaround 'checking unsigned value is negative' code defect in mark_from +* Workaround 'comparison of identical expressions' false code defects +* Workaround 'Condition 0!=GETENV() is always false' cppcheck style warnings +* Workaround 'condition is always false' cppcheck warning in get_next_stack +* Workaround 'condition is always true' cppcheck style warnings in GC_init +* Workaround 'function is never used' cppcheck style warnings +* Workaround 'insecure libc pseudo-random number generator used' code defect +* Workaround 'int shift by negative amount' false code defect in finalize +* Workaround 'local variable size too big' static analyzer warning +* Workaround 'memory leak: result' cppcheck false error (POSIX) +* Workaround 'null pointer dereference' false positive in push_next_marked +* Workaround 'obsolescent bcopy, bzero called' cppcheck warnings (POSIX) +* Workaround 'obsolescent usleep called' cppcheck warning (POSIX) +* Workaround 'obsolete function alloca() called' cppcheck warnings +* Workaround 'passing untyped NULL to variadic function' cppcheck warning +* Workaround 'pointer used before comparison to null' code defect (pthread) +* Workaround 'possible null pointer dereference' cppcheck warnings +* Workaround 'potential multiplication overflow' code defect in de_win (cord) +* Workaround 'redundant assignment of *result to itself' cppcheck warning +* Workaround 'resource leak' false positives in alloc_MS, bl/envfile_init +* Workaround 'same expression on both sides of ==' cppcheck style warning +* Workaround 'same expression on both sides of OR' cppcheck style warning +* Workaround 'struct member is never used' cppcheck style warnings +* Workaround 'tainted int used as loop bound' static analysis tool warning +* Workaround 'Uninitialized variable' cppcheck errors +* Workaround 'unused variable' cppcheck style warnings +* Workaround 'va_list used before va_start' cppcheck error in cord +* Workaround 'value of macro unknown' cppcheck info messages +* Workaround 'value of REDIRECT_MALLOC/FREE unknown' cppcheck info messages +* Workaround 'value of SIGBUS unknown' cppcheck info messages +* Workaround 'value of WINAPI unknown' cppcheck info messages +* Workaround 'variable hides enumerator with same name' cppcheck warnings +* Workaround 'variable reassigned before old value used' cppcheck warnings +* Workaround 'waiting while holding lock' code defect in stop_world (Unix) +* Workaround false 'uninitialized var use' code defect (initsecondarythread) + +Also, includes 7.4.6 changes + + +== [7.6.0] 2016-08-02 == + +* ABORT_ARGn log details at INFO level (Android) +* Add 'pragma message' to gc.h to detect inconsistent WIN64/_WIN64 (MS VC) +* Add API function to calculate total memory in use by all GC blocks +* Add API function to set/modify GC log file descriptor (Unix) +* Add alloc_size attribute to GC_generic_malloc +* Add alt-stack registration support +* Add assertion for GC_new_kind boolean arguments +* Add assertion on lock status to GC_alloc_large and its callers +* Add build scripts for VC 9 (Win32/64) +* Add build system plumbing for building with -Werror +* Add incremental GC support for Darwin/arm64 +* Add profiling callback events to indicate start/end of reclaim phase +* Add support for enumerating the reachable objects in the heap +* Add toggle-ref support (following Mono GC API) +* Added instructions to README.md for building from git +* Adjust code indentation of malloc/calloc/str[n]dup +* Allow fork() automatic handling on Android with API level 21+ +* Allow specific TLS attributes for GC_thread_key +* Allow thread local allocations from within pthread TLS destructors +* Allow to force GC_dump_regularly set on at compilation +* Altera NIOS2 support +* Change 'cord' no-argument functions declaration style to ANSI C +* Check DATASTART is less than DATAEND even assertions off +* Check for execinfo.h by configure +* Code refactoring of GC_push_finalizer/thread/typed_structures +* Code refactoring regarding 'data start' definition for FreeBSD +* Consistently set type of DATASTART/END to ptr_t (code refactoring) +* Consistently use int[] type for '_end' symbol (code refactoring) +* Consistently use outermost parentheses for DATASTART/END, STACKBOTTOM +* Define GC_LINUX_THREADS, NO_EXECUTE_PERMISSION in configure for NaCl +* Define ROUNDUP_PAGESIZE, ROUNDUP_GRANULE_SIZE macros (code refactoring) +* Define public GC_GENERIC_OR_SPECIAL_MALLOC and GC_get_kind_and_size +* Do no declare kernel_id field of GC_Thread_Rep for 64-bit Android +* Do not allow SHORT_DBG_HDRS if KEEP_BACK_PTRS or MAKE_BACK_GRAPH +* Do not warn of missing PT_GNU_RELRO segment when custom DSO filter used +* Document GC_register_my_thread returned value +* Dump the block information in CSV format +* Eliminate redundant *flh check for null in GC_allocobj +* Enable atomic-uncollectable in operator new in gc_cpp.h +* Enable build with musl libc +* Enable gc.h inclusion by client without implicit include windows.h (Win32) +* Enable huge_test for Win64 (and LLP64 target) +* Enable thread-local storage for Android Clang +* Enable thread-local storage usage for GC_malloc/calloc_explicitly_typed +* Export GC_push_all_eager, GC_push_finalizer_structures +* Fix 'arg parameter might be clobbered by setjmp' compiler warning +* Fix assertion in GC_mark_from for non-heap regions +* Fix compilation for Android clang/arm with bfd linker +* Fix integer shift undefined behavior in GC_init_explicit_typing +* Fix missing new-line and redundant trailing dot in WARN messages +* Fix STACKBOTTOM for Solaris 11/x86 +* Fix tag collision between ENABLE_DISCLAIM and KEEP_BACK_PTRS +* Fix unchecked fork() result in gctest (Unix, Cygwin) +* Fix user-defined signals drop by marker threads +* Fix various typos in comments and documentation +* FreeBSD/arm support improvement +* GC_make_descriptor code refactoring (eliminate two local variables) +* GC_malloc[_atomic] global and thread-local generalization with kind +* GC_malloc_[atomic_]uncollectable generalization +* GC_scratch_alloc code refactoring (and WARN message improvement) +* Group all compact fields of GC_arrays to fit in single page +* Handle load_segs overflow in register_dynlib_callback gracefully +* Harmonize OSX/iOS configuration; enable compiling for iPhone simulator +* Implement event callbacks for profiling (following Mono GC API) +* Implement the finalization extension API +* Implement thread suspend/resume API (Linux threads only) +* Improve documentation for disappearing links in gc.h +* Make heap growth more conservative after GC_gcollect_and_unmap call +* Mark fo_head, finalize_now with a single GC_push_all call (refactoring) +* Move MessageBox invocation code from GC_abort to a separate routine (Win32) +* NaCl/arm initial support; NaCl runtime fixes for other CPUs +* New macro (GC_ALWAYS_MULTITHREADED) to set multi-threaded mode implicitly +* New macro (NO_WINMAIN_ENTRY) to prefer main() instead of WinMain in test +* New macro (REDIRECT_MALLOC_IN_HEADER) to enable source-level redirection +* Process all PT_LOAD segments before PT_GNU_RELRO segments (Glibc) +* Re-implement GC_finalized_malloc using GC_malloc_kind +* Refactoring of android_thread_kill/pthread_kill calls +* Refactoring of GC_Xobjfreelist (use single array to keep free lists) +* Refactoring of thread-local *_freelists (use single array of free lists) +* Refine description in README how to build from source repository +* Refine GC_free_space_divisor comment regarding its initial value +* Reformat code of gc_cpp.cc/h +* Remove 'opp' local variable in GC_malloc_X +* Remove 'sig' argument of GC_suspend_handler_inner (code refactoring) +* Remove code commented out by 'ifdef UNDEFINED' +* Remove hb_large_block field (use 1 extra bit of hb_flags instead) +* Remove obsolete BACKING_STORE_ALIGNMENT/DISPLACEMENT macros for Linux/IA64 +* Remove redundant casts in GC_generic_or_special_malloc and similar +* Remove unsupported FreeBSD/ia64 case from gcconfig.h file +* Remove unused GC_gcjdebugobjfreelist +* Rename ATOMIC_UNCOLLECTABLE to GC_ATOMIC_UNCOLLECTABLE +* Replace non-API occurrences of GC_word to word (code refactoring) +* Return GC_UNIMPLEMENTED instead of abort in GC_get_stack_base (OS/2) +* Show WoW64 warning message if running 32-bit on Win64 (enabled by macro) +* Standalone profiling callback for threads suspend/resume +* Support (add machine description for) TILE-Gx and TILEPro targets +* Support build for Android 64-bit (arm64, mips64, x64) +* Support FreeBSD/aarch64, FreeBSD/mips +* Support iOS7 64-bit (AArch64) and iOS8+ 32/64-bit (Darwin) +* Support MinGW build in scripts +* Turn off sigsetjmp workaround for Android/x86 starting from NDK r8e +* Use magic header on objects to improve disclaim_test +* Workaround 'sa_sigaction member missing' compiler error (Android/x32) +* Workaround 'unresolved __tls_get_addr' error for Android NDK Clang +* Workaround a bug in winpthreads causing parallel marks deadlock (MinGW) + +Also, includes 7.4.4 changes + + +== [7.4.24] 2023-05-25 == + +* Adjust CORD_ec comment placement in ec.h +* Do not mix debug and non-debug allocations in disclaim tests +* Eliminate 'cast signed to bigger unsigned' CSA warning in WARN calls +* Ensure GC_NO_PTHREAD_SIGMASK defined if no GC_pthread_sigmask prototype +* Fix GC_thread_is_registered for finished threads +* Fix GC_unregister_my_thread call before GC functions usage in gctest +* Fix missing lock while updating GC_in_thread_creation in GC_exit_check +* Fix null pointer dereference in TRACE_TARGET +* Fix of GC_bytes_allocd increment in GC_generic_malloc_inner +* Remove redundant 'ifdef THREADS' around LOCK/UNLOCK in call_with_alloc_lock + +Also, includes 7.2q changes. + + +== [7.4.22] 2022-08-26 == + +* Eliminate 'new_l may be used uninitialized' gcc warning in os_dep (Cygwin) +* Eliminate 'possible loss of data' compiler warning in GC_envfile_getenv +* Fix 'undeclared getpagesize' compiler warning on AIX and OSF1 +* Fix GC_dirty() argument in GC_malloc_explicitly_typed_ignore_off_page +* Fix SIGSEGV caused by dropped stack access from child process in gctest +* Fix abort in Win32 DllMain if PARALLEL_MARK +* Fix assertion violation of GC_thread_key alignment if pthread-based TLS +* Fix comment in GC_init regarding GC_init_parallel call +* Fix stack overflow in gctest on Alpine Linux/s390x +* Revert "Remove nested always-false ifdef for HPUX and FREEBSD" +* Use SIGRTMIN+6 as suspend signal if sigrt-signals on OpenBSD +* Workaround crash in FreeBSD rand() by avoiding its concurrent usage + +Also, includes 7.2p changes. + + +== [7.4.20] 2021-09-28 == + +* Do not hold GC_fault_handler_lock when in Sleep (Windows) +* Eliminate 'static GC_sysinfo definition has incomplete type' Clang warning +* Eliminate 'unused function GC_add_map_entry' compiler warning +* Eliminate 'while clause does not guard' GCC warning in GC_parse_map_entry +* Fix OS_TYPE and USE_MMAP_ANON definitions for Cygwin/x64 +* Fix abort in GC_printf when gctest is built as WinMain executable (Cygwin) +* Fix configure message about 'AIX gcc optimization fix' +* Fix cordtest build in SMakefile.amiga +* Prevent GetThreadContext failure (Windows) +* Refactoring of WoW64 workaround (Win32) + +Also, includes 7.2o changes + + +== [7.4.18] 2019-03-01 == + +* Fix 'wrong __data_start/_end pair' error on Android +* Fix thread_info() count argument value (OS X) + +Also, includes 7.2n changes + + +== [7.4.16] 2018-12-13 == + +* Fix 'collecting from unknown thread' abort in leak-finding mode for Win32 +* Fix 'undefined reference to GC_incremental' linker error in pthread_start +* Fix GC_register_disclaim_proc for leak-finding mode +* Fix concurrent bitmap update in GC_dirty +* Fix marking of finalizer closure object +* Fix marks and hb_n_marks consistency when disclaim returns true +* Fix missing curses.h in cord/de when compiling manually (MS VC, MinGW) +* Refine documentation in gc_disclaim.h + +Also, includes 7.2m changes + + +== [7.4.14] 2018-08-11 == + +* Cleanup BCC Makefile (remove absolute GC paths, fix del cmd, update clean) +* Do not call GC_dirty_inner unless GC_incremental +* Eliminate 'cast between incompatible function types' compiler warning +* Eliminate 'comparing signed and unsigned values' compiler warnings (bcc) +* Eliminate 'language extension used' Clang warning in gc.h +* Eliminate 'possibly incorrect assignment in CORD_vsprintf' compiler warning +* Eliminate 'switch statement contains no case label' compiler warning +* Eliminate 'variable might be uninitialized' warning in win32_start_inner +* Eliminate duplicate clear_mark_bit call when removing disappearing link +* Fix 'collecting from unknown thread' abort in leak-finding mode +* Fix compilation by digimars.mak (DMC) and by WCC makefile +* Fix cords for MANUAL_VDB +* Fix dependency on gc_cpp source in BCC_MAKEFILE and NT_MAKEFILE +* Fix gctest in leak-finding mode +* Fix missing GC_dirty calls for GC-allocated objects used internally +* Fix missing GC_dirty invocation from debug_end_stubborn_change +* Fix multi-threaded gctest for the case of NTHREADS is set to zero +* Fix typos in ChangeLog and generic_malloc +* Keep pointer to the start of previous entry in remove_specific_after_fork +* New API function (GC_is_init_called) to check if BDWGC is initialized +* Remove code duplication in gcj_malloc and malloc_explicitly_typed +* Remove duplicate local variable in reclaim_block +* Remove libatomic_ops license information from README +* Workaround 'dynamic exception specifications deprecated in C++11' warning + +Also, includes 7.2l changes + + +== [7.4.12] 2018-04-19 == + +* Define GC_FREEBSD_THREADS and GC_ADD_CALLER macros for kFreeBSD +* Fix comment for debug_generic_malloc_inner[_ignore_off_page] +* Fix gctest with musl libc on s390x +* Fix missing new-line in ABORT_ARG definition +* Fix null pointer dereference in get_private_path_and_zero_file (Symbian) +* Fix type of local variables receiving result of PHT_HASH +* Remove API symbols renaming in WCC_MAKEFILE + +Also, includes 7.2k changes + + +== [7.4.10] 2018-01-22 == + +* Fix error code in abort message if sem_wait failed in start_world (NetBSD) +Also, includes 7.2j changes + + +== [7.4.8] 2017-12-22 == + +* Eliminate 'this statement may fall through' GCC warnings +* Eliminate 'value exceeds maximum object size' GCC warning in huge_test +* Fix data race in make_descriptor when setting explicit_typing_initialized +* Fix marking of disclaim-reachable objects in the incremental mode +* Update comment in finalized_disclaim to match FINALIZER_CLOSURE_FLAG +Also, includes 7.2i changes + + +== [7.4.6] 2017-10-26 == + +* Add configure --enable-gcov option (enable code coverage analysis) +* Add configure check whether to define NO_GETCONTEXT +* Adjust GC_memalign comment +* Allow HAVE_DL_ITERATE_PHDR to be defined by client (musl) +* Allow PKG_CHECK_MODULES in configure.ac to be commented out easily +* Avoid busy waiting in mark_thread while GC_parallel is false +* Better document minimum value of size argument for typed allocations +* Change type of THREAD_TABLE_INDEX result to int in win32_threads.c +* Consistently use 'msec' instead of 'ms' in comments in pthread_support +* Do not define amiga_get_mem, MacTemporaryNewPtr unless really used (extra) +* Do not produce .tar.bz2 distribution file (configure) +* Do not require libatomic_ops for single-threaded builds (configure) +* Do not warn of missing PT_GNU_RELRO segment when custom DSO filter used +* Document GWW_VDB in gcdescr.html +* Eliminate 'cast to void* from int' compiler warnings (Darwin/x64) +* Eliminate 'conditional expression is always true' code defect in GC_init +* Eliminate 'FP divide-by-zero' static analyzer warning +* Eliminate 'incompatible function pointer' warning in mark_some (MinGW/x86) +* Eliminate 'ISO C forbids an empty translation unit' GCC pedantic warning +* Eliminate 'ISO C forbids object to function pointer conversion' warning +* Eliminate 'locally defined symbol imported' MS linker warnings (cord) +* Eliminate 'null dereference' code defect warning in register_finalizer +* Eliminate 'possible loss of data' compiler warnings (MS VC) +* Eliminate 'printf format specifier mismatch' compiler warning (tools) +* Eliminate 'type defaults to int in declaration' warning (REDIRECT_MALLOC) +* Eliminate 'value stored is never read' warning of Clang static analyzer +* Eliminate duplicate log messages in GC_mark_from +* Eliminate most of collisions in GC_threads on Linux/x64 +* Ensure GC initialized when atfork_prepare is called by client +* Fix 'arg parameter might be clobbered by setjmp' compiler warning +* Fix 'bogus LR' detection in FindTopOfStack (Darwin) +* Fix 'execvp argument incompatible pointer type' compiler warning (tools) +* Fix 'GetVersion deprecated' compiler warning in os_dep (MS VC) +* Fix 'incompatible pointer' compiler warning in GC_init_dyld (OS X 64-bit) +* Fix 'incompatible ptr-to-int conversion' compiler warning in push_all_stack +* Fix 'ISO C90 does not support %lf, %lg gnu_printf formats' GCC warnings +* Fix 'ISO C90 forbids mixed declarations and code' compiler warning +* Fix 'missing libc-version.h' build error (uClibc/x86[_64]) +* Fix 'replacement operator delete cannot be inline' GCC warning (Cygwin) +* Fix 'variable unused' compiler warning in FirstDLOpenedLinkMap +* Fix 'zero-size array is extension' Clang warning in os_dep (Linux/x86) +* Fix (adjust) GC_scratch_alloc actual argument type +* Fix deadlock in GC_help_marker caused by use of mark_cv of parent process +* Fix finalize.c compilation in 'strict ANSI' mode +* Fix GC shared library tests failure related to dl_iterate_phdr (musl) +* Fix gc.h compliance to strict ANSI (pthreads) +* Fix GC_bytes_allocd incrementation in case of allocation failure +* Fix GC_jmp_buf multiple definition +* Fix GC_noop6 definition to avoid its calls to be optimized away +* Fix gctest failure if PARALLEL_MARK (musl) +* Fix gctest thread stack overflow (musl-gcc) +* Fix initsecondarythread_test runtime failure if GC compiled w/o threads +* Fix lack of 2 trailing zeros in _MSC_VER numbers +* Fix local variable declarations in disclaim_bench +* Fix missing #error pragma +* Fix missing .exe for disclaim test filenames in Makefile (MinGW) +* Fix missing atomic/[un]collectable/realloc_count increments in gctest +* Fix missing new-line and redundant trailing dot in WARN messages +* Fix missing new-line at format strings end in subthread_create test +* Fix mixed include of GC public header and gc_priv.h in disclaim bench/test +* Fix potential overflow in decrement when computing GC_markers_m1 +* Fix printf format specifiers in extra files (cppcheck warnings) +* Fix pthread_start compilation if single-obj-compilation (Linux) +* Fix register_finalizer call in disclaim_bench for GC_DEBUG +* Fix static assertion violation in LONG_MULT for 64-bit targets +* Fix tag collision between ENABLE_DISCLAIM and KEEP_BACK_PTRS +* Fix thread id leaks in subthread_create and threadkey_test +* Fix threaded tests runtime crash if GC_NO_THREAD_REDIRECTS supplied +* Fix tools/setjmp_t to prevent nested_sp inlining +* Fix typo in CHECK_GCLIB_VERSION name (test) +* Fix typos in comments/documentation (ews4800, extend_size_map, push_roots) +* Fix unchecked fork() result in gctest (Unix, Cygwin) +* Improve detection of internal libatomic_ops (configure) +* Move libraries version info to the beginning of Makefile.am +* Prevent abort in register_data_segments for Symbian +* Process all PT_LOAD segments before PT_GNU_RELRO segments (Glibc) +* Refine Makefile.direct comment about multi-threaded GC build +* Refine README about library source downloading +* Refine should_invoke_finalizers documentation +* Remove all generated files by NT_X64_THREADS_MAKEFILE 'clean' target +* Remove non-existent configure option in simple_example.html +* Replace C++ style comments to C ones, remove commented out code (extra) +* Support CFLAGS_EXTRA to pass extra user-defined compiler flags (configure) +* Support CFLAGS_EXTRA when checking for inline and dladdr (configure) +* Suppress 'tainted string passed to vulnerable operation' false defects +* Suppress MS VC warnings about unused param, const condition (NT_MAKEFILE) +* Update bdwgc mailing list online archive link in documentation +* Update shared libraries version info to differentiate against v7.2 +* Use AC_DEFINE for defining NO_GETCONTEXT in configure +* Workaround 'index out of bounds' UBSan false warning in push_marked +* Workaround 'mmap() resource handle leak' static analyzer warning +* Workaround 'redundant assignment of *result to itself' cppcheck warning +* Workaround 'resource leak' error reported by cppcheck (tools, test) +Also, includes 7.2h changes + + +== [7.4.4] 2016-05-25 == + +* Allow GC_FAST_MALLOC_GRANS() multiple use in a function +* Also enable the TSX workaround for Linux/x86 +* Avoid unstructured procfs on Solaris +* Change cord/de main() declaration style from K-R to ANSI C +* Change no-argument functions declaration style to ANSI C (cord) +* Do not include sigcontext.h and asm/sigcontext.h +* Eliminate 'divide by zero' compiler warning in cordtest +* Eliminate warning about 64-bit pointer-to-int cast (Win64/pthreads-w32) +* Eliminate warnings detected by Cppcheck in cord de[_win] +* Fix 'comparison of non-null parameter is always false' warning (Clang) +* Fix 'CORD_iter5 unused result' code defect in cordxtra +* Fix 'GC_generic_malloc_inner_ignore_off_page not used' compiler warning +* Fix 'implicit declaration of vsnprintf' GCC warning (if strict ANSI mode) +* Fix 'signed-to-bigger-unsigned value assignment' in GC_init_size_map +* Fix 'signed-to-bigger-unsigned value assignment' warning for hb_map +* Fix 'signed-to-bigger-unsigned value assignment' warning in GC_setpagesize +* Fix 'statement unreachable' compiler warning in GC_mark_from +* Fix 'statement unreachable' compiler warning in memalign +* Fix 'unused label' compiler warning in cord/de +* Fix 'value truncated' compiler warning in CORD_cat (MS VC) +* Fix 'variable unused' warning in GC_save_callers +* Fix 'visibility attribute not supported' GCC warning (IBM AIX) +* Fix CMake warning about CMP0054 by unquoting instances of HOST +* Fix Cygwin64 build +* Fix GC_REALLOC to call GC_FREE if new size is zero and pointer is non-NULL +* Fix Makefile.direct for Cygwin +* Fix __alloc_size__ availability detection (Clang) +* Fix abort message in GC_move_long_link +* Fix and code refactoring of lock elision workaround (Linux/x64) +* Fix assertion on mark_lock_holder for non-unique NUMERIC_THREAD_ID +* Fix data race in GC_init_explicit_typing +* Fix gc.mak regarding msvc_dbg and test (MSVC) +* Fix missing error handling of pthread_attr_init/getstacksize +* Fix missing error handling of pthreads_mutex_init and cond_wait +* Fix missing numeric casts in cord +* Fix potential left shift overflows in finalize.c (64-bit targets) +* Fix pthreads-win32 name in comments and documentation +* Fix setup_mark_lock missing prototype +* Fix unchecked fcntl() result +* Fix unchecked pointer dereference in check_ints (gctest) +* Fix unchecked pthread_join() result in threadkey_test +* Fix unchecked sigdelset() result in pthread_support +* Fix undefined PTRFREE/NORMAL in gc_inline.h +* Prefix PREFETCH_FOR_WRITE with GC_ as used in gc_inline.h public header +* Relax mark_mutex attribute needed to disable elision (Linux/x64) +* Remove (deprecate) TODO file +* Remove code duplication in GC_realloc +* Remove duplicate new-line in OUT_OF_MEMORY message (cord) +* Remove references to missing linux_threads.c from documentation +* Revert "Move asm machine-dependent files to 'src' folder" (partly) +* Support Android API level 21 +* Update compiler options in gc.mak (Win32) +* Use mmap instead of sbrk (Hurd) +* Workaround 'comparison is always false' GCC warning in GC_FAST_MALLOC_GRANS +* Workaround 'identical expr on both sides of bitwise op' warning +* Workaround Linux NTPL lock elision bug +* Workaround false warning about unreachable code path +* Workaround invalid '_end' symbol on Android clang 3.5+ +Also, includes 7.2g changes + + +== [7.4.2] 2014-06-03 == + +* Add config option to use STGRTMIN-based signals for thread suspend/resume +* Allow parallel mark to be enabled on powerpc-linux systems +* Check for Fujitsu compiler in builtin_unwind logic (enable FX10/K-Computer) +* Fix 'Array subscript is above array bounds' GCC warning in GC_new_kind/proc +* Fix 'attribute declaration must precede definition' warning (clang-3.1) +* Fix (enable) Cygwin-64 build +* Fix GC_finalized_malloc failure on disclaim_test +* Fix GC_sig_suspend initialization when non-constant SIGRTMIN used +* Fix MS VC redefinition warning for functions declared with GC_ATTR_MALLOC +* Fix TEXT() usage for concatenated strings in GC_CreateLogFile (Win32) +* Fix data roots registration for Android/x86 and NDK ARM 'gold' linker +* Fix find stackbottom on BlueGene P/Q systems +* Fix machdep .lo files path in configure (SPARC, IA-64) +* Fix ok_init assignment (missing cast) in GC_new_kind_inner +* Fix typos in names in AUTHORS and ChangeLog files +* Remove barrett_diagram file duplicated by tree.html +* Remove non-existing DISCARD_WORDS from GC data structure ASCII diagram +* Restore contribution information for ancient releases in ChangeLog +Also, includes 7.2f changes + + +== [7.4.0] 2013-11-17 == + +* Add 'bytes reclaimed' counters to public GC_prof_stats_s +* Add AArch64 (64-bit ARM) target support +* Add GC_LONG_REFS_NOT_NEEDED ifdefs to exclude long link functionality +* Add GC_get_prof_stats[_unsafe]() to GC public API +* Add GC_push_all/conditional() to GC public API +* Add assertion on number_of_objs to GC_extend_size_map +* Add assertion to GC_enable() ensuring no counter underflow +* Add assertion to LOCK definition that lock is not already held +* Add assertion to LONG_MULT and remove useless assert in PUSH_CONTENTS_HDR +* Add double-lock assertion to GC_acquire_mark_lock +* Add manual POSIX fork handling support (Android) +* 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 GC_start_mark_threads() to allow parallel marker in fork child +* Add public setter and getter for GC_push_other_roots +* Add support of Android logger +* Add tests for GC_register/move/unregister_long_link +* Add thread suspend/resume signals public setters (POSIX threads) +* Added long weakref support +* Adjust GC_dont_expand/gc/precollect and GC_print_stats type to match gc.h +* Adjust README.md title and references to doc .html files in it +* Adjust build scripts to enable additional test library in staticrootstest +* Adjust logged messages in start_mark_threads and GC_thr_init +* Adjust printf format specifiers in GC_print_trace +* Allow not to rely on __data_start value (Linux) +* Allow pthread_kill error code logging in GC_suspend/resume (debugging) +* Allow to compile GC_inner_start_routine aside from extra/gc.c +* Allow to omit libc atexit() call +* Avoid LOCK/UNLOCK hard-coding in gc_locks.h for PS3 target +* Better document GC_warn_proc in gc.h +* Call GC_on_abort (with NULL argument) on exit(1) +* Call GC_stats/verbose_log_printf instead of GC_log_printf if print_stats +* Change policy regarding version numbers ("micro" part instead of "alpha") +* Changed C99-style designated init of GC_dl_hashtbl struct to use C89-style +* Check GC_base result in GC_print_all_smashed_proc +* Check that SIG_SUSPEND and SIG_THR_RESTART are different (Pthreads) +* Check traceable_allocator.allocate result before dereference in test_cpp +* Code refactoring of GC_x_printf (move shared code to macro) +* Convert readme to markdown +* Default to use libc_stack_end in single-threaded GC on glibc targets +* Define GC_VSNPRINTF internal macro in misc.c (code refactoring) +* Define functions in darwin_semaphore.h as inline instead of static +* Define old_bus_handler static variable only if used (Unix) +* Detect dladdr() presence by configure +* Disable find-leak GC_gcollect on GC abnormal EXIT +* Do not define _setjmp/_longjmp macros in mach_dep.c +* Do not duplicate android_log_write output to GC log file (Android) +* Do not include sigcontext.h if NO_SIGCONTEXT_H (Linux) +* Do not set GC_lock_holder by call_with_alloc_lock if assertions disabled +* Do not use pthread_getattr_np if NO_PTHREAD_GETATTR_NP specified +* 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 'unused variable' compiler warning in start_mark_threads (HP/UX) +* Eliminate Clang warning for GC_pthread_exit attribute +* Eliminate GCC warning about uninitialized 'hhdr' in GC_allochblk_nth +* Eliminate GCC warning in GC_get_main_stack_base (OpenBSD) +* Eliminate GCC warnings in setjmp_t.c, test_cpp and cord 'de' app +* Eliminate GC_first_nonempty atomic value reload in GC_mark_local assertion +* 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 +* Enable on-demand debug logging in GC_FindTopOfStack (Darwin) +* Enable prefetch operations by default (GCC 3.0+) +* Enable staticrootstest for the case of GC shared library build +* Enable thread-local allocation support for Clang on Cygwin +* Explicitly specify that Darwin, Linux and Solaris platforms have dladdr +* Fix ABORT definition for mingw32ce (WinCE) +* Fix AM_CONFIG_HEADER in configure for autoconf-2.69-1 +* Fix GC_CreateThread and GC_beginthreadex definition for Cygwin +* Fix GC_INIT_CONF_ROOTS in gc.h for Android +* Fix GC_INLINE definition to comply with ISO C90 standard (GCC) +* Fix GC_remove_all_threads_but_me for Android (fork support) +* Fix debug_register_displacement calls from GC_debug_generic_malloc_inner +* Fix dyn_load.c compilation for Android 4.3 +* Fix make disclaim_test to link with new GNU ld linking rules +* Improve GC error printing atomicity in GC_debug_X and GC_print_obj +* Improve GC output atomicity in GC_print_obj, GC_print_all_errors +* Improve debug-only messages of add/remove_roots and init_linux_data_start +* Improve fork test logging in gctest +* Improve logged messages about heap size and usage +* Improve logging for Android differentiating messages by log level +* Improve staticrootstest (add global data to library, add lib w/o GC_INIT) +* 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 +* Make GC_generic_malloc_ignore_off_page() public +* Make GC_mark_lock_holder variable static +* Make GC_print_trace always thread-safe and remove 'lock' argument +* Mark GC_started_thread_while_stopped() as GC_INNER +* Minimize code duplication in GC_mark_and_push +* Move 'include setjmp.h' from mach_dep.c to gc_priv.h +* Move GC_OPENBSD_UTHREADS definition to private/gcconfig.h (OpenBSD) +* Move GC_get_suspend/thr_restart_signal to misc.c for NaCl and OpenBSD +* Move LOCK/UNLOCK from GC_unregister_disappearing_link_inner outer +* Port BDWGC to Android/x86 +* Postpone the suspend signal in GC_dirty_init only if used to stop world +* Prepend '#' symbol to GC number in logged messages +* Prevent POSIX fork if mprotect_thread is started (Darwin) +* Prevent abort on GC_err/warn_printf write failure +* Prevent misleading AC_MSG_ERROR/AS_IF errors reported in configure.ac +* 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 +* Redirect WRITE to __android_log_write if GC_ANDROID_LOG (Android) +* Refine comment of GC_is_heap_ptr and GC_thread_is_registered in gc.h +* Register dynamic libraries via dl_iterate_phdr on Android and OpenBSD +* Remove DebugBreak on WriteFile failure (Win32) +* Remove GC_BUILD definition from build scripts +* Remove abort on open log failure from GC_write (Win32) +* Remove configure.ac outdated revision number +* Remove nested EXPECT in GC_core_finalized_malloc +* Remove nested always-false ifdef for HPUX and FREEBSD +* Remove redundant GC_err_printf before abort +* Remove unused UTHREAD_SP_OFFSET macro (OpenBSD) +* Rename subthread_create to subthreadcreate_test (Makefile) +* Replace GC_COND_LOG_PRINTF calls with WARN for allocation failure messages +* Replace GC_log/err_printf() followed by ABORT with ABORT_ARGn() +* Replace GC_stats_log_printf with GC_DBG/INFOLOG_PRINTF +* Replace SIG_SUSPEND/THR_RESTART macros to variables in pthread_stop_world +* Replace Win32 GC_delete_gc_thread with GC_delete_gc_thread_no_free +* Replace conditional GC_log_printf calls with GC_COND/VERBOSE_LOG_PRINTF +* Replace sprintf with defensive snprintf +* Replace var-args GC_noop with GC_noop6 (to eliminate Clang warning) +* Simplify LOCK/UNLOCK macro definition for static code analysis tools +* Specify GC_malloc result is unused in some tests +* Specify GC_pthread_join result is unused in threadkey_test +* Specify LT_INIT in configure.ac +* Start of port to QNX +* Support rthreads introduced in OpenBSD 5.2+ +* Suppress 'GC_dont_gc deprecated' warning in gc.h if GC_DONT_GC +* Tag GC malloc routines with alloc_size attribute for Clang 3.2+ +* Test NO_WRAP_MARK_SOME macro to suppress WRAP_MARK_SOME-specific code +* Turn off GC_LOOP_ON_ABORT functionality if GC compiled with NO_DEBUGGING +* Turn on world-stop delay logging at debug level by default for Android +* Use EXPECT in GC_COND/VERBOSE_LOG_PRINTF +* Use GC_log_printf for logging instead of GC_[err_]printf +* Use compiler TLS for Android NDK gcc/arm +* Use memcpy (BCOPY) instead of strcpy (to suppress GCC warning) +* Use pthread API to operate thread-local data on Linux if no compiler TLS +* Workaround 'ELF_DATA/EM_ALPHA redefined' warning in Android linker.h +* Workaround 'unresolved __tls_get_addr' error for Android NDK Clang +Also, includes 7.2e, 7.2d, 7.2c, 7.2b changes + + +== [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 (porting done by Djamel Magri) +* 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 +* Remove duplicate MacOS_Test_config.h file +* 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 +Also, includes 7.2 changes + + +== [7.2q] 2023-05-25 == + +* Fix CORD_next() indent inside loop in test_basics() of cordtest +* Fix DCL_LOCK_STATE placement in GC_set_oom_fn +* Fix GC_excl_table overrun on overflow in GC_exclude_static_roots +* Fix IRIX5 defined wrongly on Tandem S-Series and WinCE/mips +* Fix comparisons to heap boundary in GC_get_back_ptr_info and GC_mark_from +* Fix disabling of automatic dynamic libraries registration +* Fix double initialization of main thread local free lists on Win32 +* Fix joinable threads shutdown on NaCl +* Fix loop condition over dll_thread_table in GC_lookup_pthread (Win32) +* Fix missing GC_CALLBACK for GC_waitForSingleObjectInfinite +* Fix missing libalphagc.so dependency in Makefile.direct +* Fix missing result check of pthread_attr_getdetachstate in pthread_create +* Fix overlapping region assertion in mark_some if malloc redirect on Linux +* Fix potential SIGSEGV on out-of-memory in gctest +* Fix typos in comments and documentation +* Fix unregistering of thread created by intercepted pthread_create on NaCl +* Fix use of unset errno after pthread_create call +* Invoke GC_oom_fn if GC_make_array_descriptor failed because of no memory + + +== [7.2p] 2022-08-25 == + +* Avoid potential race in GC_init_real_syms after GC_allow_register_threads +* Define SUNOS5SIGS macro for kFreeBSD +* Do not assert that GC is initialized at DLL_THREAD_DETACH (Win32) +* Ensure typed objects descriptor is never located in the first word +* Fix GC_make_descriptor for zero length argument +* Fix SUNOS5SIGS documentation to match macro definition in gcconfig.h +* Fix assertion violation in GC_allow_register_threads on Windows +* Fix get_maps failure when GC_repeat_read returns zero +* Fix hb_obj_kind type in documentation (ASCII diagram) describing hblkhdr +* Fix missing lock when GC_generate_random_valid_address is called +* Fix nodist_libgc_la_SOURCES value in Makefile.am for Solaris/sparc +* Fix oldProc initialization in gc_cleanup and eliminate related warnings +* Fix parallel_initialized assertion violation in initsecondarythread (Win32) +* Fix propagation of out-of-memory occurred in GC_make_sequence_descriptor +* Fix race between calloc_explicitly_typed and push_complex_descriptor +* Fix typos in comments of .c files, gc.h and a typo in debugging.html +* Refer to Makefile.direct instead of deleted Makefile file in README +* Remove checking of RS6000 completely +* Remove non-working check of M68K in gctest +* Revert addition of msvc_dbg.h in include.am + + +== [7.2o] 2021-09-28 == + +* Add loop to handle abort error like in suspend logic on Darwin +* Disable mprotect-based incremental GC if /proc roots are used (Linux) +* Explicitly zero-initialize trace_buf (fix trace_buf initialization) +* Fix 'ACCESS_VIOLATION in marker' GC warning on Win32 async thread start +* Fix 'GC_generic_malloc must be available' GCC error in new_gc_alloc.h +* Fix 'expected function body after declarator' clang error in gc_cpp.cc +* Fix 'write to GC log failed' error +* Fix GC_proc_fd value in child process at fork (Solaris) +* Fix assertion violation in register_dynlib_callback on Android +* Fix configure message about 'AIX gcc optimization fix' +* Fix data race regarding *rlh value in generic_malloc_many +* Fix first_thread stack_base initialization if custom GC_stackbottom (Win32) +* Fix fread failure after enable_incremental if malloc is redirected (Linux) +* Fix gc_cflags variable name in configure (HP/UX) +* Fix handling of areas smaller than page size on recycle scratch area +* Fix incorrect define GC_OPENBSD_THREADS on sparc64 +* Fix misaligned tlfs passed to AO_load on m68k +* Fix missing GC_quiet declaration in pcr_interface.c +* Fix missing gc_dlopen.c in CMake script +* Fix missing scratch_last_end_ptr update (Irix) +* Fix overflow of scratch_free_ptr value +* Fix page_was_[ever_]dirty() for static roots (Solaris) +* Fix printf format specifier in simple_example.html +* Fix save_callers for multi-threaded case if built-in backtrace unavailable +* Fix various typos in comments and documentation files +* Fix word size, data start and alignment for OpenBSD/mips64(el) +* Prevent WARN of incompatible incremental GC if default or manual VDB +* Reduce a time period between GetExitCodeThread and SuspendThread (Win32) +* Remove a misleading comment about Solaris in gc.h + + +== [7.2n] 2019-03-01 == + +* Fix 'mprotect remapping failed' abort on NetBSD with PaX enabled +* Fix 'unexpected mark stack overflow' abort in push_all_stack +* Fix BSD_TIME variant of MS_TIME_DIFF for the case of a.tv_usec < b.tv_usec +* Fix GetThreadContext stale register values use if WoW64 (Win32) +* Fix executable memory allocation in GC_unix_get_mem +* Fix invalid initializer of CLOCK_TYPE variables if BSD_TIME + + +== [7.2m] 2018-12-11 == + +* Fix comment typos in CMakeLists.txt, backgraph.c, de.c, gcconfig.h +* Fix hbp overflow in GC_install_counts +* Fix start_world not resuming all threads on Darwin +* Guard against potential buffer overflow in CORD_next and CORD_pos_fetch + + +== [7.2l] 2018-08-10 == + +* Fix 'pointer arithmetic with NULL' code defect in print_callers +* Fix Borland version in documentation to match that in BCC_MAKEFILE +* Fix comment about inv_sz computation in setup_header +* Fix comments style in configure.ac and Makefile.am +* Fix GC_is_valid_displacement and GC_is_visible for non-small objects +* Fix global operator delete definition for C++14 in gc_cpp +* Fix infinite restarting of mark_some when a static root disappeared (Linux) +* Fix large object base computation in PUSH_CONTENTS() if MARK_BIT_PER_OBJ +* Fix mark stack overflow checking in push_selected +* Fix MSWIN32 macro redefinition (WCC) +* Fix potential null dereference in GC_CONS +* Fix register_dynamic_libraries on Windows 10 +* Fix result computation in n_set_marks +* Fix return type in GC_set_warn_proc API documentation +* Fix typo in comment for CORD_ec_flush_buf prototype +* Fix typos in ChangeLog +* Fix VirtualQuery call in case of malloc failure (Win32) +* Install gc.3 man page instead of copying gc.man to doc folder (configure) +* Perform thread_suspend in loop as it may be interrupted (Darwin) +* Workaround 'template-id not supported in this context' compiler error (WCC) + + +== [7.2k] 2018-04-19 == + +* Fix arguments delimiter in pcr_interface.c (PCR) +* Fix assertion violation in DllMain of win32_threads +* Fix data race during apply_to_each_object(reset_back_edge) +* Fix dbg_mlc.c/o file name in documentation +* Fix include gc_gcj.h in thread_local_alloc.c +* Fix man section number (3) +* Fix missing GC_generic_malloc_words_small implementation in new_gc_alloc.h +* Fix missing SIGBUS handler setup for kFreeBSD +* Fix null dereference in print_callers on backtrace_symbols failure +* Fix the collector hang when it is configured with --enable-gc-debug +* Fix thread_suspend fail for threads registered from key destructor (OS X) +* Fix typo in AIX macro name +* Fix typo in comment in specific.h + + +== [7.2j] 2018-01-21 == + +* Fix GC allocation mutex in child after a fork +* Fix last_reclaimed..gc_no interval comparison to threshold in unmap_old +* Fix libgc version which was changed in linkage breaking way +* Fix missing EOLn output in threadlibs tool + + +== [7.2i] 2017-12-21 == + +* Avoid data race in finalized_count (gctest) +* Fix assertion violation about disabled cancel in try_to_collect_inner +* Fix data race in a list referenced by A.aa (gctest) +* Fix data race in do_local_mark when comparing active_count to helper_count +* Fix data race in GC_init_explicit_typing +* Fix data race in last_stop_count access (suspend_handler_inner) +* Fix data race in mark_thread when updating mark_no +* Fix data race when getting object size in explicitly-typed allocators +* Fix lack of barriers to synchronize memory for suspend_handler +* Fix typos in cdescr.html, extend_size_map and ews4800 doc, README.sgi +* Prevent 'Unexpected heap growth' in single-threaded gctest (Linux/x64) + + +== [7.2h] 2017-10-12 == + +* Add gctest as a test (CMake) +* Change no-argument functions declaration style to ANSI C (extra files) +* Do not allow SHORT_DBG_HDRS if KEEP_BACK_PTRS or MAKE_BACK_GRAPH +* Ensure oom_fn callback executed on out-of-memory in calloc +* Fix '~' operator application to unsigned values shorter than word +* Fix 'context local variable might be clobbered by setjmp' compiler warning +* Fix 'doc' files installation folder +* Fix 'shift count >= width of type' compiler warning in GC_SQRT_SIZE_MAX +* Fix ALL_INTERIOR_POINTERS name in comments and documentation +* Fix AO_SRC_DIR target name in NT_*_MAKEFILE +* Fix assertion in GC_mark_from for non-heap regions +* Fix assertion in GC_steal_mark_stack for non-heap regions +* Fix assertion violation in GC_repeat_read if --enable-redirect-malloc +* Fix assertion violation in GC_wait_builder called from start_mark_threads +* Fix assertion violation in mark_local checking GC_mark_stack_top +* Fix assertion violation in return_single_freelist in child process +* Fix bm_huge initialization for 64-bit targets (gctest) +* Fix broken external links in documentation +* Fix bytes count passed to add_to_our_memory in backgraph new_back_edges +* Fix calloc_explicitly_typed in case of lb*n overflow +* Fix CMake warning about CMP0054 by unquoting instances of HOST +* Fix conditional expression in pos_fetch, next non-macro definitions (cord) +* Fix configure --disable-munmap handling +* Fix CORD_substr_closure for the case when CORD_from_fn returns C string +* Fix crash in FirstDLOpenedLinkMap if app linked statically (Alpine Linux) +* Fix double lock in pthread_detach (Cygwin, winpthreads) +* Fix double multiplication of lb by n in calloc_explicitly_typed +* Fix enable_parallel_mark condition in CMake script +* Fix external libatomic_ops pkg-config-based detection +* Fix gc_allocator.h file name in new_gc_alloc.h comment +* Fix gc_backptr.h, gc_mark.h, GC_DS_TAGS names in documentation +* Fix gc_cleanup destructor for non-heap objects (gc_cpp) +* Fix GC_collect_or_expand to prevent allocation size value wrap-around +* Fix GC_incremental declaration/definition type mismatch +* Fix GC_mark_stack_top assertion violation properly in mark_local +* Fix GC_remove_specific invocation from remove_all_threads_but_me +* Fix GC_requested_heapsize increment in GC_init +* Fix GC_setspecific to prevent garbage collection inside +* Fix GC_SIZE_MAX definition (Linux/musl-gcc) +* Fix GCJ support in CMake build script +* Fix gctest crash if configure --enable-handle-fork on Darwin +* Fix get_maps on proc maps file asynchronous growth +* Fix hb_n_marks underflow in clear_fl_marks if MARK_BIT_PER_OBJ +* Fix header filename in gcconfig.h comment +* Fix infinite mark_some calls after memory mapping disappeared (Glibc) +* Fix integer shift undefined behavior in GC_init_explicit_typing +* Fix leak_test crash in print_callers if free() is redirected +* Fix Makefile.direct recursive invocation +* Fix malloc routines to prevent size value wrap-around (fix CVE-2016-9427) +* Fix missing win32_threads.c compilation for Cygwin (CMake) +* Fix MS VC warning about compiling unused checksums and thread_local_alloc +* Fix name typos in GC_FAST_MALLOC_GRANS comment +* Fix null dereference in reclaim_block if DONT_ADD_BYTE_AT_END +* Fix OSF1 host pattern in CMakeLists.txt +* Fix PCR-Makefile by removing compilation of a missing file +* Fix potential data race in GC_SysVGetDataStart (SPARC) +* Fix potential integer overflow in GC_find_limit_* functions +* Fix printf arguments type in print_callers +* Fix pthread_detach for threads not yet registered (Cygwin, winpthreads) +* Fix pthread_join to avoid thread removal on failure (Cygwin, winpthreads) +* Fix pthread_join when thread is registered in thread key destructor +* Fix push_complex_descriptor to avoid unlimited global mark stack growth +* Fix removal of dead threads in a child process +* Fix SIGSEGV in GC_is_marked when gc_cleanup is used in leak-finding mode +* Fix SIGSEGV in mark_from called from do_local_mark if WRAP_MARK_SOME +* Fix Solaris/sparc detection in case of strict C compliance is enforced +* Fix STACKBOTTOM for Solaris 11/x86 +* Fix storage class of local variable in register_dynamic_libraries (Irix) +* Fix tools/setjmp_t hang (OS X) +* Fix typed_test to prevent fails in malloc_explicitly_typed (64-bit) +* Fix undefined HEAP_START in register_dynamic_libraries +* Fix USE_CUSTOM_SPECIFIC mode (if manually enabled) for Win32 +* Fix USE_GET_STACKBASE_FOR_MAIN definition in gcconfig.h +* Fix various typos in comments, documentation and printed messages +* Handle load_segs overflow in register_dynlib_callback gracefully +* Prevent misleading AC_MSG_ERROR/AS_IF errors reported in configure.ac +* Replace (fix) 'objs' acronym in comments with 'objects' word +* Revert "Skip GC_DS_PER_OBJECT objs with negative descriptor in GC_mark_from" +* Update documentation about bugs reporting and new releases notification +* Update Download information in GC overview document +* Update shared libraries version info (v7.2) +* Workaround a bug in winpthreads causing parallel marks deadlock (MinGW) +* Workaround missing getcontext() in Docker osrf/ubuntu_32bit + + +== [7.2g] 2016-05-23 == + +* Fix 'illegal option -xassembler-with-cpp' error (Oracle SunCC) +* Fix 'implicit declaration of function' compiler warnings in cord/de +* Fix CFLAGS in configure regarding -O flag passing to SunCC compiler +* Fix FirstDLOpenedLinkMap for case libgc not 1st dynamically linked (NetBSD) +* Fix GC initialization in cord de_win for Cygwin +* Fix GC_get_stack_base if called before GC_init (Win32) +* Fix OSX issue with pthread_attr_setstacksize failure +* Fix Unicode Win32 API calls in cord de_win +* Fix USE_COMPILER_TLS macro duplicate description in README +* Fix cord de_win WndProc prototype parameters for 64-bit (Win64) +* Fix file descriptor resource leak in GC_register_data_segments (OS/2) +* Fix filename printing in cordtest +* Fix missing cord_pos.h, ec.h among installed headers (Automake) +* Fix missing GC_get_stack_base for Amiga +* Fix missing msvc_dbg.h in dist_noinst_HEADERS (Automake) +* Fix mistyped ARM_THREAD_STATE macro (Darwin/arm) +* Fix null-pointer dereferences on out-of-memory in cord and tests +* Fix potential multiplication overflow in check_heap_stats (gctest) +* Fix race (and potential deadlock) at marker threads initialization +* Fix signedness of char values passed to isspace, iscntrl, isxdigit +* Fix typo (items numbering) in GC_finalize_all documentation +* Fix typos in ERROR_FL, GC_malloc_uncollectable comments +* Fix typos in gc_priv.h, in README for ews4800 +* Fix unresolved vsnprintf in misc.c and snprintf in cordtest (DJGPP, VC) +* Fix various spelling errors +* Fix vsprintf_args initialization/cleanup in CORD_vsprintf for EMX +* Regenerate configure files using official libtool release (v2.4.2) +* Remove documentation about obsolete GC_REDIRECT_TO_LOCAL +* Skip GC_DS_PER_OBJECT objects with negative descriptor in GC_mark_from +* windows-untested: Fix paths to msvc_dbg.c/h + + +== [7.2f] 2014-06-03 == + +* Fix 'Bad signal in suspend_handler' abort on FreeBSD-9.2 +* Fix 'source file in a subdirectory' Automake warnings +* Fix ABORT message in GC_restart_handler +* Fix ADD_DEFINITION in CMakeLists.txt for kFreeBSD +* Fix CMakeLists.txt: do not override CMAKE_OSX_ARCHITECTURES +* Fix GC_alloc_large by bumping GC_collect_at_heapsize in GC_add_to_heap +* Fix GC_scratch_last_end_ptr update on GC_scratch_alloc failure +* Fix GET_MEM argument rounding in GC_scratch_alloc and similar +* Fix PARALLEL_MARK for Windows 7+ +* Fix build (broken by fenv.h inclusion) on Linux/x64 under uClibc +* Fix crash when using GC_malloc_many() as first allocation call +* Fix mark stack excessive growth during parallel mark +* Fix or remove broken URLs in documentation +* Fix out-of-memory case in new_back_edges, push_in_progress (backgraph) +* Fix typo in GC_collect_or_expand comment +* Fix typos in GC overview file, gc_config_macros.h, gc_cpp.h, README.changes +* Regenerate configure files by automake 1.14.1, libtool 2.4.2.418 +* Update emails/links due to project site and ML transition + + +== [7.2e] 2013-11-10 == + +* Add weak attribute to avoid __data_start undefined messages (s390x) +* Add weak stubs for pthread_cancel API +* Adjust 'pthread_[un]register_cancel undefined ref' workaround (Pthreads) +* Append _test suffix to 'initsecondarythread' binary file names +* Enable PARALLEL_MARK and THREAD_LOCAL_ALLOC for FreeBSD in configure +* Fix 'stack section' pointer passed to push_all_stack_sections (Pthreads) +* Fix GC_CreateThread 'dwStackSize' argument type for Win64 +* Fix GC_PTHREAD_PTRVAL definition for GC_PTHREADS_PARAMARK (Win32) +* Fix GC_clear_stack by declaring 'dummy' local array as volatile +* Fix GC_get_stack_base assembly code (Cygwin/Clang) +* Fix GC_malloc_explicitly_typed_ignore_off_page for large allocations +* Fix GC_marker_Id elements initialization (WinCE) +* Fix GC_print_trace missing unlock +* Fix GC_unix_mmap_get_mem for open of /dev/zero failure +* Fix GC_win32_free_heap compilation error for Cygwin +* Fix GC_win32_free_heap to prevent memory leak if USE_GLOBAL_ALLOC +* Fix Win32 GC_write preventing potential infinite recursion at abort +* Fix assertion violation in GC_mark_from prefetch loop +* Fix collection of objects referenced only from GC_mark_stack_X variables +* Fix dwSize argument of VirtualFree call in detect_GetWriteWatch (Win32) +* Fix heap sections overflow for Win32/Cygwin with enabled parallel marker +* Fix min_bytes_allocd preventing potential infinite loop in GC_allocobj +* Fix missing tabs in SMakefile.amiga file +* Fix null-pointer dereference in CORD_substr_closure +* Fix old_segv/bus_act variables initialization for FreeBSD +* Fix potential double fclose in test_extras (cordtest) +* Fix pthread_attr_t resource leak in pthread_create +* Fix race in GC_print_all_errors regarding GC_leaked +* Fix sizeof in GC_push_thread_structures +* Fix stackbottom/stack_end assignment in GC_call_with_gc_active +* Fix tests makefile to link with new GNU ld linking rules +* Fix typos in comments and documentation +* Fix unportable '==' test operators in configure +* Fix vsprintf_args cleanup in CORD_vsprintf +* Merge FreeBSD New ports collection for boehm-gc v7.2d +* Replace GC_DBG_RA with GC_DBG_EXTRAS macro +* Replace deprecated [CXX]INCLUDES to AM_C[PP]FLAGS in configure.ac file +* Use __builtin_extract_return_addr in GC_RETURN_ADDR_PARENT (gcc/x86) + + +== [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/x64 +* 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 +* Remove some local variables that are unused +* 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 == + +* configure_atomic_ops.sh: Remove. +* Makefile.direct (dist gc.tar): Remove configure_atomic_ops.sh. +* Makefile.am (EXTRA_DIST): Add autogen.sh. + +* NT_STATIC_THREADS_MAKEFILE (.cpp.obj): Remove duplicate .cpp +filename passed. +* NT_X64_THREADS_MAKEFILE (.cpp.obj): Use lowercase file +extension. +* NT_X64_STATIC_THREADS_MAKEFILE (.cpp.obj): Likewise. +* NT_MAKEFILE (.cpp.obj): Likewise. + +* alloc.c (GC_add_current_malloc_heap, GC_build_back_graph, +GC_traverse_back_graph): Move prototype to gc_priv.h. +* checksums.c (GC_page_was_ever_dirty): Likewise. +* dbg_mlc.c (GC_default_print_heap_obj_proc): Likewise. +* dyn_load.c (GC_parse_map_entry, GC_get_maps, +GC_segment_is_thread_stack, GC_roots_present, GC_is_heap_base, +GC_get_next_stack): Likewise. +* finalize.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): Likewise. +* gcj_mlc.c (GC_start_debugging, GC_store_debug_info): Likewise. +* malloc.c (GC_extend_size_map, GC_text_mapping): Likewise. +* mark_rts.c (GC_mark_thread_local_free_lists): Likewise. +* misc.c (GC_register_main_static_data, GC_init_win32, +GC_setpagesize, GC_init_linux_data_start, +GC_set_and_save_fault_handler, GC_init_dyld, GC_init_netbsd_elf, +GC_initialize_offsets, GC_bl_init, GC_do_blocking_inner, +GC_bl_init_no_interiors): Likewise. +* os_dep.c (GC_greatest_stack_base_below, GC_push_all_stacks): +Likewise. +* reclaim.c (GC_check_leaked): Likewise. +* win32_threads.c (GC_gww_dirty_init): Likewise. +* darwin_stop_world.c (GC_is_mach_marker, GC_mprotect_stop, +GC_mprotect_resume): Move prototype to darwin_stop_world.h. +* pthread_support.c (GC_FindTopOfStack): Likewise. +* dyn_load.c (GC_cond_add_roots): Merge adjacent definitions. +* mark.c (GC_page_was_ever_dirty): Remove (as already declared). +* mark_rts.c (GC_roots_present): Change return type to void +pointer (to match the prototype); return NULL instead of FALSE. +* mark_rts.c (GC_add_roots_inner): Cast GC_roots_present() result. +* os_dep.c (NEED_PROC_MAPS): Move definition to gcconfig.h. +* os_dep.c (GC_write_fault_handler): Make STATIC. +* os_dep.c (GC_set_write_fault_handler): New function (only if +GC_WIN32_THREADS). +* pthread_start.c (GC_start_rtn_prepare_thread, +GC_thread_exit_proc): Move prototype to pthread_support.h. +* pthread_support.c (GC_nacl_initialize_gc_thread, +GC_nacl_shutdown_gc_thread, GC_unblock_gc_signals): +Likewise. +* pthread_support.c (GC_stop_init): Move prototype to +pthread_stop_world.h. +* win32_threads.c (GC_write_fault_handler): Remove prototype. +* win32_threads.c (GC_register_my_thread_inner): Call +GC_set_write_fault_handler instead of SetUnhandledExceptionFilter +(only if MPROTECT_VDB). +* doc/README.win32: Add information about DMC. +* include/private/gc_priv.h (GC_set_write_fault_handler): New +prototype (only if GC_WIN32_THREADS and MPROTECT_VDB). + +* misc.c (vsnprintf): Redirect to vsprintf() if NO_VSNPRINTF. + +* win32_threads.c (GC_unregister_my_thread): Use KNOWN_FINISHED() +instead of FINISHED macro. +* tests/test.c (check_heap_stats): Round up max_heap_sz value for +Win32 (same as for USE_MMAP). + +* tests/test.c (check_heap_stats): Adjust printf format specifier +for max_heap_sz; cast max_heap_sz accordingly. + +* doc/README.solaris2: Add note. + +* configure.ac (SOLARIS25_PROC_VDB_BUG_FIXED): Don't define for +Solaris/x86 2.10+. + +* tests/threadkey_test.c (SKIP_THREADKEY_TEST): Skip the test if +defined; explicitly define for some targets. + +* mark.c (GC_dirty): Add prototype (only if MANUAL_VDB). +* stubborn.c (GC_dirty): Likewise. +* include/private/gcconfig.h (GWW_VDB, MPROTECT_VDB, PCR_VDB, +PROC_VDB): Undefine if MANUAL_VDB. +* include/private/gcconfig.h (DEFAULT_VDB): Don't define if +MANUAL_VDB. +* os_dep.c (async_set_pht_entry_from_index): Define for +MANUAL_VDB. +* os_dep.c (GC_read_dirty): Set GC_dirty_maintained only if +success; if ioctl() failed then just print warning instead of +aborting. + +* include/private/gc_priv.h (GC_ASSERT): Use "%d" (instead of %ld) +for line number printing. + +* os_dep.c (GC_read_dirty): Add debug logging if DEBUG_DIRTY_BITS +(for PROC_VDB only); print errors via GC_err_printf; rename "ps" +and "np" local variables to npages and pagesize, respectively; +remove "current_addr" local variable. + +* os_dep.c (GC_get_main_stack_base): Convert to GC_get_stack_base +for BeOS and OS/2; define HAVE_GET_STACK_BASE. +* os_dep.c (GET_MAIN_STACKBASE_SPECIAL): Define when a specific +GC_get_main_stack_base implementation is defined. +* os_dep.c (GC_get_main_stack_base): Define that based on +GC_get_stack_base() in a single place (only if +GET_MAIN_STACKBASE_SPECIAL is unset); check GC_get_stack_base() +result. + +* mark.c (GC_push_selected): Remove "push_fn" argument (use +GC_push_all directly); update the documentation. +* mark.c (GC_push_conditional): Simplify the code (for better +readability). + +* mark.c (alloc_mark_stack): Use FALSE/TRUE (instead of 0/1) for +boolean local variables. +* doc/README.macros (GC_PREFER_MPROTECT_VDB): Update. +* os_dep.c (GC_page_was_dirty, GC_page_was_ever_dirty, +GC_remove_protection): Define for GWW_VDB and PROC_VDB in a single +place. +* os_dep.c (GC_page_was_dirty, GC_page_was_ever_dirty): Compute +PHT_HASH(h) only once (store result to a local variable). + +* doc/README.solaris2: Update. + +* include/private/gcconfig.h (end, InitStackBottom): Declare +extern variable for RTEMS. +* include/private/gcconfig.h (DATASTART, DATAEND, STACKBOTTOM): +Update (for RTEMS). +* include/private/gcconfig.h (DATAEND): Fix a typo in the macro +name (for RTEMS). +* tests/test.c (CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER): +Replace with CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER (for RTEMS). + +* include/private/gcconfig.h (MPROTECT_VDB): Enable for Solaris in +single-threaded environment. + +* include/private/gcconfig.h (MPROTECT_VDB): Undefine if PROC_VDB. +* tests/test.c (NUMBER_ROUND_UP): New macro. +* tests/test.c (check_heap_stats): Round up total expected heap +size to the nearest 4 MiB bound. +* tests/test.c (check_heap_stats): Print the current and expected +heap sizes in case of failure. + +* checksums.c (GC_check_blocks, GC_check_dirty): Do log printing +only if GC_print_stats; print errors using GC_err_printf. +* checksums.c (GC_check_blocks): Join adjacent printf() calls into +a single one. + +* pthread_support.c (pthread_join): Add assertion (check thread is +finished). +* pthread_support.c (GC_register_my_thread): Don't detach the +thread if invoked from the thread destructor. +* win32_threads.c (GC_register_my_thread): Likewise. +* win32_threads.c (GC_unregister_my_thread): Don't delete the +thread (just set FINISHED) if the thread is not detached (only if +GC_PTHREADS); add assertion (check the thread is not finished). +* tests/threadkey_test.c (main): Join some created threads. + +* pthread_support.c (GC_delete_gc_thread): Rename "gc_id" local +variable to "t". +* win32_threads.c (GC_delete_gc_thread): Likewise. +* pthread_support.c (pthread_join, pthread_detach, +pthread_cancel): Rename "thread_gc_id" local variable to "t". +* win32_threads.c (GC_pthread_detach): Likewise. +* win32_threads.c (GC_delete_gc_thread): Remove "gc_nvid" local +variable. +* win32_threads.c (GC_pthread_join): Rename "joinee" local +variable to "t". + +* pthread_stop_world.c (pthread_sigmask): Undefine even if not +DEBUG_THREADS. +* pthread_stop_world.c (GC_unblock_gc_signals): New function (only +if GC_EXPLICIT_SIGNALS_UNBLOCK). +* pthread_support.c (GC_unblock_gc_signals): New prototype. +* pthread_support.c (GC_register_my_thread_inner, +GC_register_my_thread): Call GC_unblock_gc_signals (only if +GC_EXPLICIT_SIGNALS_UNBLOCK); add comment. +* include/private/gcconfig.h (GC_EXPLICIT_SIGNALS_UNBLOCK): New +macro. + +* pthread_stop_world.c (GC_suspend_handler_inner): Remove "dummy", +"sig" local variables; rename my_thread local variable to "self". + +* tests/threadkey_test.c (LIMIT): Use smaller value (don't create +more than 30 in parallel by default). + +* tests/threadkey_test.c (key_once, main): Work around for Solaris +PTHREAD_ONCE_INIT. +* tests/threadkey_test.c (LIMIT): Use smaller value for Solaris. + +* dyn_load.c (GC_FirstDLOpenedLinkMap): Remove unused "r" local +variable. +* pthread_support.c (GC_unregister_my_thread_inner): Revert back +GC_remove_specific invocation; add a comment. +* include/private/thread_local_alloc.h (GC_remove_specific): +Revert back. +* specific.c (slow_getspecific): Cast qtid to AO_t. +* include/private/specific.h (key_create, setspecific, +remove_specific): Remove "extern" keyword. +* include/private/specific.h (getspecific): Change type of "qtid" +local variable to unsigned long. + +* pthread_support.c (GC_check_tls): Fix "#endif" comment. +* include/gc.h (GC_REDIRECT_TO_LOCAL): Remove deprecated comment. +* include/private/thread_local_alloc.h (THREAD_LOCAL_ALLOC): +Remove redundant test of the macro. + +* backgraph.c (add_edge): Recognize DEBUG_PRINT_BIG_N_EDGES macro. +* os_dep.c (GC_set_and_save_fault_handler): Recognize +SIGACTION_FLAGS_NODEFER_HACK macro. +* pthread_support.c (mark_mutex): Recognize GLIBC_2_1_MUTEX_HACK +macro. +* pthread_support.c (GC_acquire_mark_lock): Remove commented out +code. +* include/private/gc_priv.h (SUNOS5SIGS): Don't include +sys/siginfo.h on Linux. +* include/private/gcconfig.h (FORCE_WRITE_PREFETCH): New macro +recognized, force PREFETCH_FOR_WRITE to be defined on x86. +* include/private/gcconfig.h (USE_HPUX_FIXED_STACKBOTTOM): New +macro recognized (for HP/UX). + +* os_dep.c (GC_gww_page_was_ever_dirty): Fix comment (for +GWW_VDB). +* os_dep.c (GC_dirty_init): Use memset() for GC_written_pages +resetting (for PROC_VDB). + +* tests/threadkey_test.c: New file. +* tests/tests.am (TESTS, check_PROGRAMS): Add 'threadkey_test'. +* tests/tests.am (threadkey_test_SOURCES, threadkey_test_LDADD): +New variable. + +* pthread_support.c (GC_unregister_my_thread_inner): Don't call +GC_remove_specific. +* include/private/thread_local_alloc.h (GC_remove_specific): +Remove (since it is empty for all targets). +* pthread_support.c (GC_record_stack_base): New inline function. +* win32_threads.c (GC_record_stack_base): Likewise. +* pthread_support.c (GC_register_my_thread_inner): Invoke +GC_record_stack_base. +* win32_threads.c (GC_register_my_thread_inner): Likewise. +* pthread_support.c (GC_register_my_thread): If thread is FINISHED +then call GC_record_stack_base, clear FINISHED, initialize +thread-local list and return success. +* win32_threads.c (GC_register_my_thread): Likewise. +* include/gc.h (GC_register_my_thread): Update documentation. +* include/private/thread_local_alloc.h (GC_thread_key): Likewise. + +* thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Join +adjacent "#ifdef". +* thread_local_alloc.c (GC_malloc_atomic): Call +GC_core_malloc_atomic (instead of GC_core_malloc). + +* pthread_start.c (GC_start_rtn_prepare_thread): Change return +type to GC_thread. +* pthread_start.c (GC_inner_start_routine): Pass the current +thread descriptor to pthread_cleanup_push (same as in +win32_threads.c). +* pthread_stop_world.c (GC_push_all_stacks): Rename "me" local +variable to "self". +* win32_threads.c (GC_push_all_stacks): Likewise. +* pthread_stop_world.c (GC_suspend_all, GC_start_world): Rename +"my_thread" local variable to "self". +* pthread_support.c (GC_unregister_my_thread_inner): New static +function. +* pthread_support.c (GC_unregister_my_thread, +GC_thread_exit_proc): Use GC_unregister_my_thread_inner. +* win32_threads.c (GC_register_my_thread, GC_unregister_my_thread, +GC_do_blocking_inner): Rename "t" local variable to "thread_id". +* win32_threads.c (GC_wait_marker, GC_notify_all_marker): Rename +"id" local variable to "thread_id". + +* pthread_support.c (GC_unregister_my_thread): Call pthread_self +only once. +* win32_threads.c (GC_pthread_start_inner): Likewise. +* pthread_support.c (GC_unregister_my_thread): Add debug output. +* win32_threads.c (GC_unregister_my_thread): Likewise. +* pthread_support.c (GC_register_my_thread, +GC_start_rtn_prepare_thread): Rename "my_pthread" local variable +to "self". + +* include/gc.h (GC_HIDE_POINTER, GC_REVEAL_POINTER): Define +unconditionally (do not test GC_I_HIDE_POINTERS); update the +comment. +* include/gc.h (HIDE_POINTER, REVEAL_POINTER): Define as alias to +GC_HIDE/REVEAL_POINTER, respectively. +* include/private/gc_pmark.h (GC_I_HIDE_POINTERS): Do not define. +* include/private/gc_priv.h (GC_I_HIDE_POINTERS): Likewise. + +* include/gc.h (GC_register_my_thread): Refine the comment. + +* include/gc_inline.h (GC_MALLOC_WORDS, GC_CONS): Add missing +parentheses. +* include/gc_typed.h (GC_get_bit, GC_set_bit, +GC_CALLOC_EXPLICITLY_TYPED): Likewise. + +* include/private/gcconfig.h (NO_GETCONTEXT): Add missing ')'. + +* include/private/gcconfig.h (NO_GETCONTEXT): Do not use +getcontext(2) on m68k because it is not implemented there. + +* alloc.c (GC_clear_a_few_frames): Use BZERO(). +* mark_rts.c (GC_clear_roots, GC_rebuild_root_index): Likewise. +* reclaim.c (GC_start_reclaim): Likewise. +* blacklst.c (total_stack_black_listed): Remove "len" local +variable. +* dbg_mlc.c (GC_generate_random_valid_address): Replace "for" +statement with "do-while" one. +* dyn_load.c (GC_register_dynamic_libraries, +GC_register_dynlib_callback): Remove redundant parentheses. + +* cord/cordxtra.c (CORD_from_file_lazy_inner): Suppress +"unused result" compiler warning for fread(). + +* os_dep.c (GC_write_fault_handler): Break when in_allocd_block +is set to true. + +* dbg_mlc.c (GC_has_other_debug_info): Change return type to int; +return -1 if the object has (or had) debugging info but was +marked deallocated. +* include/private/dbg_mlc.h (GC_has_other_debug_info): Likewise. +* dbg_mlc.c (GC_has_other_debug_info): Update documentation; +remove "ohdr" local variable. +* dbg_mlc.c (GC_debug_free): Don't call GC_free if the object has +probably been deallocated. +* dbg_mlc.c (GC_debug_free): Don't actually free the object even +in the leak-finding mode if GC_findleak_delay_free. +* dbg_mlc.c (GC_check_leaked): New function (only unless +SHORT_DBG_HDRS). +* doc/README.environment (GC_FINDLEAK_DELAY_FREE): Document. +* doc/README.macros (GC_FINDLEAK_DELAY_FREE): Likewise. +* include/private/dbg_mlc.h (START_FLAG, END_FLAG): Use GC_WORD_C +on 64-bit architectures. +* include/private/dbg_mlc.h (NOT_MARKED): Remove redundant +parentheses. +* include/private/dbg_mlc.h (GC_HAS_DEBUG_INFO): Update (due to +GC_has_other_debug_info change). +* include/private/gc_priv.h (GC_findleak_delay_free): New global +variable declaration (unless SHORT_DBG_HDRS). +* misc.c (GC_findleak_delay_free): New global variable; recognize +GC_FINDLEAK_DELAY_FREE. +* misc.c (GC_init): Recognize GC_FINDLEAK_DELAY_FREE environment +variable (unless SHORT_DBG_HDRS). +* reclaim.c (GC_check_leaked): Declare (unless SHORT_DBG_HDRS). +* reclaim.c (GC_add_leaked): Don't add the object to leaked list +if marked as deallocated. + +* dbg_mlc.c (GC_has_other_debug_info): Fix punctuation in the +comment. +* dbg_mlc.c (GC_FREED_MEM_MARKER): New macro. +* dbg_mlc.c (GC_debug_free): Use GC_FREED_MEM_MARKER. +* dbg_mlc.c (GC_smashed): Refine documentation. +* mark.c (GC_push_selected): Change dirty_fn return type to +GC_bool. +* os_dep.c (GC_page_was_ever_dirty): Make GC_INNER. +* reclaim.c (GC_reclaim_small_nonempty_block): Remove "kind" +local variable. +* reclaim.c (GC_reclaim_block): Pass true constant to +GC_reclaim_small_nonempty_block (instead of report_if_found). +* doc/README.autoconf: Update; fix a typo. +* include/private/gcconfig.h (GC_WORD_C): New macro. + +* dbg_mlc.c (GC_store_debug_info_inner): Cast "linenum". +* dbg_mlc.c (GC_check_annotated_obj): Fix punctuation in the +comment. +* dbg_mlc.c (GC_print_smashed_obj): Add (and print) "msg" +argument. +* dbg_mlc.c (GC_debug_free, GC_print_all_smashed_proc): Pass +message to GC_print_smashed_obj. +* dbg_mlc.c (GC_debug_free): Call GC_size once. +* dbg_mlc.c (GC_debug_realloc): Calculate old_sz only if +allocation succeeded; remove unnecessary check for object is +smashed (since this is done in GC_debug_free); remove "clobbered" +local variable. + +* dbg_mlc.c (GC_store_debug_info_inner, GC_store_debug_info): +Rename "integer" argument to "linenum"; change the type of the +argument to int. +* gcj_mlc.c (GC_store_debug_info): Likewise. +* dbg_mlc.c (GET_OH_LINENUM): New macro. +* dbg_mlc.c (GC_print_obj, GC_print_smashed_obj): Use +GET_OH_LINENUM; adjust print format specifier. +* dbg_mlc.c (GC_debug_malloc, GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, +GC_debug_generic_malloc_inner, +GC_debug_generic_malloc_inner_ignore_off_page, +GC_debug_malloc_stubborn, GC_debug_malloc_atomic, +GC_debug_malloc_uncollectable, +GC_debug_malloc_atomic_uncollectable): Remove unnecessary cast of +"i". +* gcj_mlc.c (GC_debug_gcj_malloc): Likewise. + +* os_dep.c (GC_linux_stack_base): Rename to +GC_linux_main_stack_base. +* os_dep.c (GC_freebsd_stack_base): Rename to +GC_freebsd_main_stack_base; adjust error message. +* pthread_stop_world.c (GC_stop_init): Use GC_SEM_INIT_PSHARED +as an argument for sem_init(). +* pthread_support.c (pthread_create): Likewise. +* pthread_support.c (pthread_create): Abort in case sem_init() +fails. +* include/private/gc_priv.h (GC_SEM_INIT_PSHARED): Define. +* tests/initsecondarythread.c: Include gcconfig.h; call GC_INIT +from main() if it should be done from the primordial thread only. + +* alloc.c: Don't include sys/types.h for ArmCC. +* dyn_load.c: Likewise. +* os_dep.c: Likewise. +* mach_dep.c (_setjmp, _longjmp): Redirect to setjmp/longjmp for +ArmCC. +* mark.c (GC_noop): Define specially for ArmCC. +* include/private/gc_priv.h (GC_noop): Likewise. +* misc.c (GC_init): Don't test pointers comparison for ArmCC. +* misc.c: Don't include unistd.h for ArmCC. +* os_dep.c (pages_executable): Rename to GC_pages_executable; +make STATIC. +* os_dep.c (GC_unix_mmap_get_mem): Don't define for ArmCC. +* ptr_chck.c (GC_is_visible): Explicitly cast +(GC_DS_PER_OBJECT-GC_INDIR_PER_OBJ_BIAS) to word (to suppress +a compiler warning). +* include/private/gcconfig.h: Recognize __arm. +* include/private/gcconfig.h (HBLKPTR): Define for ArmCC. +* include/private/gcconfig.h (HBLKPTR): Add parentheses for +"bytes" argument. + +* pthread_support.c (GC_get_nprocs): Don't define for Android. +* pthread_support.c (GC_dummy_thread_local): Don't test +GC_LINUX_THREADS. +* include/gc_config_macros.h (GC_ADD_CALLER, GC_RETURN_ADDR): +Define for Android. + +* mach_dep.c (NO_GETCONTEXT): Move to gcconfig.h. +* os_dep.c (GC_write_fault_handler): Don't include ucontext.h if +NO_GETCONTEXT. +* include/private/gcconfig.h (GETPAGESIZE): Define as a sysconf +call for Android. + +* include/private/gc_locks.h (WIN32_LEAN_AND_MEAN, NOSERVICE): +Define before including windows.h. +* include/private/gc_priv.h (WIN32_LEAN_AND_MEAN, NOSERVICE): +Likewise. +* include/private/thread_local_alloc.h (WIN32_LEAN_AND_MEAN, +NOSERVICE): Likewise. +* include/private/gc_priv.h (MS_TIME_DIFF): Avoid floating-point +arithmetics; add a comment. + +* mark.c (GC_clear_hdr_marks): Don't test USE_MARK_BYTES. +* extra/setjmp_t.c (main): Don't test USE_MARK_BITS. +* include/private/gc_pmark.h (SET_MARK_BIT_EXIT_IF_SET): Likewise. +* include/private/gc_pmark.h (SET_MARK_BIT_EXIT_IF_SET): Remove +"mark_byte" local variable. +* include/private/gc_pmark.h (OR_WORD_EXIT_IF_SET): Add a comment +about that AO_or() is not used by GC unless USE_MARK_BITS +explicitly set. +* include/private/gc_priv.h (OR_WORD): Likewise. +* include/private/gc_pmark.h (INCR_MARKS): Remove trailing ';', +add parentheses. +* include/private/gc_priv.h (ONES): Define before use by +MAKE_COOLER. +* include/private/gc_priv.h (MARK_BITS_SZ): Define where used. +* include/private/gc_priv.h (OR_WORD): Don't define if +USE_MARK_BYTES. +* include/private/gcconfig.h (USE_MARK_BYTES); Remove duplicate +definition; simplify expression. + +* os_dep.c (GC_get_maps): Always close the file. +* pthread_support.c (GC_get_nprocs): Likewise. +* os_dep.c (READ): Define similarly across the file (without +parameters). +* pthread_support.c (GC_get_nprocs): Use signed int type for "i" +and "len" local variables (since read() may return -1). +* include/private/gc_pmark.h (LONG_MULT): Add prefix/suffix +double underscore; add "volatile" for asm. +* include/private/gc_pmark.h (LONG_MULT): Add missing +parentheses. +* include/private/gc_priv.h (OR_WORD): Likewise. +* include/private/gc_priv.h (OR_WORD): Remove unnecessary brackets +and ';' symbol. + +* os_dep.c (GC_get_stack_base): Implement for Android (same as +for Linux). +* pthread_support.c (GC_get_nprocs): Return 1 (instead of -1) if +failed to open "stat" file (not to issue a warning twice); update +the comment. +* pthread_support.c (GC_thr_init): Call sysconf() on Android to +get the number of CPUs. + +* include/private/gc_priv.h (_GNU_SOURCE): Revert one of the +recent patches regarding this macro as the macro should be set +(to 1) before including any other system header. + +* doc/README.environment (GC_INITIAL_HEAP_SIZE, +GC_MAXIMUM_HEAP_SIZE): Update. + +* misc.c (GC_parse_mem_size_arg): Allow 'k', 'M', 'G' suffixes in +heap size specifier; return 0 if not a valid one. +* include/gc_cpp.h: Explicitly define inline one-argument delete +operator for Cygwin (as a workaround). +* tests/test_cpp.cc (main): Suppress compiler warnings about +"assigned value is unused". + +* misc.c (GC_parse_mem_size_arg): New function. +* misc.c (GC_init): Use GC_parse_mem_size_arg(). +* pthread_stop_world.c (tkill): Declare for Android. + +* include/private/gc_priv.h (_GNU_SOURCE): Include features.h +first (except for NaCl) and then define the macro to 1 if not yet. + +* tests/tests.am (TESTS, check_PROGRAMS): Add +'initsecondarythread'. +* tests/tests.am (initsecondarythread_SOURCES, +initsecondarythread_LDADD): New variable. + +* dbg_mlc.c (GC_store_debug_info_inner): Always define; add +"const" to its string argument. +* dbg_mlc.c (GC_store_debug_info): Call GC_store_debug_info_inner. +* dbg_mlc.c (GC_debug_free): Set GC_have_errors in case of +smashed or previously deallocated found. +* dbg_mlc.c (GC_check_heap_block): Replace while loop with a for +one. +* reclaim.c (GC_reclaim_check): Likewise. +* dbg_mlc.c (GC_check_heap_proc): Remove redundant cast to word. +* os_dep.c (GC_get_stack_base): Don't initialize +stackbase_main_self/ss_sp on Solaris if thr_main() is zero (thus +calling GC_INIT() from a non-primordial thread is possible now). +* reclaim.c (GC_add_leaked): Turn into an inline one. +* reclaim.c (GC_reclaim_small_nonempty_block): +Change report_if_found type from int/word to boolean. +* include/private/gc_priv.h (GC_start_reclaim): Likewise. +* include/private/gc_priv.h (set_mark_bit_from_hdr, +clear_mark_bit_from_hdr): Place closing parenthesis properly. + +* os_dep.c (GC_get_main_stack_base): Try to use +pthread_attr_getstack first for Linux if THREADS. +* doc/README.macros (USE_GET_STACKBASE_FOR_MAIN): Adjust text +alignment. + +* dbg_mlc.c (GC_generate_random_backtrace_no_gc): Fix a message +typo. +* dbg_mlc.c (GC_debug_malloc): Add a comment (about zero size). +* dbg_mlc.c (GC_strdup): Call GC_err_printf instead of WARN (in +case of NULL argument). +* dbg_mlc.c (GC_free): In case of NULL argument, just return +(without any warning printed); eliminate "uncollectable" local +variable. + +* configure.ac (THREADDLLIBS): Use alternate thread library on +Solaris 8. +* configure.ac (need_atomic_ops_asm): Set to true only for SPARC +Solaris. +* configure.ac: Don't use libdl on mips-sgi-irix6. + +* mach_dep.c (NO_GETCONTEXT); Define for RTEMS. +* mach_dep.c (GC_with_callee_saves_pushed): Don't call +__builtin_unwind_init() for RTEMS; use setjmp() without the +leading underscore (for RTEMS). +* tests/test.c (BIG): Use smaller value for RTEMS. +* tests/test.c (main): Customize for RTEMS. + +* configure.host: Remove doubled words in comments. +* os_dep.c: Likewise. +* doc/README: Likewise. +* extra/setjmp_t.c: Likewise. +* tests/huge_test.c: Likewise. +* extra/setjmp_t.c (getpagesize, nested_sp, main, g): Replace the +K&R-style function definition with the ANSI C one. +* extra/setjmp_t.c (nested_sp): Implement in the same way as +GC_approx_sp. + +* dyn_load.c (GC_dyld_sections): Add more sections. +* dyn_load.c (GC_dyld_add_sect_fmts): New static variable. +* dyn_load.c (L2_MAX_OFILE_ALIGNMENT): New macro. +* dyn_load.c (GC_dyld_image_add, GC_dyld_image_remove): Improve +logging; add support for on-demand sections. + +* gcj_mlc.c (GC_gcj_malloc_initialized): Use STATIC unless +GC_ASSERTIONS. +* include/private/gc_priv.h (GC_gcj_malloc_initialized): Don't +declare (as external) unless GC_ASSERTIONS. +* os_dep.c (GC_win32_free_heap): Clear GC_heap_bases[] also for +Cygwin; add FIXME. +* include/private/gcconfig.h: Include for RTEMS. +* include/private/gcconfig.h: Add "#error" for every "-->" mark. +* include/private/gcconfig.h (CLEAR_DOUBLE): Turn the code into +an expression. +* include/private/pthread_support.h (SUSPENDED_EXT): Add new flag +(which existed previously as SUSPENDED and still exists in GCJ). +* include/private/pthread_support.h (DISABLED_GC): Change the +value (as it is already used by SUSPENDED_EXT). + +* tests/test.c (reverse_test): Modify count (BIG) for +ppc64-darwin. + +* reclaim.c (GC_print_all_errors): Recognize new GC_ABORT_ON_LEAK +macro and environment variable; abort if any error has been +printed provided the environment variable (or macro) is set. +* doc/README.environment (GC_ABORT_ON_LEAK): Document. +* doc/README.macros (GC_ABORT_ON_LEAK): Likewise. + +* os_dep.c (GC_unix_sbrk_get_mem, GC_unix_get_mem): Don't define +for RTEMS. +* include/private/gcconfig.h (RTEMS): Add support for. +* include/private/gcconfig.h (GET_MEM): Use calloc() for RTEMS. + +* mallocx.c (GC_malloc_uncollectable): Move to malloc.c (since +it is used internally in some places). + +* dbg_mlc.c (GC_register_finalizer_no_order): Remove redundant +declaration. +* dbg_mlc.c (GC_debug_malloc_replacement, +GC_debug_realloc_replacement): Rename RA to GC_DBG_RA. +* malloc.c (GC_debug_malloc_replacement): Likewise. +* mallocx.c (GC_debug_realloc_replacement): Likewise. +* dbg_mlc.c (GC_store_debug_info): Move proto from dbg_mlc.h. +* malloc.c (GC_strdup, GC_strndup, GC_wcsdup): Move to mallocx.c. +* malloc.c: Include errno.h only REDIRECT_MALLOC; remove redundant +includes of string.h. +* mallocx.c: Include string.h (for GC_strdup). +* include/private/dbg_mlc.h (GC_store_debug_info): Move declaration +to dbg_mlc.c. +* include/private/gc_locks.h (UNCOND_LOCK, UNCOND_UNLOCK): Remove +redundant trailing ';'. +* include/private/gc_priv.h (START_WORLD, COND_DUMP): Likewise. +* include/private/gc_locks.h (LOCK, UNLOCK): Place opening '{' +properly. +* include/private/gc_priv.h (GC_DBG_RA): Move from dbg_mlc.c, +malloc.c, mallocx.c. + +* alloc.c (GC_check_heap, GC_print_all_smashed): Move the +definition from misc.c. +* dbg_mlc.c (GC_debug_malloc_atomic_uncollectable): Define as +public. +* include/gc.h (GC_debug_malloc_atomic_uncollectable): Declare. +* include/gc.h (GC_MALLOC_ATOMIC_UNCOLLECTABLE): Define new public +macro. +* dbg_mlc.c (MAX_SMASHED): Don't define if already set. +* reclaim.c (MAX_LEAKED): Likewise. +* dbg_mlc.c (GC_add_smashed): Add FIXME about the concurrent +access to the global array. +* reclaim.c (GC_add_leaked): Likewise. +* misc.c (GC_print_back_height): Set on if GC_PRINT_BACK_HEIGHT +(new macro) is defined. +* doc/README.macros (GC_PRINT_BACK_HEIGHT): Document. +* misc.c (GC_dump_regularly, GC_init): Replace 0/1 for +GC_dump_regularly and GC_print_back_height variables with +FALSE/TRUE. +* reclaim.c (GC_print_all_errors): Refine the comment. + +* tests/test.c (reverse_test_inner): Undo one of the previous +patches which shifts "c" and "d" pointers only if +ALL_INTERIOR_POINTERS (since interior pointers are always +recognized in stacks). + +* misc.c (GC_stdout, GC_stderr): Move the definition to the place +where GC_log is defined (Unix only). +* misc.c (GC_init): Recognize "GC_ONLY_LOG_TO_FILE" environment +variable and the similar macro; redirect GC_stdout and GC_stderr +to GC_log if "GC_LOG_FILE" environment variable is set unless +prohibited by GC_ONLY_LOG_TO_FILE (Unix only). +* doc/README.environment (GC_ONLY_LOG_TO_FILE): Document. +* doc/README.macros (GC_ONLY_LOG_TO_FILE): Likewise. + +* misc.c (GC_stdout, GC_write): Rename GC_stdout to GC_log (Win32 +only). +* misc.c (GC_write): Add for MacOS (and OS/2); change WRITE() +accordingly. +* misc.c (GC_printf): Check GC_quiet before va_start(). + +* allchblk.c (GC_freehblk): Use GC_log_printf instead of GC_printf +inside "if (GC_print_stats)" branch. +* alloc.c (GC_collect_or_expand): Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* headers.c (GC_scratch_alloc): Likewise. +* os_dep.c (GC_get_maps, GC_remap, PROTECT, +GC_write_fault_handler, GC_dirty_init, GC_mprotect_thread): Likewise. +* alloc.c (min_bytes_allocd): Use GC_log_printf instead of +GC_printf for DEBUG_THREADS output. +* darwin_stop_world.c (GC_stack_range_for, GC_suspend_thread_list, +GC_stop_world, GC_thread_resume, GC_start_world): Likewise. +* pthread_start.c (GC_inner_start_routine): Likewise. +* pthread_stop_world.c (GC_suspend_handler, GC_restart_handler, +GC_push_all_stacks, GC_suspend_all, GC_stop_world, +GC_start_world): Likewise. +* pthread_support.c (GC_mark_thread, GC_get_nprocs, +GC_start_rtn_prepare_thread, pthread_create): Likewise. +* checksums.c (GC_update_check_page): Use GC_printf() instead of +GC_err_printf() for error printing. +* checksums.c (GC_check_blocks, GC_check_dirty): Use GC_log_printf +instead of GC_printf for logging purposes. +* dyn_load.c (sys_errlist, sys_nerr, errno): Move declaration of +external variable outside from GC_register_dynamic_libraries. +* dyn_load.c (GC_register_dynamic_libraries): Don't use +sys_errlist value if errno equals to sys_nerr. +* dyn_load.c (GC_register_dynamic_libraries): Use GC_log_printf +instead of GC_printf for DL_VERBOSE output. +* dyn_load.c (GC_dyld_image_add, GC_dyld_image_remove, +GC_init_dyld): Use GC_log_printf instead of GC_printf for +DARWIN_DEBUG output. +* os_dep.c (catch_exception_raise): Use GC_log_printf +instead of GC_printf for DEBUG_EXCEPTION_HANDLING output. +* reclaim.c (GC_print_free_list): Move "n" increment out of +GC_printf() call. + +* win32_threads.c (DEBUG_CYGWIN_THREADS, DEBUG_WIN32_PTHREADS, +DEBUG_WIN32_THREADS): Remove. +* win32_threads.c (GC_register_my_thread_inner, +GC_win32_start_inner): Use GC_log_printf instead of GC_printf +inside "if (GC_print_stats)" branch. +* win32_threads.c (GC_PTHREAD_PTRVAL): New macro (defined only if +GC_PTHREADS). +* win32_threads.c (GC_delete_gc_thread, NUMERIC_THREAD_ID, +GC_pthread_join, GC_pthread_create): Use GC_PTHREAD_PTRVAL +macro. +* win32_threads.c (GC_push_stack_for, GC_mark_thread, +GC_CreateThread, GC_beginthreadex, GC_pthread_join, +GC_pthread_create, GC_pthread_start_inner, GC_thread_exit_proc, +GC_mark_thread_local_free_lists): Use GC_log_printf instead of +GC_printf for DEBUG_THREADS output. +* win32_threads.c (GC_win32_start_inner, GC_CreateThread, +GC_beginthreadex, GC_pthread_join, GC_pthread_create, +GC_pthread_start_inner, GC_thread_exit_proc): Cast +GetCurrentThreadId result to long; don't cast value of pthread_t +type to int; adjust printf format specifiers. +* doc/README.win32 (DEBUG_WIN32_PTHREADS): Remove obsolete +information. + +* tests/test.c (cons, small_cons, gcj_cons, check_ints, +check_uncollectable_ints, print_int_list, check_marks_int_list, +fork_a_thread, finalizer, mktree, chktree, alloc8bytes, +alloc_small, tree_test, typed_test, check_heap_stats, WinMain, +test, main): Remove unnecessary casts of GC_printf calls to void. + +* allchblk.c (GC_print_hblkfreelist): Adjust (make uniform across +BDWGC) printed message (adjust letters case, terminating dot and +new line symbols). +* alloc.c (GC_check_fl_marks): Likewise. +* backgraph.c (new_back_edges): Likewise. +* checksums.c (GC_check_dirty): Likewise. +* darwin_stop_world.c (GC_push_all_stacks, +GC_suspend_thread_list): Likewise. +* dbg_mlc.c (GC_print_type, GC_debug_free, GC_debug_realloc, +store_old): Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* mark.c (GC_initiate_gc, GC_mark_some, GC_mark_from, GC_push_all, +GC_push_selected, GC_push_next_marked_dirty): Likewise. +* mark_rts.c (GC_exclude_static_roots_inner): Likewise. +* os_dep.c (GC_remap, GC_default_push_other_roots, +GC_push_thread_structures, GC_dirty_init, GC_read_dirty, +catch_exception_raise_state, catch_exception_raise_state_identity, +GC_mprotect_thread_notify, GC_mprotect_thread, +catch_exception_raise): Likewise. +* pthread_stop_world.c (GC_print_sig_mask, GC_push_all_stacks, +GC_stop_world, GC_stop_init): Likewise. +* pthread_support.c (GC_thr_init, GC_register_my_thread_inner, +GC_start_routine): Likewise. +* win32_threads.c (GC_register_my_thread_inner, +GC_push_all_stacks, GC_win32_start_inner, GC_pthread_join, +GC_pthread_start_inner): Likewise. +* alloc.c (GC_expand_hp_inner): Realign the code. +* mark.c (GC_mark_from, GC_mark_local, GC_do_parallel_mark): +Likewise. +* misc.c (GC_init): Likewise. +* os_dep.c (GC_dirty_init, GC_read_dirty): Likewise. +* include/private/gc_pmark.h (PUSH_CONTENTS_HDR): Likewise. +* tests/test.c (run_one_test): Likewise. +* misc.c (GC_err_puts): Document. +* misc.c (GC_err_write): Remove. +* os_dep.c (dump_maps): Likewise. +* include/private/gc_priv.h (GC_err_write): Likewise. +* os_dep.c (GC_print_address_map): Call GC_err_puts() instead of +dump_maps() and GC_err_write(). +* os_dep.c (GC_read_dirty): Remove redundant brackets. + +* tests/test.c (reverse_test_inner): Test interior pointer +recognition only if ALL_INTERIOR_POINTERS. +* tests/test.c (run_one_test): Replace GC_all_interior_pointers +with GC_get_all_interior_pointers(); simplify the expression. +* tests/test.c (check_heap_stats): Replace GC_bytes_allocd and +GC_bytes_allocd_before_gc with GC_get_total_bytes(). +* tests/test.c (main): Replace GC_gc_no with GC_get_gc_no(). + +* dbg_mlc.c (GC_debug_strdup, GC_debug_free): Output a portability +warning if the argument is NULL and GC is in leaks detection mode. +* dbg_mlc.c (GC_debug_strndup, GC_debug_wcsdup): New public +function definition. +* malloc.c (GC_strndup, GC_wcsdup, strndup): Likewise. +* mallocx.c (GC_posix_memalign): Likewise. +* malloc.c (strdup): Fix string size value; rename "len" to "lb". +* mallocx.c: Include errno.h unless WinCE (otherwise include +windows.h for Win32 error constants). +* win32_threads.c: Define WIN32_LEAN_AND_MEAN and NOSERVICE before +windows.h inclusion. +* misc.c (GC_init): Register at-exit callback if GC_find_leak +(even if GC_FIND_LEAK macro is unset). +* pthread_stop_world.c (NACL_STORE_REGS, +__nacl_suspend_thread_if_needed, GC_nacl_initialize_gc_thread): +Use BCOPY() instead of memcpy(). +* pthread_support.c (GC_init_real_syms): Likewise. +* doc/README.macros (GC_DEBUG_REPLACEMENT, GC_REQUIRE_WCSDUP): +Document new macro. +* doc/README.macros (REDIRECT_MALLOC): Update documentation. +* include/gc.h (GC_strndup, GC_posix_memalign, GC_debug_strndup): +New API function prototype. +* include/gc.h (GC_MALLOC, GC_REALLOC): Redirect to +GC_debug_malloc/realloc_replacement() if GC_DEBUG_REPLACEMENT. +* include/gc.h (GC_STRDUP): Remove redundant parentheses. +* include/leak_detector.h (realloc, strdup): Likewise. +* include/gc.h (GC_STRNDUP): New API macro. +* include/gc.h (GC_NEW, GC_NEW_ATOMIC, GC_NEW_STUBBORN, +GC_NEW_UNCOLLECTABLE): Add missing parentheses. +* include/gc.h (GC_wcsdup, GC_debug_wcsdup): New API function +prototype (only if GC_REQUIRE_WCSDUP). +* include/gc.h (GC_WCSDUP): New API macro (only if +GC_REQUIRE_WCSDUP). +* include/leak_detector.h: Include stdlib.h and string.h after gc.h (unless +GC_DONT_INCLUDE_STDLIB). +* include/leak_detector.h (malloc, calloc, free, realloc): +Undefine symbol before its redefinition. +* include/leak_detector.h (strndup, memalign, posix_memalign): +Redefine to the corresponding GC function. +* include/leak_detector.h (wcsdup): Redefine to GC_WCSDUP (only +if GC_REQUIRE_WCSDUP). +* include/leak_detector.h (CHECK_LEAKS): Add comment; don't define +the macro if already defined. + +* misc.c (GC_abort): Use _exit() (instead of DebugBreak) on Win32 +when doing code static analysis (to inform the tool that the +function is a no-return one). +* os_dep.c (GC_linux_stack_base): Remove a duplicate validation +of the length of "stat" file; use signed int type for "i", +"buf_offset" and "len" local variables (since read() may +return -1). + +* blacklst.c (GC_bl_init_no_interiors): New function (the code +moved from GC_bl_init). +* blacklst.c (GC_bl_init): Invoke GC_bl_init_no_interiors unless +GC_all_interior_pointers mode; remove unnecessarily parameter cast +for GC_scratch_alloc call. +* include/private/gc_priv.h (GC_bl_init): Move the function +declaration to misc.c file. +* misc.c (GC_bl_init_no_interiors): Add a prototype. +* misc.c (GC_set_all_interior_pointers): Allow values other than 0 +and 1; allow altering GC_set_all_interior_pointers value even +after GC initialization. +* obj_map.c (GC_initialize_offsets): Clear GC_valid_offsets and +GC_modws_valid_offsets if GC_all_interior_pointers is off. +* misc.c (GC_init): Don't call GC_initialize_offsets() unless +GC_all_interior_pointers mode. + +* alloc.c (GC_finish_collection): Remove redundant brackets; +adjust code indentation. +* blacklst.c (GC_add_to_black_list_normal): Simplify expression +(to improve code readability). +* blacklst.c (GC_is_black_listed): Join nested "if" (into a single +conditional expression); initialize "nblocks" just before the loop +beginning. +* misc.c (GC_init): Don't compute initial_heap_sz if GC is already +initialized. +* include/private/gc_priv.h (GC_initialize_offsets): Move the +function declaration to misc.c file. +* obj_map.c (GC_initialize_offsets): Remove offsets_initialized +static variable since the function is called only once. +* tests/middle.c (main): Use setter for GC_all_interior_pointers; +adjust printf format specifier (and cast the value passed to). + +* doc/README.macros (SMALL_CONFIG, LARGE_CONFIG): Refine the +documentation. +* include/private/gc_hdrs.h (LOG_BOTTOM_SZ): Ignore SMALL_CONFIG +if LARGE_CONFIG is defined. +* include/private/gc_priv.h (CPP_LOG_HBLKSIZE): Likewise. + +* alloc.c (GC_finish_collection): Replace "#else #ifdef" with +"#elif". +* include/private/gc_priv.h (CPP_LOG_HBLKSIZE, LOG_PHT_ENTRIES, +MAX_ROOT_SETS, MAX_HEAP_SECTS): Likewise. +* alloc.c (GC_expand_hp_inner): Check for GC_collect_at_heapsize +overflow even if not LARGE_CONFIG. +* dbg_mlc.c (GC_check_heap_proc): Check "oh" size even if +SMALL_CONFIG. +* finalize.c (GC_print_finalization_stats): Fix "#endif" comment. +* doc/README.environment (GC_LOG_FILE, GC_PRINT_VERBOSE_STATS, +GC_FULL_FREQUENCY): Refine the documentation. + +* extra/msvc_dbg.c: Test _MSC_VER macro; include "gc.h" (for +GC_word). +* extra/msvc_dbg.c (ULONG_PTR): Replace with GC_ULONG_PTR; define +as word. + +* dbg_mlc.c (GC_get_back_ptr_info, GC_print_obj, +GC_print_smashed_obj, GC_debug_free_inner): Add a code for a +LINT-like tool to instruct it that the function is invoked only +with valid parameters (otherwise a SEGV is ok); recognize LINT2 +new macro. +* misc.c (GC_abort): Instruct a LINT-like tool that the function +never returns in fact. +* os_dep.c (GC_linux_stack_base): Check for read buffer overflow; +close the file immediately after read; use STRTOULL() instead of +decoding the address number manually. +* include/private/gc_priv.h (EXPECT): Don't specify outcome for a +LINT-like tool. +* include/private/gc_priv.h (GC_all_interior_pointers): Instruct a +LINT-like tool that the value is restricted to zero and one only +(required since the variable is global and its value is used as a +part of array index expression is some places). + +* dbg_mlc.c (GC_make_closure): Fix SEGV in case GC_malloc returns +NULL. +* dbg_mlc.c (GC_debug_register_finalizer, +GC_debug_register_finalizer_no_order, +GC_debug_register_finalizer_unreachable, +GC_debug_register_finalizer_ignore_self): Handle out of memory +case properly (similar to GC_register_finalizer_inner). +* headers.c (GC_install_header): Handle the case when alloc_hdr() +returns NULL. +* os_dep.c (GC_get_maps_len): Defend against missing "maps" file. +* pthread_support.c (GC_mark_thread): Place a dummy return +statement (which uses "id" argument) before the actual use of "id" +as an array index (to suppress a warning produced by some static +code analysis tools). +* win32_threads.c (GC_mark_thread): Likewise. +* pthread_support.c (GC_thr_init): Abort (with the appropriate +message) if out of memory. + +* finalize.c (GC_register_finalizer_inner): Fix a typo in a +comment. +*include/private/gcconfig.h (STACKBOTTOM): Likewise. +* gcj_mlc.c (GC_core_gcj_malloc): Replace 0/1 with TRUE/FALSE in +EXPECT (the 2nd argument). +* malloc.c (GC_core_malloc_atomic, GC_core_malloc, GC_free): +Likewise. +* mark.c (GC_mark_and_push, GC_mark_and_push_stack): Likewise. +* thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Likewise. +* include/private/gc_hdrs.h (HC_GET_HDR): Likewise. +* include/private/gc_priv.h (SMALL_OBJ): Likewise. +* include/private/specific.h (getspecific): Likewise. +* pthread_support.c (LOCK_STATS): Add a comment. + +* include/gc_pthread_redirects.h (GC_NO_DLOPEN, +GC_NO_PTHREAD_SIGMASK, GC_PTHREAD_CREATE_CONST, +GC_PTHREAD_EXIT_ATTRIBUTE, GC_NO_PTHREAD_CANCEL): Move the +definition to gc_config_macros. + +* pthread_support.c (pthread_cancel, GC_pthread_cancel_t, +GC_pthread_cancel): Test GC_NO_PTHREAD_CANCEL (instead of NACL and +GC_PTHREAD_EXIT_ATTRIBUTE). +* include/gc_pthread_redirects.h (GC_pthread_cancel, +pthread_cancel): Likewise. +* pthread_support.c (GC_pthread_create, GC_pthread_sigmask, +GC_pthread_join, GC_pthread_detach, GC_pthread_cancel): Realign +code. +* include/gc_pthread_redirects.h (GC_PTHREAD_EXIT_ATTRIBUTE): +Define as empty for NaCl. +* include/gc_pthread_redirects.h (GC_NO_PTHREAD_CANCEL): New macro +defined. + +* dyn_load.c (GC_init_dyld): Do not invoke +_dyld_bind_fully_image_containing_address() if GC_no_dls (as it is +not required to register the main data segment in that case). +* include/gc.h (GC_no_dls): Adjust the comment. + +* dyn_load.c (GC_MUST_RESTORE_REDEFINED_DLOPEN): Test +GC_NO_DLOPEN. +* gc_dlopen.c: Likewise. +* include/gc_pthread_redirects.h (GC_dlopen, dlopen): Likewise. +* gc_dlopen.c: Don't include dlfcn.h (as it is included in +gc_pthread_redirects.h). +* pthread_support.c (pthread_sigmask, GC_pthread_sigmask_t, +GC_pthread_sigmask): Test GC_NO_PTHREAD_SIGMASK (instead of +GC_DARWIN_THREADS, GC_OPENBSD_THREADS and NACL). +* include/gc_pthread_redirects.h (GC_pthread_sigmask, +pthread_sigmask): Likewise. +* win32_threads.c (pthread_sigmask, GC_pthread_sigmask): Test +GC_NO_PTHREAD_SIGMASK (instead of GC_WIN32_PTHREADS). +* pthread_support.c (pthread_create, GC_pthread_create_t, +GC_pthread_create): Rename GC_PTHREAD_CONST to +GC_PTHREAD_CREATE_CONST. +* win32_threads.c (GC_pthread_create): Likewise. +* include/gc_pthread_redirects.h: Likewise. +* include/gc_pthread_redirects.h (GC_NO_DLOPEN, +GC_NO_PTHREAD_SIGMASK): New macro defined. +* include/gc_pthread_redirects.h (GC_PTHREAD_CREATE_CONST): Set to +empty for NaCl. +* include/gc_pthread_redirects.h (GC_PTHREAD_EXIT_ATTRIBUTE): Do +not define for Android (as CANCEL_SAFE is not defined). + +* include/gc.h (GC_ADD_CALLER, GC_RETURN_ADDR, +GC_HAVE_BUILTIN_BACKTRACE, GC_CAN_SAVE_CALL_STACKS): Move +definition to gc_config_macros.h file. +* include/gc_config_macros.h: Check the file is included from gc.h +file. +* include/gc_version.h: Likewise. + +* gc_dlopen.c: Empty unit for NaCl. +* os_dep.c: Include fcntl.h for NaCl. +* os_dep.c (GC_get_main_stack_base): Ignore +USE_GET_STACKBASE_FOR_MAIN macro for NaCl. +* os_dep.c (GC_get_stack_base): Return GC_UNIMPLEMENTED for NaCl. +* os_dep.c (GC_remap): Use mmap (instead of mprotect) for NaCl. +* pthread_start.c (GC_inner_start_routine): Don't invoke +pthread_cleanup_push/pop for NaCl. +* pthread_stop_world.c (GC_nacl_num_gc_threads, +GC_nacl_thread_idx, GC_nacl_park_threads_now, +GC_nacl_thread_parker, GC_nacl_gc_thread_self, +GC_nacl_thread_parked, GC_nacl_thread_used, +GC_nacl_thread_parking_inited, GC_nacl_thread_alloc_lock): New +variable (fo NaCl only). +* pthread_stop_world.c (GC_remove_allowed_signals, +suspend_handler_mask, GC_stop_count, GC_world_is_stopped, +GC_retry_signals, SIG_THR_RESTART, GC_suspend_ack_sem, +GC_restart_ack_sem, GC_suspend_handler_inner, GC_suspend_handler, +GC_restart_handler): Don't define for NaCl. +* pthread_support.c (GC_get_nprocs): Likewise. +* include/private/gc_priv.h (SIG_SUSPEND): Likewise. +* include/private/gcconfig.h (LINUX): Likewise. +* pthread_stop_world.c (GC_push_all_stacks): Push register storage +for NaCl. +* pthread_stop_world.c (GC_suspend_all, GC_stop_world, +GC_start_world): Implement for NaCl. +* pthread_stop_world.c (GC_stop_world): Don't define unused "i" +local variable for OpenBSD (and NaCl). +* pthread_stop_world.c (NACL_STORE_REGS): New macro definition for +NaCl. +* pthread_stop_world.c (nacl_pre_syscall_hook, +__nacl_suspend_thread_if_needed, nacl_post_syscall_hook, +GC_nacl_initialize_gc_thread, GC_nacl_shutdown_gc_thread): New +function (for NaCl only). +* pthread_stop_world.c (GC_stop_init): Empty for NaCl. +* pthread_support.c (pthread_cancel, pthread_sigmask): Don't +redirect for NaCl. +* include/gc_pthread_redirects.h (pthread_cancel, +pthread_sigmask): Likewise. +* pthread_support.c (GC_nacl_initialize_gc_thread, +GC_nacl_shutdown_gc_thread): New internal prototype (NaCl only). +* pthread_support.c (GC_new_thread, GC_delete_thread): Initialize +and shutdown thread for NaCl. +* pthread_support.c (GC_thr_init): Call sysconf for NaCl. +* pthread_support.c (GC_pthread_exit): Call GC_thread_exit_proc +for NaCl. +* include/gc.h: Don't include features.h for NaCl. +* include/gc_pthread_redirects.h (GC_PTHREAD_CONST): New macro. +* include/gc_pthread_redirects.h (GC_pthread_create): Use +GC_PTHREAD_CONST instead of const. +* win32_threads.c (GC_pthread_create): Likewise. +* pthread_support.c (GC_pthread_create_t, GC_pthread_create, +pthread_create): Likewise. +* include/private/gcconfig.h (NACL): Recognize NaCl. +* include/private/gcconfig.h (GC_LINUX_THREADS): Valid for NaCl. +* include/private/pthread_stop_world.h (thread_stop_info): Add +reg_storage member; define NACL_GC_REG_STORAGE_SIZE macro (for +NaCl only). +* include/private/pthread_support.h (GC_nacl_gc_thread_self): +Declare internal variable (for NaCl only). + +* mach_dep.c (GC_with_callee_saves_pushed): Fix FE_ALL_EXCEPT +macro. + +* mark.c (GC_mark_some): Prefix and suffix "asm" and "volatile" +keywords with double underscore. +* os_dep.c (catch_exception_raise, catch_exception_raise_state, +catch_exception_raise_state_identity): Add GC_API_OSCALL to +function definition. +* os_dep.c (catch_exception_raise_state, +catch_exception_raise_state_identity): Move definition to be +before GC_ports. +* os_dep.c (catch_exception_raise): Declare to have the symbol +defined before GC_ports. +* os_dep.c (GC_ports): Store references to catch_exception_raise, +catch_exception_raise_state, catch_exception_raise_state_identity +(to prevent stripping these symbols as dead). +* os_dep.c (catch_exception_raise, catch_exception_raise_state, +catch_exception_raise_state_identity): Mark these symbols as +"referenced dynamically" via an assembler directive (unless +NO_DESC_CATCH_EXCEPTION_RAISE). +* include/private/gc_priv.h (GC_API_OSCALL): New macro (defined +similar to GC_API but as if GC_DLL is always defined). + +* os_dep.c: Don't include signal.h for GC_write_fault_handler on +Win32. +* os_dep.c (SIG_OK): Don't return true unless SIGSEGV or SIGBUS on +FreeBSD. +* os_dep.c (CODE_OK): Use SEGV_ACCERR on FreeBSD (define +SEGV_ACCERR for older FreeBSD releases). + +* dyn_load.c (GC_register_map_entries, +GC_register_dynamic_libraries_dl_iterate_phdr): Calculate +DATASTART only once if DATASTART_IS_FUNC. +* dyn_load.c (GC_register_dynamic_libraries_dl_iterate_phdr): +Calculate DATAEND only once if DATAEND_IS_FUNC. +* dyn_load.c: Add comment to some endif; realign some code. +* dyn_load.c (GC_init_dyld): Don't use +_dyld_bind_fully_image_containing_address if +NO_DYLD_BIND_FULLY_IMAGE defined; add FIXME. +* include/private/gcconfig.h (GC_data_start, GC_find_limit): +Declare if used by DATASTART/DATAEND, respectively. +* include/private/gcconfig.h (DATASTART_IS_FUNC, DATAEND_IS_FUNC): +Define if DATASTART/DATAEND is a function, respectively. +* include/private/gcconfig.h (GETPAGESIZE, NO_PTHREAD_TRYLOCK, +NO_DYLD_BIND_FULLY_IMAGE): Define for Darwin/arm as well; include +unistd.h. + +* os_dep.c (GC_setpagesize, GC_task_self, PROTECT, UNPROTECT): +Reorder to remove redundant ifdef for Win32. +* os_dep.c: Add comment to some endif. +* os_dep.c: Include pthread.h (for Linux even if single-threaded) +if USE_GET_STACKBASE_FOR_MAIN; also include it for Darwin. +* os_dep.c (STACKBOTTOM): Redefine for Darwin (unless prohibited +for some reason). +* os_dep.c (GC_get_main_stack_base): Allow +USE_GET_STACKBASE_FOR_MAIN for Linux even if single-threaded; add +assertion for the returned result. +* os_dep.c (GC_get_stack_base): Define for Darwin if +multi-threaded. +* os_dep.c (SIG_OK, CODE_OK): Add comment (for FreeBSD). +* os_dep.c (ID_STOP, ID_RESUME): Define only if threads. +* os_dep.c (catch_exception_raise): Remove redundant parentheses; +refine the documentation. + +* NT_MAKEFILE: Define _CRT_SECURE_NO_DEPRECATE for C++ files as +well. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* doc/README.macros (USE_GET_STACKBASE_FOR_MAIN): Refine. +* include/gc.h (GC_INIT): Document. +* include/private/gc_priv.h (GC_MACH_HEADER, GC_MACH_SECTION, +GC_GETSECTBYNAME): Define depending only on the word size (i.e., +define these macros also for ARM). +* tests/test.c (check_heap_stats): Print main thread stack bottom +as well (only if verbose mode is on). + +* mach_dep.c (GC_with_callee_saves_pushed): Fix and improve code +introduced by the previous patch (if GETCONTEXT_FPU_EXCMASK_BUG +and X86_64). + +* darwin_stop_world.c (GC_FindTopOfStack): Prefix and suffix +"volatile" keyword with double underscore. +* mach_dep.c (GETCONTEXT_FPU_EXCMASK_BUG): Recognize new macro and +include fenv.h if defined (unless NO_GETCONTEXT or HAVE_PUSH_REGS). +* mach_dep.c (GC_with_callee_saves_pushed): Restore FPU exception +mask corrupted by getcontext if GETCONTEXT_FPU_EXCMASK_BUG. +* include/private/gcconfig.h (GETCONTEXT_FPU_EXCMASK_BUG): Define +for Linux/x64 (since its GLibc getcontext currently has the bug). + +* allchblk.c (GC_use_entire_heap): Change type to int (as declared +in gc.h); set the default value depending on new GC_USE_ENTIRE_HEAP +macro. +* misc.c (GC_init): Test GC_USE_ENTIRE_HEAP environment variable to +alter the default value of GC_use_entire_heap. +* doc/README.environment (GC_USE_ENTIRE_HEAP): Document. +* doc/README.macros (GC_USE_ENTIRE_HEAP): Likewise. + +* include/private/gcconfig.h (PARALLEL_MARK): Do not make it cause +MPROTECT_VDB undefining. + +* include/private/gcconfig.h (DYNAMIC_LOADING): Fix filename in +the comment. + +* include/private/gc_priv.h (_GC_arrays): Move the conditional +macro definitions (shortcuts for GC_arrays members) into the +structure body. + +* darwin_stop_world.c (GC_mach_handler_thread, +GC_use_mach_handler_thread, +GC_darwin_register_mach_handler_thread): Define only if +MPROTECT_VDB. +* darwin_stop_world.c (GC_suspend_thread_list): Use +GC_mach_handler_thread and GC_use_mach_handler_thread only if +MPROTECT_VDB. +* darwin_stop_world.c (GC_stop_world): Reset GC_mach_threads_count +only if defined (i.e. unless GC_NO_THREADS_DISCOVERY). +* misc.c (GC_init): Fix comment for GWW_VDB. +* os_dep.c (DARWIN_EXC_STATE, DARWIN_EXC_STATE_COUNT, +DARWIN_EXC_STATE_T, DARWIN_EXC_STATE_DAR): New macros. +* os_dep.c (catch_exception_raise): Use DARWIN_EXC_STATE, +DARWIN_EXC_STATE_COUNT, DARWIN_EXC_STATE_T, DARWIN_EXC_STATE_DAR. +* pthread_support.c (GC_thr_init): Define "dummy" local variable +only unless GC_DARWIN_THREADS. +* include/private/gcconfig.h (MPROTECT_VDB): Define for Darwin +even in the single-threaded mode; define for iPhone/iPad. +* include/private/gcconfig.h (IA64): Remove unnecessary "ifdef" +around "undef". +* include/private/gcconfig.h (HEURISTIC1): Remove unused for +Cygwin. +* include/private/gcconfig.h (STACKBOTTOM): Use fixed address for +Darwin/arm (instead of HEURISTIC1). + +* misc.c (GC_write): Replace multiple "ifdef/endif" with "elif" +(for ECOS and NOSYS). +* os_dep.c (GC_get_main_stack_base): Likewise. +* os_dep.c (GC_get_main_stack_base): Check +USE_GET_STACKBASE_FOR_MAIN macro before checking STACKBOTTOM one; +remove "dummy" variable (use result one instead). +* doc/README.macros (SN_TARGET_PS3): Document. +* extra/threadlibs.c (main): Don't output "-lpthread" (and "-ldl") +for Android. +* include/private/pthread_support.h: Fix comment for "endif". + +* misc.c (GC_allocate_ml): Define global variable if SN_TARGET_PS3. +* misc.c (GC_init): Initialize GC_allocate_ml if SN_TARGET_PS3. +* os_dep.c (SIGSEGV): Define to dummy zero if SN_TARGET_PS3. +* os_dep.c (GC_unix_mmap_get_mem): Don't define if SN_TARGET_PS3. +* os_dep.c (GC_default_push_other_roots, +GC_push_thread_structures): Define for SN_TARGET_PS3. +* include/private/gc_locks.h (GC_allocate_ml, LOCK, UNLOCK): Define +for SN_TARGET_PS3. +* include/private/gcconfig.h (SN_TARGET_PS3): Recognize new macro +(Sony PS/3 target). +* include/private/gcconfig.h (THREADS): Define unconditionally if +SN_TARGET_PS3. +* include/private/gcconfig.h (GET_MEM): Define for SN_TARGET_PS3. + +* alloc.c (GC_collect_or_expand): Replace NIL with NULL in message. +* dbg_mlc.c (GC_debug_malloc, GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, +GC_debug_generic_malloc_inner, +GC_generic_malloc_inner_ignore_off_page, GC_debug_malloc_stubborn, +GC_debug_malloc_atomic, GC_debug_malloc_uncollectable, +GC_debug_malloc_atomic_uncollectable): Likewise. +* gcj_mlc.c (GC_debug_gcj_malloc): Likewise. +* dbg_mlc.c (GC_check_annotated_obj): Replace NIL with NULL in a +comment. +* dyn_load.c (GC_FirstDLOpenedLinkMap): Likewise. +* mark_rts.c (GC_roots_present): Likewise. +* doc/README: Likewise. +* include/private/gc_hdrs.h (IS_FORWARDING_ADDR_OR_NIL): Likewise. +* include/private/gc_priv.h (_GC_arrays): Likewise. + +* configure.ac: Use AC_CHECK_LIB() to check for pthread instead of +just blindly linking to -lpthread, as Android includes pthread +support within libc and does not provide a separate libpthread. +* dyn_load.c (GC_register_dynamic_libraries): Skip current link map +entry if l_addr is NULL (Android/bionic only). +* pthread_stop_world.c (android_thread_kill): New internal function +(Android only). +* pthread_stop_world.c (GC_suspend_all, GC_start_world): Call +android_thread_kill (based on tkill) instead of pthread_kill on +Android (since pthread_kill cannot be used safely on the platform). +* pthread_support.c (GC_new_thread): Store thread Id (obtained from +gettid) for use by android_thread_kill (Android only). +* include/private/pthread_support.h (GC_Thread_Rep): Add kernel_id +structure member (Android only). +* include/private/gcconfig.h: Recognize __x86_64 macro as a synonym +of __x86_64__ (Darwin); define __environ macro (Android on M68K). + +* allchblk.c (GC_freehblk): Print extended error message (done via +GC_printf() before aborting with a short message) only if +GC_print_stats. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* os_dep.c (GC_get_maps, GC_register_data_segments, GC_remap, +PROTECT, GC_write_fault_handler, GC_mprotect_thread): Likewise. +* pthread_stop_world.c (GC_start_world): Likewise. +* win32_threads.c (GC_register_my_thread_inner): Likewise. +* os_dep.c (GC_get_main_stack_base, GC_register_data_segments, +GC_dirty_init): Remove redundant print of an error message before +aborting with the same message. +* os_dep.c (GC_register_data_segments): Remove format specifier +from the string passed to GC_err_puts(); use ABORT instead of EXIT +(if invalid executable type). +* os_dep.c (GC_remap): Adjust printf format specifier (for long +type). +* os_dep.c (GC_dirty_init): Print a message about SIG_IGN detected +(for SIGSEGV/BUS) only if GC_print_stats. +* os_dep.c (catch_exception_raise): Join 2 adjacent GC_err_printf +calls. + +* tests/test.c (main): Print the relevant message if GWW_VDB. +* include/private/gcconfig.h: Don't define MPROTECT_VDB for Win32 +on x64 if compiled by GCC. + +* tests/staticrootstest.c: Include string.h for memset() prototype. +* tests/thread_leak_test.c (main): Fix printf() format specifiers. + +* CMakeLists.txt: Check enable_parallel_mark on Darwin. +* configure.ac: Likewise. +* darwin_stop_world.c (DARWIN_SUSPEND_GC_THREADS, +DARWIN_QUERY_TASK_THREADS): Rename to GC_NO_THREADS_DISCOVERY and +GC_DISCOVER_TASK_THREADS, respectively. +* os_dep.c (DARWIN_SUSPEND_GC_THREADS): Likewise. +* pthread_support.c (DARWIN_SUSPEND_GC_THREADS): Likewise. +* darwin_stop_world.c (DARWIN_QUERY_TASK_THREADS): Don't define +(and remove FIXME). +* darwin_stop_world.c (GC_use_threads_discovery): Add GC_API; +comment; remove FIXME. +* win32_threads.c (GC_NO_DLLMAIN): Rename to +GC_NO_THREADS_DISCOVERY. +* tests/test.c (GC_NO_DLLMAIN): Likewise. +* doc/README.macros (GC_NO_DLLMAIN): Likewise. +* doc/README.win32 (GC_NO_DLLMAIN): Likewise. +* doc/README.macros (GC_NO_THREADS_DISCOVERY): Update the comment. +* win32_threads.c (GC_win32_dll_threads): Define as macro to true +if GC_DISCOVER_TASK_THREADS (and not GC_NO_THREADS_DISCOVERY); +update the comment. +* win32_threads.c (GC_use_DllMain): Rename to +GC_use_threads_discovery; do not set GC_win32_dll_threads if +GC_DISCOVER_TASK_THREADS. +* win32_threads.c (GC_started_thread_while_stopped, +GC_lookup_thread_inner, UNPROTECT_THREAD, GC_lookup_pthread, +GC_thr_init, GC_pthread_create, DllMain): Rewrite some expressions +which use GC_win32_dll_threads to minimize the possibility of +an "unreachable code" compiler warning when GC_win32_dll_threads +is defined as a macro. +* win32_threads.c (GC_unregister_my_thread): Don't call +GC_delete_thread() if GC_win32_dll_threads and THREAD_LOCAL_ALLOC +(since can't happen); use "t" local variable only if not +GC_win32_dll_threads. +* doc/README.macros (GC_DISCOVER_TASK_THREADS): Document. +* include/gc.h (GC_use_DllMain): Rename to +GC_use_threads_discovery but keep old name as a macro definition. +* include/gc.h (GC_use_threads_discovery): Declare also for +Darwin; update the comment. +* tests/test.c (main): Call GC_use_threads_discovery for Darwin +(to test the mode if possible). + +* darwin_stop_world.c (DARWIN_SUSPEND_GC_THREADS, +DARWIN_QUERY_TASK_THREADS): New macro recognized. +* darwin_stop_world.c (GC_query_task_threads): add STATIC; +initialize to false; define as macro if DARWIN_SUSPEND_GC_THREADS +or DARWIN_QUERY_TASK_THREADS; remove FIXME. +* darwin_stop_world.c (GC_use_threads_discovery): New function +(for setting GC_query_task_threads value). +* darwin_stop_world.c (GC_mach_handler_thread, +GC_use_mach_handler_thread, GC_mach_thread, GC_MAX_MACH_THREADS, +GC_mach_threads, GC_mach_threads_count, GC_suspend_thread_list, +GC_darwin_register_mach_handler_thread): Define only if not +DARWIN_SUSPEND_GC_THREADS. +* darwin_stop_world.c (GC_stop_world, GC_start_world): Exclude +the code for GC_query_task_threads case from compilation unless +DARWIN_SUSPEND_GC_THREADS. +* os_dep.c (GC_darwin_register_mach_handler_thread): Declared only +if Darwin threads and not DARWIN_SUSPEND_GC_THREADS. +* os_dep.c (GC_mprotect_thread): Call +GC_darwin_register_mach_handler_thread only if THREADS and not +DARWIN_SUSPEND_GC_THREADS. +* pthread_support.c (marker_mach_threads): Don't define if +DARWIN_SUSPEND_GC_THREADS. +* pthread_support.c (GC_mark_thread): Don't fill in +marker_mach_threads if DARWIN_SUSPEND_GC_THREADS. +* include/private/gc_locks.h (GC_need_to_lock): Always declare for +THREADS case. + +* darwin_stop_world.c (GC_query_task_threads): Don't define to +false for DARWIN_DONT_PARSE_STACK case; unconditionally initialize +the variable to false (for now). +* darwin_stop_world.c (GC_push_all_stacks): Call task_threads() +only if not DARWIN_DONT_PARSE_STACK. +* darwin_stop_world.c (GC_stop_world, GC_start_world): Use the +approach based on task_threads() only if GC_query_task_threads +else use GC_threads table. + +* darwin_stop_world.c (GC_mach_threads): Remove static qualifier. +* darwin_stop_world.c (GC_stop_init): Remove (as we do not need to +really clear GC_mach_threads[]). +* darwin_stop_world.c (GC_stop_world): Reset GC_mach_threads_count +(instead of calling GC_stop_init). +* include/private/pthread_support.h (GC_stop_init): Remove proto. +* pthread_support.c (GC_stop_init): Add proto (unless Darwin). +* pthread_support.c (GC_thr_init): Don't call GC_stop_init() if +GC_DARWIN_THREADS. + +* darwin_stop_world.c (GC_stack_range_for): New static function +(move the code from GC_push_all_stacks). +* darwin_stop_world.c (GC_push_all_stacks): Call +GC_stack_range_for(); rename kern_return local variable to +kern_result. +* darwin_stop_world.c (GC_is_mach_marker): Change argument type +from mach_port_t to thread_act_t. +* pthread_support.c (GC_is_mach_marker): Likewise. + +* darwin_stop_world.c (GC_push_all_stacks): Fix "my_task" local +variable initialization (always call current_task()). +* pthread_support.c (GC_thr_init, GC_register_my_thread_inner): +Don't set thread's stop_info.stack_ptr value for Darwin. +* include/private/darwin_stop_world.h (thread_stop_info): Update +the comment for stack_ptr. + +* darwin_stop_world.c (GC_push_all_stacks): Rename "r", "me" local +variables to "kern_return" and "my_thread" ones, respectively; +call mach_port_deallocate() unconditionally. +* darwin_stop_world.c (GC_stop_world): Don't call mach_thread_self +if DEBUG_THREADS. + +* darwin_stop_world.c (GC_mach_thread): Move from +darwin_stop_world.h. +* include/private/darwin_stop_world.h (GC_mach_thread): Remove. +* win32_threads.c (GC_start_world): Define "thread_id" local +variable only if GC_ASSERTIONS; decide whether to resume a thread +based on its "suspended" field value; assert that suspended thread +stack_base is non-zero and the thread is not our one. + +* darwin_stop_world.c (GC_thread_resume): New inline function +(move code from GC_thread_resume). +* darwin_stop_world.c (GC_start_world): Check result of +task_threads(); call GC_thread_resume(). +* os_dep.c (GC_malloc_heap_l, GC_is_malloc_heap_base): Define +only if not CYGWIN32. +* os_dep.c (GC_is_heap_base): Call GC_is_malloc_heap_base() only +if not CYGWIN32. + +* darwin_stop_world.c (FindTopOfStack): Change return type to +ptr_t (from long); make GC_INNER; add GC_ prefix. +* darwin_stop_world.c (GC_push_all_stacks): Add thread_blocked +local variable (initialized from the corresponding GC_thread +field unless GC_query_task_threads); add assertion that our +thread is not blocked; prefix FindTopOfStack with GC_ and remove +no longer needed cast to ptr_t of the result; handle thread +blocked case (and remove FIXME); use GC_push_all_stack_sections +unless GC_query_task_threads (and remove FIXME). +* pthread_support.c (GC_FindTopOfStack): Declare (if needed). +* pthread_support.c (GC_do_blocking_inner): Call +GC_save_regs_in_stack (if needed) before acquiring the lock. +* win32_threads.c (GC_do_blocking_inner): Likewise. +* pthread_support.c (GC_do_blocking_inner): Set/clear topOfStack +field of GC_thread (Darwin only). +* include/private/pthread_support.h (GC_thread): Add topOfStack +field for Darwin (unless DARWIN_DONT_PARSE_STACK). + +* finalize.c (GC_check_finalizer_nested): Change return type to +char pointer (instead of int pointer); use explicit cast for +GC_finalizer_nested assignment. +* pthread_support.c (GC_check_finalizer_nested): Likewise. +* win32_threads.c (GC_check_finalizer_nested): Likewise. +* finalize.c (GC_finalizer_nested): Change type to unsigned char. +* finalize.c (GC_notify_or_invoke_finalizers): Change type of +"pnested" local variable to char pointer. +* pthread_support.c (GC_do_blocking_inner, +GC_call_with_gc_active): Use explicit cast for "thread_blocked" +field assignment. +* win32_threads.c (GC_lookup_pthread): Use explicit cast for +"suspended" field assignment. +* win32_threads.c (GC_Thread_Rep): Use short type for +finalizer_skipped; use char type for finalizer_nested and flags +fields and reorder some fields (to minimize GC_Thread_Rep +structure size). +* include/private/pthread_support.h (GC_Thread_Rep): Likewise. +* win32_threads.c (GC_Thread_Rep): Use char type for suspended +field (instead of GC_bool). +* include/private/pthread_support.h (GC_Thread_Rep): Use char type +for thread_blocked field (instead of short). + +* darwin_stop_world.c (GC_query_task_threads): New variable (or +macro). +* darwin_stop_world.c (GC_push_all_stacks): Use +GC_query_task_threads (to choose between algorithms based on +kernel task_threads and based on GC_threads table); update FIXME; +remove commented out GC_push_one statements. +* pthread_support.c (GC_thr_init, GC_do_blocking_inner, +GC_call_with_gc_active, GC_register_my_thread_inner): Initialize +stack_ptr field for all platforms. +* pthread_support.c (GC_call_with_gc_active): Initialize +saved_stack_ptr field for all platforms. +* include/private/darwin_stop_world.h (thread_stop_info): Add +stack_ptr field; change type of already_suspended from int to +GC_bool. + +* darwin_stop_world.c (GC_MAX_MACH_THREADS): New macro. +* darwin_stop_world.c (GC_mach_threads, GC_stop_init): Use +GC_MAX_MACH_THREADS instead of THREAD_TABLE_SZ. +* darwin_stop_world.c (GC_mach_threads): Add FIXME. +* darwin_stop_world.c (GC_stop_init, GC_suspend_thread_list, +GC_stop_world): Use FALSE and TRUE for already_suspended field and +"changed", "found" variables. +* darwin_stop_world.c (GC_is_mach_marker): New prototype (only if +PARALLEL_MARK). +* darwin_stop_world.c (GC_suspend_thread_list): Change return type +to GC_bool; change type of "changed", "found" to GC_bool; make +"my_thread" as an argument (instead of acquiring/deallocating it +locally); do not add my_thread, GC_mach_handler_thread and marker +threads to GC_mach_threads table; check for overflow of +GC_mach_threads table; increase GC_mach_threads_count if "found" +is true and info.suspend_count is non-zero. +* darwin_stop_world.c (GC_suspend_thread_list, GC_start_world): +Adjust "thread" format specifiers for GC_printf(); search thread +in "old_list" starting from the previous found one. +* darwin_stop_world.c (GC_stop_world): Rename "changes" to +"changed" local variable; remove "result" variable; adjust +GC_printf debugging message. +* darwin_stop_world.c (GC_start_world): Do not check for +my_thread and GC_use_mach_handler_thread (since they are not added +to GC_mach_threads table); call thread_info() only if +DEBUG_THREADS or GC_ASSERTIONS. +* pthread_support.c (marker_mach_threads): New static variable (if +Darwin). +* pthread_support.c (GC_is_mach_marker): New function (if Darwin). +* pthread_support.c (GC_mark_thread): Fill in marker_mach_threads +table (if Darwin). + +* alloc.c (GC_parallel): Define only if THREADS. +* misc.c (GC_get_parallel): Likewise. +* include/gc.h (GC_parallel, GC_get_parallel, +GC_get_suspend_signal, GC_allow_register_threads, +GC_register_my_thread, GC_unregister_my_thread): Define only if +GC_THREADS. +* include/gc.h (GC_get_heap_size): Fix a typo in a comment. + +* configure.ac: Use `AC_C_INLINE'. +* include/private/gc_priv.h (GC_INLINE): Use "inline" keyword +(determined by configure AC_C_INLINE) if HAVE_CONFIG_H is defined. + +* dyn_load.c (DL_ITERATE_PHDR_STRONG): New macro (define for +FreeBSD). +* dyn_load.c (GC_register_main_static_data): Move the definition +above GC_register_dynamic_libraries_dl_iterate_phdr one (FreeBSD +case); unconditionally return FALSE if DL_ITERATE_PHDR_STRONG. +* dyn_load.c (GC_register_dynamic_libraries_dl_iterate_phdr): Test +GC_register_main_static_data() result (instead of direct testing +of dl_iterate_phdr (to prevent a compiler warning). +* os_dep.c (CODE_OK): Test si_code also for the value of 2 +(FreeBSD case; required for FreeBSD v7+). +* os_dep.c (CODE_OK): Properly use parentheses (HPUX case). +* include/private/gcconfig.h (DATASTART): Cast etext argument in +GC_FreeBSDGetDataStart() call; remove unnecessary "&" (FreeBSD +case). + +* include/private/specific.h (quick_thread_id): Define thru +GC_approx_sp(); define as a macro. +* include/private/specific.h (getspecific): Use GC_INLINE instead +of __inline__ (to work around Sun CC which does not recognize +inline keyword surrounded with underscores). + +* darwin_stop_world.c (FindTopOfStack): Simplify condition +expressions. +* darwin_stop_world.c (GC_push_all_stacks): Merge two variants +of this function (DARWIN_DONT_PARSE_STACK). +* darwin_stop_world.c (GC_push_all_stacks): Add a check for our +thread is found (same as in pthread_stop_world.c). +* darwin_stop_world.c (GC_push_all_stacks): Print the number of +scanned threads if verbose (same as in pthread_stop_world.c). + +* darwin_stop_world.c (GC_push_all_stacks): Reset +thread_state_count value before every thread_get_state call; +refine the comment for thread_state_count. +* darwin_stop_world.c (GC_push_all_stacks): Ignore rsp, rip/eip, +rflags, cs, fs, gs, ss, ds, es, __pc registers; uncomment ebp +register pushing. +* darwin_stop_world.c (GC_push_all_stacks): Set outCount to +GC_MACH_THREAD_STATE_COUNT (instead of THREAD_STATE_MAX). +* darwin_stop_world.c (GC_push_all_stacks): Remove FIXME and WARN +for x86. + +* doc/README.macros (DARWIN_DONT_PARSE_STACK): Fix a typo. +* darwin_stop_world.c (GC_use_mach_handler_thread): Change type +to GC_bool. +* darwin_stop_world.c (GC_suspend_thread_list, GC_start_world): +Simplify the expressions involving GC_use_mach_handler_thread. +* darwin_stop_world.c (GC_darwin_register_mach_handler_thread): +Initialize GC_use_mach_handler_thread to TRUE (instead of 1). + +* include/gc_pthread_redirects.h (GC_pthread_sigmask, GC_dlopen, +pthread_sigmask, dlopen): Don't define for pthreads-win32 (and +don't include signal.h and dlfcn.h). + +* dyn_load.c (GC_register_dynlib_callback): Add FIXME. + +* include/private/gcconfig.h: Add support for FreeBSD on ppc64. + +* os_dep.c (PROTECT, UNPROTECT): Correct VM_PROT_EXEC to +VM_PROT_EXECUTE. + +* os_dep.c (os2_alloc): Don't set PAG_EXECUTE unless +pages_executable is on. +* os_dep.c (os2_alloc): Add FIXME (for recursion). +* os_dep.c (UNPROTECT): Abort with a more informative message if +pages_executable is on ("mprotect" case). +* os_dep.c (PROTECT, UNPROTECT): Set VM_PROT_EXEC if +pages_executable is on (Darwin case). +* pthread_support.c (GC_init_real_syms): Abort with an informative +message if libgc is linked after libpthread. + +* dyn_load.c (GC_register_dynlib_callback): Adjust "start" pointer +for 64-bit targets. +* pthread_support.c (start_mark_threads): Expand PTHREAD_CREATE +macro. +* pthread_support.c (start_mark_threads): Call INIT_REAL_SYMS() +since REAL(pthread_create) is used. +* pthread_support.c (PTHREAD_CREATE): Remove unused. + +* extra/threadlibs.c (main): Remove --wrap for "read" (since not +wrapped anymore). +* doc/README.linux (GC_USE_LD_WRAP): Likewise. +* os_dep.c (__wrap_read): Likewise. + +* include/gc_pthread_redirects.h: Test GC_PTHREADS and GC_H at the +beginning of the file. +* include/gc_pthread_redirects.h (GC_PTHREAD_EXIT_ATTRIBUTE): New +macro (defined only for Linux and Solaris). +* include/gc_pthread_redirects.h (GC_pthread_cancel, +GC_pthread_exit): Declare new API function (only if +GC_PTHREAD_EXIT_ATTRIBUTE). +* include/gc_pthread_redirects.h (pthread_cancel, pthread_exit): +Redirect (if GC_PTHREAD_EXIT_ATTRIBUTE). +* include/private/pthread_support.h (DISABLED_GC): New macro. +* pthread_support.c (pthread_cancel, pthread_exit): Restore +original definition or declare "real" function (if needed and +GC_PTHREAD_EXIT_ATTRIBUTE). +* pthread_support.c (GC_pthread_cancel_t, GC_pthread_exit_t): +Declare new types if needed. +* pthread_support.c (GC_pthread_cancel, GC_pthread_exit): New +function definition (only if GC_PTHREAD_EXIT_ATTRIBUTE). +* pthread_support.c (GC_init_real_syms): Initialize pointers to +the "real" pthread_cancel and pthread_exit (only if +GC_PTHREAD_EXIT_ATTRIBUTE). +* pthread_support.c (GC_unregister_my_thread): Enable collections +if DISABLED_GC was set (only if GC_PTHREAD_EXIT_ATTRIBUTE). +* pthread_support.c (pthread_cancel, pthread_exit): New wrapped +function definition (only if GC_PTHREAD_EXIT_ATTRIBUTE defined). +* pthread_support.c (GC_start_routine): Refine the comment. +* extra/threadlibs.c (main): Adjust --wrap (add "read", +"pthread_exit", "pthread_cancel" but remove "sleep"). +* doc/README.linux (GC_USE_LD_WRAP): Likewise. + +* include/gc.h (GC_MALLOC_STUBBORN): Remove trailing ';' in the +macro definition. +* include/gc.h (GC_reachable_here): Likewise. +* include/gc.h (GC_reachable_here): Prefix and postfix "volatile" +with double '_'. + +* pthread_start.c: New file. +* CMakeLists.txt (SRC): Add pthread_start.c. +* Makefile.am (libgc_la_SOURCES): Likewise. +* Makefile.direct (CSRCS): Likewise. +* Makefile.direct (OBJS): Add pthread_start.obj. +* extra/gc.c: Add a comment; include pthread_start.c. +* pthread_support.c (start_info): Move the struct definition down +closer to its usage. +* pthread_support.c (GC_thread_exit_proc): Replace STATIC with +GC_INNER. +* pthread_support.c (GC_inner_start_routine): Move to the +definition to pthread_start.c; leave only the prototype; remove +STATIC. +* pthread_support.c (GC_start_rtn_prepare_thread): New function +(contains parts of the original GC_inner_start_routine). + +* configure.ac (NO_EXECUTE_PERMISSION): Add comment. +* doc/README.macros (NO_EXECUTE_PERMISSION): Update the +documentation. +* include/gc.h (GC_set_pages_executable, GC_get_pages_executable): +New API function declaration. +* os_dep.c (OPT_PROT_EXEC): Remove (superseded by +pages_executable). +* os_dep.c (pages_executable): New static variable. +* os_dep.c (IGNORE_PAGES_EXECUTABLE): New macro (used by +GC_get_pages_executable only). +* os_dep.c (GC_unix_mmap_get_mem, GC_remap, PROTECT, UNPROTECT): +Replace OPT_PROT_EXEC with pages_executable. +* os_dep.c (GC_unix_mmap_get_mem, GC_remap, GC_win32_get_mem, +GC_wince_get_mem, UNPROTECT): Undefine IGNORE_PAGES_EXECUTABLE. +* os_dep.c (GC_win32_get_mem, GC_wince_get_mem, GC_remap, PROTECT, +UNPROTECT): Use PAGE_EXECUTE_... only if pages_executable is on. +* os_dep.c (GC_set_pages_executable, GC_get_pages_executable): New +API function definition. + +* tests/test.c (check_heap_stats): Increase max_heap_sz by 20% for +64-bit CPUs (to prevent "Unexpected heap growth" failure on Win64, +at least). + +* tests/test.c (check_heap_stats): Increase max_heap_sz by 25% for +32-bit CPUs (to prevent "Unexpected heap growth" failure). + +* gc_dlopen.c (dlopen): Prototype REAL_DLFUNC if GC_USE_LD_WRAP. +* pthread_support.c (pthread_create, pthread_join, pthread_detach, +pthread_sigmask): Likewise. +* gc_dlopen.c (dlopen): Remove cast (redundant since the prototype +is added). +* gc_dlopen.c (GC_dlopen): Fix return type. +* pthread_support.c (GC_init_real_syms): Don't define +LIBPTHREAD_NAME, LIBPTHREAD_NAME_LEN, len, namebuf and +libpthread_name if RTLD_NEXT. + +* gc_dlopen.c (disable_gc_for_dlopen): Update the comment. +* gc_dlopen.c (dlopen): Likewise. +* include/gc.h (GC_enable_incremental): Refine the comment. +* include/gc.h (DECLSPEC_NORETURN): Define macro as empty if +missing (only for Win32). +* include/gc.h (GC_ExitThread): Use DECLSPEC_NORETURN. +* win32_threads.c (GC_ExitThread): Likewise. +* include/gc.h (GC_endthreadex): Add a comment. + +* include/cord.h: Fix typos. + +* Makefile.am (EXTRA_DIST): Add "CMakeLists.txt" and +"tests/CMakeLists.txt". +* doc/doc.am (dist_pkgdata_DATA): Add "doc/README.cmake". + +* mach_dep.c (NO_GETCONTEXT): Also define if AVR32. +* include/private/gcconfig.h (AVR32): New macro (also define the +supplementary macros for the target). +* include/private/thread_local_alloc (USE_COMPILER_TLS): Don't +define for AVR32. + +* tests/leak_test.c (main): Explicitly define as returning int +(to prevent a spurious test failure on some Linux/alpha targets). +* tests/thread_leak_test.c (main): Likewise. +* tests/thread_leak_test.c: Initialize GC_find_leak in the main +thread (before GC_INIT) only. +* tests/leak_test.c (main): Use GC_set_find_leak() instead of +accessing GC_find_leak directly. +* tests/thread_leak_test.c (main): Likewise. + +* include/gc.h (GC_find_leak, GC_finalize_on_demand, +GC_java_finalization, GC_dont_expand, GC_no_dls, +GC_dont_precollect): Simplify the comment (remove the information +about data races since the value is boolean). + +* os_dep.c (GC_get_stack_base, GC_get_main_stack_base): New +Solaris-specific implementation (based on thr_stksegment). +* os_dep.c (stackbase_main_self, stackbase_main_ss_sp): New static +variable used by the Solaris-specific GC_get_stack_base(). + +* pthread_support.c (GC_mark_thread_local_free_lists, +GC_check_tls): Mark (and check) only for live threads (in case of +GC_destroy_thread_local() is called already but GC_delete_thread() +is not yet). +* win32_threads.c (GC_mark_thread_local_free_lists, GC_check_tls): +Likewise. + +* NT_MAKEFILE: Remove the comment about DLL and win32s. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_MAKEFILE: Add ".SUFFIXES" directive (to handle gc_cpp.cc +properly on VS 2005+). +* NT_MAKEFILE: Update GC log file name in comments. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* doc/README.win32: Likewise. +* NT_MAKEFILE: Remove ":full" for "-debug" option (since no +longer supported by VS). +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_MAKEFILE: Commented out copying of gc_cpp.cc to gc_cpp.cpp. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* NT_STATIC_THREADS_MAKEFILE: Add -D PARALLEL_MARK option. +* NT_STATIC_THREADS_MAKEFILE: Increase stack size for gctest.exe. +* NT_X64_STATIC_THREADS_MAKEFILE: Remove "-stack" option (use the +default stack size limit). +* NT_X64_THREADS_MAKEFILE: Rename "gc64_dll.dll" to "gc64.dll". +* win32_threads.c (GC_get_next_stack): Always define (since it is +also used for Cygwin now). + +* alloc.c (GC_maybe_gc): Move GC_notify_full_gc() call upper to +be just before GC_clear_marks() call. +* include/gc_mark.h (GC_start_callback_proc): Refine the comment. + +* Makefile.am (check_LTLIBRARIES): Initialize to empty. +* tests/tests.am (TESTS, check_PROGRAMS): Add staticrootstest. +* tests/tests.am (staticrootstest_SOURCES, staticrootstest_LDADD, +libstaticrootslib_la_SOURCES, libstaticrootslib_la_LIBADD, +libstaticrootslib_la_LDFLAGS, libstaticrootslib_la_DEPENDENCIES): +Define. +* tests/tests.am (check_LTLIBRARIES): Add libstaticrootslib.la. + +* tests/staticrootstest.c: New file. +* tests/staticrootslib.c: Likewise. + +* dyn_load.c (GC_get_next_stack, GC_cond_add_roots): Define for +Cygwin as well as other win32 targets. +* dyn_load.c (GC_wnt): Define to constant true. +* dyn_load.c (GC_register_dynamic_libraries): Define for Cygwin as +well as other win32 targets. +* mark_rts.c (rt_hash, GC_roots_present, add_roots_to_index): +Don't define for Cygwin, as on other win32. +* mark_rts.c (GC_add_roots_inner, GC_clear_roots): Handle on +Cygwin as for other win32 targets. +* mark_rts.c (GC_rebuild_root_index): Don't declare on Cygwin, as +other win32. +* mark_rts.c (GC_remove_tmp_roots): Do declare on Cygwin as on +other win32. +* mark_rts.c (GC_remove_roots, GC_remove_roots_inner): Don't +declare on Cygwin as on other win32. +* mark_rts.c (GC_is_tmp_root): Do declare on Cygwin when +!NO_DEBUGGING, as on other win32 targets. +* mark_rts.c (GC_cond_register_dynamic_libraries): Handle on +Cygwin as for other win32 targets. +* os_dep.c (GC_setpagesize): Handle on Cygwin as on other win32. +* os_dep.c (GC_get_main_stack_base): Don't declare on Cygwin, as +other win32. +* os_dep.c (GC_sysinfo): Declare on Cygwin, as other win32. +* os_dep.c (GC_win32_get_mem): Declare on Cygwin, as on other +Win32, but call GC_unix_get_mem instead of GlobalAlloc. +* os_dep.c (GC_win32_free_heap): Declare on Cygwin (as empty). +* ptr_chck.c (GC_is_visible): Register dynamic libraries on Cygwin +as on other win32 platforms. +* win32_threads.c (GC_get_next_stack): Define on Cygwin as well as +for dynamic loading targets. +* include/private/gc_priv.h (GC_INNER): Don't try to use +visibility on Cygwin which does not support it. +* include/private/gc_priv.h (struct roots): Don't declare r_next +member on Cygwin as on other windows hosts. +* include/private/gc_priv.h (LOG_RT_SIZE, RT_SIZE): Don't define +likewise. +* include/private/gc_priv.h (struct _GC_arrays): Do declare +_heap_bases[] member and don't declare _root_index likewise. +* include/private/gc_priv.h (GC_heap_bases): Do define likewise. +* include/private/gc_priv.h (_SYSTEM_INFO): Do forward-declare +likewise. +* include/private/gc_priv.h (GC_sysinfo): Do declare extern +likewise. +* include/private/gcconfig.h (GC_win32_get_mem, GET_MEM): Do +prototype on Cygwin as other win32 platforms. + +* os_dep.c (GC_get_main_stack_base): Use pthread_getattr_np() and +pthread_attr_getstack() instead of GC_get_stack_base() (and check +returned stackaddr for NULL); output a warning on failure. + +* alloc.c (GC_start_call_back): Replace the definition type to +GC_start_callback_proc. +* alloc.c (GC_set_start_callback, GC_get_start_callback): New +setter/getter function. +* alloc.c (GC_try_to_collect_inner): Call GC_notify_full_gc() +unconditionally (because GC_try_to_collect_inner always does full +GC). +* include/gc_mark.h (GC_start_callback_proc): New type. +* include/gc_mark.h (GC_set_start_callback, +GC_get_start_callback): New API function declaration. + +* doc/README.macros (USE_GET_STACKBASE_FOR_MAIN): Document. +* os_dep.c (GC_get_main_stack_base): Recognize +USE_GET_STACKBASE_FOR_MAIN (only if THREADS and LINUX_STACKBOTTOM) +and use GC_get_stack_base() in this case. + +* os_dep.c (GC_get_stack_base): Add LOCK/UNLOCK() (since +GC_find_limit_with_bound() should be called with the lock held). +* backgraph.c (FOR_EACH_PRED): Fix a typo. + +* alloc.c (GC_set_stop_func, GC_get_stop_func): Add +DCL_LOCK_STATE. +* finalize.c (GC_notify_or_invoke_finalizers): Likewise. +* gc_dlopen.c (disable_gc_for_dlopen): Likewise. +* gcj_mlc.c (maybe_finalize, GC_debug_gcj_malloc): Likewise. +* mark.c (GC_print_trace): Likewise. +* misc.c (GC_set_warn_proc, GC_get_warn_proc, GC_enable, +GC_disable, GC_new_free_list, GC_new_kind, GC_new_proc, +GC_set_oom_fn, GC_get_oom_fn, GC_set_finalizer_notifier, +GC_get_finalizer_notifier): Likewise. +* os_dep.c (GC_get_stack_base, GC_print_callers): Likewise. +* pthread_support.c (GC_is_thread_tsd_valid, +GC_wait_for_gc_completion, GC_init_parallel, GC_do_blocking_inner, +GC_call_with_gc_active, GC_unregister_my_thread, pthread_join, +pthread_detach, GC_register_my_thread, GC_inner_start_routine, +pthread_create): Likewise. +* reclaim.c (GC_print_all_errors): Likewise. +* win32_threads.c (GC_is_thread_tsd_valid, GC_register_my_thread, +GC_unregister_my_thread, GC_do_blocking_inner, +GC_call_with_gc_active, GC_lookup_pthread, GC_pthread_join, +GC_pthread_start_inner, GC_thread_exit_proc, GC_pthread_detach, +GC_init_parallel): Likewise. + +* doc/README.darwin: Update. + +* CMakeLists.txt: Adjust INCLUDE_DIRECTORIES and SRC (to make it +usable on Mac OS X). +* doc/README.cmake: Update. + +* CMakeLists.txt: New file (adding CMake support). +* tests/CMakeLists.txt: Likewise. +* doc/README.cmake: Likewise. + +* configure.ac (darwin): Don't define HAS_PPC_THREAD_STATE... +macros. +* include/private/gc_priv.h (THREAD_FLD): Recognize +__DARWIN_UNIX03 instead of HAS_PPC_THREAD_STATE... macros. + +* pthread_support.c: Include and for +OpenBSD. +* pthread_support.c (get_ncpu): Define also for Darwin, NetBSD and +OpenBSD. +* pthread_support.c (GC_thr_init): Use get_ncpu() for Darwin, +NetBSD and OpenBSD. + +* mallocx.c (GC_generic_malloc_many, GC_malloc_many): Define even +if THREADS is undefined. +* include/gc.h (GC_malloc_many): Update the comment. + +* include/gc_cpp.h (GC_PLACEMENT_DELETE): Define for Embarcadero +(formerly known as Borland) C++ compiler v6.21+. +* include/gc_cpp.h (GC_NO_OPERATOR_NEW_ARRAY): Define for ancient +VC++ compilers. + +* win32_threads.c (GC_register_my_thread_inner, +GC_pthread_start_inner): Undo the previous commit changes for +the thread flags and DETACHED state (since the state is only +tested in GC_thread_exit_proc). + +* include/gc.h (GC_unregister_my_thread): Fix a typo; update the +comment. +* pthread_support.c (GC_delete_thread): Allow to delete the main +thread (don't call GC_INTERNAL_FREE for it); update the comment. +* win32_threads.c (GC_delete_thread): Likewise. +* pthread_support.c (GC_unregister_my_thread): Add an assertion +for FINISHED flag is unset. +* tests/test.c (check_heap_stats): Test the main thread +unregistering (only if THREADS). +* win32_threads.c (GC_register_my_thread_inner): Set flags to +DETACHED (only if GC_PTHREADS). +* win32_threads.c (GC_unregister_my_thread): Add FIXME (for +GC_wait_for_gc_completion). +* win32_threads.c (GC_pthread_start_inner): Clear flags detached +state if needed; set pthread_id and flags while holding the lock. + +* include/private/gc_priv.h (SIG_SUSPEND): Don't define for +OpenBSD and Darwin. + +* include/gc.h: Recognize _M_X64 (as an alias for _AMD64_). + +* test.c (main, WinMain): Consistently don't invoke +GC_enable_incremental() if MAKE_BACKGRAPH is defined, but +do invoke it even if parallel marking is enabled. + +* tests/test.c (reverse_test): Comment out a check for MSWIN32 +(when determining BIG value) assuming outdated win32s. +* tests/test.c (reverse_test): Rename to reverse_test_inner; +change the declaration (to be of GC_fn_type); call itself thru +GC_call_with_gc_active() if the argument is zero. +* tests/test.c (reverse_test): New function added calling +reverse_test_inner thru GC_do_blocking (to test GC_do_blocking and +GC_call_with_gc_active). + +* doc/README.macros (IGNORE_DYNAMIC_LOADING, PLATFORM_ANDROID): +Document. +* dyn_load.c: Don't include if PLATFORM_ANDROID. +* dyn_load.c: Include bionic (instead of ) if +PLATFORM_ANDROID. +* include/private/gcconfig.h (LINUX): Define also if +PLATFORM_ANDROID (for the windows-based toolkit). +* include/private/gcconfig.h (SEARCH_FOR_DATA_START): Explicitly +define for Android/x86 platform. +* include/private/gcconfig.h (IGNORE_DYNAMIC_LOADING): Recognize +new macro (undefine DYNAMIC_LOADING in this case). +* include/private/gcconfig.h (CANCEL_SAFE): Don't define if +PLATFORM_ANDROID. +* include/private/gcconfig.h (IF_CANCEL): Fix definition for the +explicitly defined CANCEL_SAFE. + +* allchblk.c (GC_allochblk_nth): Don't call GC_remove_protection() +if GC_DISABLE_INCREMENTAL. +* reclaim.c (GC_reclaim_generic): Likewise. +* checksums.c (GC_page_was_ever_dirty): Add prototype. +* include/private/gc_locks.h (GC_mark_lock_holder): Don't declare +unless PARALLEL_MARK. +* include/private/gc_priv.h (GC_dirty_maintained, +GC_page_was_dirty, GC_remove_protection, GC_dirty_init): Don't +declare if GC_DISABLE_INCREMENTAL. +* include/private/gc_priv.h (GC_print_finalization_stats): Don't +declare if SMALL_CONFIG. +* include/private/gcconfig.h (CHECKSUMS): Explicitly undefine if +GC_DISABLE_INCREMENTAL (since nothing to check). +* include/private/gcconfig.h (DEFAULT_VDB): Don't define if +GC_DISABLE_INCREMENTAL. +* os_dep.c (GC_dirty_maintained): Likewise. +* mark.c (GC_initiate_gc): Don't call GC_read_dirty() if +GC_DISABLE_INCREMENTAL. +* os_dep.c (GC_gww_page_was_ever_dirty, GC_page_was_ever_dirty): +Uncomment; define only if CHECKSUMS. + +* darwin_stop_world.c (GC_push_all_stacks): Fix a bug (call +GC_push_all_stack() instead of GC_push_all_stack_frames()). +* include/private/gc_priv.h (GC_push_all_stack_frames, +GC_push_all_register_frames): Rename to +GC_push_all_stack_sections and GC_push_all_register_sections, +respectively. +* mark_rts.c (GC_push_all_stack_frames, +GC_push_all_register_frames, GC_push_all_stack_part_eager_frames, +GC_push_current_stack): Likewise. +* pthread_stop_world.c (GC_push_all_stacks): Likewise. +* win32_threads.c (GC_push_stack_for): Likewise. +* misc.c (GC_call_with_gc_active): Rename "frame" local variable +to "stacksect". +* pthread_support.c (GC_call_with_gc_active): Likewise. +* win32_threads.c (GC_call_with_gc_active): Likewise. +* pthread_support.c (GC_call_with_gc_active): Update FIXME for +Darwin. +* win32_threads.c (GC_Thread_Rep): Update the comment for +traced_stack_sect. + +* darwin_stop_world.c (GC_push_all_stacks): Rename +activation_frame to traced_stack_sect. +* include/private/gc_priv.h (GC_push_all_stack_frames, +GC_push_all_register_frames): Likewise. +* include/private/pthread_support.h (GC_Thread_Rep): Likewise. +* mark_rts.c (GC_push_all_register_frames, +GC_push_all_stack_frames, GC_push_all_stack_part_eager_frames, +GC_push_current_stack): Likewise. +* pthread_stop_world.c (GC_push_all_stacks): Likewise. +* pthread_support.c (GC_call_with_gc_active): Likewise. +* win32_threads.c (GC_Thread_Rep, GC_call_with_gc_active, +GC_push_stack_for): Likewise. +* include/private/gc_priv.h (GC_activation_frame_s): Rename to +GC_traced_stack_sect_s. +* include/private/gc_priv.h (GC_activation_frame): Rename to +GC_traced_stack_sect. +* misc.c (GC_activation_frame, GC_call_with_gc_active): Likewise. +* doc/README.macros (UNICODE): Document. + +* doc/README.macros (GC_READ_ENV_FILE): Document (new macro). +* include/private/gc_priv.h (GETENV): Recognize GC_READ_ENV_FILE; +declare and use GC_envfile_getenv(). +* misc.c (GC_envfile_content, GC_envfile_length): New static +variable (only if GC_READ_ENV_FILE). +* misc.c (GC_ENVFILE_MAXLEN): New macro (used in GC_envfile_init). +* misc.c (GC_envfile_init, GC_envfile_getenv): New function (only +if GC_READ_ENV_FILE). +* misc.c (GC_init): Call GC_envfile_init() (before using GETENV) +if GC_READ_ENV_FILE. +* misc.c (GC_init): Move GC_setpagesize() and GC_init_win32() +calls to be just before GC_envfile_init() one (since the latter +uses GET_MEM). +* misc.c (GC_abort): use ExitProcess() (instead of DebugBreak) for +WinCE if NO_DEBUGGING; add a comment for DebugBreak() (for WinCE). +* mark_rts.c (GC_add_roots_inner): Remove redundant trailing '\n' +from the ABORT message. +* misc.c (GC_init): Likewise. +* os_dep.c (GC_get_main_stack_base, GC_register_data_segments): +Likewise. +* pthread_stop_world.c (GC_push_all_stacks): Likewise. +* pthread_support.c (GC_init_real_syms, start_mark_threads): +Likewise. + +* win32_threads.c (GC_get_next_stack): Don't define for Cygwin +(since unused for now). + +* dyn_load.c (HAVE_REGISTER_MAIN_STATIC_DATA): Don't define unless +GC_register_main_static_data() is defined. +* dyn_load.c (GC_register_dynamic_libraries): Define only if used +(if DYNAMIC_LOADING or PCR or Win32/CE). +* dyn_load.c (GC_register_main_static_data): Define the default +one only if DYNAMIC_LOADING. +* include/private/gc_priv.h (GC_register_dynamic_libraries): +Declare only if used (to prevent compiler warning). + +* mark_rts.c (GC_approx_sp): Add a comment (for GCC). + + +== [7.2alpha4] 2009-12-01 == + +* configure.ac (AC_CONFIG_COMMANDS): Quote srcdir value. + +* include/gc.h (GC_get_suspend_signal): New function declaration. +* misc.c (GC_get_suspend_signal): New API function (only if +THREADS). + +* alloc.c (min_bytes_allocd): Multiply GC_free_space_divisor by +two if GC_incremental (instead of TRUE_INCREMENTAL). + +* sparc_mach_dep.S (GC_push_regs): Remove the reference. + +* os_dep.c (SIZE_T, PULONG_PTR): Remove. +* os_dep.c (ULONG_PTR): Replace with GC_ULONG_PTR (defined as GC +"word"); add the comment. +* os_dep.c (GetWriteWatch_type, detect_GetWriteWatch, +GC_gww_read_dirty): Prefix ULONG_PTR with "GC_". + +* win32_threads.c (THREAD_TABLE_SZ): Change back to a power-of-two +const value (for speed). +* win32_threads.c (THREAD_TABLE_INDEX): New macro. +* win32_threads.c (GC_new_thread, GC_lookup_thread_inner, +GC_delete_gc_thread, GC_delete_thread, GC_lookup_pthread): Use +THREAD_TABLE_INDEX instead of THREAD_TABLE_SZ. +* win32_threads.c (PTHREAD_MAP_HASH): Rename to PTHREAD_MAP_INDEX. + +* win32_threads.c (THREAD_TABLE_SZ): Make the const value prime. + +* backgraph.c: Remove apostrophe char from "#error". + +* doc/README.macros (GC_DISABLE_INCREMENTAL): Document. +* include/private/gcconfig.h (GC_DISABLE_INCREMENTAL): Recognize +new macro; implicitly define it if SMALL_CONFIG. +* alloc.c (GC_incremental, GC_timeout_stop_func): Check for +GC_DISABLE_INCREMENTAL instead of SMALL_CONFIG. +* include/private/gc_priv.h (GC_incremental, TRUE_INCREMENTAL, +GC_push_conditional): Likewise. +* mark.c (GC_push_next_marked_dirty, GC_push_selected, +GC_push_conditional, GC_block_was_dirty): Likewise. +* misc.c (GC_enable_incremental): Likewise. +* misc.c (GC_init): Likewise. + +* dyn_load.c (WIN32_LEAN_AND_MEAN): Guard with ifndef. +* misc.c (WIN32_LEAN_AND_MEAN): Likewise. +* os_dep.c (WIN32_LEAN_AND_MEAN): Likewise. +* allchblk.c (GC_allochblk_nth): Fix a minor typo (don't/doesn't) +in a comment. +* backgraph.c: Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* extra/threadlibs.c (main): Likewise. +* pthread_support.c (pthread_join): Likewise. +* tests/test.c (main): Likewise. + +* mach_dep.c (GC_push_regs): Remove STATIC (just to catch +a duplicate symbol definition linker error). +* misc.c (GC_clear_stack_inner): Likewise. +* sparc_mach_dep.S (GC_push_regs): Comment out the reference. + +* include/private/gc_priv.h (GC_write_disabled): New variable +declaration (only if GC_ASSERTIONS and Win32 threads). +* misc.c (GC_write): Add assertion for GC_write_disabled value is +not on (only if THREADS). +* win32_threads.c (GC_write_disabled): New variable (only if +GC_ASSERTIONS and not Cygwin). +* win32_threads.c (GC_stop_world): Set and clear GC_write_disabled +(while holding GC_write_cs). + +* win32_threads.c (GC_please_stop): If DllMain-based thread +registration is not compiled in then define GC_please_stop as +a non-volatile variable for assertion only. +* win32_threads.c (GC_stop_world): Set and clear only if defined. +* win32_threads.c (GC_stop_world): Add the comment for GC_printf() +usage (while holding GC_write_cs). +* win32_threads.c (GC_delete_gc_thread): Likewise. +* os_dep.c (GC_remove_protection): Likewise. + +* pthread_support.c (GC_inner_start_routine): Join 3 sequential +GC_printf() calls into a single one (for DEBUG_THREADS). + +* include/private/gc_priv.h (GC_total_stacksize): New variable +declaration (only if THREADS). +* alloc.c (GC_total_stacksize): New variable (only if THREADS). +* alloc.c (min_bytes_allocd): Calculate stack_size using +GC_stackbottom only in the single-threaded case; otherwise use +GC_total_stacksize; print GC_total_stacksize value if +DEBUG_THREADS. +* darwin_stop_world.c (GC_push_all_stacks): Use "%p" printf type +specifier for lo/hi values (instead of "%lx"). +* darwin_stop_world.c (GC_push_all_stacks): Use +GC_push_all_stack_frames() instead of GC_push_all_stack(). +* darwin_stop_world.c (GC_push_all_stacks): Recalculate +GC_total_stacksize value. +* pthread_stop_world.c (GC_push_all_stacks): Likewise. +* win32_threads.c (GC_push_all_stacks): Likewise. +* win32_threads.c (GC_push_stack_for): Pass "me" argument; return +stack size; don't check for non-zero value of thread->stack_base. +* win32_threads.c (GC_push_all_stacks): Don't call +GC_push_stack_for() and don't check for "t->id == me" if +thread->stack_base is zero. + +* dyn_load.c (GC_dump_meminfo): Prefix "%lx" printf type specifier +with "0x". +* os_dep.c (PROTECT): Likewise. +* win32_threads.c (GC_mark_thread_local_free_lists): Cast p->id to +int (to match printf type specifier). + +* tests/test.c (check_heap_stats): Take into account the unmapped +memory size when checking for "Unexpected heap growth"; remove +FIXME. + +* alloc.c: Revert last change. + +* include/private/gcconfig.h (STACKBOTTOM): Add a presence check +for eCos/NOSYS. +* misc.c (GC_write): Comment out _Jv_diag_write() call (since no +longer defined in GCJ). + +* os_dep.c (brk): Rename to ecos_gc_brk. + +* alloc.c (min_bytes_allocd): Use GC_stackbottom value to compute +stack_size even if THREADS. +* doc/README.macros (DEBUG_THREADS): Document. +* pthread_support.c (DEBUG_THREADS): Remove the commented out +definition. +* win32_threads.c (DEBUG_WIN32_THREADS): Remove duplicate +definition. +* win32_threads.c: Include errno.h (except for WinCE). +* win32_threads.c (GC_win32_start_inner): Copy "start" and "param" +to local variables, and free "arg" parameter before "start" +invocation. +* win32_threads.c (GC_beginthreadex): Set errno to EAGAIN on error +(instead of calling SetLastError(ERROR_NOT_ENOUGH_MEMORY)). +* win32_threads.c (GC_beginthreadex): Return 0 on error (instead +of -1). + +* darwin_stop_world.c (GC_darwin_register_mach_handler_thread): +Use GC_INNER for the function definition. +* include/private/darwin_stop_world.h +(GC_darwin_register_mach_handler_thread): Remove the prototype. +* os_dep.c (GC_darwin_register_mach_handler_thread): Use GC_INNER +for the function prototype. +* include/private/gc_priv.h (NDEBUG): Explicitly define if +NO_DEBUGGING and not GC_ASSERTIONS (before the standard headers +inclusion). + +* include/private/gcconfig.h: Move DebugBreak() workaround (for +x86mingw32ce toolchain) to gc_priv.h (after windows.h inclusion). + +* allchblk.c (GC_unmap_old, GC_merge_unmapped, GC_allochblk, +GC_freehblk): Use GC_INNER for the function definition. +* alloc.c (GC_never_stop_func, GC_should_collect, +GC_try_to_collect_inner, GC_collect_a_little_inner, +GC_set_fl_marks, GC_add_to_our_memory, GC_add_to_heap, +GC_expand_hp_inner, GC_collect_or_expand, GC_allocobj): Likewise. +* backgraph.c (GC_build_back_graph, GC_traverse_back_graph): +Likewise. +* blacklst.c (GC_default_print_heap_obj_proc, GC_bl_init, +GC_promote_black_lists, GC_unpromote_black_lists, +GC_add_to_black_list_normal, GC_add_to_black_list_stack, +GC_is_black_listed): Likewise. +* darwin_stop_world.c (GC_push_all_stacks, GC_push_all_stacks, +GC_stop_init, GC_stop_world, GC_start_world): Likewise. +* dbg_mlc.c (GC_has_other_debug_info, GC_store_back_pointer, +GC_marked_for_finalization, GC_generate_random_backtrace_no_gc, +GC_store_debug_info, GC_start_debugging, +GC_debug_generic_malloc_inner, +GC_debug_generic_malloc_inner_ignore_off_page, +GC_debug_malloc_uncollectable, GC_debug_free_inner): Likewise. +* dyn_load.c (GC_register_dynamic_libraries, +GC_register_main_static_data, GC_init_dyld): Likewise. +* finalize.c (GC_push_finalizer_structures, GC_finalize, +GC_notify_or_invoke_finalizers, GC_print_finalization_stats): +Likewise. +* gcj_mlc.c (GC_core_gcj_malloc): Likewise. +* headers.c (GC_find_header, GC_header_cache_miss, +GC_scratch_alloc, GC_init_headers, GC_install_header, +GC_install_counts, GC_remove_header, GC_remove_counts, +GC_next_used_block, GC_prev_block): Likewise. +* mach_dep.c (GC_with_callee_saves_pushed): Likewise. +* malloc.c (GC_collect_or_expand, GC_alloc_large, +GC_generic_malloc_inner, GC_generic_malloc_inner_ignore_off_page, +GC_core_malloc_atomic, GC_core_malloc, GC_free_inner): Likewise. +* mallocx.c (GC_generic_malloc_ignore_off_page): Likewise. +* mark.c (GC_collection_in_progress, GC_clear_hdr_marks, +GC_set_hdr_marks, GC_set_mark_bit, GC_clear_mark_bit, +GC_clear_marks, GC_initiate_gc, GC_mark_some, +GC_mark_stack_empty, GC_invalidate_mark_state, +GC_signal_mark_stack_overflow, GC_mark_from, GC_help_marker, +GC_mark_init, GC_push_all, GC_push_conditional, +GC_mark_and_push_stack, GC_push_all_eager, GC_push_all_stack): +Likewise. +* mark_rts.c (GC_is_static_root, GC_roots_present, GC_approx_sp, +GC_exclude_static_roots_inner, GC_push_all_register_frames, +GC_push_all_stack_frames, GC_cond_register_dynamic_libraries, +GC_push_roots): Likewise. +* misc.c (GC_extend_size_map, GC_clear_stack, GC_err_write): +Likewise. +* new_hblk.c (GC_build_fl, GC_new_hblk): Likewise. +* obj_map.c (GC_register_displacement_inner, GC_add_map_entry, +GC_initialize_offsets): Likewise. +* os_dep.c (GC_get_maps, GC_parse_map_entry, GC_text_mapping, +GC_init_linux_data_start, GC_init_netbsd_elf, GC_setpagesize, +GC_set_and_save_fault_handler, GC_setup_temporary_fault_handler, +GC_reset_fault_handler, GC_get_register_stack_base, GC_init_win32, +GC_add_current_malloc_heap, GC_is_heap_base, GC_unmap, GC_remap, +GC_unmap_gap, GC_push_all_stacks, GC_gww_dirty_init, +GC_dirty_init, GC_read_dirty, GC_page_was_dirty, +GC_page_was_ever_dirty, GC_remove_protection, +GC_write_fault_handler, GC_mprotect_stop, GC_mprotect_resume, +GC_save_callers, GC_print_callers): Likewise. +* pthread_stop_world.c (GC_push_all_stacks, GC_stop_world, +GC_start_world, GC_stop_init): Likewise. +* pthread_support.c (GC_mark_thread_local_free_lists, +GC_lookup_thread, GC_reset_finalizer_nested, +GC_check_finalizer_nested, GC_segment_is_thread_stack, +GC_greatest_stack_base_below, GC_thr_init, GC_init_parallel, +GC_do_blocking_inner, GC_lock, GC_acquire_mark_lock, +GC_release_mark_lock, GC_wait_for_reclaim, GC_notify_all_builder, +GC_wait_marker, GC_notify_all_marker): Likewise. +* reclaim.c (GC_print_all_errors, GC_block_empty, +GC_reclaim_generic, GC_start_reclaim, GC_continue_reclaim, +GC_reclaim_all): Likewise. +* thread_local_alloc.c (GC_init_thread_local, +GC_destroy_thread_local, GC_mark_thread_local_fls_for): Likewise. +* win32_threads.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested, GC_do_blocking_inner, GC_stop_world, +GC_start_world, GC_push_all_stacks, GC_get_next_stack, +GC_acquire_mark_lock, GC_release_mark_lock, GC_wait_for_reclaim, +GC_notify_all_builder, GC_wait_marker, GC_notify_all_marker, +GC_thr_init, GC_init_parallel, GC_lock, +GC_mark_thread_local_free_lists): Likewise. +* alloc.c (GC_add_current_malloc_heap, GC_build_back_graph, +GC_traverse_back_graph): Use GC_INNER for the function prototype. +* darwin_stop_world.c (GC_mprotect_stop, GC_mprotect_resume): +Likewise. +* dbg_mlc.c (GC_default_print_heap_obj_proc): Likewise. +* dyn_load.c (GC_parse_map_entry, GC_get_maps, +GC_segment_is_thread_stack, GC_roots_present, GC_is_heap_base, +GC_get_next_stack): Likewise. +* finalize.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): Likewise. +* gcj_mlc.c (GC_start_debugging): Likewise. +* include/private/dbg_mlc.h (GC_save_callers, GC_print_callers, +GC_has_other_debug_info, GC_store_debug_info): Likewise. +* include/private/gc_hdrs.h (GC_header_cache_miss): Likewise. +* include/private/gc_locks.h (GC_lock): Likewise. +* include/private/gc_pmark.h (GC_signal_mark_stack_overflow, +GC_mark_from): Likewise. +* include/private/pthread_support.h (GC_lookup_thread, +GC_stop_init): Likewise. +* include/private/thread_local_alloc.h (GC_init_thread_local, +GC_destroy_thread_local, GC_mark_thread_local_fls_for): Likewise. +* malloc.c (GC_extend_size_map, GC_text_mapping): Likewise. +* mark.c (GC_page_was_ever_dirty): Likewise. +* mark_rts.c (GC_mark_thread_local_free_lists): Likewise. +* misc.c (GC_register_main_static_data, GC_init_win32, +GC_setpagesize, GC_init_linux_data_start, +GC_set_and_save_fault_handler, GC_init_dyld, GC_init_netbsd_elf, +GC_do_blocking_inner): Likewise. +* os_dep.c (GC_greatest_stack_base_below): Likewise. +* win32_threads.c (GC_write_fault_handler, GC_gww_dirty_init): +Likewise. +* include/private/gc_priv.h: Likewise. +* include/private/gc_priv.h (GC_INNER): Update the comment. +* doc/README.macros (GC_DLL): Update. + +* alloc.c (GC_collection_in_progress): Move the prototype to +gc_priv.h. +* gc_dlopen.c (GC_collection_in_progress): Likewise. +* pthread_support.c (GC_collection_in_progress): Likewise. +* misc.c (GC_init_parallel): Likewise. +* pthread_support.c (GC_init_parallel): Likewise. +* win32_threads.c (GC_init_parallel): Likewise. +* darwin_stop_world.c (GC_thr_init): Likewise. +* misc.c (GC_thr_init): Likewise. +* pthread_stop_world.c (GC_thr_init): Likewise. +* pthread_support.c (GC_thr_init): Likewise. +* blacklst.c (GC_clear_bl, GC_copy_bl, +GC_number_stack_black_listed): Make STATIC. +* dbg_mlc.c (GC_print_obj, GC_make_closure, +GC_debug_invoke_finalizer): Likewise. +* malloc.c (GC_alloc_large_and_clear): Likewise. +* mark.c (GC_push_selected, GC_push_marked1, GC_push_marked2, +GC_push_marked4, GC_push_marked, GC_push_next_marked, +GC_push_next_marked_dirty, GC_push_next_marked_uncollectable): +Likewise. +* misc.c (GC_clear_stack_inner): Likewise. +* os_dep.c (GC_repeat_read, GC_default_push_other_roots): Likewise. +* darwin_stop_world.c (FindTopOfStack): Make static; define only +if not DARWIN_DONT_PARSE_STACK. +* dbg_mlc.c (GC_debug_free_inner): Define only if DBG_HDRS_ALL. +* dyn_load.c (GC_repeat_read): Remove unused prototype. +* include/private/gc_pmark.h (GC_find_start): Likewise. +* misc.c (GC_read, GC_register_finalizer_no_order): Likewise. +* dyn_load.c (GC_segment_is_thread_stack): Add prototype (only if +THREADS). +* dyn_load.c (GC_register_main_static_data): Define only if +DYNAMIC_LOADING. +* finalize.c (GC_enqueue_all_finalizers): Remove unnecessary tail +"return" statement. +* gc_dlopen.c (GC_SOLARIS_THREADS): Don't recognize (since implies +GC_PTHREADS). +* include/gc.h: Fix a typo. +* include/gc_inline.h (GC_ASSERT): Define (if not defined) since +the header is public. +* include/gc_inline.h (GC_generic_malloc_many): New public +function declaration. +* mallocx.c (GC_generic_malloc_many): Make public. +* include/private/gc_priv.h (GC_INNER): Use visibility attribute +(if available). +* include/private/gc_priv.h (GC_EXTERN): Define using GC_INNER. +* include/private/gc_priv.h: Include atomic_ops.h if THREADS and +MPROTECT_VDB. +* os_dep.c: Don't include atomic_ops.h +* win32_threads.c: Likewise. +* include/private/gc_priv.h (GC_push_selected, GC_push_regs, +GC_push_marked, GC_number_stack_black_listed, +GC_alloc_large_and_clear, GC_reclaim_or_delete_all, +GC_generic_malloc_many, GC_make_closure, +GC_debug_invoke_finalizer, GC_print_obj, GC_page_was_ever_dirty): +Remove the prototype. +* mark.c (GC_page_was_ever_dirty): Add prototype (only if +PROC_VDB). +* include/private/gc_priv.h (GC_push_next_marked_dirty, +GC_push_next_marked, GC_push_next_marked_uncollectable): Move +the prototype to mark.c. +* include/private/gc_priv.h (GC_is_static_root): Declare only if +not THREADS. +* include/private/gc_priv.h (GC_free_inner): Declare only if +THREADS. +* include/private/gc_priv.h (GC_debug_free_inner): Declare only if +THREADS and DBG_HDRS_ALL. +* include/private/gc_priv.h (GC_markers): Declare GC_markers only +if PARALLEL_MARK. +* include/private/gc_priv.h (GC_register_main_static_data): Move +the prototype to misc.c. +* mach_dep.c (GC_push_regs): Make STATIC; define only along with +HAVE_PUSH_REGS definition. +* mach_dep.c (GC_clear_stack_inner): Replace K&R-style function +definition with the ANSI C one. +* mark.c (GC_started_thread_while_stopped): Declared only if not +GNU C. +* win32_threads.c (GC_started_thread_while_stopped): Don't define +if GNU C. +* mark.c (GC_mark_from): Avoid unbalanced brackets in +#if-#else-#endif blocks. +* mark_rts.c (GC_is_static_root): Define only if not THREADS. +* os_dep.c (GC_get_stack_base): Make public (for OpenBSD). +* os_dep.c (GC_page_was_ever_dirty): Comment out the function +except for PROC_VDB. +* tests/test.c (main): Don't reference GC_print_obj, +GC_make_closure, GC_debug_invoke_finalizer, +GC_page_was_ever_dirty, GC_is_fresh (in GC_noop). +* thread_local_alloc.c: Don't include "gc_inline.h". +* win32_threads.c (GC_write_fault_handler): Declare only if +MPROTECT_VDB. + +* allchblk.c (DEBUG): Remove macro (since unused). +* allchblk.c: Include private/gc_priv.h before other includes and +definitions. +* alloc.c: Likewise. +* gc_dlopen.c: Likewise. +* headers.c: Likewise. +* mallocx.c: Likewise. +* mark_rts.c: Likewise. +* new_hblk.c: Likewise. +* reclaim.c: Likewise. +* mark.c: Include private/gc_pmark.h before other includes. +* misc.c: Likewise. +* dyn_load.c (_GNU_SOURCE): Move the definition to gc_priv.h. +* pthread_support.c (_USING_POSIX4A_DRAFT10): Likewise. +* pthread_support.c (_POSIX4A_DRAFT10_SOURCE): Remove (since +already defined in gc_config_macros.h). +* dyn_load.c (GC_init_dyld): Remove parameter cast for +_dyld_register_func_for_add_image() and +_dyld_register_func_for_remove_image(); add the comment about +possible warnings; add FIXME for the deprecated +_dyld_bind_fully_image_containing_address(). +* include/private/gc_priv.h: Include gc.h before the standard +headers inclusion. +* tests/test.c: Likewise. +* include/private/gcconfig.h (DebugBreak): Update the comment. +* typd_mlc.c (ED_INITIAL_SIZE): Remove ';'. + +* configure.ac (openbsd): Define GC_OPENBSD_THREADS. +* configure.ac: Add AM_CONDITIONAL(OPENBSD_THREADS). +* configure.ac: Add sparc-openbsd case. +* doc/README.macros (GC_NETBSD_THREADS, GC_OPENBSD_THREADS): +Document. +* tests/test.c (main): Handle OpenBSD case. +* include/private/pthread_stop_world.h: Likewise. +* extra/threadlibs.c (main): Replace K&R-style function definition +with the ANSI C one. +* extra/threadlibs.c (main): Handle GC_OPENBSD_THREADS case. +* dyn_load.c (OPENBSD): Recognize (similar to NETBSD). +* include/gc_config_macros.h (GC_SOLARIS_THREADS): Recognize; +define it for OpenBSD. +* include/gc_pthread_redirects.h (GC_pthread_sigmask, +pthread_sigmask): Don't declare and redefine for OpenBSD. +* include/private/gcconfig.h: Handle OpenBSD (on arm, sh, x86, +x64, powerpc). +* mach_dep.c (NO_GETCONTEXT): Likewise. +* include/private/pthread_stop_world.h (thread_stop_info): Don't +define last_stop_count field if OpenBSD. +* misc.c (GC_init_dyld): Add declaration (if NetBSD). +* misc.c (GC_init): Don't call GC_init_netbsd_elf() for OpenBSD. +* os_dep.c (GC_init_netbsd_elf): Don't define for OpenBSD. +* os_dep.c (old_segv_act, GC_jmp_buf_openbsd): New static variable +(only if OpenBSD). +* os_dep.c (GC_fault_handler_openbsd, GC_find_limit_openbsd, +GC_skip_hole_openbsd): New static function (only if OpenBSD). +* os_dep.c (GC_get_stack_base, GC_get_main_stack_base, +GC_register_data_segments): Define specially for OpenBSD case. +* os_dep.c (GC_fault_handler_lock): Initialize to +AO_TS_INITIALIZER (instead of 0). +* pthread_support.c (GC_allocate_lock): Likewise. +* pthread_stop_world.c (NSIG, GC_print_sig_mask, +GC_remove_allowed_signals, suspend_handler_mask, GC_stop_count, +GC_world_is_stopped, GC_retry_signals, SIG_THR_RESTART, +GC_suspend_ack_sem, GC_suspend_handler_inner, GC_suspend_handler, +GC_restart_handler): Don't define and use if OpenBSD. +* pthread_stop_world.c (GC_suspend_all, GC_stop_world, +GC_start_world): Handle OpenBSD case. +* pthread_stop_world.c (GC_stop_init): Define as empty if OpenBSD. +* pthread_support.c (pthread_sigmask): Don't undefine the macro and +don't define the wrapper function if OpenBSD. +* pthread_support.c (GC_thr_init): Handle OpenBSD case. + +* dyn_load.c: Move the inclusion of private/gc_priv.h below +definition of a feature macro (_GNU_SOURCE). + +* include/gc.h (REVEAL_POINTER): Remove redundant parentheses. +* include/gc.h (GC_HIDE_POINTER, GC_REVEAL_POINTER): New macros +(only if GC_I_HIDE_POINTERS). +* backgraph.c (GET_OH_BG_PTR): Prefix REVEAL_POINTER() with "GC_". +* dbg_mlc.c (GC_get_back_ptr_info): Likewise. +* finalize.c (GC_grow_table, GC_dump_finalization, GC_finalize, +GC_enqueue_all_finalizers): Likewise. +* backgraph.c (SET_OH_BG_PTR): Prefix HIDE_POINTER() with "GC_". +* finalize.c (GC_general_register_disappearing_link, +GC_unregister_disappearing_link, GC_register_finalizer_inner, +GC_finalize): Likewise. +* include/private/dbg_mlc.h (HIDE_BACK_PTR): Likewise. +* include/private/dbg_mlc.h (GC_I_HIDE_POINTERS): Define instead +of I_HIDE_POINTERS. +* include/private/gc_priv.h (GC_I_HIDE_POINTERS): Likewise. +* include/gc.h (_GC_H): Strip leading underscore. +* include/gc_backptr.h (_GC_H): Likewise. +* include/gc_gcj.h (_GC_H): Likewise. +* include/gc_mark.h (_GC_H): Likewise. +* include/gc_typed.h (_GC_TYPED_H, _GC_H): Likewise. +* include/javaxfc.h (_GC_H): Likewise. +* include/new_gc_alloc.h (__GC_SPECIALIZE): Likewise. +* include/private/dbg_mlc.h (_GC_H): Likewise. +* include/private/gc_priv.h (_GC_H): Likewise. + +* gc_cpp.cc: Include "gc_cpp.h" instead of . + +* include/private/gc_priv.h (GC_INNER): New macro (for GC-scope +variable definitions). +* include/private/gc_priv.h (GC_EXTERN): Update the comment. +* allchblk.c (GC_unmap_threshold): Define as GC_INNER. +* alloc.c (GC_incremental, GC_world_stopped, GC_n_heap_sects, +GC_n_memory, GC_fail_count): Likewise. +* blacklst.c (GC_black_list_spacing, GC_print_heap_obj): Likewise. +* gcj_mlc.c (GC_gcj_malloc_initialized, GC_gcjobjfreelist): Likewise. +* mach_dep.c (GC_save_regs_ret_val): Likewise. +* mark.c (GC_n_mark_procs, GC_obj_kinds, GC_n_kinds, +GC_mark_stack, GC_mark_stack_limit, GC_mark_stack_size, +GC_mark_stack_top, GC_mark_state, GC_mark_stack_too_small, +GC_mark_no, GC_markers): Likewise. +* mark_rts.c (GC_root_size, GC_push_typed_structures): Likewise. +* misc.c (GC_allocate_ml, GC_debugging_started, GC_check_heap, +GC_print_all_smashed, GC_print_back_height, GC_dump_regularly, +GC_backtraces, GC_force_unmap_on_gcollect, +GC_large_alloc_warn_interval, GC_is_initialized, GC_write_cs, +GC_current_warn_proc, GC_blocked_sp, GC_activation_frame): Likewise. +* os_dep.c (GC_page_size, GC_dont_query_stack_min, +GC_no_win32_dlls, GC_wnt, GC_sysinfo, GC_push_other_roots, +GC_dirty_maintained, GC_fault_handler_lock): Likewise. +* pthread_support.c (GC_allocate_ml, GC_lock_holder, +GC_need_to_lock, GC_thr_initialized, GC_threads, +GC_in_thread_creation, GC_collecting, GC_allocate_lock, +GC_mark_lock_holder): Likewise. +* reclaim.c (GC_bytes_found, GC_fl_builder_count, GC_have_errors): +Likewise. +* win32_threads.c (GC_allocate_ml, GC_lock_holder, +GC_need_to_lock, GC_mark_lock_holder, GC_collecting): Likewise. +* extra/gc.c (GC_INNER, GC_EXTERN): Define as STATIC. +* mach_dep.c (GC_with_callee_saves_pushed): Remove redundant {}. + +* include/private/gc_priv.h (GC_bytes_allocd, GC_objfreelist, +GC_aobjfreelist): Replace GC_EXTERN to extern for SEPARATE_GLOBALS +case (since they are not defined inside GC at present). +* include/private/gc_priv.h (GC_objects_are_marked): Remove the +declaration (since made static). +* mark.c (GC_objects_are_marked): Define as STATIC. +* win32_threads.c (GC_thr_initialized, GC_in_thread_creation): +Likewise. +* mark.c (GC_N_KINDS_INITIAL_VALUE): New macro (defined and used +to initialize GC_n_kinds). +* win32_threads.c (start_mark_threads): Adjust the comment. + +* alloc.c (GC_notify_full_gc): Use GC_INLINE for a tiny static +function. +* backgraph.c (pop_in_progress, GC_apply_to_each_object): Likewise. +* mark_rts.c (add_roots_to_index): Likewise. + +* extra/gc.c: New file. +* Makefile.am (EXTRA_DIST): Add "extra/gc.c". + +* misc.c (GC_log): Remove the declaration; move the definition (to +the place where it is used); make STATIC. +* misc.c (GC_init): Use GC_err_printf() instead of GC_log_printf() +to print open log failure. +* misc.c (GC_write): Don't abort on open log failure if the GC is +compiled with GC_PRINT_VERBOSE_STATS (useful for WinCE). + +* include/private/gcconfig.h (USE_MMAP): Guard with ifndef. + +* allchblk.c (GC_fail_count, GC_large_alloc_warn_interval): Move +the variable declaration to gc_priv.h. +* alloc.c (GC_bytes_found, GC_unmap_threshold, +GC_force_unmap_on_gcollect): Likewise. +* dyn_load.c (GC_no_win32_dlls, GC_wnt): Likewise. +* finalize.c (GC_fail_count): Likewise. +* include/private/gc_locks.h (GC_allocate_ml, GC_lock_holder, +GC_collecting, GC_mark_lock_holder, GC_need_to_lock): Likewise. +* include/private/gc_pmark.h (GC_n_mark_procs, GC_mark_stack_size, +GC_mark_stack_limit, GC_mark_stack_top, GC_mark_stack, +GC_mark_stack_too_small, GC_mark_state): Likewise. +* include/private/pthread_support.h (GC_threads, +GC_thr_initialized, GC_in_thread_creation): Likewise. +* mallocx.c (GC_bytes_found): Likewise. +* mark_rts.c (GC_save_regs_ret_val, GC_world_stopped): Likewise. +* misc.c (GC_unmap_threshold): Likewise. +* os_dep.c (GC_unmap_threshold): Likewise. +* pthread_support.c (GC_markers): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist, +GC_gcj_malloc_initialized, GC_gcj_kind): Likewise. +* win32_threads.c (GC_fault_handler_lock, GC_write_cs, +GC_dont_query_stack_min, GC_markers, GC_wnt): Likewise. +* include/private/gc_priv.h (GC_EXTERN): New macro (used mostly as +a tag for now); defined after "gcconfig.h" inclusion. +* include/private/gc_priv.h: Use GC_EXTERN instead of "extern" +keyword for most global variables. +* alloc.c (GC_copyright): Add the comment about the symbol +visibility. +* finalize.c (GC_fo_entries): Likewise. +* include/private/gc_priv.h (GC_print_stats): Likewise. +* misc.c (GC_quiet): Likewise. +* mallocx.c (GC_bytes_allocd_tmp): Make the volatile variable +STATIC. +* pthread_support.c (GC_threads): Add explicit zero initializer +(to make the variable definition differ from the declaration). + +* backgraph.c (GC_quiet): Remove the declaration (not needed +anymore since gc_priv.h is always included). +* checksums.c (GC_quiet): Likewise. +* gcj_mlc.c (GC_quiet): Likewise. +* headers.c (GC_hdr_cache_hits, GC_hdr_cache_misses): Add the +comment. +* include/private/gc_hdrs.h (GC_hdr_cache_hits, +GC_hdr_cache_misses): Likewise. +* mark.c (GC_first_nonempty): Make the volatile variable STATIC. +* pthread_stop_world.c (GC_stop_count, GC_world_is_stopped): +Likewise. +* win32_threads.c (GC_please_stop, GC_max_thread_index, +GC_mark_mutex_waitcnt): Likewise. + +* pthread_support.c (GC_USE_LD_WRAP): Fix a typo (swapped 'L' and +'D') in the name. + +* gc_dlopen.c (GC_MUST_RESTORE_REDEFINED_DLOPEN): Define if dlopen +redirection is turned off; turn it on later when dlopen real +symbol is no longer needed (according to the comment and the same +as in dyn_load.c). +* gc_dlopen.c (WRAP_FUNC, REAL_FUNC): Rename to WRAP_DLFUNC and +REAL_DLFUNC, respectively (to have unique names since the +definitions may differ from that of the similar ones in +pthread_support.c). +* mark.c (source): Undefine the macro when no longer needed. +* os_dep.c (handler): Rename the type to GC_fault_handler_t (to +have the unique name across the project). +* os_dep.c (STAT_BUF_SIZE, STAT_READ); Guard with ifndef; add the +comment. +* pthread_support.c (STAT_BUF_SIZE, STAT_READ): Likewise. +* os_dep.c (sbrk): Undo sbrk() redirection (for ECOS) when no +longer needed. + +* pthread_stop_world.c (pthread_sigmask): Undefine before using +in GC_print_sig_mask() (only if DEBUG_THREADS); add the comment. +* win32_threads.c (dlopen, _beginthread): Don't undefine (since +neither redirected nor used here). +* win32_threads.c (GC_Thread_Rep): Rename "table_management" to +"tm" for short; remove "tm_" prefix. +* win32_threads.c (in_use, next): Don't define the macros; use +tm.in_use and tm.next fields, respectively (to ease debugging). +* win32_threads.c (HASH): Rename to PTHREAD_MAP_HASH (to have +unique name across the project). + +* include/private/gc_priv.h (I_HIDE_POINTERS): Define before gc.h +inclusion. +* include/private/gc_pmark.h (I_HIDE_POINTERS): Define if gc.h is +not included yet. +* finalize.c (I_HIDE_POINTERS): Don't define. +* include/private/dbg_mlc.h (I_HIDE_POINTERS): Likewise. +* misc.c (I_HIDE_POINTERS): Likewise. +* include/private/dbg_mlc.h (HIDE_POINTER, REVEAL_POINTER, +GC_hidden_pointer): Don't define if HIDE_POINTER is undefined. +* include/private/gc_pmark.h: Remove the comment about gc_priv.h +inclusion order. + +* dyn_load.c: Include gc_priv.h before using configuration +information (MACOS). +* dyn_load.c (GC_must_restore_redefined_dlopen): Rename to +GC_MUST_RESTORE_REDEFINED_DLOPEN. + +* backgraph.c (SET_OH_BG_PTR): Place outermost parenthesis +properly. +* darwin_stop_world.c: Replace "if DEBUG_THREADS" with +"ifdef DEBUG_THREADS". +* pthread_stop_world.c: Likewise. +* pthread_support.c: Likewise. +* include/gc_inline.h: Guard with GC_INLINE_H. + +* alloc.c (GC_copyright): Define as const. +* alloc.c (GC_collect_at_heapsize): Replace "static" with "STATIC" +(since the name starts with "GC_" prefix). +* dbg_mlc.c (GC_describe_type_fns): Likewise. +* dyn_load.c (GC_FirstDLOpenedLinkMap, +GC_register_dynlib_callback, GC_dyld_sections, +GC_dyld_name_for_hdr, GC_dyld_image_add, GC_dyld_image_remove): +Likewise. +* malloc.c (GC_libpthread_start, GC_libpthread_end, +GC_libld_start, GC_libld_end): Likewise. +* mark_rts.c (GC_remove_root_at_pos, GC_rebuild_root_index): +Likewise. +* os_dep.c (GC_gww_read_dirty, GC_gww_page_was_dirty, +GC_gww_page_was_ever_dirty, GC_mprotect_thread_notify, +GC_mprotect_thread_reply, GC_mprotect_thread, GC_darwin_sigbus, +GC_forward_exception): Likewise. +* pthread_support.c (GC_syms_initialized): Likewise. +* typd_mlc.c (GC_push_typed_structures_proc): Likewise. +* win32_threads.c (GC_win32_dll_threads, +GC_register_my_thread_inner, GC_lookup_pthread, GC_get_stack_min, +GC_waitForSingleObjectInfinite): Likewise. +* darwin_stop_world.c (GC_use_mach_handler_thread, +GC_use_mach_handler_thread, GC_mach_threads_count): Replace +"static" with "STATIC" and add zero initializer. +* os_dep.c (GC_task_self, GC_ports, GC_mprotect_state, +GC_sigbus_count): Likewise. +* headers.c (free_hdr): Replace "static" with GC_INLINE. +* misc.c (GC_tmp): Rename static variable to fwrite_gc_res. +* os_dep.c (memory): Rename static variable to ecos_gc_memory. +* os_dep.c (async_set_pht_entry_from_index): Make static (for +MPROTECT_VDB case). +* pthread_support.c (GC_real_pthread_create, +GC_real_pthread_sigmask, GC_real_pthread_join, +GC_real_pthread_detach, GC_init_real_syms): Use REAL_FUNC() macro +for static GC_real_XXX symbols. +* win32_threads.c (GC_may_be_in_stack): Remove "GC_" prefix. + +* alloc.c (GC_finish_collection): Replace getenv() with GETENV(). +* dyn_load.c (GC_init_dyld): Likewise. +* os_dep.c (GC_print_callers): Likewise. +* dyn_load.c (GC_dyld_name_for_hdr): Cast _dyld_get_image_name() +result (since it's always of "struct mach_header" type). +* dyn_load.c (GC_init_dyld): Cast GC_dyld_image_add and +GC_dyld_image_remove (to always have the first argument of +"struct mach_header" pointer type). + +* configure.ac: Add threads support for OpenBSD case (threads may +not work correctly for it). + +* acinclude.m4: Rename to m4/gc_set_version.m4. +* m4/libtool.m4: Delete the file. +* m4/lt~obsolete.m4: Likewise. +* m4/ltoptions.m4: Likewise. +* m4/ltsugar.m4: Likewise. +* m4/ltversion.m4: Likewise. + +* include/private/gcconfig.h: Define DebugBreak() as _exit(-1) for +x86mingw32ce toolchain to workaround the incorrect DebugBreak() +declaration in winbase.h (the workaround would turn into a no-op +when DebugBreak() will be defined as a macro in the toolchain). + +* include/private/gcconfig.h: Recognize __i386__ if WinCE (for +x86mingw32ce toolchain). +* include/private/gcconfig.h (NO_GETENV): Don't define for CeGCC +toolchain (or if already defined). +* include/private/gcconfig.h (NO_GETENV_WIN32): New macro (always +defined for WinCE or if NO_GETENV is defined). +* misc.c (GC_CreateLogFile): Use NO_GETENV_WIN32 macro instead of +NO_GETENV one. + +* configure.ac: Add AC_CONFIG_MACRO_DIR([m4]). +* Makefile.am: Add "ACLOCAL_AMFLAGS = -I m4". +* libtool.m4: Remove. +* m4/libtool.m4: New file (generated). +* m4/lt~obsolete.m4: Likewise. +* m4/ltoptions.m4: Likewise. +* m4/ltsugar.m4: Likewise. +* m4/ltversion.m4: Likewise. + +* include/gc.h (GC_UNDERSCORE_STDCALL): Recognize new macro; +prefix GC_CreateThread and GC_ExitThread with '_' if defined. +* doc/README.macros (GC_UNDERSCORE_STDCALL): Document. + +* alloc.c (GC_collect_or_expand): Add "retry" argument; add the +comments; don't use "default" stop_func on a retry if +GC_dont_expand. +* alloc.c (GC_allocobj): Pass "retry" argument to +GC_collect_or_expand(). +* malloc.c (GC_alloc_large): Likewise. +* include/private/gc_priv.h (GC_collect_or_expand): Move the +declaration to malloc.c; add "retry" argument. + +* alloc.c (GC_start_call_back): Move the variable definition from +misc.c. +* include/private/gc_priv.h (GC_start_call_back): Remove the +declaration. +* alloc.c (GC_notify_full_gc): Remove unnecessary cast of 0. +* alloc.c (GC_try_to_collect_inner): Also call stop_func at the +beginning of the function. +* include/gc.h (GC_try_to_collect): Refine the comment about +stop_func. + +* alloc.c (GC_default_stop_func, GC_try_to_collect_general, +GC_gcollect): Add the comment. +* alloc.c (GC_try_to_collect_general): Move the assertion on +stop_func != 0 to GC_try_to_collect(). +* alloc.c (GC_try_to_collect_general): If stop_func == 0 then use +GC_default_stop_func instead (holding the lock). +* alloc.c (GC_gcollect): Pass 0 as stop_func instead of +GC_default_stop_func (to prevent data races). + +* Makefile.direct: Move "define arguments" documentation to +doc/README.macros; add reference to doc/README.macros. +* Makefile.dj: Change the documentation reference to +doc/README.macros. +* README.QUICK: Likewise. +* configure.ac: Likewise. +* allchblk.c: Remove unnecessary "-D" from the comment. +* doc/README.macros: Likewise. +* README.environment: Likewise. +* include/gc.h: Likewise. +* include/gc_inline.h: Likewise. +* include/private/gcconfig.h: Likewise. +* README.QUICK: Fix a typo. + +* misc.c (GC_CreateLogFile): Use FILE_ATTRIBUTE_NORMAL for +CreateFile(); don't immediately flush every write if very verbose. + +* doc/README.win32: Replace ".exe.log" to ".gc.log". +* doc/README.win64: Likewise. +* doc/README.win64: Fix a typo. +* misc.c (GC_CreateLogFile): Strip executable file extension for +the log file; use ".gc.log" extension (instead of ".log"). + +* include/gc_config_macros.h: Avoid the redefinition of +GC_xxx_THREADS macros. + +* alloc.c (GC_try_to_collect_general): Change the type of "result" +local variable to GC_bool. + +* include/gc_config_macros.h: Use old behavior for FreeBSD and +NetBSD platform detection code (check that other GC_xxx_THREADS +are undefined); add FIXME. + +* include/gc_config_macros.h: Rearrange the platform detection +code (GC_WIN32_PTHREADS implies GC_WIN32_THREADS; define +GC_THREADS first if GC_XXX_THREADS already set; define proper +GC_XXX_THREADS if GC_THREADS; define GC_PTHREADS in a single +place; define _REENTRANT if posix threads except for Win32). + +* alloc.c (GC_try_to_collect_general): New function (move the code +from GC_try_to_collect, pass force_unmap argument). +* alloc.c (GC_try_to_collect, GC_gcollect): Call +GC_try_to_collect_general(). +* alloc.c (GC_gcollect_and_unmap): New public function. +* include/gc.h (GC_gcollect_and_unmap): New function declaration. +* tests/test.c (window_proc): Call GC_gcollect_and_unmap() on +WM_HIBERNATE event (instead of GC_set_force_unmap_on_gcollect() +and GC_gcollect()). + +* include/gc.h (GC_allow_register_threads, GC_register_my_thread, +GC_unregister_my_thread, GC_malloc_many): Refine the comment. +* include/gc.h (GC_malloc_many, GC_NEXT): Declare unconditionally +(that is, don't depend on GC_THREADS macro). +* include/gc.h: Don't check for __CYGWIN32__ and __CYGWIN__ along +with a check for GC_PTHREADS (since the former implies the +latter). + +* include/gc.h (GC_SOLARIS_THREADS): Don't check for. +* include/gc.h (GC_MIN, GC_MAX): Don't define. +* mallocx.c (GC_malloc_many): Add comment to #endif. + +* configure.ac: Drop the subdir-objects Automake option, since +it's incompatible with picking source files from libatomic_ops. + +* allchblk.c (GC_fail_count, GC_large_alloc_warn_interval): Add +"extern" keyword to a global variable declaration (some compilers +require it). +* alloc.c (GC_bytes_found, GC_unmap_threshold, +GC_force_unmap_on_gcollect): Likewise. +* dyn_load.c (GC_no_win32_dlls, GC_wnt): Likewise. +* finalize.c (GC_fail_count): Likewise. +* include/private/gc_hdrs.h (GC_hdr_cache_hits, +GC_hdr_cache_misses): Likewise. +* mallocx.c (GC_bytes_found): Likewise. +* mark_rts.c (GC_save_regs_ret_val, GC_world_stopped): Likewise. +* misc.c (GC_unmap_threshold): Likewise. +* os_dep.c (GC_unmap_threshold, GC_old_allocator): Likewise. +* pthread_support.c (GC_markers): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist, +GC_gcj_malloc_initialized, GC_gcj_kind): Likewise. +* win32_threads.c (GC_fault_handler_lock, GC_write_cs, +GC_dont_query_stack_min, GC_markers, GC_wnt): Likewise. + +* tests/huge_test.c: Define GC_IGNORE_WARN (if not defined) to +suppress misleading GC "Out of Memory!" warning printed on every +GC_MALLOC(LONG_MAX) call. +* tests/huge_test.c: Include "gc.h" instead of . +* tests/huge_test.c (main): Replace K&R-style function definition +with the ANSI C one. + +* dyn_load.c (GC_register_dynamic_libraries): Always use +lpMaximumApplicationAddress value for WinCE (even for old +versions). +* os_dep.c (VER_PLATFORM_WIN32_CE): Define if not in winbase.h. +* os_dep.c (GC_dont_query_stack_min): New global variable (only if +WinCE and THREADS). +* os_dep.c (GC_setpagesize): Adjust lpMaximumApplicationAddress +for WinCE (prior to version 6) if not _WIN32_WCE_EMULATION; set +GC_dont_query_stack_min for older WinCE (prior to version 5). +* win32_threads.c (GC_dont_query_stack_min): Declare. +* win32_threads.c (GC_get_stack_min): Rename the macro to +GC_wince_evaluate_stack_min for WinCE; update the comment. +* win32_threads.c (GC_push_stack_for, GC_get_next_stack): Use +GC_wince_evaluate_stack_min() instead of GC_get_stack_min() for +WinCE and don't update thread's last_stack_min value (only if +GC_dont_query_stack_min). +* win32_threads.c (GC_push_stack_for): Skip assertion for WinCE if +GC_dont_query_stack_min (since the evaluated stack_min value may +be incorrect if the stack is bigger than 64 KiB). + +* gc_dlopen.c (GC_dlopen): Add function redirector (only if +GC_USE_LD_WRAP). +* include/gc.h: Include "gc_pthread_redirects.h" even if +GC_USE_LD_WRAP or GC_NO_THREAD_REDIRECTS. +* include/gc_pthread_redirects.h (GC_PTHREAD_REDIRECTS_H): Don't +define and check for (since included only from gc.h). +* include/gc_pthread_redirects.h: Declare "GC_" symbols even if +GC_USE_LD_WRAP or GC_NO_THREAD_REDIRECTS. +* include/gc_pthread_redirects.h: Include signal.h only to get +sigset_t definition. + +* Makefile.direct: Document GC_REGISTER_MEM_PRIVATE. +* mark_rts.c (GC_is_tmp_root): Define also for WinCE unless +NO_DEBUGGING (that is, replace _WIN32_WCE_EMULATION with MSWINCE). +* os_dep.c (GC_sysinfo): Remove explicit global variable +initialization to "{0}" (revert back the previous change) since it +might produce a warning. + +* allchblk.c (GC_large_alloc_warn_interval): Move declaration from +gc_priv.h. +* allchblk.c (GC_large_alloc_warn_suppressed): Move definition +from misc.c; define as STATIC. +* include/private/gc_priv.h (GC_large_alloc_warn_interval, +GC_large_alloc_warn_suppressed): Remove declaration. +* alloc.c (GC_bytes_found): Add "defined in" comment. +* mallocx.c (GC_bytes_found): Likewise. +* misc.c (GC_unmap_threshold): Likewise. +* os_dep.c (GC_old_allocator): Likewise. +* pthread_support.c (GC_markers): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist, +GC_gcj_malloc_initialized, GC_gcj_kind): Likewise. +* win32_threads.c (GC_markers): Likewise. +* alloc.c (GC_start_time): Explicitly initialize to 0 or NULL (to +be distinctive from a variable declaration). +* backgraph.c (GC_max_height, GC_deepest_obj): Likewise. +* blacklst.c (GC_old_normal_bl, GC_incomplete_normal_bl, +GC_old_stack_bl, GC_incomplete_stack_bl): Likewise. +* checksums.c (GC_faulted, GC_n_dirty_errors, +GC_n_faulted_dirty_errors, GC_n_changed_errors, GC_n_clean, +GC_n_dirty, GC_bytes_in_used_blocks): Likewise. +* dbg_mlc.c (GC_smashed): Likewise. +* finalize.c (GC_old_dl_entries): Likewise. +* gcj_mlc.c (GC_gcj_kind, GC_gcj_debug_kind, GC_gcjobjfreelist, +GC_gcjdebugobjfreelist): Likewise. +* mach_dep.c (GC_save_regs_ret_val): Likewise. +* mark.c (GC_n_rescuing_pages, GC_mark_stack, GC_mark_stack_limit, +GC_mark_stack_top): Likewise. +* misc.c (GC_min_sp, GC_high_water, GC_bytes_allocd_at_reset): +Likewise. +* os_dep.c (GC_data_start, GC_page_size, GC_sysinfo, +GC_old_segv_handler, GC_old_bus_handler, +GC_old_bus_handler_used_si, GC_old_segv_handler_used_si, +GC_proc_buf, GC_proc_fd, GC_vd_base): Likewise. +* pthread_stop_world.c (GC_stop_count, GC_stopping_pid): Likewise. +* reclaim.c (GC_leaked): Likewise. +* typd_mlc.c (GC_explicit_kind, GC_array_kind, GC_ext_descriptors, +GC_typed_mark_proc_index, GC_array_mark_proc_index, +GC_eobjfreelist, GC_arobjfreelist): Likewise. +* win32_threads.c (GC_pthread_map_cache, GC_marker_cv, +GC_marker_Id): Likewise. +* dbg_mlc.c (GC_smashed, GC_n_smashed): Define as STATIC. +* gcj_mlc.c (GC_gcjdebugobjfreelist): Likewise. +* os_dep.c (GC_vd_base): Likewise. +* pthread_support.c (GC_mark_threads): Likewise. +* reclaim.c (GC_leaked): Likewise. +* typd_mlc.c (GC_bm_table): Likewise. +* mark_rts.c (GC_save_regs_ret_val): Change declaration type to +that of definition; add "defined in" comment. +* mark_rts.c (GC_push_current_stack): Remove unnecessary cast for +GC_save_regs_ret_val. +* misc.c (GC_check_heap, GC_print_all_smashed, +GC_start_call_back): Remove unnecessary cast (of 0). +* misc.c (GC_LARGE_ALLOC_WARN_INTERVAL): New tuning macro. +* misc.c (GC_large_alloc_warn_interval): Initialize to +GC_LARGE_ALLOC_WARN_INTERVAL value. +* misc.c (GC_tmp): Change to "static". +* os_dep.c (GC_mprotect_state): Define as static. +* pthread_support.c (dummy_thread_local): Prefix with "GC_". +* win32_threads.c (WinMain): Remove FIXME for WinCE. + +* os_dep.c (PROTECT, UNPROTECT): Use distinct ABORT messages. + +* configure.ac: Rewrite the tests for external or internal +libatomic_ops. +* configure.ac: In particular, drop the symbolic links. Add option +--with-libatomic-ops for forced selection. +* Makefile.am: Adjust the path of source files from libatomic_ops +to not use the links. +* Makefile.am (libgc_la_LIBADD): Add $(ATOMIC_OPS_LIBS). This will +be empty if we use the bundled AO sources. + +* Makefile.am: Strip version suffix for libatomic_ops directory. +* build_atomic_ops.sh: Likewise. +* build_atomic_ops.sh.cygwin: Likewise. +* configure_atomic_ops.sh: Likewise. +* Makefile.direct: Remove AO_VERSION definition; strip version +suffix for libatomic_ops directory. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* gc.mak: Likewise. + +* libatomic_ops: Rename from "libatomic_ops-1.2". + +* alloc.c (GC_version): Add "const" keyword. +* alloc.c (GC_get_version): New public function. +* include/gc.h (GC_get_version): New function declaration; update +the comment for the GC version. + +* include/private/gc_locks.h (GC_allocate_ml, GC_lock_holder, +GC_collecting, GC_mark_lock_holder, GC_need_to_lock): Use "extern" +(for the global variable declaration) again. +* include/private/gc_pmark.h (GC_n_mark_procs, GC_mark_stack_size, +GC_mark_stack_limit, GC_mark_stack_top, GC_mark_stack, +GC_mark_stack_too_small, GC_mark_state): Likewise. +* include/private/gcconfig.h (GC_register_stackbottom): Likewise. +* include/private/pthread_support.h (GC_threads, +GC_thr_initialized, GC_in_thread_creation): Likewise. +* include/private/gc_priv.h: Likewise. + +* real_malloc.c: Include private/config.h if HAVE_CONFIG_H. + +* allchblk.c (GC_hblkfreelist): Define as STATIC. +* blacklst.c (GC_total_stack_black_listed): Likewise. +* include/private/gc_priv.h (GC_hblkfreelist, GC_stopped_mark, +GC_total_stack_black_listed, GC_push_stubborn_structures): Remove +declaration. +* mark_rts.c (GC_stopped_mark): Add declaration (only if +THREAD_LOCAL_ALLOC). +* allchblk.c (GC_fail_count): Move the declaration out of +GC_allochblk_nth(); remove "extern". +* alloc.c (IF_THREADS): Remove unused macro. +* alloc.c (GC_world_stopped): Define only if THREAD_LOCAL_ALLOC. +* alloc.c (GC_stopped_mark): Set GC_world_stopped value only if +THREAD_LOCAL_ALLOC. +* alloc.c (GC_bytes_found, GC_collection_in_progress, +GC_check_tls, GC_unmap_threshold, GC_force_unmap_on_gcollect): +Remove K&R-style "extern" for the declaration. +* dbg_mlc.c (GC_free_inner): Likewise. +* dyn_load.c (GC_repeat_read, GC_roots_present, GC_is_heap_base, +GC_get_next_stack, GC_no_win32_dlls, GC_wnt): Likewise. +* finalize.c (GC_fail_count): Likewise. +* include/private/gc_hdrs.h (GC_hdr_cache_hits, +GC_hdr_cache_misses): Likewise. +* include/private/gc_locks.h (GC_allocate_ml, GC_lock_holder, +GC_lock, GC_collecting, GC_mark_lock_holder, GC_need_to_lock): +Likewise. +* include/private/gc_pmark.h (GC_mark_procs, GC_n_mark_procs, +GC_mark_stack_size, GC_mark_stack_limit, GC_mark_stack_top, +GC_mark_stack, GC_mark_stack_too_small, GC_mark_state): Likewise. +* include/private/gc_priv.h (GC_current_warn_proc, GC_obj_kinds, +GC_n_kinds, GC_fo_entries, GC_n_heap_sects, GC_n_memory, +GC_page_size, GC_sysinfo, GC_black_list_spacing, +GC_objects_are_marked, GC_incremental, GC_dirty_maintained, +GC_root_size, GC_debugging_started, GC_large_alloc_warn_interval, +GC_large_alloc_warn_suppressed, GC_blocked_sp, +GC_activation_frame, GC_push_other_roots, +GC_push_finalizer_structures, GC_push_thread_structures, +GC_push_typed_structures, GC_start_call_back, GC_is_initialized, +GC_check_heap, GC_print_all_smashed, GC_print_all_errors, +GC_print_heap_obj, GC_have_errors, GC_print_stats, +GC_dump_regularly, GC_backtraces, GC_print_back_height, +GC_debug_generic_malloc_inner, +GC_debug_generic_malloc_inner_ignore_off_page, +GC_fl_builder_count, GC_mark_no, GC_help_marker, +GC_setup_temporary_fault_handler, GC_reset_fault_handler): Likewise. +* include/private/gcconfig.h (GC_SysVGetDataStart, +GC_FreeBSDGetDataStart, GC_register_stackbottom, +GC_MacTemporaryNewPtr, GC_amiga_get_mem): Likewise. +* include/private/pthread_support.h (GC_threads, +GC_thr_initialized, GC_in_thread_creation): Likewise. +* malloc.c (GC_text_mapping): Likewise. +* mallocx.c (GC_bytes_found): Likewise. +* mark.c (GC_check_dirty, GC_started_thread_while_stopped): Likewise. +* mark_rts.c (GC_save_regs_ret_val): Likewise. +* misc.c (GC_clear_stack_inner, GC_init_parallel, GC_init_win32, +GC_setpagesize, GC_init_linux_data_start, +GC_set_and_save_fault_handler, GC_unmap_threshold): Likewise. +* os_dep.c (GC_unmap_threshold, GC_push_all_stacks, +GC_darwin_register_mach_handler_thread): Likewise. +* pthread_support.c (GC_markers, GC_collection_in_progress): +Likewise. +* tests/test.c (GC_amiga_free_all_mem): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist, +GC_gcj_malloc_initialized, GC_gcj_kind): Likewise. +* win32_threads.c (GC_write_fault_handler, GC_gww_dirty_init, +GC_fault_handler_lock, GC_write_cs, GC_markers): Likewise. +* misc.c (GC_read, GC_register_finalizer_no_order, GC_init_dyld): +Move the declaration out of GC_init(); remove "extern". +* os_dep.c (GC_abort): Add the comment; add workaround to suppress +compiler "unreachable code" warnings for ABORT callers (where +ABORT is followed by a dummy return statement). +* os_dep.c (GC_old_allocator): Move the declaration out of +GC_default_push_other_roots(); remove "extern". +* darwin_stop_world.c (GC_mprotect_stop, GC_mprotect_resume): +Move the declaration out of GC_stop_world() and GC_start_world() +(only if MPROTECT_VDB); remove "extern". + +* win32_threads.c (GC_get_stack_min, GC_push_stack_for, +GC_get_next_stack): Recognize _WIN32_WCE_EMULATION macro (used for +WinCE emulation and for custom WinCE 6 devices); add the comment. +* win32_threads.c (GC_get_stack_min): Cast pointer to word instead +of DWORD. +* win32_threads.c (GC_get_next_stack): Don't use and maintain the +latest known stack_min value for WinCE (if GC_get_stack_min is +defined as a macro); update the comments. +* win32_threads.c (GC_wnt): Don't declare for WinCE. + +* Makefile.direct: Document EMPTY_GETENV_RESULTS. +* gcj_mlc.c (GC_clear_stack): Remove declaration. +* malloc.c (GC_clear_stack): Likewise. +* mallocx.c (GC_clear_stack): Likewise. +* typd_mlc.c (GC_clear_stack): Likewise. +* gcj_mlc.c (GENERAL_MALLOC, GENERAL_MALLOC_IOP): Rename to +GENERAL_MALLOC_INNER and GENERAL_MALLOC_INNER_IOP, respectively; +remove "lb" unnecessary cast to word. +* include/private/gc_priv.h (GC_clear_stack): Add declaration. +* include/private/gc_priv.h (GENERAL_MALLOC, GENERAL_MALLOC_IOP): +Move common declaration from typd_mlc.c and malloc.c; remove +unnecessary result and "lb" parameter casts. +* include/private/thread_local_alloc.h: Guard against duplicate +header file inclusion. +* os_dep.c (USE_MUNMAP): Replace "-->" with an error directive for +the case when USE_MMAP is not defined. +* pthread_support.c (GC_is_thread_tsd_valid): New internal +function (only if GC_ASSERTIONS and THREAD_LOCAL_ALLOC); move the +code from thread-local GC_malloc(); add FIXME for the condition. +* win32_threads.c (GC_is_thread_tsd_valid): Likewise. +* thread_local_alloc.c (GC_gcjobjfreelist): Change the type (to +match that of its definition). +* thread_local_alloc.c (GC_destroy_thread_local): Add a cast for +GC_gcjobjfreelist. +* thread_local_alloc.c (GC_lookup_thread, GC_lookup_thread_inner): +Remove unused declaration; don't include pthread.h. +* thread_local_alloc.c (GC_is_thread_tsd_valid): New declaration +(only if GC_ASSERTIONS). +* thread_local_alloc.c (GC_malloc): Use GC_is_thread_tsd_valid() +instead of GC_lookup_thread(). +* win32_threads.c (GC_lookup_thread_inner): Define as STATIC. +* win32_threads.c (UNPROTECT): Rename to UNPROTECT_THREAD (to have +id different from that in os_dep.c). + +* allchblk.c (GC_enough_large_bytes_left): Replace "inline static" +with GC_INLINE. +* include/private/gc_priv.h (fixed_getenv): Likewise. +* alloc.c (GC_max, GC_min): Replace "static INLINE" with +GC_INLINE. +* mark_rts.c (rt_hash): Likewise. +* win32_threads.c (GC_get_max_thread_index): Likewise. +* include/private/gc_priv.h (INLINE): Prefix with "GC_"; include +"static"; define for Sun CC; define for VC++ (and other +compilers). +* pthread_support.c: Don't define __inline__ for non-GNU compilers +(not needed anymore). + +* NT_THREADS_MAKEFILE: Remove file (since it duplicates gc.mak). +* Makefile.in: Remove reference to NT_THREADS_MAKEFILE. +* Makefile.am: Likewise. +* Makefile.dj: Likewise. +* Makefile.direct: Likewise. +* doc/README.win32: Add reference to gc.mak. +* NT_X64_THREADS_MAKEFILE: Likewise. + +* Makefile.direct: Remove references to acinclude.m4, libtool.m4. + +* autogen.sh: Update. + +* Makefile.am: Don't add libtool.m4 to EXTRA_DIST. +* acinclude.m4: Fix underquoting of GC_SET_VERSION. +* README.QUICK: Update information for Makefile. +* Makefile.am: Do not distribute the substituted bdw-gc.pc. +* configure.ac: Add AM conditional analog to KEEP_BACK_PTRS. +* tests/tests.am: Use it here to conditionally enable tracetest +when possible. + +* dyn_load.c (GC_wnt): Update the comment. +* dyn_load.c (GC_register_dynamic_libraries): Add the comment for +_WIN32_WCE_EMULATION; recognize GC_REGISTER_MEM_PRIVATE (new +macro); call GC_is_heap_base() only if check for Type succeeded. + +* mark_rts.c (GC_is_tmp_root): Don't define unless NO_DEBUGGING; +update the comment. +* include/private/gc_priv.h (GC_is_tmp_root): Remove declaration. + +* include/private/gcconfig.h (CANCEL_SAFE, IF_CANCEL): new macros. +* include/private/gc_priv.h (DISABLE_CANCEL, RESTORE_CANCEL, +ASSERT_CANCEL_DISABLED): New macros. +* alloc.c (GC_maybe_gc): Assert cancellation disabled. +(GC_collect_a_little_inner,GC_try_to_collect, GC_collect_or_expand): +Disable cancellation. +(GC_add_to_our_memory): Check for overflow. +* misc.c (GC_cancel_disable_count): declare. +(GC_init, GC_write): Disable cancellation. +(GC_init): Remove redundant GC_is_initialized test. +* os_dep.c (GC_repeat_read): Assert cancellation disabled. +(GC_get_stack_base): Disable cancellation. +* pthread_stop_world.c (GC_suspend_handler_inner): Disable +cancellation. +* pthread_support.c (GC_mark_thread): Permanently disable +cancellation. +(GC_wait_for_gc_completion, GC_wait_builder, GC_wait_marker): +Assert cancellation disabled. +(fork handling): Disable cancellation, fix comment. +(GC_pthread_create): Disable cancellation. +(GC_unregister_my_thread): Disable cancellation. +* Makefile.direct: Document NO_CANCEL_SAFE. + +* Makefile: Remove outdated file (Makefile.direct should be used +instead). + +* include/gc.h (GC_use_DllMain): Refine the comment. + +* configure.ac: Add documentation to AC_DEFINE for GC_THREADS and +EMPTY_GETENV_RESULTS. +* configure.ac: Fix a typo. +* Makefile.am: Likewise. + +* checksums.c (GC_checksum, GC_update_check_page): Remove +"register" keyword in local variable declarations (for the code +used only for debugging or which is not time-critical). +* dbg_mlc.c (GC_has_other_debug_info, GC_store_debug_info, +GC_store_debug_info_inner, GC_check_annotated_obj, GC_print_obj, +GC_print_smashed_obj, GC_debug_end_stubborn_change, +GC_debug_invoke_finalizer): Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Likewise. +* mallocx.c (GC_realloc): Likewise. +* mark_rts.c (GC_print_static_roots, GC_is_static_root, +GC_clear_roots): Likewise. +* misc.c (GC_write): Likewise. +* os_dep.c (GC_print_callers): Likewise. +* dyn_load.c (GC_register_dynamic_libraries): Rename "i" local +variable to "j" for the nested loop (just not to hide the similar +variable in the outer one). +* mark_rts.c (GC_print_static_roots): Output an error message +using GC_err_printf() (instead of GC_printf()). + +* configure.ac: Move include flag from ${INCLUDE} ... +* Makefile.am: ... to AM_CPPFLAGS and also add the build directory. +* configure.ac: Call AM_CONFIG_HEADER([include/private/config.h]). +* configure.ac: Add documentation to all AC_DEFINE either directly +or using AH_TEMPLATE. + +* win32_threads.c (GC_waitForSingleObjectInfinite): New static +function (only if GC_WINMAIN_REDIRECT). +* win32_threads.c (WinMain): Call GC_waitForSingleObjectInfinite() +thru GC_do_blocking() instead of calling WaitForSingleObject() +directly. + +* pthread_support.c (start_mark_threads): Refine printed message. +* win32_threads.c (GC_thr_init): Likewise. + +* Makefile.direct (GC_WINMAIN_REDIRECT): Add the comment for. +* Makefile.direct (NO_GETENV): Update the comment. +* include/gc.h (GC_WINMAIN_WINCE_LPTSTR): Remove macro. +* include/gc.h (GC_WinMain): Remove declaration. +* include/gc.h (WinMain): Define (as GC_WinMain) if and only if +GC_WINMAIN_REDIRECT. +* tests/test.c (GC_COND_INIT): Define as GC_INIT() also in case of +WinCE target unless GC_WINMAIN_REDIRECT is defined. +* tests/test.c (WINMAIN_LPTSTR): New macro. +* tests/test.c (WinMain): Use WINMAIN_LPTSTR instead of LP[W]STR +and GC_WINMAIN_WINCE_LPTSTR. +* win32_threads.c (start_mark_threads): Add the comment for +MARK_THREAD_STACK_SIZE. +* win32_threads.c: Recognize new GC_WINMAIN_REDIRECT macro. +* win32_threads.c (WINMAIN_LPTSTR, WINMAIN_THREAD_STACK_SIZE): New +macro (only if GC_WINMAIN_REDIRECT). +* win32_threads.c: Undefine WinMain macro if GC_WINMAIN_REDIRECT. +* win32_threads.c (GC_WinMain): Add prototype (only if +GC_WINMAIN_REDIRECT). +* win32_threads.c (main_thread_args, WinMain): Rename +GC_WINMAIN_WINCE_LPTSTR to WINMAIN_LPTSTR. +* win32_threads.c (WinMain): Call GC_INIT() instead of GC_init(); +use WINMAIN_THREAD_STACK_SIZE. +* win32_threads.c (WinMain): Call GC_deinit() and +DeleteCriticalSection() only if WinCE; add FIXME. + +* os_dep.c (GC_get_main_stack_base): add assertion for mem_base +value returned by GC_get_stack_base(). + +* Makefile.direct (MUNMAP_THRESHOLD, GC_FORCE_UNMAP_ON_GCOLLECT): +Add the comment for. +* alloc.c (GC_unmap_threshold, GC_force_unmap_on_gcollect): +Declare external variable (only if USE_MUNMAP). +* alloc.c (GC_try_to_collect): Temporarily set GC_unmap_threshold +value to 1 if GC_force_unmap_on_gcollect and restore it before +unlocking (only if USE_MUNMAP). +* doc/README.environment (GC_FORCE_UNMAP_ON_GCOLLECT): Add +information for. +* include/gc.h (GC_set_force_unmap_on_gcollect, +GC_get_force_unmap_on_gcollect): New public function prototype. +* include/gc.h (GC_FORCE_UNMAP_ON_GCOLLECT): New macro is +recognized. +* misc.c (GC_FORCE_UNMAP_ON_GCOLLECT): Likewise. +* include/gc.h (GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT): New +internal macro (used by GC_INIT only). +* misc.c (GC_force_unmap_on_gcollect): New global variable. +* misc.c (GC_init): Recognize new "GC_FORCE_UNMAP_ON_GCOLLECT" +environment variable (and set GC_force_unmap_on_gcollect). +* misc.c (GC_set_force_unmap_on_gcollect, +GC_get_force_unmap_on_gcollect): New public function. +* tests/test.c (window_proc): Call GC_set_force_unmap_on_gcollect +to force the mode on if WM_HIBERNATE; restore the mode after +GC_gcollect(). + +* Makefile.direct (LARGE_CONFIG): Update information. +* include/gc.h (GC_stop_func): Refine the comment. + +* configure.ac: Use EMPTY_GETENV_RESULTS instead of NO_GETENV for +Win32 (workaround for Wine bug). + +* allchblk.c (GC_freehblk): Adjust local variables indentation. +* mallocx.c (GC_generic_malloc_many): Likewise. +* typd_mlc.c (GC_malloc_explicitly_typed_ignore_off_page, +GC_calloc_explicitly_typed): Likewise. +* typd_mlc.c (GC_make_array_descriptor): Remove unnecessary +brackets. + +* configure.ac: Replace GC_WIN32_THREADS with GC_THREADS. +* configure.ac: Process enable_parallel_mark option for Cygwin and +Win32; define THREAD_LOCAL_ALLOC for Win32. + +* include/private/gc_priv.h: Define AO_ASSUME_WINDOWS98 if +PARALLEL_MARK (required for VC++ x86). + +* dbg_mlc.c (GC_generate_random_backtrace): Call +GC_try_to_collect(GC_never_stop_func) instead of GC_gcollect(); +if GC is disabled then print error message and return. +* include/gc.h (GC_try_to_collect): Refine the comment. +* include/private/gc_priv.h (GC_never_stop_func): Fix return type; +refine the comment. + +* add_gc_prefix.c: Move the file to the new "extra" directory. +* AmigaOS.c: Likewise. +* gcname.c: Likewise. +* if_mach.c: Likewise. +* if_not_there.c: Likewise. +* MacOS.c: Likewise. +* msvc_dbg.c: Likewise. +* setjmp_t.c: Likewise. +* threadlibs.c: Likewise. +* EMX_MAKEFILE: Prepend setjmp_t.c with "extra" directory. +* Makefile: Prepend AmigaOS.c, MacOS.c, add_gc_prefix.c, gcname.c, +if_mach.c, if_not_there.c, msvc_dbg.c, setjmp_t.c, threadlibs.c +with "extra" directory. +* Makefile.am: Likewise. +* Makefile.direct: Likewise. +* Makefile.dj: Likewise. +* Makefile.in: Likewise. +* NT_MAKEFILE: Prepend msvc_dbg.obj with "extra" directory. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* NT_THREADS_MAKEFILE: Prepend msvc_dbg.c with "extra" directory. +* gc.mak: Likewise. +* PCR-Makefile: Prepend if_mach.c, if_not_there.c with "extra" +directory. +* SMakefile.amiga: Prepend AmigaOS.c, setjmp_t.c with "extra" +directory. +* doc/simple_example.html: Update for threadlibs.c. +* os_dep.c: Prepend included AmigaOS.c with "extra" directory. + +* include/gc.h (GC_do_blocking, GC_call_with_gc_active): New +function prototype. +* include/private/gc_priv.h (STOP_WORLD): Replace a no-op (for the +single-threaded case) with an assertion check for the state to be +not a "do-blocking" one. +* include/private/gc_priv.h (blocking_data): Move the structure +definition from pthread_support.c; change "fn" return type to void +pointer. +* include/private/gc_priv.h (GC_activation_frame_s): New structure +type. +* include/private/gc_priv.h (GC_push_all_stack_frames): New +function declaration (only if THREADS). +* include/private/gc_priv.h (GC_world_stopped): Don't declare +unless THREADS. +* include/private/gc_priv.h (GC_blocked_sp, +GC_activation_frame_s): New declaration (only if not THREADS). +* include/private/gc_priv.h (GC_push_all_register_frames): New +function declaration (only for IA-64). +* include/private/gc_priv.h (NURSERY, GC_push_proc): Remove +obsolete (unused) symbols. +* include/private/gc_priv.h (GC_push_all_stack_partially_eager): +Remove declaration (since it is static now). +* mark_rts.c (GC_push_all_stack_partially_eager): Move from mark.c +(for code locality) and make STATIC. +* mark_rts.c (GC_push_all_register_frames): New function (only for +IA-64). +* mark_rts.c (GC_push_all_stack_frames): New function (only if +THREADS). +* mark_rts.c (GC_add_trace_entry): New function prototype (used by +GC_push_all_stack_partially_eager(), only if TRACE_BUF). +* mark_rts.c (GC_push_all_stack_part_eager_frames): New function. +* mar_rts.c (GC_save_regs_ret_val): Move the declaration out of a +function body (only for IA-64). +* mark_rts.c (GC_push_current_stack): Call +GC_push_all_stack_part_eager_frames() instead of +GC_push_all_stack_partially_eager(). +* mark_rts.c (GC_push_current_stack): Call +GC_push_all_register_frames() instead of GC_push_all_eager() for +IA-64 backing store. +* misc.c (GC_do_blocking_inner): Declare function (if THREADS +only). +* misc.c (GC_blocked_sp, GC_blocked_register_sp, +GC_activation_frame): New global variables (only if not THREADS). +* misc.c (GC_call_with_gc_active, GC_do_blocking_inner): New API +function (only if not THREADS). +* misc.c (GC_do_blocking): Move the function from +pthread_support.c. +* include/private/pthread_support.h (GC_Thread_Rep): Add +"activation_frame" field. +* pthread_stop_world.c (GC_push_all_stacks): Call +GC_push_all_stack_frames() and GC_push_all_register_frames instead +of GC_push_all_stack() and/or GC_push_all_eager(); don't check for +STACK_GROWS_UP here. +* pthread_support.c (GC_do_blocking_inner): Remove "static"; store +"fn" result back to "client_data" field. +* pthread_support.c (GC_call_with_gc_active): New API function. +* win32_threads.c (GC_call_with_gc_active): Likewise. +* win32_threads.c (GC_Thread_Rep): Add "thread_blocked_sp" and +"activation_frame" fields. +* win32_threads.c (GC_new_thread): Add assertion checking for +thread_blocked_sp is NULL. +* win32_threads.c (GC_do_blocking_inner): New function. +* win32_threads.c (GC_stop_world): Don't suspend a thread if its +thread_blocked_sp is non-NULL. +* win32_threads.c (GC_push_stack_for): Use thread +"activation_frame" (if non-NULL); use "thread_blocked_sp" if +non-NULL (instead of calling GetThreadContext()); "UNPROTECT" the +thread before modifying its last_stack_min; call +GC_push_all_stack_frames() instead of GC_push_all_stack(); update +the comments. + +* alloc.c (GC_default_stop_func): New static variable (initialized +to GC_never_stop_func). +* alloc.c (GC_set_stop_func, GC_get_stop_func): New function. +* alloc.c (GC_timeout_stop_func): Define as GC_default_stop_func +(instead of GC_never_stop_func) if SMALL_CONFIG (or NO_CLOCK), +else call GC_default_stop_func() before getting "current_time". +* alloc.c (GC_maybe_gc): Expand GC_gcollect_inner() macro (for +FIXME comment). +* alloc.c (GC_maybe_gc, GC_collect_a_little_inner): add FIXME for +replacing GC_never_stop_func with GC_default_stop_func (if +possible). +* alloc.c (GC_gcollect): Use GC_default_stop_func. +* alloc.c (GC_collect_or_expand): Use GC_default_stop_func +(instead of GC_never_stop_func) unless it is triggered due to out of +memory; don't increment GC_fail_count and don't output warning +(before trying to collect again) in case the collection has been +interrupted (by GC_default_stop_func) and the heap expansion has +failed too. +* include/gc.h (GC_set_stop_func, GC_get_stop_func): New function +prototypes. + +* os_dep.c (GC_get_stack_base): Add FIXME; add assertion for +GC_get_writable_length() result. + +* configure.ac: Don't use -lpthread -ldl for Cygwin. + +* NT_THREADS_MAKEFILE: Make it back equal to gc.mak. + +* include/private/gcconfig.h (GWW_VDB): Undefine if +USE_GLOBAL_ALLOC (since incompatible). +* os_dep.c (GetWriteWatch_alloc_flag): Define as 0 unless GWW_VDB +is defined. +* os_dep.c (GC_unmap_threshold): Declare (for use in +GC_init_win32) if USE_MUNMAP. +* os_dep.c (GC_init_win32): Turn off memory unmapping if +GlobalAlloc() is used. +* os_dep.c (GC_win32_get_mem): Define and use new +VIRTUAL_ALLOC_PAD macro; don't waste an extra memory page unless +MPROTECT_VDB is in use. + +* Makefile: Replace "version.h" with "include/gc_version.h". +* include/gc_version.h: Likewise. + +* alloc.c (GC_collect_or_expand): Output heap size in WARN() +(before returning FALSE) for convenience. + +* allchblk.c (GC_allochblk_nth): Use GC_PRIdPTR in WARN() format +string. +* pthread_support.c (start_mark_threads, GC_thr_init): Likewise. +* win32_threads.c (GC_delete_thread): Likewise. +* include/private/gc_priv.h (GC_PRIdPTR): New macro. +* pthread_stop_world.c (GC_suspend_handler_inner): Remove +unnecessary cast for WARN argument. +* pthread_support.c (start_mark_threads): if pthread_create() +failed then don't try to create other marker threads and (after +printing a warning) adjust GC_markers and GC_parallel values; log +GC_markers value (possibly adjusted) after that. + +* win32_threads.c (start_mark_threads): if pthread_create() is +failed then don't try to create other marker threads and (after +printing a warning) adjust GC_markers and GC_parallel values. +* win32_threads.c (mark_mutex_event, builder_cv, mark_cv): Move +the definition upper (to be visible in start_mark_threads()). +* win32_threads.c (start_mark_threads): if CreateThread() or +_beginthreadex() is failed then don't try to create other marker +threads and (after printing a warning) adjust GC_markers, +GC_parallel values, and destroy the event objects (either only +some for the uncreated threads if DONT_USE_SIGNALANDWAIT or all if +not a single thread is created). +* win32_threads.c (GC_thr_init): Log GC_markers value (possibly +adjusted) after start_mark_threads() call. + +* Makefile.am: Back remove "GC_" prefix for PTHREADS, +DARWIN_THREADS, WIN32_THREADS (for configure.ac). + +* include/private/gc_priv.h: Change include of config.h to +private/config.h. +* include/private/gc_pmark.h: Likewise. +* gc_cpp.cc: Likewise. +* tests/test.c: Likewise. +* tests/test_cpp.cc: Include private/config.h (if HAVE_CONFIG_H); +undefine GC_BUILD. + +* finalize.c (GC_general_register_disappearing_link): Return +GC_SUCCESS, GC_DUPLICATE, GC_NO_MEMORY (instead of 0, 1 and 2, +respectively). +* include/gc.h (GC_NO_MEMORY): New macro (defined as 2). +* include/gc.h (GC_register_disappearing_link, +GC_general_register_disappearing_link): Update the comment. +* typd_mlc.c (GC_calloc_explicitly_typed): Use GC_NO_MEMORY macro. +* finalize.c (GC_general_register_disappearing_link, +GC_register_finalizer_inner): Recalculate the hash table index +after GC_oom_fn succeeded (since the table may grow while not +holding the lock) and check again that the entry is still not in +the table (free the unused entry otherwise unless DBG_HDRS_ALL). +* finalize.c (GC_register_finalizer_inner): Initialize "hhdr" +local variable (to prevent a compiler warning). +* finalize.c (GC_register_finalizer_inner): Don't modify the data +pointed by "ocd" and "ofn" in GC_register_finalizer_inner() failed +(due to out of memory). + +* alloc.c (GC_set_fl_marks, GC_clear_fl_marks): Transform loop to +suppress compiler "variable might be uninitialized" warnings. + +* Makefile.direct (DONT_USE_SIGNALANDWAIT): Add the comment for. +* win32_threads.c (DONT_USE_SIGNALANDWAIT): Always define for +WinCE. +* win32_threads.c (THREAD_HANDLE): Cast Id (of DWORD type) to +HANDLE thru word type (to avoid a compiler warning) for WinCE. +* win32_threads.c (GC_marker_cv, GC_marker_Id): New static array +(only if DONT_USE_SIGNALANDWAIT). +* win32_threads.c (start_mark_threads): Initialize GC_marker_Id +and GC_marker_cv for each helper thread (only if +DONT_USE_SIGNALANDWAIT). +* win32_threads.c (GC_mark_mutex_state): New static variable (only +if DONT_USE_SIGNALANDWAIT). +* win32_threads.c (GC_mark_mutex_waitcnt, +signalObjectAndWait_func): Don't define if DONT_USE_SIGNALANDWAIT. +* win32_threads.c (GC_acquire_mark_lock, GC_release_mark_lock): +Use InterlockedExchange() over GC_mark_mutex_state (instead of +AO_fetch_and_add()) if DONT_USE_SIGNALANDWAIT. +* win32_threads.c (GC_wait_marker, GC_notify_all_marker): +Implement wait/broadcast primitives using Win32 multiple events +(one for each marker thread) if DONT_USE_SIGNALANDWAIT (instead of +using Win32 SignalObjectAndWait). +* win32_threads.c (GC_thr_init): Don't declare hK32 local +variable, don't check for GC_wnt, and don't initialize +signalObjectAndWait_func if DONT_USE_SIGNALANDWAIT. + +* alloc.c (GC_finish_collection): Call GC_print_finalization_stats +if GC_print_stats (after getting "done_time"). +* finalize.c (GC_old_dl_entries): New static variable (only if not +SMALL_CONFIG). +* finalize.c (GC_finalize): Save current GC_dl_entries value (only +if not SMALL_CONFIG). +* finalize.c (GC_print_finalization_stats): Define if and only if +not SMALL_CONFIG; use GC_old_dl_entries value; use GC_log_printf() +instead of GC_printf(); use "%lu" (instead of "%u") print format +specifier; use unsigned long type for "ready" counter (for LP64 +targets). +* misc.c (GC_dump): No longer call GC_print_finalization_stats() +here (since it is called from GC_finish_collection()). +* misc.c (STACKBASE): Remove unused macro undef (for NOSYS and +ECOS). + +* alloc.c (GC_expand_hp): Replace GC_init_inner() call with +GC_init() one. +* malloc.c (GC_alloc_large, GC_generic_malloc_inner): Likewise. +* mallocx.c (GC_generic_malloc_many): Likewise. +* misc.c (GC_enable_incremental): Likewise. +* alloc.c (GC_expand_hp): Update the comment. +* mark.c (GC_obj_kinds): Likewise. +* win32_threads.c (GC_allow_register_threads): Likewise. +* private/gc_priv.h (GC_init_inner): Remove function declaration. +* misc.c (GC_init_inner): Replace with public GC_init(). + +* gcj_mlc.c (GC_gcj_fake_mark_proc): New static function. +* gcj_mlc.c (GC_init_gcj_malloc): If mp is 0 then supply +GC_gcj_fake_mark_proc (aborting with the appropriate message) +instead. + +* os_dep.c (GC_wince_get_mem): If VirtualAlloc() returns NULL (due +to out of memory) then don't increment GC_n_heap_bases and don't +call VirtualAlloc() again (with MEM_COMMIT). +* os_dep.c (GC_remap): Abort with a more informatory message if +VirtualAlloc() fails due to out of memory; update FIXME. + +* Makefile: Fix typo for msvc_dbg.c. +* Makefile.direct: Likewise. +* Makefile.am: Prefix PTHREADS, DARWIN_THREADS, WIN32_THREADS with +"GC_". +* Makefile.dj: Don't reference remove files (nursery.c, +gc_nursery.h, gc_copy_descr.h). +* NT_MAKEFILE: Don't define __STDC__ macro (no longer used). +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* gc.mak: Likewise. +* NT_MAKEFILE: Remove unnecessary -DGC_BUILD (since it is always +defined in the source files). +* NT_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* gc.mak: Likewise. +* NT_X64_THREADS_MAKEFILE: Fix typo for -DGC_NOT_DLL. +* NT_STATIC_THREADS_MAKEFILE: Replace GC_WIN32_THREADS with +GC_THREADS. +* NT_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_THREADS_MAKEFILE: Likewise. +* gc.mak: Likewise. +* NT_MAKEFILE: Define _CRT_SECURE_NO_DEPRECATE to suppress the +compiler warnings. +* NT_STATIC_THREADS_MAKEFILE: Likewise. +* NT_X64_STATIC_THREADS_MAKEFILE: Place -D_CRT_SECURE_NO_DEPRECATE +before "$*.C" (and "$*.CPP"). +* NT_X64_THREADS_MAKEFILE: Likewise. + +* doc/README.solaris2: Replace GC_SOLARIS_THREADS with GC_THREADS. +* doc/README.win32: Replace GC_WIN32_THREADS with GC_THREADS. +* doc/README.win64: Add info about mingw-w64; add note for VC++ +warnings suppression. + +* os_dep.c (GC_forward_exception): Fix logic in several places. +(OSX-specific) + +* include/private/gc_priv.h (MAX_HEAP_SECTS): Guard with ifndef. + +* Makefile.direct: Copy missing information for -DSHORT_DBG_HDRS +from Makefile. +* Makefile: Remove the information about "define arguments" (which +is incomplete and outdated compared to that in Makefile.direct); +add help reference to Makefile.direct. +* Makefile.dj: Likewise. + +* alloc.c (world_stopped_total_time, world_stopped_total_divisor): +Replace "STATIC" with "static" in the definition (since the +symbols aren't prefixed with "GC_"). +* win32_threads.c (marker_sp, marker_bsp, marker_last_stack_min, +start_mark_threads, mark_mutex, builder_cv, mark_cv, +mark_mutex_event, signalObjectAndWait_func, main_thread_start): +Likewise. +* pthread_support.c (GC_wait_builder): Define as STATIC. +* win32_threads.c (GC_wait_builder): Likewise. + +* misc.c (GC_get_heap_size_inner, GC_get_free_bytes_inner): New +API function. +* include/gc_pmark.h (GC_get_heap_size_inner, +GC_get_free_bytes_inner): New function declaration. + +* include/gc.h: Recognize __CEGCC__ (as a synonym for _WIN32_WCE). +* include/gc_config_macros.h: Likewise. +* include/gc.h (GC_MAXIMUM_HEAP_SIZE): Recognize new macro. +* include/gc.h (GC_INIT_CONF_MAXIMUM_HEAP_SIZE): New macro (for +internal use). +* include/gc_config_macros.h: Always include stddef.h if GCC. +* include/gc_config_macros.h (GC_API): Define for CeGCC in the +same way as for MinGW. +* include/gc_config_macros.h (GC_API): Group the definition for +all cases together (check for GC_DLL only once). +* include/gc_pthread_redirects.h: Group non-Darwin code together. +* tests/test.c: Recognize GC_PRINT_VERBOSE_STATS (only if GC_DLL). + +* Makefile.direct (GC_PTHREADS_PARAMARK, GC_IGNORE_GCJ_INFO, +GC_PRINT_VERBOSE_STATS, GC_DONT_EXPAND, GC_INITIAL_HEAP_SIZE, +GC_FREE_SPACE_DIVISOR, GC_TIME_LIMIT, GC_FULL_FREQ): Add the +comment for. +* misc.c (GC_init_inner): Recognize GC_PRINT_VERBOSE_STATS (new +macro). +* dyn_load.c (GC_wnt): Change definition to TRUE for WinCE; add +FIXME and the comment for WinCE. +* gcj_mlc.c (GC_init_gcj_malloc): Recognize GC_IGNORE_GCJ_INFO +(new macro). +* include/gc.h (GC_HAVE_BUILTIN_BACKTRACE): Don't define for VC++ +WinCE (since backtrace() is unimplemented). +* include/private/gc_priv.h (GC_n_heap_bases): Remove declaration +(since static). +* os_dep.c (GC_n_heap_bases): Define as STATIC; move the +definition to be above GC_is_heap_base(). +* include/private/gcconfig.h: Don't define NOSYS for WinCE on ARM +(both for MinGW and CeGCC toolchains). +* include/private/gcconfig.h: Recognize __CEGCC__ and +__MINGW32CE__ (as synonyms for __WIN32_WCE). +* include/private/gcconfig.h: If SH4 then don't set config +parameters for SH. +* include/private/thread_local_alloc.h (GC_key_create): Don't +abort on failures, just return -1 in these cases (this also +prevents compilation error for targets where ABORT is defined +indirectly as an inline assembler sequence). +* mark.c (WRAP_MARK_SOME): Also define for WinCE; add FIXME for +the GCC-based cross-compiler. +* mark.c (ext_ex_regn, mark_ex_handler): Don't define unless +WRAP_MARK_SOME is defined; define also for WinCE case; don't +check for _WIN64 (since WRAP_MARK_SOME is undefined for it). +* mark.c (GC_mark_some): Use __try/__except also for WinCE; update +the comment. +* misc.c: Include signal.h after gc_pmark.h included; check for +MSWINCE instead of _WIN32_WCE. +* misc.c (GC_init_inner): Remove duplicate GC_setpagesize() call. +* misc.c: Don't include for WinCE targets. +* misc.c (GC_write): Define _MAX_PATH if undefined (workaround for +CeGCC toolchain). +* misc.c (GC_write): Use OutputDebugStringW() instead of +_CrtDbgReport() for WinCE targets. +* os_dep.c (GC_least_described_address): Define as STATIC. +* os_dep.c (GC_register_data_segments): Fix code indentation. +* os_dep.c (GC_wince_get_mem): Initialize "result" local variable +(to prevent a compiler warning). +* os_dep.c (GC_dirty_init): Add comment for WinCE target. +* tests/test.c: Don't include winbase.h directly if GCC for WinCE, +include assert.h instead. +* tests/test.c (tiny_reverse_test): Define and use +TINY_REVERSE_UPPER_VALUE macro (4 if VERY_SMALL_CONFIG else 10); +useful for WinCE. +* win32_threads.c (GC_Thread_Rep): Don't declare "handle" field +for WinCE (since thread Id is used as a "real" thread handle). +* win32_threads.c (THREAD_HANDLE): New macro. +* win32_threads.c (GC_register_my_thread_inner): Don't recognize +DONT_IMPORT_GETCURTHREAD anymore; don't record thread handle on +WinCE. +* Makefile.direct (DONT_IMPORT_GETCURTHREAD): Remove comment for. +* win32_threads.c (UNPROTECT, GC_fault_handler_lock): Don't check +for MSWINCE. +* win32_threads.c (GC_delete_gc_thread, GC_delete_thread): Don't +close thread handle on WinCE (since it's a thread Id). +* win32_threads.c (GC_suspend): Don't check for MSWINCE in the +MPROTECT-related code (for the case if MPROTECT_VDB would be +implemented for WinCE). +* win32_threads.c (GC_suspend, GC_start_world, GC_push_stack_for): +Use THREAD_HANDLE(t) to obtain thread handle. +* win32_threads.c (GC_PTHREADS_PARAMARK): New macro recognized; +implicitly define GC_PTHREADS_PARAMARK if GC_PTHREADS; include +pthread.h; define NUMERIC_THREAD_ID(id) if undefined yet; replace +GC_PTHREADS with GC_PTHREADS_PARAMARK where appropriate (for the +parallel mark support). +* win32_threads.c (start_mark_threads): Use int type for "i" local +variable (instead of "unsigned") to prevent a compiler warning. +* win32_threads.c (start_mark_threads): Don't check CreateThread() +result for -1; call CloseHandle() for the handle created by +CreateThread() (on WinCE); don't use errno (since errno.h is +missing on some targets like WinCE) when printing warning on a +marker thread creation failure. +* win32_threads.c (signalObjectAndWait_func): Define for WinCE. +* win32_threads.c (GC_wait_marker): Remove unnecessary assertion +for non-zero signalObjectAndWait_func (to make the code compilable +for WinCE). +* win32_threads.c (GC_thr_init): Allow PARALLEL_MARK for WinCE; +use GC_sysinfo to get processors count if WinCE; don't check for +SignalObjectAndWait() if WinCE; replace GC_PTHREADS with +GC_PTHREADS_PARAMARK. +* win32_threads.c (GC_thr_init): Recognize GC_MIN_MARKERS new +macro (useful for testing parallel marking on WinCE). +* win32_threads.c (GC_win32_start, main_thread_start): Define as +STATIC. +* win32_threads.c: Don't define main_thread_args, +main_thread_start(), WinMain() for WinCE if GC_DLL. +* win32_threads.c (WINCE_MAIN_STACK_SIZE): Remove useless macro +(since the stack size parameter is ignored on WinCE). +* win32_threads.c (main_thread_start): Remove forward declaration; +place its definition before WinMain() one. +* win32_threads.c (WinMain): Abort if GC_CreateThread() or +WaitForSingleObject() failed (for the main thread). + +* allchblk.c (MUNMAP_THRESHOLD): Move macro definition out of +a function. +* allchblk.c (GC_unmap_threshold): New global variable definition +(initialized to MUNMAP_THRESHOLD). +* allchblk.c (GC_unmap_old): Use GC_unmap_threshold instead of +MUNMAP_THRESHOLD; skip unmapping if GC_unmap_threshold is 0. +* doc/README.environment (GC_UNMAP_THRESHOLD): Add information. +* misc.c (GC_unmap_threshold): New variable declaration. +* misc.c (GC_init_inner): Recognize "GC_UNMAP_THRESHOLD" +environment variable to set GC_unmap_threshold value (only if +USE_MUNMAP). + +* dbg_mlc.c (OFN_UNSET): New macro (to detect +GC_register_finalizer() failures). +* dbg_mlc.c (store_old): Add a check for register_finalizer() +failure caused by an out-of-memory event (leave *ofn and *ocd +unmodified in that case). +* dbg_mlc.c (GC_debug_register_finalizer, +GC_debug_register_finalizer_no_order, +GC_debug_register_finalizer_unreachable, +GC_debug_register_finalizer_ignore_self): Initialize my_old_fn +to OFN_UNSET; clear *ocd and *ofn for non-heap objects (the same +as in GC_register_finalizer_inner()). + +* Makefile.direct (GC_DLL): Add the comment for. +* doc/README.macros: Fix a typo. +* doc/README.macros (_DLL, GC_DLL, GC_NOT_DLL): Update info. +* doc/README.macros (__STDC__): Remove info. +* dbg_mlc.c (GC_get_back_ptr_info, GC_generate_random_heap_address, +GC_generate_random_valid_address, GC_print_backtrace, +GC_generate_random_backtrace, GC_register_describe_type_fn): Add +GC_API and GC_CALL to function definition. +* malloc.c (GC_generic_malloc): Likewise. +* mallocx.c (GC_incr_bytes_allocd, GC_incr_bytes_freed): Likewise. +* mark.c (GC_mark_and_push): Likewise. +* misc.c (GC_new_free_list_inner, GC_new_free_list, +GC_new_kind_inner, GC_new_kind, GC_new_proc_inner, GC_new_proc): +Likewise. +* include/gc_backptr.h (GC_get_back_ptr_info, +GC_generate_random_heap_address, GC_generate_random_valid_address, +GC_generate_random_backtrace, GC_print_backtrace): Add GC_API and +GC_CALL to function prototype. +* include/gc_mark.h (GC_mark_and_push, GC_new_free_list, +GC_new_free_list_inner, GC_new_kind, GC_new_kind_inner, +GC_new_proc, GC_new_proc_inner, GC_generic_malloc, +GC_register_describe_type_fn): Likewise. +* include/new_gc_alloc.h (GC_incr_bytes_allocd, GC_incr_mem_freed, +GC_generic_malloc_words_small): Likewise. +* gc_cpp.cc: Include "config.h" (if HAVE_CONFIG_H defined). +* include/private/gc_pmark.h: Likewise. +* include/private/gc_priv.h: Likewise. +* tests/test.c: Likewise. +* gc_cpp.cc: Define GC_BUILD. +* include/private/gc_pmark.h: Likewise. +* include/private/gc_priv.h: Likewise. +* gc_dlopen.c (WRAP_FUNC, REAL_FUNC): New macro. +* gc_dlopen.c (dlopen): Add GC_API to the wrapper function +definition. +* pthread_support.c (GC_pthread_create, GC_pthread_sigmask, +GC_pthread_join, GC_pthread_detach, pthread_sigmask, pthread_join, +pthread_detach, pthread_create): Likewise. +* win32_threads.c (GC_pthread_join, GC_pthread_create, +GC_pthread_sigmask, GC_pthread_detach): Likewise. +* gc_dlopen.c (dlopen): Use WRAP_FUNC and REAL_FUNC macros. +* include/gc_backptr.h: Include "gc.h". +* include/gc_backptr.h: Use extern "C" for the exported functions. +* include/gc_mark.h: Likewise. +* include/gc_config_macros.h (GC_THREADS): Define the macro if any +GC_XXX_THREADS is defined. +* include/gc_config_macros.h (_PTHREADS, _POSIX4A_DRAFT10_SOURCE): +Move the definitions below the place where GC_NETBSD_THREADS and +GC_DGUX386_THREADS are defined. +* include/gc_config_macros.h (GC_DLL): Don't define (even if _DLL +is defined) for GCC. +* include/gc_config_macros.h (GC_API): Define for Cygwin (in the +same way as for VC++); define for GCC v4+ (other than already +recognized MinGW/Cygwin) as a "default" visibility attribute if +GC_DLL is defined. +* include/gc_config_macros.h (GC_ATTR_MALLOC, GC_ATTR_ALLOC_SIZE): +New macro. +* include/gc.h (GC_malloc, GC_malloc_atomic, GC_strdup, +GC_malloc_uncollectable, GC_malloc_stubborn, GC_memalign, +GC_malloc_atomic_uncollectable, GC_malloc_ignore_off_page, +GC_malloc_atomic_ignore_off_page, GC_debug_malloc, +GC_debug_malloc_atomic, GC_debug_strdup, +GC_debug_malloc_uncollectable, GC_debug_malloc_stubborn, +GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, +GC_debug_malloc_replacement): Add GC_ATTR_MALLOC attribute. +* include/gc_gcj.h (GC_gcj_malloc, GC_debug_gcj_malloc, +GC_gcj_malloc_ignore_off_page): Likewise. +* include/gc.h (GC_malloc, GC_malloc_atomic, +GC_malloc_uncollectable, GC_malloc_stubborn, +GC_malloc_atomic_uncollectable, GC_malloc_ignore_off_page, +GC_malloc_atomic_ignore_off_page, GC_debug_malloc, +GC_debug_malloc_atomic, GC_debug_malloc_uncollectable, +GC_debug_malloc_stubborn, GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, +GC_debug_malloc_replacement: Add GC_ATTR_ALLOC_SIZE attribute +(for the first argument). +* include/gc_gcj.h (GC_gcj_malloc, GC_debug_gcj_malloc, +GC_gcj_malloc_ignore_off_page): Likewise. +* include/gc.h (GC_memalign, GC_realloc, GC_debug_realloc, +GC_debug_realloc_replacement): Add GC_ATTR_ALLOC_SIZE attribute +(for the second argument). +* include/gc.h (GC_malloc, GC_malloc_atomic, GC_strdup, +GC_malloc_uncollectable, GC_malloc_stubborn, GC_memalign, +GC_malloc_atomic_uncollectable, GC_free, GC_base, GC_size, +GC_realloc, GC_expand_hp, GC_set_max_heap_size, +GC_exclude_static_roots, GC_add_roots, GC_remove_roots, +GC_register_displacement, GC_debug_register_displacement, +GC_try_to_collect, GC_malloc_ignore_off_page, +GC_malloc_atomic_ignore_off_page, GC_debug_malloc, +GC_debug_malloc_atomic, GC_debug_strdup, +GC_debug_malloc_uncollectable, GC_debug_malloc_stubborn, +GC_debug_malloc_ignore_off_page, +GC_debug_malloc_atomic_ignore_off_page, GC_debug_free, +GC_debug_realloc, GC_debug_malloc_replacement, +GC_debug_realloc_replacement, GC_finalization_proc, +GC_register_finalizer, GC_debug_register_finalizer, +GC_register_finalizer_ignore_self, +GC_debug_register_finalizer_ignore_self, +GC_register_finalizer_no_order, +GC_debug_register_finalizer_no_order, +GC_register_finalizer_unreachable, +GC_debug_register_finalizer_unreachable, +GC_register_disappearing_link, +GC_general_register_disappearing_link, +GC_unregister_disappearing_link, GC_noop1, GC_warn_proc, +GC_set_warn_proc, GC_ignore_warn_proc, GC_fn_type, +GC_call_with_alloc_lock, GC_stack_base_func, +GC_call_with_stack_base, GC_same_obj, GC_pre_incr, GC_post_incr, +GC_is_visible, GC_is_valid_displacement, GC_same_obj_print_proc, +GC_is_valid_displacement_print_proc, GC_is_visible_print_proc, +GC_malloc_many, GC_CreateThread, GC_beginthreadex, +GC_endthreadex): Comment out (or remove if single and meaningless) +function argument names (to avoid identifiers out of the name +space). +* include/gc_gcj.h (GC_init_gcj_malloc, GC_gcj_malloc, +GC_debug_gcj_malloc, GC_gcj_malloc_ignore_off_page): Likewise. +* include/gc.h (GC_try_to_collect): Update the comment. +* include/gc.h (GC_size, GC_register_my_thread): Add const +qualifier for the argument referent. +* misc.c (GC_size): Likewise. +* pthread_support.c (GC_register_my_thread_inner, +GC_register_my_thread): Likewise. +* win32_threads.c (GC_register_my_thread_inner, +GC_register_my_thread): Likewise. +* include/gc.h (GC_INIT_CONF_ROOTS): New macro for internal use +(define instead of GC_INIT() for Cygwin and AIX). +* include/gc.h (GC_DONT_EXPAND, GC_MAX_RETRIES, +GC_FREE_SPACE_DIVISOR, GC_FULL_FREQ, GC_TIME_LIMIT, GC_IGNORE_WARN, +GC_INITIAL_HEAP_SIZE): Recognize new macro. +* include/gc.h (GC_INIT_CONF_DONT_EXPAND, GC_INIT_CONF_MAX_RETRIES, +GC_INIT_CONF_FREE_SPACE_DIVISOR, GC_INIT_CONF_FULL_FREQ, +GC_INIT_CONF_TIME_LIMIT, GC_INIT_CONF_IGNORE_WARN, +GC_INIT_CONF_INITIAL_HEAP_SIZE): New macro for internal use. +* include/gc.h (GC_INIT): Use GC_INIT_CONF_XXX macros. +* include/gc_mark.h: Prefix GC_H with '_'. +* include/gc_mark.h (GC_least_plausible_heap_addr, +GC_greatest_plausible_heap_addr, GC_debug_header_size): Use GC_API +for the public variable declaration. +* include/new_gc_alloc.h (GC_objfreelist_ptr, GC_aobjfreelist_ptr, +GC_uobjfreelist_ptr, GC_auobjfreelist_ptr): Likewise. +* include/gc_pthread_redirects.h (GC_pthread_create, +GC_pthread_sigmask, GC_dlopen, GC_pthread_join, GC_pthread_detach): +Use GC_API for the wrapper prototype. +* include/gc_pthread_redirects.h (pthread_create, pthread_join, +pthread_detach, pthread_sigmask, dlopen): Undefine unconditionally +before redirecting. +* include/new_gc_alloc.h: Replace GC_incr_mem_freed() with +GC_incr_bytes_freed(); remove FIXME. +* include/private/gc_priv.h (GC_make_closure, +GC_debug_invoke_finalizer, GC_noop): Remove GC_API for the private +function. +* tests/test.c (GC_print_stats): Handle GC_DLL case regardless of +the target. + +* finalize.c (GC_general_register_disappearing_link, +GC_register_finalizer_inner): Remove unnecessary "ifdef THREADS" +guard for LOCK/UNLOCK(). +* finalize.c (GC_general_register_disappearing_link, +GC_register_finalizer_inner): Get GC_oom_fn value before releasing +the lock (to prevent data races). +* gcj_mlc.c (GC_gcj_malloc, GC_debug_gcj_malloc, +GC_gcj_malloc_ignore_off_page): Likewise. +* mallocx.c (GC_generic_malloc_ignore_off_page): Likewise. +* include/gc_inline.h (GC_FAST_MALLOC_GRANS): Use GC_get_oom_fn() +instead of GC_oom_fn (to prevent data races). +* malloc.c (GC_generic_malloc): Likewise. +* mallocx.c (GC_memalign): Likewise. +* pthread_support.c (pthread_create): Likewise. +* gcj_mlc.c (maybe_finalize): Acquire the lock before setting +last_finalized_no value to prevent data races. +* include/gc.h (GC_gc_no, GC_get_gc_no, GC_oom_fn, GC_set_oom_fn, +GC_set_find_leak, GC_set_finalize_on_demand, +GC_set_java_finalization, GC_set_finalizer_notifier, +GC_set_dont_expand, GC_set_full_freq, GC_set_non_gc_bytes, +GC_set_no_dls, GC_set_free_space_divisor, GC_set_max_retries, +GC_set_dont_precollect, GC_set_time_limit, GC_warn_proc): Refine +the comment. +* misc.c (GC_set_oom_fn): Likewise. +* include/gc.h (GC_general_register_disappearing_link): Refine the +comment (replace "soft" word with "weak"). +* misc.c (GC_oom_fn, GC_get_gc_no, GC_get_parallel, +GC_set_finalizer_notifier, GC_set_find_leak): Add the comment. +* misc.c (GC_set_oom_fn, GC_get_oom_fn, GC_set_finalizer_notifier, +GC_get_finalizer_notifier): Use LOCK/UNLOCK to prevent data races. + +* dbg_mlc.c: Guard include with ifndef MSWINCE; include +"private/dbg_mlc.h" before it. +* malloc.c: Likewise. +* dbg_mlc.c (GC_debug_strdup): Use memcpy() instead of strcpy() +for WinCE (since deprecated); evaluate strlen() only once; don't +set errno for WinCE. +* malloc.c (GC_strdup): Likewise. +* dyn_load.c (GC_wnt): Define as macro (FALSE) for WinCE. +* include/gc.h (GC_unregister_my_thread): Refine the comment. +* include/gc.h (GC_uintptr_t, GC_beginthreadex, GC_endthreadex): +Don't declare for WinCE. +* include/gc.h (GC_WINMAIN_WINCE_LPTSTR): New macro (WinCE only). +* include/gc.h (GC_WinMain): Remove GC_API. +* include/gc.h (GC_WinMain): Use GC_WINMAIN_WINCE_LPTSTR for +lpCmdLine. +* tests/test.c (GC_WinMain): Likewise. +* win32_threads.c (main_thread_args, GC_WinMain): Likewise. +* include/gc_config_macros.h (ptrdiff_t): Guard with +ifndef _PTRDIFF_T_DEFINED; define _PTRDIFF_T_DEFINED macro. +* include/private/gc_locks.h: Guard include "atomic_ops.h" with +ifdef GC_PTHREADS (and not GC_WIN32_THREADS). +* mark.c: Include "atomic_ops.h" if PARALLEL_MARK. +* thread_local_alloc.c: Include "atomic_ops.h" if GC_GCJ_SUPPORT. +* win32_threads.c: Include "atomic_ops.h" if MPROTECT_VDB. +* include/private/gc_locks.h: Use include "atomic_ops.h" instead +of include . +* include/private/gc_priv.h: Likewise. +* include/private/gc_locks.h (GC_allocate_ml, GC_need_to_lock): +Don't export (replace GC_API to "extern"). +* win32_threads.c (GC_allocate_ml): Don't export. +* include/private/gc_priv.h (DebugBreak): Define as macro for +WinCE (if not UNDER_CE and DebugBreak is not defined yet). +* include/private/gc_priv.h (UNALIGNED): Rename to UNALIGNED_PTRS +(since "UNALIGNED" is defined in winnt.h of WinCE). +* mark.c (UNALIGNED): Likewise. +* include/private/gcconfig.h (ARM32): Recognize _M_ARM and _ARM_. +* include/private/gcconfig.h (ALIGNMENT): Check always defined. +* include/private/gcconfig.h: Allow GC_WIN32_THREADS for WinCE. +* include/private/thread_local_alloc.h: Define USE_WIN32_SPECIFIC +for WinCE (since __declspec(thread) is unsupported). +* include/private/thread_local_alloc.h (TLS_OUT_OF_INDEXES): +Define for WinCE (if undefined). +* malloc.c (GC_malloc): Remove outdated comment about disabling +signals. +* misc.c: Don't include (since not used anymore and may +break TEXT() macro defined in winnt.h). +* misc.c (GC_init_inner): Don't use GetModuleHandle() and +InitializeCriticalSectionAndSpinCount() for WinCE. +* misc.c (GC_init_inner): Replace GetModuleHandleA() with +GetModuleHandle() (and use TEXT() macro controlled by UNICODE). +* misc.c (LOG_FILE): Remove unused macro; don't use _T() macro. +* misc.c (GC_CreateLogFile): New static function (Win32/WinCE +only); move the code from GC_write(); replace GETENV() with +GetEnvironmentVariable(); replace CreateFileA() with +CreateFile(); use TEXT() macro (for Unicode support); replace +strcat() with memcpy() (since deprecated in WinCE). +* misc.c (GC_write): Define as STATIC. +* win32_threads.c (GC_attached_thread): Likewise. +* misc.c (GC_write): Use GC_CreateLogFile(). +* misc.c: Define vsnprintf macro as StringCchVPrintfA for WinCE. +* misc.c (GC_abort): Try to invoke MessageBoxA() dynamically +(Win32 only) if DONT_USE_USER32_DLL is defined. +* misc.c (GC_abort): Duplicate msg to GC log file (for Win32 and +WinCE). +* misc.c (GC_abort): Use a more user-friendly abort if +NO_DEBUGGING (Win32 only). +* os_dep.c: Include "atomic_ops.h" only if MPROTECT_VDB (and +THREADS). +* os_dep.c (detect_GetWriteWatch): Use TEXT() for GetModuleHandle +(for Unicode support); check GetModuleHandle() result. +* tests/test.c: Don't define assert for WinCE (since may be +redefined by "assert.h" included from libatomic_ops). +* tests/test.c (FAIL): Define as ABORT for all targets (except +for PCR). +* tests/test.c (n_tests): Don't use AO_t. +* tests/test.c (check_heap_stats): Don't cast n_tests. +* tests/test.c (inc_int_counter): New function (for n_tests atomic +incrementation). +* tests/test.c (run_one_test): Test GC_memalign() for all targets. +* tests/test.c (run_one_test): Avoid unbalanced brackets in +#if-#else-#endif blocks. +* tests/test.c (run_one_test): Replace AO_fetch_and_add1() and +private LOCK/UNLOCK with GC_call_with_alloc_lock(inc_int_counter). +* tests/test.c (check_heap_stats): Replace +"if (sizeof(char *) > 4)" with "#if CPP_WORDSZ == 64" to suppress +"unreachable code" compiler warning. +* tests/test.c (WinMain): Set cmd type to LPWSTR (for WinCE +"UNDER_CE" mode); else use LPSTR type (for Win32 and WinCE). +* tests/test.c (thr_window): Replace "L" string prefix with +TEXT(). +* thread_local_alloc.c: Check THREADS is defined (to prevent other +compiler errors and warnings otherwise). +* tests/test.c (WinMain): Recognize GC_NO_DLLMAIN macro (for +GC_use_DllMain()). +* Makefile.direct (GC_NO_DLLMAIN, DONT_IMPORT_GETCURTHREAD): Add +the comments for. +* win32_threads.c (GC_register_my_thread_inner): Recognize +DONT_IMPORT_GETCURTHREAD macro. +* win32_threads.c: Recognize GC_NO_DLLMAIN macro (to exclude +DllMain support if needed). +* win32_threads.c (GC_NO_DLLMAIN): Define implicitly if DllMain +thread registration is unsupported for a given configuration. +* win32_threads.c (GC_use_DllMain): Update the comment; refine +ABORT message. +* win32_threads.c (GC_use_DllMain, +GC_started_thread_while_stopped, GC_register_my_thread_inner, +GC_lookup_thread_inner, GC_delete_gc_thread, +GC_allow_register_threads, GC_lookup_pthread, +GC_push_thread_structures, GC_stop_world, GC_push_all_stacks): +Check for GC_NO_DLLMAIN. +* win32_threads.c (GC_Thread_Rep.tm_in_use, GC_attached_thread, +DllMain): Don't define if GC_NO_DLLMAIN. +* win32_threads.c (GC_stop_world): Declare "i" and "max" local +variables only if not GC_NO_DLLMAIN (to suppress compiler +warning). +* win32_threads.c (GC_mark_thread, start_mark_threads): Use +CreateThread() instead of _beginthreadex() for WinCE. +* win32_threads.c (MARK_THREAD_STACK_SIZE, WINCE_MAIN_STACK_SIZE): +New macros defined (used by start_mark_threads(), WinMain()). +* win32_threads.c (GC_thr_init): Exclude parallel-specific code on +WinCE for now (since getenv(), GetProcessAffinityMask() and +SignalObjectAndWait() are missing on WinCE). +* win32_threads.c (GC_thr_init): replace GetModuleHandleA() with +GetModuleHandle(); replace CreateEventA() with CreateEvent(); use +TEXT() macro (for Unicode support). + +* include/gc.h (GC_has_static_roots_func): New typedef (user filter +callback). +* include/gc.h (GC_register_has_static_roots_callback): Use +GC_has_static_roots_func type. +* dyn_load.c (GC_has_static_roots, +GC_register_has_static_roots_callback): Likewise. +* dyn_load.c (GC_has_static_roots, +GC_register_has_static_roots_callback): Define on all platforms. +* dyn_load.c (GC_register_dynlib_callback, +GC_register_dynamic_libraries, GC_init_dyld): Replace K&R-style +functions definition with the ANSI C one. +* dyn_load.c (GC_register_dynlib_callback): Use new local variable +"callback" (initialized from GC_has_static_roots) to minimize data +races. +* dyn_load.c (GC_register_dynamic_libraries_dl_iterate_phdr, +GC_cond_add_roots): Define as STATIC. +* mark_rts.c (GC_remove_roots_inner): Likewise. +* dyn_load.c (GC_dyld_image_add): Don't call GC_add_roots() for +sections smaller than pointer size (just to avoid acquiring the +lock unnecessarily). +* dyn_load.c (GC_dyld_name_for_hdr): Define unconditionally (not +only for DARWIN_DEBUG). +* dyn_load.c (GC_dyld_image_add): Replace GC_add_roots() call with +LOCK + GC_add_roots_inner() + UNLOCK. +* dyn_load.c (GC_dyld_image_add): Call GC_has_static_roots() user +callback (if set) holding the lock; if it returns 0 then don't call +GC_add_roots_inner() for that region. +* dyn_load.c (GC_register_has_static_roots_callback): Put +"callback" value to GC_has_static_roots on all platforms. +* dyn_load.c (GC_has_static_roots): Update the comments. +* include/gc.h (GC_exclude_static_roots, GC_add_roots, +GC_remove_roots, GC_register_has_static_roots_callback): Likewise. +* include/private/gc_priv.h (struct roots): Likewise. +* include/private/gc_priv.h (GC_remove_roots_inner): Move prototype +to mark_rts.c and declare it as STATIC. +* include/private/gc_priv.h (GC_exclude_static_roots_inner): New +prototype. +* dyn_load.c (GC_register_dynamic_libraries_dl_iterate_phdr): Use +GC_exclude_static_roots_inner() instead of GC_exclude_static_roots. +* misc.c (GC_init_inner): Likewise. +* mark_rts.c (GC_exclude_static_roots_inner): New function (move +all the code from GC_exclude_static_roots(); add the comment. +* mark_rts.c (GC_add_roots_inner, GC_exclude_static_roots_inner): +add alignment assertion for the lower bound; add assertion for the +lower bound to be less than the upper one. +* mark_rts.c (GC_add_roots_inner, GC_exclude_static_roots): Adjust +the upper bound (round down to be of a pointer-aligned value); +return in case of an empty range. +* mark_rts.c (GC_exclude_static_roots): Acquire the lock and call +GC_exclude_static_roots_inner(). +* mark_rts.c (GC_remove_roots): Quickly check the bounds and return +in case of a do-nothing case (before acquiring the lock). + +* finalize.c (GC_fail_count): New external variable declaration. +* finalize.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): New function declarations (if THREADS +only). +* finalize.c (GC_finalizer_nested, GC_finalizer_skipped): New +static global variables (used internally by GC_finalize() and +GC_check_finalizer_nested()). +* finalize.c (GC_check_finalizer_nested): New static function +definition (only if not THREADS, used internally by +GC_notify_or_invoke_finalizers() to minimize the probability of +a deep recursion when a client finalizer tries to allocate GC +memory). +* finalize.c (GC_finalize): Reset GC_finalizer_nested value (or +call GC_reset_finalizer_nested()) if last heap expansion failed. +* finalize.c (GC_notify_or_invoke_finalizers): Access GC_gc_no, +GC_finalizer_now, GC_finalize_on_demand, GC_finalizer_notifier, +last_finalizer_notification variables holding the lock (to avoid +data races). +* finalize.c (GC_finalizer_notifier): Add comment. +* finalize.c (GC_notify_or_invoke_finalizers): Add "quick" check +for an empty finalization queue (only if THREADS and not +KEEP_BACK_PTRS/MAKE_BACK_GRAPH). +* finalize.c (GC_notify_or_invoke_finalizers): Call +GC_check_finalizer_nested() and skip GC_invoke_finalizers() call +if appropriate. +* include/private/pthread_support.h (GC_Thread_Rep): Add unsigned +finalizer_nested and finalizer_skipped fields (for internal use +by the multi-threaded GC_check_finalizer_nested()). +* win32_threads.c (GC_Thread_Rep): Likewise. +* pthread_support.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): New function definitions (the +multi-threaded variants of that in finalize.c). +* win32_threads.c (GC_reset_finalizer_nested, +GC_check_finalizer_nested): Likewise. + +* alloc.c (GC_stopped_mark): Remove GC_log_printf("") (not needed +anymore and GCC produces a warning for it). +* alloc.c (GC_stopped_mark): Adjust printf argument type +specifier. +* backgraph.c: Include dbg_mlc.h before ifdef MAKE_BACK_GRAPH (for +the case when the configuration information comes from a config.h +file). +* checksums.c: Likewise. +* include/gc_allocator.h (GC_ATTR_UNUSED): Use "__unused__" +keyword instead of "unused". +* include/gc_allocator.h: Fix typos in comments. +* thread_local_alloc.c: Likewise. +* include/javaxfc.h (GC_finalize_all): Update comment. +* include/private/gc_priv.h (GC_API_PRIV): New macro (defined as +GC_API and serves only as a marker for the private but exported +symbols used by test.c only). +* include/private/gc_priv.h (GC_abort, GC_arrays, GC_is_marked, +GC_printf, GC_err_printf, GC_log_printf): Replace GC_API decl with +GC_API_PRIV one. +* include/private/gc_priv.h (GC_fo_entries): Don't export it +outside a DLL. +* include/private/gc_priv.h (GC_ATTR_FORMAT_PRINTF): New macro +designated to check the arguments correctness of printf-like +functions (currently works only for GCC v3+). +* include/private/gc_priv.h (GC_printf, GC_err_printf, +GC_log_printf): Use GC_ATTR_FORMAT_PRINTF attribute. + +* dyn_load.c (HAVE_DL_ITERATE_PHDR): Break definition from use. +Define for FreeBSD 7.0+. + +* mach_dep.c: Don't include ucontext.h with NO_GETCONTEXT. + +* include/gc_gcj.h (GC_init_gcj_malloc): Improve descriptive +comment. + +* allchblk.c (GC_merge_unmapped): Don't assume that adjacent +free blocks have different mapping status. Correctly handle gap +between blocks. +(GC_split_block): Remove dead code setting hb_flags. Add comment. +(GC_allochblk): Split blocks also in generational-only mode. +* os_dep.c (GC_unmap_gap): Don't really use munmap. + +* include/private/gc_priv.h (GC_unmapped_bytes): Define as 0 for +not USE_MUNMAP case. + +* Makefile.direct (MARK_BIT_PER_OBJ, PRINT_BLACK_LIST, +USE_PROC_FOR_LIBRARIES): Fix typo in the comments. +* Makefile.direct (USE_MMAP, USE_MUNMAP, THREAD_LOCAL_ALLOC, +PARALLEL_MARK, STATIC): Update the comments. +* include/private/gcconfig.h (GC_PREFER_MPROTECT_VDB): New macro +recognized (only if MPROTECT_VDB). +* Makefile.direct (DONT_USE_USER32_DLL, GC_PREFER_MPROTECT_VDB): +Add the comments for. +* os_dep.c (detect_GetWriteWatch): Recognize "GC_USE_GETWRITEWATCH" +environment variable (only if MPROTECT_VDB, if the variable is +unset when GC_PREFER_MPROTECT_VDB macro controls the strategy). +* doc/README.environment (GC_USE_GETWRITEWATCH): New variable. +* include/private/gcconfig.h (MPROTECT_VDB): Add FIXME for +USE_MUNMAP and PARALLEL_MARK cases (to relax the conditions in +the future). +* misc.c (GC_get_heap_size, GC_get_free_bytes): Ignore the memory +space returned to OS (GC_unmapped_bytes). +* include/gc.h (GC_get_heap_size, GC_get_free_bytes): Update the +comments. +* misc.c (GC_get_unmapped_bytes): New API function. +* include/gc.h (GC_get_unmapped_bytes): New API prototype. +* os_dep.c (GC_dirty_init): Move "ifdef GWW_VDB" block out of +"ifdef MSWIN32" one (for Cygwin). + +* pthread_support.c (GC_allow_register_threads): New API function. +* win32_threads.c (GC_allow_register_threads): Likewise. +* include/gc.h (GC_allow_register_threads): New API prototype. +* include/gc.h (GC_register_my_thread, GC_unregister_my_thread): +Update the comments. +* pthread_support.c (GC_register_my_thread): Check the collector +is in the multi-threaded mode. +* win32_threads.c (GC_register_my_thread): Likewise. + +* finalize.c (GC_finalize_all): Always call GC_invoke_finalizers +instead, following Ivan's original patch. + +* allchblk.c (GC_allochblk_nth): Add assertion. +* checksums.c: Add GC_record_fault, GC_was_faulted, +CC_n_faulted_dirty_errors. +(GC_check_dirty): Remove register declarations, print +dirty bit errors on faulted pages. +* os_dep.c (GC_write_fault_handler): Call GC_record_fault(). +* os_dep.c (GC_remove_protection): Compute index correctly. + + +== [7.2alpha2] 2009-06-12 == + +* dbg_mlc.c (GC_print_smashed_obj): Convert a group of printf() +calls into a single one (for output atomicity). +* typd_mlc.c (GC_calloc_explicitly_typed): Don't declare and use +GC_finalization_failures variable; check the result of +GC_general_register_disappearing_link() (for lack of memory) +instead. +* finalize.c (GC_finalization_failures): Remove unused global +variable. +* finalize.c (GC_general_register_disappearing_link, +GC_general_register_disappearing_link): Don't update the value of +GC_finalization_failures (since unused). +* include/private/gc_pmark.h (PUSH_ONE_CHECKED_STACK, +GC_PUSH_ONE_STACK, GC_PUSH_ONE_HEAP): The first parameter is of +word type now (as FIXUP_POINTER requires numeric argument). +* finalize.c (GC_ignore_self_finalize_mark_proc): GC_PUSH_ONE_HEAP +requires the first parameter of word type. +* mark.c (PUSH_GRANULE): Likewise. +* mark.c (GC_push_one, GC_push_all_eager): Likewise. +* finalize.c (GC_finalize_all): Call GC_invoke_finalizers() or +GC_finalizer_notifier directly, instead +of GC_INVOKE_FINALIZERS() to prevent infinite looping. +* include/javaxfc.h: Clarify GC_finalize_all comment. +* gcj_mlc.c: Include gc_pmark.h before "ifdef GC_GCJ_SUPPORT" (not +after) for configuration information. +* gcj_mlc.c (GC_gcj_malloc_ignore_off_page): Add comment. +* gcj_mlc.c (GC_gcj_malloc_ignore_off_page): Check "op" local +variable for NULL before dereferencing it, return GC_oom_fn() in +this case. +* typd_mlc.c (GC_malloc_explicitly_typed, +GC_malloc_explicitly_typed_ignore_off_page): Transform the code to +suppress compiler warning (for uninitialized "lg" variable). + +* win32_threads.c (GC_unregister_my_thread): add false assertion +in unreachable code. + +* pthread_support.c (GC_inner_start_routine): Don't release the +GC lock between GC_register_my_thread_inner() and +GC_init_thread_local() calls (post the "registered" even after +calling GC_init_thread_local()). +* win32_threads.c (GC_register_my_thread, GC_unregister_my_thread): +Use GC_lookup_thread_inner() instead of GC_lookup_thread() and +acquire the GC lock only once. +* win32_threads.c (GC_thr_init): Call GC_register_my_thread_inner() +directly instead of GC_register_my_thread() since I_HOLD_LOCK +and our (main) thread is not registered yet (add assertion for it). +* win32_threads.c (GC_init_parallel): Call GC_lookup_thread_inner() +directly instead of GC_lookup_thread() (since I_HOLD_LOCK). +* win32_threads.c (GC_lookup_thread): Remove unused function. +* win32_threads.c: Remove "#error GC_DLL untested with Cygwin". +* win32_threads.c (GC_win32_dll_threads): Define as FALSE macro +also if THREAD_LOCAL_ALLOC or GC_PTHREADS. +* win32_threads.c (GC_use_DllMain): Call ABORT also if GC_PTHREADS +(for Cygwin). +* win32_threads.c (GC_push_stack_for): Add parentheses around "&&" +(inside GC_ASSERT) to prevent compiler warning. +* win32_threads.c (GC_push_all_stacks): Remove FIXME for +PARALLEL_MARK. +* win32_threads.c (MAX_MARKERS, GC_markers): Move the definitions +to a place before GC_get_next_stack(). +* win32_threads.c (marker_sp, marker_bsp): New static arrays (same +as in pthread_support.c). +* win32_threads.c (marker_last_stack_min): New static arrays (the +same semantics as for last_stack_min of GC_Thread_Rep). +* win32_threads.c (GC_get_next_stack): Handle marker threads. +* win32_threads.c (GC_mark_thread): Save the current stack pointer +to marker_[b]sp. +* win32_threads.c (start_mark_threads): Initialize +marker_last_stack_min elements (to "unset" value). + +* misc.c (GC_set_oom_fn, GC_set_all_interior_pointers, +GC_set_finalize_on_demand, GC_set_java_finalization, +GC_set_finalizer_notifier, GC_set_dont_expand, GC_set_full_freq, +GC_set_no_dls, GC_set_free_space_divisor, GC_set_max_retries, +GC_set_dont_precollect, GC_set_time_limit, GC_set_warn_proc): +Change return type to void (these API functions no longer return +the old value). +* include/gc.h: Likewise. +* tests/test.c (main, WinMain, test): Remove explicit cast to void +for GC_set_warn_proc(). +* misc.c (GC_get_oom_fn, GC_get_all_interior_pointers, +GC_get_finalize_on_demand, GC_get_java_finalization, +GC_get_finalizer_notifier, GC_get_dont_expand, GC_get_full_freq, +GC_get_no_dls, GC_get_free_space_divisor, GC_get_max_retries, +GC_get_dont_precollect, GC_get_time_limit, GC_get_warn_proc): New +API functions (to get the current value of the corresponding R/W +public variables). +* include/gc.h: Likewise. +* include/gc.h (GC_set_warn_proc, GC_set_free_space_divisor): +Update the comment. +* misc.c (GC_ignore_warn_proc): New API call-back function. +* include/gc.h (GC_ignore_warn_proc): Likewise. +* misc.c (GC_set_find_leak, GC_get_find_leak, GC_set_non_gc_bytes, +GC_get_non_gc_bytes): New API setter and getter functions (for the +public GC_find_leak and GC_non_gc_bytes variables, respectively). +* include/gc.h: Likewise. +* include/gc.h (GC_memalign): Add proto to GC API. +* mallocx.c (GC_memalign): Use GC_API, GC_CALL for the definition. +* tests/test.c (run_one_test): Test GC_memalign() on Win32 too, +remove GC_memalign() proto. +* misc.c (GC_write): Use multi-byte (A) variants of Win32 +GetModuleFileName() and CreateFile(). +* tests/test.c (main): Replace K&R-style function definition with the +ANSI C one. + +* include/private/gcconfig.h (PLATFORM_ANDROID): New macro +recognized (for Linux on ARM32 without glibc). +* include/private/gcconfig.h (STRTOULL): Define for all targets +(define as "strtoul" for most targets except for LLP64/Win64). +* misc.c (GC_init_inner): Use STRTOULL instead of atoi/atol() +(cast the result to word type) to decode values of "GC_TRACE", +"GC_INITIAL_HEAP_SIZE", "GC_MAXIMUM_HEAP_SIZE" environment +variables. + +* include/gc_allocator.h: Add gc_allocator_ignore_off_page. +* tests/test_cpp.cc: Add call to gc_allocator_ignore_off_page. + +* win32_threads.c (GC_release_mark_lock): Correct misspelling of +AO_load in assertion. + +* win32_threads.c (MAX_THREADS): Define as 1 if GC_win32_dll_threads +is defined as FALSE (otherwise the size of dll_thread_table is near +200 KiB for 32-bit). +* win32_threads.c (GC_use_DllMain): Optimize for THREAD_LOCAL_ALLOC. +* win32_threads.c (GC_Thread_Rep): Add backing_store_end and +backing_store_ptr fields for IA64 support. +* win32_threads.c (GC_register_my_thread_inner): Set +backing_store_end field to reg_base value for IA64 (same as in +pthread_support.c). +* win32_threads.c (SET_PTHREAD_MAP_CACHE): Put parentheses in the +"right" places, remove ';'. +* win32_threads.c (GC_fault_handler_lock): Declare only +if MPROTECT_VDB (and not WinCE). +* win32_threads.c (GC_suspend): Acquire and release +GC_fault_handler_lock only if MPROTECT_VDB (and not WinCE). +* win32_threads.c (GC_suspend): Define as STATIC. +* win32_threads.c (GC_push_stack_for): Fix WARN() format specifier +(should be word-compliant, "%p" is used w/o "0x"), don't cast sp. +* win32_threads.c (GC_push_all_stacks): Convert a group of printf() +calls into a single one (for output atomicity). +* win32_threads.c (GC_get_next_stack): Unprotect thread descriptor +before altering its last_stack_min ("thread" variable is added). +* win32_threads.c (GC_get_next_stack): Remove unnecessary checks for +"s" is non-NULL. +* win32_threads.c (GC_get_next_stack): Don't call GC_may_be_in_stack +if WinCE. +* win32_threads.c (GC_get_next_stack): Pass current_min value to +GC_get_stack_min as-is (without -1). +* win32_threads.c (GC_wait_marker): Remove FIXME and use "release" +version of AO_fetch_and_sub1(). +* win32_threads.c (GC_win32_start_inner, GC_win32_start): convert int +to pointer (and vice versa) thru word type to suppress warnings. +* win32_threads.c (GC_mark_mutex_waitcnt): Fix comment, always +access atomically. +* misc.c: Change GC_THREADS tests back to THREADS. + +* allchblk.c (GC_print_hblkfreelist, GC_dump_regions): Convert +a group of printf() calls into a single one (for output atomicity). +* include/gc.h (GC_set_all_interior_pointers, GC_set_full_freq, +GC_set_time_limit): New prototypes. +* misc.c (GC_set_all_interior_pointers, GC_set_full_freq, +GC_set_time_limit): New public setter/getter functions. +* include/gc.h: Fix (and remove outdated) comments for thread-local +allocation. +* include/gc.h: Fix typos in comments. +* misc.c (GC_init_inner, GC_printf): Likewise. +* include/gc.h (GC_unregister_disappearing_link): Refine comment. +* include/gc.h (GC_stack_base): Recognize _M_IA64 macro. +* misc.c (GC_stack_last_cleared, GC_min_sp, GC_high_water, +GC_bytes_allocd_at_reset, DEGRADE_RATE): Define only if THREADS. +* misc.c (GC_stack_last_cleared, GC_min_sp, GC_high_water, +GC_bytes_allocd_at_reset): Define as STATIC. +* misc.c (GC_get_heap_size, GC_get_free_bytes, +GC_get_bytes_since_gc, GC_get_total_bytes): Acquire the GC lock to +avoid data races. +* misc.c (GC_write_cs): Define only if THREADS (Win32/WinCE only). +* misc.c (GC_init_inner): Initialize GC_write_cs only if THREADS. +* misc.c (GC_init_inner): Use GC_INITIAL_HEAP_SIZE (if available) to +set the default initial value of initial_heap_sz. +* misc.c (GC_deinit): Destroy GC_write_cs only if THREADS. +* misc.c (GC_init_inner): Fix WARN() format specifier (should be +word-compliant, "%p" is used w/o "0x"). +* misc.c (GC_init_inner): Don't recognize "GC_PAUSE_TIME_TARGET" +environment variable if SMALL_CONFIG. +* misc.c (GC_init_inner): Recognize "GC_FULL_FREQUENCY" environment +variable to set initial GC_full_freq value (if not SMALL_CONFIG). +* doc/README.environment (GC_FULL_FREQUENCY): Add information. +* doc/README.environment (GC_MARKERS): Refine information. +* misc.c (GC_init_inner): Change GC_ASSERT to GC_STATIC_ASSERT where +possible. +* misc.c (IF_NEED_TO_LOCK): New macro (instead of GC_need_to_lock). +* misc.c (GC_write): Use IF_NEED_TO_LOCK for handling GC_write_cs. +* misc.c (GC_abort): Don't define if SMALL_CONFIG. +* misc.c (GC_abort): Directly use WRITE() instead of GC_err_printf() +(to prevent possible infinite recursion). + +* finalize.c (finalization_mark_proc): Replace K&R-style declaration +with ANSI C one. +* finalize.c (GC_grow_table, GC_register_finalizer_inner, +GC_enqueue_all_finalizers): Remove outdated comments about disabling +signals. +* finalize.c (GC_general_register_disappearing_link): Fix assertion +to catch NULL "obj" value. +* finalize.c (GC_unregister_disappearing_link): Check "link" +alignment before gaining the lock. +* finalize.c (GC_finalize): Refine comment. +* finalize.c (GC_finalize): Fix WARN() format specifier (should be +word-compliant, "%p" is used w/o "0x"). +* finalize.c (GC_invoke_finalizers): Initialize "bytes_freed_before" +variable (to 0) to suppress compiler warning. +* include/gc_gcj.h (MARK_DESCR_OFFSET): Move to private/gc_pmark.h. +* include/gc_gcj.h: add "extern C" header and tail. +* include/private/gc_pmark.h: Remove GC_do_parallel_mark(), +GC_help_wanted, GC_helper_count, GC_active_count declarations (move +the comments to the place where these symbols are defined in mark.c). +* mark.c: Add STATIC GC_do_parallel_mark() declaration (for use by +GC_mark_some_inner, if PARALLEL_MARK only). +* mark.c (GC_mark_some_inner, GC_help_wanted, GC_helper_count, +GC_active_count, GC_do_parallel_mark): Define as STATIC. +* pthread_support.c (GC_mark_thread): Likewise. +* typd_mlc.c (GC_explicit_typing_initialized, GC_explicit_kind, +GC_array_kind, GC_ext_descriptors, GC_ed_size, GC_avail_descr, +GC_typed_mark_proc_index, GC_array_mark_proc_index, GC_eobjfreelist, +GC_arobjfreelist): Likewise. +* include/private/gc_pmark.h (PUSH_CONTENTS_HDR): Change GC_ASSERT +for HBLKSIZE to GC_STATIC_ASSERT. +* mark.c (GC_noop): Define for Borland C the same as for Watcom. +* mark.c (GC_noop, GC_mark_and_push): Add ARGSUSED tag. +* pthread_support.c (GC_do_blocking_inner): Likewise. +* mark.c (GC_mark_from): Initialize "limit" (to 0) in the default +switch branch to suppress compiler warning. +* mark.c (GC_return_mark_stack): Append new-line to printf message. +* mark.c: Remove unused GC_true_func(), GC_PUSH_ALL(). +* pthread_support.c (GC_mark_thread): Add dummy "return 0" to +suppress compiler warning. +* pthread_support.c (start_mark_threads): Move the code limiting +"GC_markers" value (and printing a warning) to GC_thr_init(). +* pthread_support.c (GC_thr_init): Silently limit "GC_markers" value +if based on the number of CPUs. +* pthread_support.c (GC_thr_init): Treat incorrect "GC_markers" +values as one. +* pthread_support.c (GC_register_my_thread_inner): Add a check for +"stack_end" is non-NULL (the same as in win32_threads.c). +* pthread_support.c (pthread_create): Call GC_oom_fn before giving up +with ENOMEM. +* thread_local_alloc.c (return_single_freelist): Convert "for" loop +to "while" one to suppress "possible extraneous ';'" warning. + +* darwin_stop_world.c (GC_push_all_stacks): Recognize ARM32. +* include/private/gc_priv.h (GC_THREAD_STATE_T): Define for ARM32 +(Darwin only). +* include/private/gcconfig.h: Add machine-specific part for DARWIN. +* include/private/gcconfig.h (ARM32): Define config parameters for +DARWIN (iPhone). + +* alloc.c (GC_FULL_FREQ, GC_DONT_EXPAND, GC_FREE_SPACE_DIVISOR, +GC_TIME_LIMIT): New macros (used to control the default initial +values of GC_full_freq variable, GC_dont_expand, +GC_free_space_divisor, GC_time_limit respectively). +* include/private/gc_priv.h (TIME_LIMIT): Remove macro (replaced +with GC_TIME_LIMIT in alloc.c). +* alloc.c (GC_need_full_gc, GC_stopped_mark, GC_finish_collection): +Define as STATIC. +* mark_rts.c (GC_push_current_stack, GC_push_gc_structures): Likewise. +* include/private/gc_priv.h (GC_stopped_mark, GC_finish_collection): +Move the prototypes to alloc.c, make STATIC. +* include/private/gc_priv.h (GC_push_current_stack, +GC_push_gc_structures, GC_push_regs_and_stack): Remove prototypes +(move the comments to the places where these functions are defined). +* mach_dep.c (GC_push_regs_and_stack): Move to mark_rts.c and define +as STATIC. +* alloc.c (GC_timeout_stop_func, GC_stopped_mark, +GC_print_heap_sects): Convert a group of printf() calls into +a single one (for output atomicity). +* mark_rts.c (GC_print_static_roots): Likewise. +* alloc.c (GC_stopped_mark): Output blank line (when logging) for +convenience to delimit collections. +* alloc.c (GC_clear_a_few_frames): Rename NWORDS to CLEAR_NWORDS; +make "frames" local variable volatile (to prevent optimization). +* alloc.c (GC_try_to_collect_inner, GC_stopped_mark, +GC_finish_collection, GC_allocobj): Remove outdated comments about +disabling signals. +* include/private/gc_priv.h (GC_register_displacement_inner, +GC_gcollect_inner): Likewise. +* alloc.c (GC_try_to_collect_inner, GC_stopped_mark, +GC_finish_collection): Initialize "start_time" local variable (to 0) +to suppress compiler warning. +* mark_rts.c (GC_add_roots_inner): Likewise. +* alloc.c (GC_RATE, MAX_PRIOR_ATTEMPTS): Guard with "ifndef". +* include/private/gc_priv.h (clock, GC_stop_world, GC_start_world, +GC_acquire_mark_lock, GC_release_mark_lock, GC_notify_all_builder, +GC_wait_for_reclaim, GC_notify_all_marker, GC_wait_marker): Replace +K&R-style function prototypes with ANSI C one. +* include/private/gc_priv.h (ABORT): Define as DebugBreak() for +Win32/WinCE if SMALL_CONFIG (the same as in GC_abort()). +* include/private/gc_priv.h (ROUNDED_UP_WORDS, abs): Remove unused +macros. +* include/private/gc_priv.h (GC_noop): Declare for Borland C the +same as for Watcom. +* mark_rts.c (GC_push_conditional_with_exclusions): Add ARGSUSED tag. + +* dbg_mlc.c (GC_store_debug_info, GC_store_debug_info_inner): Remove +outdated comment about disabling signals. +* mallocx.c (GC_malloc_uncollectable, +GC_malloc_atomic_uncollectable): Likewise. +* os_dep.c: Likewise. +* dbg_mlc.c (GC_debug_change_stubborn, GC_debug_end_stubborn_change): +Add ARGSUSED tag. +* pthread_stop_world.c (GC_suspend_handler, +GC_suspend_handler_inner): Likewise. +* dbg_mlc.c (GC_debug_free, GC_debug_realloc): Fix printf message. +* dbg_mlc.c (GC_debug_realloc): Set "result" to NULL in the default +switch branch to suppress compiler warning. +* dyn_load.c (GC_init_dyld): Use ABORT() instead of GC_abort(). +* include/private/darwin_semaphore.h (sem_init): Likewise. +* include/javaxfc.h: Replace "GC_H" with "_GC_H". +* include/private/dbg_mlc.h (GC_has_other_debug_info, +GC_store_debug_info): Replace K&R-style function prototypes with ANSI +C one. +* include/private/gcconfig.h (GC_FreeBSDGetDataStart, real_malloc, +GC_win32_get_mem, GC_wince_get_mem, GC_unix_get_mem): Likewise. +* include/private/pthread_support.h (GC_stop_init): Likewise. +* include/private/gcconfig.h: Refine comment about setting +GC_stackbottom. +* include/private/gcconfig.h (FIXUP_POINTER): Put parentheses in the +"right" places. +* include/private/pthread_support.h (GC_Thread_Rep): Refine comment +for "stack_end" field. +* mallocx.c (GC_malloc_uncollectable, +GC_malloc_atomic_uncollectable): Remove cast to undefined "hbklk". +* os_dep.c (GC_USE_MEM_TOP_DOWN): New macro (for setting +GC_mem_top_down to MEM_TOP_DOWN for debug purposes). +* os_dep.c (GC_gww_read_dirty, catch_exception_raise): Fix WARN() +format specifier (should be word-compliant, "%p" is used w/o "0x"). +* pthread_stop_world.c (GC_suspend_handler_inner): Likewise. +* os_dep.c (GC_dirty_init): Append new-line to printf messages. +* os_dep.c (GC_mprotect_thread): Fix GC_err_printf message. +* os_dep.c (GC_save_callers): Change GC_ASSERT to GC_STATIC_ASSERT. +* pthread_stop_world.c (GC_retry_signals, GC_suspend_ack_sem): Define +as STATIC. +* pthread_stop_world.c (GC_push_all_stacks): Add assertion for that +"thread_blocked" is not set for the current thread. +* real_malloc.c: Add "extern GC_quiet" to suppress compiler warning. +* reclaim.c (GC_reclaim_all): Initialize "start_time" (to 0) to +suppress compiler warning. + +* tests/test.c (check_heap_stats): Avoid unbalanced brackets in ifdef. + +* win32_threads.c: restructure parallel marking mutex initialization. +* win32_threads.c, alloc.c, darwin_stop_world.c, mallocx.c, mark.c, +pthread_stop_world.c, pthread_support.c: Add runtime conditions +on GC_parallel were appropriate. +* pthread_support.c: Condition marker_bsp on ia64. +(GC_segment_is_thread_stack): Fix loop upper bound. +* reclaim.c: Limit some assertions to PARALLEL_MARK. +* pthread_support.c: Don't acquire mark lock for thread-local +allocation. +* include/private/gc_priv.h: Don't define parallel mark sync +support just for THREAD_LOCAL_ALLOC. + +* include/private/gcconfig.h: refine MINGW32 test. +* mark.c: Add win64/gcc tests. + +* test.c (fork_a_thread, reverse_test, alloc8bytes, tree_test, +typed_test, run_one_test, check_heap_stats, main, test): Replace +all K&R-style function definitions with ANSI C ones. +* trace_test.c (main): Likewise. +* test.c (GC_COND_INIT): Define as GC_INIT() also in case of +THREAD_LOCAL_ALLOC. +* test.c (reverse_test): Call fork_a_thread() only if GC_PTHREADS +or GC_WIN32_THREADS; remove fork_a_thread() macros definition. +* test.c (reverse_test): Use "volatile" when clearing "b" and "c" +local variables (to suppress "assigned value is never used" +compiler warning). +* test.c (tree_test): Use public GC_noop1() instead of private +GC_noop(). +* test.c (typed_test): Likewise. +* test.c (check_heap_stats): Define and assign value to +"late_finalize_count" local variable only if its value is used +(if FINALIZE_ON_DEMAND defined). +* test.c (main): Remove DJGPP-specific initialization of +GC_stackbottom (not needed anymore, handled in gcconfig.h). +* trace_test.c: Guard #define GC_DEBUG with #ifndef. +* trace_test.c: Include "gc_backptr.h". +* trace_test.c (main): Call GC_INIT(). +* trace_test.c (main): Add "return 0" statement. + +* dyn_load.c (GC_register_dynlib_callback): Use new index j +instead of i in the inner loop. + +* tests/test.c: Increment n_tests with fetch_and_add when possible, +avoiding need to export lock. + +* include/gc_pthread_redirects.h: +- dlfcn.h is included for dlopen() proto before undefining +"dlopen" (so, it's possible now to include dlfcn.h after +gc.h from user code); +- GC_dlopen() proto is added (except for Darwin as +it's missing there); +- "dlopen" is explicitly undefined (before its redefinition). +* include/gc.h: +- "process.h" is included besides "windows.h" +(for _beginthreadex/_endthreadex); win32 only. +- GC_NO_THREAD_DECLS is moved to the right place +(before closing "extern C"). +* pthread_support.c: Fix out of memory handling for Thread_Reps. +* win32_threads.c: Don't include process.h on winCE, +improve out of memory handling for thread structures, don't +define GC_beginthreadex and GC_endthreadex for winCE. + +* tests/test.c: Change gcj vtable decriptor type from size_t to +GC_word. + +* gcj_mlc.c: Add comment. +* tests/test.c: Change NTEST to NTHREADS. Fork 5 threads by default. +Run reverse_test a second time in each thread.Add comments. +Don't rely on AO_fetch_and_add. + +* dyn_load.c (GC_register_dynlib_callback, +GC_register_dynamic_libraries_dl_iterate_phdr): Add support +for GNU_PT_RELRO relocations. + +* Makefile, Makefile.direct: GC_SOLARIS_PTHREADS was replaced +by GC_SOLARIS_THREADS. +* include/gc.h: Improve finalizer documentation. +* mips_sgi_mach_dep.s: Replace _MIPS_SIM_ABI32 with _ABIO32. +* pthread_stop_world.c, Makefile.dj: Fix typos. + +* win32_threads.c (GC_new_thread): Make first_thread +visible to the whole file. +(UNPROTECT): New macro. +(GC_push_stack_for, GC_suspend, GC_start_world): unprotect +thread structures before writing. +(GC_suspend): Acquire GC_fault_handler_lock before suspending +thread. +* os_dep.c: export GC_fault_handler_lock. +(GC_remove_protection): Check if already unprotected. + +* doc/README.win32: Add OpenWatcom warning. +* include/private/gcconfig.h: Really check it in. + +* os_dep.c (GC_get_stack_base, windows): Replace with Dave Korn's +code from gcc version. +* os_dep.c: make gc compilable (optionally) for Cygwin with +GetWriteWatch-based virtual dirty bit implementation ("os_dep.c" file). +* os_dep.c: Make non-win32 GC_write_fault_handler STATIC. +* mark.c (GC_noop): fix declaration definition mismatch for DMC. +* include/private/gcconfig.h: Enable MPROTECT_VDB and GWW_VDB for +Watcom (Win32 only). It works. + +* mach_dep.c: Don't use __builtin_unwind_init for register +state on PowerPC/Darwin. + +* doc/gcdescr.html: Improve description of object freelist +structure. +* include/private/gc_priv.h: Fix comment for _size_map. + +* os_dep.c (GC_linux_stack_base): Relax sanity test. + +* include/private/gc_pmark.h (PUSH_CONTENTS_HDR for +MARK_BIT_PER_OBJ): Add missing backslash before eoln. + +* misc.c (GC_set_warn_proc): Implicitly initialize GC on +non-Cygwin win32. + +* configure.ac: Enable thread-local allocation for sparc-linux. + +* alloc.c (GC_try_to_collect): Remove duplicate initialization +check. +* malloc.c (GC_generic_malloc): Remove lw to eliminate single- +threaded warnings. +* mallocx.c (GC_generic_malloc_ignore_off_page): Likewise. + +* allchblk.c, backgraph.c, dbg_mlc.c, dyn_load.c, +finalize.c, include/private/gc_pmark.h, malloc.c, mark.c, +os_dep.c, pthread_stop_world.c, pthread_support.c, reclaim.c, +thread_local_alloc.c. +* misc.c: Refine comment. + +* os_dep.c: Define GC_GWW_BUF_LEN more intelligently. Add FIXME +comment. + +* win32_threads.c (GC_push_stack_for): Yet another attempt +at the stack_min finding logic. Try to clean up the existing code +while minimizing VirtualQuery calls. +(GC_win32_start_inner): Register thread before GC_printf. +Produce more output with DEBUG_THREADS. +*include/gc.h: Update obsolete comments. + +* tests/test.c: +(gcj_class_struct2): Use cast instead of l suffix. +Cast GetLastError to int in various places. +Avoid unused result warning from incr/decr macros. +Add cast for fake_gcj_mark_proc. +Cast GC_gc_no to unsigned in printf. + +* include/gc.h: Fix two typos in comments. + +* finalize.c: Fix typo in comment. + +* blacklst.c (GC_print_source_pointer): Don't call GC_print_heap_obj +with lock. + +* reclaim.c: (GC_reclaim_block): Scan even nearly full blocks +if we are checking for leaks. + +* win32_threads.c: Remove mark lock spinning. +* win32_threads.c, pthread_support.c: Update GC_unlocked_count, +GC_spin_count, and GC_block_count using atomic operations. +* tests/test.c: Declare n_tests as AO_t only if we have threads. + +* win32_threads.c: Support PARALLEL_MARK. Make printf arg +types agree with format specifiers. +Add STATIC for GC_threads. +* include/private/gcconfig.h: Add FIXME comment. +* tests/test.c (run_ine_test): Replace LOCK/UNLOCK use with +AO_fetch_and_add1_full. Declare n_tests as AO_t. +(WinMain): Don't call GC_use_DllMain. +with PARALLEL_MARK or THREAD_LOCAL_ALLOC. + +* alloc.c (GC_try_to_collect_inner): Don't print redundant +GC_bytes_allocd and GC_gc_no. +(GC_stopped_mark): Print average world stop time. +* include/private/gc_priv.h (MS_TIME_DIFF): Add cast. + +* misc.c, doc/README.environment: Add support for +GC_FREE_SPACE_DIVISOR and GC-disable-incremental. +* include/gc.h: Make GC_set_free_space_divisor correspond to +(somewhat unfortunate) reality. + +(Mostly improves LLP64 support.) +* backgraph.c, checksums.c, dbg_mlc.c, finalize.c, mark.c, +misc.c, reclaim.c: Changed some int and long type to word or size_t +(and vice versa where appropriate) +* gcj_mlc.c, include/private/dbg_mlc.h, include/private/gcconfig.h, +include/private/thread_local_alloc.h, mark.c, +misc.c, thread_local_alloc.c, win32_threads.c: Added intermediate +casts to word type when casting from int to pointer (or pointer +to int, or data pointer to code pointer) - just to remove the +corresponding compiler warning. +* ptr_chck.c (GC_is_visible): cast int const to word type to +prevent left shift overflow. +* os_dep.c: change the type of GC_mem_top_down global variable +(containing a flag) to DWORD. +* include/gc_config_macros.h: define GC_SOLARIS_THREADS if GC_THREADS +is defined on SunOS/x64. +* misc.c (GC_init_size_map): Ifdef out GC_ASSERT as a workaround +for VC++ 2008 x64 (v15.00.21022.08 for x64) compiler bug +(the compiler gets hung if invoked with -Ox -D +ALL_INTERIOR_POINTERS -D GC_ASSERTIONS) +* backgraph.c: cast GC_gc_no value to unsigned short when +assigned/compared to height_gc_no field of back_edges. +* os_dep.c (GC_remove_protection): Add ARGSUSED. +* win32_threads.c (GC_thread_exit_proc): Remove unused local +variable. +* mark.c (GC_check_dirty): Move declaration out of func body. + +* doc/gcinterface.html: Improve REDIRECT_MALLOC documentation. +* include/gc.h (GC_register_my_thread): Improve comment. + +* Makefile.direct: Add comment for -DCHECKSUMS. + +* thread_local_alloc.c, include/private/thread_local_alloc.h: +Fix typos in comments. +* finalize.c: Declare mark_procs and GC_register_finalizer_inner +STATIC. +* malloc.c (GC_free): Move size calculation below assertion. + +* win32_threads.c (GC_get_stack_min, GC_may_be_in_stack): +Add one entry VirtualQuery cache, I_HOLD_LOCK assertions. +(GC_push_stack_for, GC_get_next_stack) : Hopefully fix WINCE support. + +* finalize.c (GC_general_register_disappearing_link): Add +assertion. +* malloc.c (GC_generic_malloc): Round lb to granules, not words. +* mallocx.c (GC_generic_malloc_ignore_off_page): Round lb to +granules, not words. + +* mach_dep.c (NO_GETCONTEXT): Define for sparc linux. +* configure.ac: Define mach_dep for sparc-linux. + +* mark_rts.c (GC_approx_sp): Use volatile to avoid common +warning. + +* dyn_load.c (GC_cond_add_roots): Fix GC_get_next_stack argument +order. + +* alloc.c, dbg_mlc.c, dyn_load.c, finalize.c, gcj_mlc.c, +include/gc.h, include/gc_config_macros.h, include/gc_cpp.h, +include/gc_gcj.h, include/gc_mark.h, include/gc_typed.h, +include/javaxfc.h, include/private/gc_locks.h, +include/private/gc_priv.h, malloc.c, mallocx.c, mark.c, mark_rts.c, +misc.c, obj_map.c, os_dep.c, pthread_support.c, ptr_chck.c, +stubborn.c, tests/test.c, thread_local_alloc.c, typd_mlc.c +win32_threads.c: Add GC_CALL and GC_CALLBACK macro invocations. +* test.c: Remove some old K&R code. + +* win32_threads.c (GC_may_be_in_stack): New. (GC_Thread_Rep): +Add last_stack_min. (GC_push_stack_for): Use last_stack_min. +(GC_get_next_stack): Add limit argument, use_last_stack_min. +(GC_suspend): make stack_base assignment conditional. +* dyn_load.c (win32 GC_cod_add_roots): Pass limit to +GC_get_next_stack. +* configure_atomic_ops.sh: Remove. +* build_atomic_ops.sh, build_atomic_ops.sh.cygwin, doc/README.win32, +Makefile.direct: Partially support build directories whose path +name contains blanks. +* Makefile.am: Support new files (build_atomic_ops.sh, +build_atomic_ops.sh.cygwin) + +* include/private/gc_locks.h, include/private/gc_pmark.h, +include/private/gc_priv.h, include/private/gcconfig.h, +mach_dep.c, mark_rts.c, misc.c, os_dep.c, pthread_stop_world.c, +pthread_support.c, thread_local_alloc.c, typd_mlc.c, win32_threads.c: +Fix comments. + +* pthread_support.c: Comment out LOCK_STATS. +* include/gc.h: Fix comments. + +* misc.c (GC_init_inner): Enable GC_LOG_FILE on Cygwin. +* include/private/gcconfig.h: Consider USE_MMAP for Cygwin. +* os_dep.c (GC_get_main_stack_base): Use alternate definition +with USE_MMAP. +* include/private/gc_priv.h: Sometimes define SETJMP on Cygwin. + +* doc/README: Make it clearer when Makefile.direct is assumed. +* cord/cord.am: install include/cord.h. + +* win32_threads.c (GC_pthread_join, GC_pthread_start_inner): +Remove unused variables. +* darwin_stop_world.c: Always declare GC_thr_init(). +* dbg_mlc.c (GC_debug_free_inner): Don't touch oh_sz if +SHORT_DBG_HDRS is defined. +* include/private/gc_pmark.h (OR_WORD_EXIT_IF_SET, parallel +mark, USE_MARK_BITS version): Refer to correct parameter name. + +* finalize.c (GC_general_register_disappearing_link): Remove +redundant code. +* gcj_mlc.c (GC_init_gcj_malloc): Add cast to signed. +* os_dep.c: (GC_write_fault_handler): Remove remaining +references to deleted variable "code". Remove redundant +FREEBSD definitions. +* include/private/gcconfig.h (GWW_VDB): Define for X86_64 when +defined for x86. (STATIC): Define as "static" with NO_DEBUGGING. + +* include/private/gc_priv.h: Update MAX_HEAP_SECTS. + +* dbg_mlc.c (GC_print_smashed_obj): Increase robustness with +smashed string, (GC_debug_free_inner): Mark as free. +* mallocx.c (GC_malloc_many): Always clear new block if +GC_debugging_started. +* reclaim.c: Move GC_debugging_started from +GC_reclaim_small_nonempty_block() to GC_reclaim_generic(), +which is also called directly. +* doc/README: Fix spelling error. Update license summary. +* include/gc.h (GC_PRE_INCR3, GC_POST_INCR3): add (void **) casts. +* tests/test.c: Don't define GC_DEBUG if already defined. + +* doc/simple_example.html: update --enable-full-debug reference, +Make HTML formatting standards compliant. +* doc/debugging.html, doc/leak.html: Fix HTML formatting bugs. +* doc/gcinterface.html: specify encoding. + +* doc/simple_example.html: Update thread-local allocation +description. + +* configure.ac: Check for gc-debug earlier; replace remaining +full-debug tests. +* include/gc.h, ptr_chck.c (GC_pre_incr, GC_post_incr): +Use signed offset type. Use ptr_t internally. +* doc/gcinterface.html: Update LOCAL_MALLOC description. +* doc/README.autoconf, doc/leak.html, doc/README.DGUX386: +Fix full-debug reference. +* include/gc.h: Rewrite GC_..._INCR and friends. +* tests/test.c: Minimally test GC_..._INCR and friends. + +* mark.c: (GC_push_next_marked, GC_push_next_marked_dirty, +GC_push_next_marked_uncollectable): Never invoke GC_push_marked +on free hblk. +* headers.c: Test COUNT_HDR_CACHE_HITS not USE_HDR_CACHE. +(GC_header_cache_miss): Always blacklist pointers for free +hblks. Add assertion and comment. +* pthread_support.c (GC_register_my_thread): Fix #if indentation. +* include/private/gc_hdrs.h: USE_HDR_CACHE is no longer tested. +Delete it. +* include/private/gc_pmark.h: (PUSH_OBJ): Add assertion. + +* alloc.c, include/gc_mark.h, Makefile.direct: Improve comments. + +* configure.ac: Set win32_threads on MinGW. + +Ivan's description of the patch follows. Note that a few pieces like +the GC_malloc(0) patch, were not applied since an alternate had been +previously applied. A few differed stylistically from the rest of +the code (mostly casts to void * instead of target type), +or were classified as too minor to bother. Note that +all of Ivan's static declarations which did not correct outright +naming bugs (as a few did), where replaced by STATIC, which is +ignored by default. + +- minor bug fixing (for FreeBSD, for THREAD_LOCAL_ALLOC and for +GC_malloc(0)); +- addition of missing getter/setter functions for public variables +(may be useful if compiled as Win32 DLL); +- addition of missing GC_API for some exported functions; +- addition of missing "static" declarator for internal functions +and variables (where possible); +- replacement of all remaining K&R-style definitions with ANSI +C ones (__STDC__ macro is not used anymore); +- addition of some Win32 macro definitions (that may be missing in +the standard headers supplied with a compiler) for GWW_VDB mode; +- elimination of most compiler warnings (except for +"uninitialized data" warning); +- several typos correction; +- missing parenthesis addition in macros in some header files of +"libatomic_ops" module. + +My highlights based on reading the patch: + +* allchblk.c: Remove GC_freehblk_ptr decl. +Make free_list_index_of() static. +* include/gc.h: Use __int64 on Win64, define GC_oom_func, +GC_finalizer_notifier_proc, GC_finalizer_notifier_proc, +add getter and setters: GC_get_gc_no, GC_get_parallel, +GC_set_oom_fn, GC_set_finalize_on_demand, +GC_set_java_finalization, GC_set_dont_expand, +GC_set_no_dls, GC_set_max_retries, GC_set_dont_precollect, +GC_set_finalizer_notifier. Always define GC_win32_free_heap. +gc_config_macros.h: Define _REENTRANT after processing +GC_THREADS. +* include/gc_cpp.h: Improve GC_PLACEMENT_DELETE test, +handling of operator new[] for old Windows compilers. +* include/gc_inline.h (GC_MALLOC_FAST_GRANS): Add parentheses +around arguments. +* dbg_mlc.c, malloc.c, misc.c: Add many GC_API specs. +* mark.c (GC_mark_and_push_stack): Fix source argument for +blacklist printing. +* misc.c: Fix log file naming based on environment variable +for Windows. Make GC_set_warn_proc and GC_set_free_space_divisor +just return current value with 0 argument. Add DONT_USE_USER32_DLL. +Add various getters and setters as in gc.h. +* os_dep.c: Remove no longer used GC_disable/enable_signals +implementations. (GC_get_stack_base): Add pthread_attr_destroy +call. No longer set GC_old_bus_handler in DARWIN workaround. +* pthread_support.c: GC_register_my_thread must also +call GC_init_thread_local. + +* Makefile.direct, mach_dep.c: Add support for NO_GETCONTEXT. +* mach_dep.c: Include signal.h. +* gc_priv.h: Factor out INLINE declaration. + +* include/private/gcconfig.h: Update MIPS/LINUX config. +* doc/gcdescr.html: Fix typo. +* mach_dep.c (GC_with_callee_saves_pushed): Don't rely on getcontext +for MIPS/LINUX. + +* configure.ac: SPARC fixes. +* thread_local_alloc.c(GC_mark_thread_local_fls_for): Include +size 0, except for gcj. +* doc/gc.man: Expand C++ cautions. +* include/gc_inline.h: Fix comments. + + +== [7.1] 2008-05-03 == + +* doc/gcinterface.html: Improve C++ interface documentation. + +* allchblk.c (GC_allochblk): Check for overflow during size +rounding. +* tests/huge_test.c: New. +* Makefile.direct, tests/tests.am: Add huge_test.c + +* pthread_support.c: Fix typo in comment. +* os_dep.c (GC_win32_get_mem): Add heap section only if +allocation succeeded. + +* malloc.c: (free replacement) Fix caller address space check. + +* finalize.c (GC_grow_table): Dereference table in null-check. + +* win32_threads.c (GC_delete_gc_thread, GC_delete_thread): +Consistently call CloseHandle. (GC_suspend): Call +GC_delete_gc_thread. +* tests/test.c: Don't reference GC_print_stats if not exported. + +* tests/test.c (run_one_test): Don't mention pthread_self(). +* misc.c: Declare GC_thr_init(). + +* allchblk.c (add_to_fl): disable assertions with USE_MUNMAP, +and refine assertions to handle huge unmergeable blocks. +(GC_allochblk_nth): Add comment. + +* include/private/gcconfig.h: Add missing FREEBSD macro +consistency test. + +* allchblk.c (GC_enough_large_bytes_left): No longer take +parameters; return free list index bound. +(GC_merge_unmapped): Don't access nexthdr until after null test. +(Fixes bug in 1/29/08 check-in.) (GC_allochblk): Calculate +when splitting is allowable only once here, not when considering each +block. (GC_allochblk_nth): Accept new may_split parameter. +Avoid some redundant tests for exact size matches. +* alloc.c (GC_should_collect): Cache min_bytes_allocd. +(GC_maybe_gc): Make locking assertion testable. +* mark_rts.c: Fix indentation. +* pthread_stop_world.c: Replace old GC_err_printf1 reference. +* tests/test.c: Remove (void) casts. Optionally print some +timing information. + +* windows-untested/gc.def: Remove CreateThread line. +* windows-untested/README: New file. +* win32_threads.c (GC_use_DllMain): Force collector initialization. +* include/gc.h (GC_use_DllMain): Clarify usage rules in comment. +* mark.c (GC_mark_from): Slightly simplify GC_DS_PER_OBJECT code. +* include/gc_cpp.h: Add matching placement delete overloads +everywhere. +* include/private/gc_locks.h (NO_THREAD): Add cast. +* include/private/gcconfig.h: Add test for __HP_aCC. +* configure.ac, tests/tests.am: Avoid libgccpp on HP/UX. + +* doc/README.win32: Fix typo. +* configure.ac: Fix printing of enable-shared result. + +* misc.c (GC_init_inner): Assert !GC_need_to_lock only when +defined. (GC_call_with_stack_base): Add GC_API. +* os_dep.c (GC_get_stack_base): Add GC_API. +* win32_threads.c: (GC_register_my_thread, GC_unregister_my_thread): +Add GC_API. +* include/gc.h: Add GC_API annotations. +* include/private/gc_locks.h: Define UNCOND_LOCK etc. also for +PCR. +* include/private/gc_pmark.h: Fix comments. + +* include/private/gc_priv.h, mark_rts.c, typd_mlc.c: +Add GC_push_typed_structures() to push GC_ext_descriptors. + +* tests/test.c: Call GC_INIT for DARWIN; test system type using +gcconfig.h-defined macros. + +* allchblk.c (GC_merge_unmapped, GC_freehblk): Refuse to create +blocks large enough that their size, when interpreted as a signed +value, would be negative. +* include/private/gc_priv.h: Comment hb_sz range limit. + +* mark.c (GC_push_next_marked): correct comment. +* Makefile.direct: document NO_PROC_STAT. +* include/private/gcconfig.h: Accommodate NO_PROC_STAT. + + +== [7.1alpha2] 2008-01-10 == + +* Makefile.am: Mention atomic_ops.c and atomic_ops_sysdeps.S +again. Refer to build directory as ".". + +* configure.ac: Ignore --enable-parallel-mark on Darwin for now. +* darwin_stop_world.c: Add FIXME comment for parallel marker. + +* include/private/gc_priv.h: Update MAX_ROOT_SETS +and LOG_PHT_ENTRIES to handle larger heaps. + +* include/gc.h (GC_INIT,GC_init): Update comments. + +* allchblk.c, alloc.c, include/private/gc_priv.h: +Track GC_bytes_dropped and use in GC triggering decisions. +* alloc.c (min_bytes_allocd): Weight atomic blocks less. + +* alloc.c (GC_add_to_heap): Call GC_install_header(p) AFTER +adjusting p. + +* Makefile.am: Add NT_X64_THREADS_MAKEFILE. + +* NT_X64_STATIC_THREADS_MAKEFILE: Clean up obsolete comment. +* alloc.c: Add declaration for GC_add_current_malloc_heap. +* win32_threads.c (GC_beginthreadex): Clean up error +return code. +* doc/README.win64, NT_X64_THREADS_MAKEFILE, Makefile.direct: +Add NT_X64_THREADS_MAKEFILE. + +* alloc.c: Define GC_version instead of in version.h. +* version.h: Remove. +* include/gc_version.h: Move most of version.h here. +* include/gc.h: Include gc_version.h. +* gcname.c, add_gc_prefix.c: include gc.h instead of version.h. +* Makefile.direct, Makefile.dj, Makefile.am, include/include.am: +Adjust for version.h rename. + +* configure.ac: Put libatomic_ops links in build directory. +* Makefile.am: Don't mention atomic_ops.c and atomic_ops_sysdeps.S +as nodist sources. + +* include/gc.h, doc/README.macros: Add GC_NO_THREAD_REDIRECTS, +GC_NO_THREAD_DECLS, don't test explicitly for GC_SOLARIS_THREADS. + +* alloc.c: Deal correctly with address wrapping for +GC_greatest_plausible_heap_addr and GC_least_plausible_heap_addr. +* finalize.c, include/gc.h (GC_register_disappearing_link, +GC_register_finalizer_inner): Improve out-of-memory handling. +* include/private/gc_pmark.h: Fix comment spelling. + +* include/gc_inline.h, include/gc_tiny_fl.h: cleanups to make usable +in other contexts. + +* include/gc.h: Don't define GC_HAVE_BUILTIN_BACKTRACE for uclibc. + +* gc_cpp.cc: Don't include gc_cpp.h from local directory. + +* allchblk.c, configure.ac (add --enable-munmap) + +* dyn_load.c (GC_dyld_image_add): Remove ifdef clause and use the macro +GC_GETSECTBYNAME instead. +* include/private/gc_priv.h: Define GC_GETSECTBYNAME according to the +architecture (Darwin). + +* reclaim.c (GC_bytes_found): Expand comment. +* thread_local_alloc.c (GC_malloc_atomic, GC_gcj_malloc): Pass +granules, not bytes, to GC_FAST_MALLOC_GRANS. +* include/gc.h: Never include gc_local_alloc.h. +* tests/test.c: Add size zero allocation tests. + +* malloc.c: Update GC_large_allocd_bytes on explicit deallocation. +* allchblk.c: Sanity check GC_max_large_allocd_bytes. + +* Makefile.direct: Invoke $(MAKE) instead of make. + +* doc/scale.html: Reflect gc7 thread local allocation behavior. + +* include/extra/gc.h, include/extra/gc_cpp.h: New. +* include/include.am: Install gc.h and gc_cpp.h in $(prefix)/include +again. + +* pthread_support.c (GC_thr_init): Use sysconf(_SC_NPROCESSORS_ONLN) +for HURD. + +* include/private/gcconfig.h: Add Linux/mips-64 support. + +* dbg_mlc.c: Use random() on all glibc systems. +* mach_dep.c (GC_with_callee_saves_pushed): Don't use getcontext() on +HURD. Add comment. +* pthread_stop_world.c (GC_suspend_handler, GC_stop_init): Accommodate +systems without SA_SIGINFO. + +* include/gc.h (GC_PTR_STORE): Fix non-DEBUG parentheses. +* tests/test.c (run_one_test): Add GC_PTR_STORE test. +No longer test for RS6000. + +* alloc.c, backgraph.c, headers.c, include/private/gc_priv.h: +Maintain GC_our_memory and GC_n_memory. +* dbg_mlc.c (GC_print_smashed_obj): Improve message. +(GC_print_all_smashed_proc): Pass client object address instead of +base. +* dyn_load.c (sort_heap_sects): New. (GC_register_map_entries): +Register sections that are contiguous and merged with our heap. +* malloc.c, os_dep.c (GC_text_mapping): Check for just base name +of libraries. +* malloc.c (calloc): Check for special callers even with +USE_PROC_FOR_LIBRARIES. Move assertion. Add rudimentary +malloc/free tracing. +* misc.c: No longer call GC_init_lib_bounds explicitly. +* thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Always +initialize on demand. +* tests/test.c: Call GC_INIT only when required. + +* Makefile.direct: Remove comment fragment. +* tests/tests.am: Add smashtest. +* configure.ac: Define GC_USE_DLOPEN_WRAP with redirect-malloc. +* pthread_support.c: Fix comment spelling. +* include/private/gcconfig.h: Define USE_PROC_FOR_LIBRARIES with +GC_LINUX_THREADS and REDIRECT_MALLOC. +* tests/smash_test.c: Initial check-in. +* obj_map.c: Print log entry to correct file. +* include/private/thread_local_alloc.h: Add TlsAlloc error check. + +* alloc.c (GC_stopped_mark): Call GC_add_current_malloc_heap() +while world is still running. +* os_dep.c (GC_is_heap_base): Don't call GC_add_current_malloc_heap() +with world stopped. +* include/gc.h (GC_INIT for cygwin): Always call GC_add_roots. +* misc.c (GC_init/GC_init_inner): Perform all work in +GC_init_inner. +* Makefile.direct: Expand -DUSE_MUNMAP comment. + +* include/gc.h: Define uintptr_t explicitly for VC++6. +* msvc_dbg.c (GetModuleBase): Revert to strcat if strcat_s doesn't +exist. + + +== [7.0] 2007-07-02 == + +* include/gc_config_macros.h: Also check for IA64 when setting +GC_HPUX_THREADS. +* mallocx.c: Change my_bytes_allocd to signed_word. +* include/gc_pthread_redirects.h: Remove obsolete Solaris threads +(as opposed to pthreads) support. + +* mach_dep.c (GC_with_callee_saves_pushed): Don't use getcontext() +on ARM/Linux. Check getcontext() return value. + +* backgraph.c (per_object_func): Make argument types consistent. +(GC_traverse_back_graph): Mark GC_deepest_obj. + +* finalize.c (GC_finalize): Change dl_size and fo_size to size_t. +* os_dep.c (GC_win32_get_mem): Add GC_mem_top_down option. + +* doc/README.win32, doc/README, README.QUICK: Fix some of the worst +anachronisms. +* dyn_load.c: Partially support cygwin, but don't enable it yet. + +* Makefile.am: Use -no-undefined for libgc. +* Makefile.direct: Document USE_PROC_FOR_LIBRARIES. +* dyn_load.c (GC_register_map_entries): Rename prot_buf to prot +consistently. +* misc.c: Fix some WARN calls. Move GC_is_initialized setting and +GC_thr_init() call. +* os_dep.c: Consistently use WARN where appropriate. +* thread_local_alloc.c: Revert change to GC_WIN32_THREADS test. Instead +remove inappropriate pthread.h include. +* doc/README.linux: Remove some anachronisms. + +* alloc.c: Also use GC_check_tls on non-Linux systems. +* mallocx.c (GC_reclaim_generic): Remove bogus declaration. +* include/private/gc_priv.h (GC_reclaim_generic): Declare correctly +with prototype. + +* alloc.c (GC_adj_bytes_allocd): Avoid (long) casts, fix comment. +(GC_print_heap_sects): Use size_t instead of unsigned long. +* thread_local_alloc.c (GC_lookup_thread): Define in the correct +context. +* win32_threads.c, include/gc_config_macros.h: The last of Romano +Paolo Tenca's patch. Move stdint.h include to gc_config_macros.h. +* include/gc_inline.h: Avoid gc_priv.h dependencies. +* tests/test.c (check_heap_stats): Replace unsigned long with size_t. + +* NT_X64_STATIC_THREADS_MAKEFILE: Replace obsolete -debugtype:cv. +* mark_rts.c (GC_push_roots): Fix kind type. + +* doc/README.win64: New file. +* doc/doc.am, Makefile.direct: Add README.win64. + +* Makefile.am, Makefile.direct: Add NT_X64_STATIC_THREADS_MAKEFILE. +* NT_X64_STATIC_THREADS_MAKEFILE: Fix warning flags. +* allochblk.c, alloc.c, blacklst.c, dbg_mlc.c, dyn_load.c, +finalize.c, headers.c, mach_dep.c, malloc.c, mark.c, misc.c, +obj_map.c, os_dep.c, ptr_chck.c, reclaim.c, typd_mlc.c, +win32_threads.c, cord/de_win.c, include/gc_mark.h, +include/private/gc_hdrs.h, include/private/gc_pmark.h, +include/private/gc_priv.h, tests/test_cpp.cc: +Replace old style function declarations. Clean up integral types. +Remove register declarations. The change in malloc.c and the +"int descr" declaration in mark.c are the most likely to have +been real bugs outside of Win64. +* msvc_dbg.c: Disable on Win64. +* win32_threads.c: Add x64 support. +* include/gc.h: no backtrace on x64 for now. + +* msvc_dbg.c(GetModuleBase): Replace strcat with strcat_s. + +* include/gc.h: (GC_word, GC_signed_word): Fix Win64 definitions. +Don't include windows.h in an extern "C" context. +* include/private/gcconfig.h: Fix Win64 configuration. +* tests/test.c: Eliminate more old style function definitions. +Cleanup pointer and integer casts for Win64. +* tests/test_cpp.cc: Don't include gc_priv.h. +* NT_STATIC_THREADS_MAKEFILE: Restrict suffixes for VC++ 2005. +* NT_X64_STATIC_THREADS_MAKEFILE: New. + +* win32_threads.c: Separate out DEBUG_WIN32_PTHREADS_STACK. Ignore +FINISHED threads for suspension. (GC_pthread_join): Add +pthread_self() cast. (GC_pthread_start_inner): Execute cleanup +handler when popping it. +* include/private/gc_locks.h: Inline THREAD_EQUAL for +GC_WIN32_PTHREADS. Define USE_PTHREAD_LOCKS only if we have +pthreads. + +* gc_dlopen.c, thread_local_alloc.c, threadlibs.c, win32_threads.c, +tests/test.c: Accommodate GC_WIN32_PTHREADS. +* include/gc.h: Don't include windows.h for GC_WIN32_PTHREADS. +* include/gc_config_macros.h: Define both PTHREADS and +GC_WIN32_THREADS. +* include/private/gc_locks.h: Nonstandard definitions of +NUMERIC_THREAD_ID for GC_WIN32_PTHREADS. +* doc/README.win32, Makefile.direct: Include documentation +for GC_WIN32_PTHREADS. +* Makefile.direct: Remove some anachronisms in the documentation. + +* Makefile.am: Move includes to bottom. Add better library +dependencies. Increment library version. Remove "SUBDIRS += .". +* cord/cord.am, tests/tests.am: Add better library dependencies. +Remove now unnecessary dependencies. +* include/gc.h (GC_beginthreadex, GC_endthreadex, GC_ExitThread): +Move to define on all Windows platforms. (_beginthread): define +to generate error if used. + +* include/private/gc_locks.h: Format to 80 columns. + +* malloc.c(GC_free): Ignore bad frees on MSWIN32 with REDIRECT_MALLOC. +* NT_MAKEFILE: msvc_dbg.h is in include/private. Don't use cvars +rc. +* misc.c (WIN32 GC_write): Define GC_need_to_lock in single-threaded +case. +* win32_threads.c: Test for __MINGW32__ in addition to _MINGW_VER. +(GC_CreateThread, GC_beginthreadex): Deallocate args even if we fail. +* include/gc.h: Add GC_reachable_here(). (GC_WinMain): Add GC_API. +(GC_beginthreadex, GC_endthreadex, GC_ExitThread): Declare. +* tests/test.c: Add GC_reachable_here() call. + +* alloc.c (GC_try_to_collect): Call GC_init if necessary. +* tests/thread_leak_test.c: Don't unconditionally define +GC_LINUX_THREADS. + +* Makefile.am: Remove extra_ldflags_libgc definition. + +* include/private/gc_priv.h: Define AO_REQUIRE_CAS. + +* finalize.c (GC_unreachable_finalize_mark_proc): Don't return void +value. + + +== [7.0alpha9] 2007-05-15 == + +* Some gc6.9 changes. +* Change FindTopOfStack decl in darwin_stop_world.c. +* Move some static tests from misc.c to gcconfig.h. Use #error. +* Add GC_print_free_list() function (thanks to Bruce Hoult). +* Add GC_GNU_THREADS support on HURD (thanks to Aleksey Demakov, +Barry DeFreese, and possibly other Debian maintainers). +* __GNUC__ was misspelled as __GNUC in thread_local_alloc.h (thanks to +Peter Wang). +* Integrated various MacOSX patches and tried to reconcile them (thanks to +Allan Hsu, several contributors at Apple, and probably others). +* Added some casts to powerpc.h in libatomic_ops to silence warnings. + +* Makefile.am: Include NT_STATIC_THREADS_MAKEFILE in dist. +* include/private/gc_locks.h: GC_compare_and_exchange, GC_atomic_add: +remove. NUMERIC_THREAD_ID, THREAD_EQUAL: New. GC_lock_holder: now +unsigned long. I_DONT_HOLD_LOCK, I_HOLD_LOCK: Update. +* pthread_stop_world.c, pthread_support.c, win32_threads.c: Use +NUMERIC_THREAD_ID, THREAD_EQUAL. +* include/private/gcconfig.h: GENERIC_COMPARE_AND_SWAP: Remove. +* include/private/thread_local_alloc.h: Don't USE_COMPILER_TLS on +ARM. + +* dbg_mlc.c, include/gc.h, finalize.c: Merge Alexandre Oliva's +GC_debug_register_finalizer_unreachable() patch from gcc tree. +* thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Add assertions +to check GC has been initialized. + +* include/gc_cpp.h: Documentation updates. +* include/gc_config_macros.h: Don't check for __ppc__ to set +DARWIN_THREADS. +* Makefile.am: Include configure_atomic_ops.sh in dist. + +* Makefile.am: Don't distribute copied atomic_ops files. Include +libatomic_ops with "make dist". +* configure.ac: Enable THREAD_LOCAL_ALLOC for Cygwin with threads. +* win32_threads.c: Report error for Cygwin + GC_DLL. + +* Makefile.direct: Update THREAD_LOCAL_ALLOC documentation. +* cord/de_win.c: Rename and move AboutBox. Call GC_INIT. Remove +MakeProcInstance anachronism. +* doc/README.macros: Officially remove elif prohibition. +Remove documentation for defunct SRC_M3 support. +* include/gc.h: Remove more SRC_M3 references. +* include/private/gcconfig.h: Remove still more SRC_M3 references. +GC_SOLARIS_THREADS no longer needs to be checked separately. + +* thread_local_alloc.c, include/private/thread_local_alloc.h: +Spell __declspec correctly. +* NT_STATIC_THREADS_MAKEFILE: Enable thread-local allocation. + +* doc/README.win32: Adjust GC_win32_dll_threads rules again. + +* mark.c (GC_mark_some wrapper): Restructure for readability, handle +GC_started_thread_while_stopped. +* misc.c (Win32 GC_write): Lock GC_write_cs only if needed. +* win32_threads.c: (client_has_run): remove, +GC_started_thread_while_stopped, GC_attached_thread: add. +(GC_push_all_stacks): Add verbose output. +(DllMain): Avoid initializing collector or the like. +Never update both thread tables. +* doc/README.win32: Adjust GC_win32_dll_threads rules. + +* pthread_stop_world.c (GC_push_all_stacks): Print thread count with +GC_PRINT_VERBOSE_STATS. + +* configure.ac: Comment out redundant +AC_DEFINE(NO_EXECUTE_PERMISSION). +* sparc_mach_dep.S: Remove single quote in comment. +* include/private/gcconfig.h: Fix DATAEND for NONSTOP. +* win32_threads.c: Include stdint.h for Mingw. Add GC_API for DllMain. +(GC_use_DllMain): Fix assertion. + +* configure.ac: Introduce extra_ldflags_libgc. Use it for Darwin. +* Makefile.am (libgc_la_LDFLAGS): Use extra_ldflags_libgc. +* include/private/gcconfig.h: Enable MPROTECT_VDB for all Darwin +targets. Remove comments. +Prepare ppc64 support for Darwin. + +* darwin_stop_world.c (GC_push_all_stacks): Fix compiler warnings. +Make i unsigned. +(GC_stop_world): Likewise. Remove unused GC_thread p. +(GC_start_world): Likewise. + +* os_dep.c: Define GC_darwin_register_mach_handler_thread extern. +Remove double SIG_HNDLR_PTR definition. +(GC_forward_exception): Fix compiler warnings, make i unsigned. +Initialize thread_state to NULL. +(catch_exception_raise): Fix compiler warnings, make i unsigned. + +* include/private/gc_priv.h (NEED_FIND_LIMIT, FREEBSD variant): +also define for X86_64. +* configure.ac: Move generic gnu (Hurd) case to below kfreebsd case. +* README.changes: Point to ChangeLog. + +* darwin_stop_world.c: Move THREAD_FLD defines to ... +* include/private/gc_priv.h: ... here. +Fix THREAD_STATE definitions for ppc64. +* os_dep.c (catch_exception_raise): Use THREAD_FLD for exc_state member +access. + +* configure.ac (i586-darwin): Replaced HAS_I386_THREAD_STATE_* with +HAS_X86_THREAD_STATE32_*. +(x86_64-*-darwin*): Extended the above check for x86_64-*-darwin* with +HAS_X86_THREAD_STATE64_*. +Added value 1 in the above AC_DEFINE's. Important for the upcoming +Leopard. +* include/private/gcconfig.h: Modified X86_64 define for Darwin. +Removed __x86_64__ check in POWERPC section. Added base definitions +for the X86_64 Darwin port. +* include/private/gc_priv.h: Added GC_MACH_HEADER and GC_MACH_SECTION +to distinguish between 32 and 64-bit applications. Added definitions +for X86_64 Darwin. +* darwin_stop_world.c: Added HAS_X86_THREAD_STATE64___RAX. And +replaced HAS_I386_THREAD_STATE___EAX with HAS_X86_THREAD_STATE32___EAX. +(GC_push_all_stacks): Added code for X86_64 Darwin. Even for the +!DARWIN_DONT_PARSE_STACK. Maybe obsolete. +* dyn_load.c (GC_dyld_name_for_hdr): Use GC_MACH_HEADER. +(GC_dyld_image_add): Use GC_MACH_HEADER and GC_MACH_SECTION. +Distinguish between getsectbynamefromheader_64 and +getsectbynamefromheader. +* os_dep.c (catch_exception_raise): Introduce exception definition for +X86_64 Darwin. Replaced old i386_EXCEPTION_STATE_* definition with +x86_EXCEPTION_STATE32_*. Add X86_64 for exc_state.faultvaddr. + + +== [7.0alpha7] 2006-09-19 == + +* More 6.7 changes. +* Declare GC_dump() in gc.h. +* Add --enable-large-config, which just defines the LARGE_CONFIG macro. +* Make GlobalAlloc address alignment a bit more intuitive (thanks to +Charles Mills). +* Use #elif in the definitions of GET_MEM. +* Overhaul porting.html. Remove corresponding text from README. +* Fix typo in DARWIN section of gcconfig.h. +* Fix Darwin thread memory leak (thanks to Bruce Mitchener). +* Update x86 AO_test_and_set implementation to use "=q". +* Add $(EXEEXT) to many tests in tests/tests.am. (Corresponds to a +6.7 fix, which no longer applied.) +* Fix Darwin/PPC port. +* Fix Cygwin/threads port. +* Fix gcj malloc support. +* For GNU-style make, don't build libatomic_ops unless threads are requested. +This should allow single-threaded builds on platforms which do not +currently support libatomic_ops. +* Clean up and hopefully fix the CFLAGS calculation for GNU build. +(Substantially improves things on HP/UX.) +* Integrated Andrei Polushin's Visual C++ patches. These provide for +stack traces, better C++ debug support, and better log file handling. +Note that these change the location of the log file to a the path of the +executable with a .log extension. To get the old behavior back, define +OLD_WIN32_LOG_FILE. For the time being, I'm checking his project +files and the like into a windows-untested subdirectory. They +are almost certainly already out of date, but better than what we had +before. +* Fixed some win32 threads bugs, and added support for _beginthreadex. +* Fix zero size thread local allocation so that explicit deallocation +works correctly. +* Removed serious bug in GC_malloc_uncollectable(large size). +* Do not try to do thread-local gcj allocation in incremental mode. There +are races in setting up the descriptor. +* Add GC_INIT() to middle.c, fix some more GC_printfn calls. +* Some assertions erroneously used I_HOLD_LOCK() negatively, even though +it can now spuriously return TRUE. +* Rename SUNOS5 macro and OS name to SOLARIS and SUNOS5DL to SOLARISDL. +* On Linux and some Un*x variants, allocate memory by first trying sbrk, +and then switching to mmap if that fails. +* Fixed /proc/x/maps reading to deal with asynchronous deletions. +* Fix REDIRECT_MALLOC with threads on Linux. It now usually seems to work +with ugly hacks that include having calloc behave differently when it is +called from ld.so or the pthreads library. A reasonable amount of +infrastructure was added to support some of this. (Thanks to Roland McGrath +for ideas and information.) +* Import various updated build scripts. +* Add GC_register_has_static_roots_callback (thanks to Andrew Haley). +* Fix serious bugs in GC_malloc_atomic_uncollectable(). +* Return GC_SUCCESS form GC_get_stack_base(). +* Fix several atomic_ops problems on IA64 with HP Compiler. +* Update to atomic_ops-1.2. +* Fix hb_n_marks description and reclaim.c assertion. +* Various additional win32 threads fixes. +* Enable GC_ASSERTIONS for Debug build with NT_THREADS_MAKEFILE. + + +== [7.0alpha5] 2005-09-29 == + +* More 6.6, 6.7 changes. +* Some Solaris fixes, including some more general changes in how +the assembly pieces of mach_dep.c are handled. +* Removed a lot of SOLARIS_THREADS-specific code that was only +needed with the old implementation. This included many (mostly no-op) +versions of GC_is_fresh. +* Don't use atomic_ops in gc_locks.h unless we need threads. +* Fixed USE_MARK_BITS, which is once again the default without PARALLEL_MARK. +* Removed Solaris GC_INIT hack. It's a workaround for a long dead bug, +and it seemed to be wrong anyway. +* Changed win32_threads.c to require preprocessor-based interception +of thread routines by default. A client call to GC_use_DllMain is +now required to get the old behavior in which DllMain is used to implicitly +register threads. This was done for uniformity with other platforms, and +because the DllMain solution seemed to require very tricky code which, +at least in the past, imposed hard bounds on the number of threads. +* Many small changes to make thread support work again on Cygwin. +* Moved definition of allocator lock etc. to pthread_support.c and +win32_threads.c for those two cases. +* Got rid of the FASTLOCK() machinery. It doesn't seem useful on modern +platforms. +* 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 +are it just works, and I think the reasons for it not working disappeared +a while ago. +* Fixed bugs in hb_n_marks calculation and assertion. +* Don't use __builtin_expect for pre-3.0 gcc. +* Define GWW_VDB only for recent Microsoft tool chains. +* Add overview.html to doc directory. +* Fix NT_STATIC_THREADS_MAKEFILE, various compiler warnings. +* Made thread local allocation sort of work with Cygwin. The code should +be there to deal with other Windows variants, But non-Cygwin Windows +threads need more bug fixes. + + +== [7.0alpha4] 2005-08-02 == + +* Various 6.5, 6.6 changes. +* Removed GC_brief_async_signal_safe_sleep and used atomic_ops instead +(thanks to Ben Maurer). +* Integrated build patches from Davide Angelocola and Petter Urkedal. +* Fix dynamic-linker-based pthread call redirection. +* Renamed RS6000 to POWERPC/AIX. +* Allow recovery from SIGSEGV in marker on Linux. This works around +a race in thread stack marking if /proc is used to find roots. We do +that by default with malloc redirection and threads. This involved +moving some GC_find_limit and SETJMP related declarations to gc_priv.h. +* Added doc/porting.html file. +* Added ADD_HEAP_GUARD_PAGES for sbrk/*nix platforms to debug extreme +memory overwrite errors. +* Added trivial NO_INCREMENTAL flag to facilitate debugging. +* Added GC_getattr_np-based GC_get_stack_base (untested). +* Separated thread local allocation into a separate file and added the +beginning of win32 support for that. + + +== [7.0alpha3] 2005-04-28 == + +* Added support for dlopen-based interception of pthread functions. +This is only half done. The gc.h redefinitions currently interfere. +* Integrated major automake overhaul from Petter Urkedal. + + +== [7.0alpha2] 2005-04-07 == + +* GC_bytes_allocd was incremented by a possibly uninitialized variable +in GC_generic_malloc_inner. (Bug introduced in gc7.0alpha1. Thanks +to Ben Hutchings for tracking it down.) +* Win32 fixes (thanks to Ben Hutchings and Maurizio Vairani). +* Integrated Ben Hutchings' GetWriteWatch-based virtual dirty bit +implementation for win32. +* Removed pc_gc.tar and floppy targets in Makefile.direct. Removed +pc_excludes file. +* No longer include GC_bytes_wasted when evaluating allocation progress. +Since we are now counting live memory, it no longer makes sense. +* Applied Davide Angelocola's configure patch. There are now separate +Makefile.am's in the cord and tests subdirectory, more tests, etc. +* Renamed configure.in to configure.ac. +* Merged a very small number of Nathanael Nerode's configure.ac +cleanups from the gcc tree. Unfortunately, that file is a bit +different from ours. +* Changed EINTR handling in sem_wait slightly. +* Restructure the root marking code. Remove all traces of +USE_GENERIC_PUSH_REGS, and effectively make it the default. +Make it easier to pass a context pointer to the mark routine, in +case we ever want to do precise stack marking. +* Replace GC_start_blocking() and GC_end_blocking() with GC_do_blocking(). +This remains undocumented, and only implemented for pthreads. But it +removes an otherwise unavoidable race with stores of callee-save +registers. +* Fix GC_n_mark_bits for the default MARK_BIT_PER_GRANULE case. This +resulted in bogus complaints in heap dumps. +* Upgrade to libatomic_ops-1.0, and update build structure to match. +* Remove SRC_M3 support. Clean up lock initialization code in misc.c. +* Removed gc_local_alloc.h. If THREAD_LOCAL_ALLOC is defined, the +thread local allocation routines are now called automatically. +* Renamed gc_inl.h back to gc_inline.h. Changed the interface appreciably +since locking has turned into a dominant issue, and in-line allocation +only makes sense if it's no worse than thread-local allocation. +Gc_inline.h is now also used to implement thread-local allocation. +* Finished replacing stubborn allocation with manual write barrier. +Untested. +* Use thread-local allocation code by default. +* Added GC_register_my_thread and friends for Posix and win32. +* Patch for GWW_VDB from Ben Hutchings. +* Removed explicit THREAD_LOCAL_ALLOC tests, since that now always +redefines GC_malloc. +* Removed now unused AIX memory allocation code. +* Various minor fixes for bugs introduced in 7.0alpha1. + + +== [7.0alpha1] 2004-11-09 == + +* Remove GC_PROTO, VOLATILE, GC_PTR, and GC_CONST. Assume ANSI C compiler +and use ANSI constructs unconditionally. +* Introduce #elif and #error in some of the appropriate places. +* Remove GC_printf cruft. Use stdargs. +* Remove separate Solaris threads support. Use the more generic Posix +implementation. +* Use atomic_ops for atomic operations and memory barriers. +* Clean up MPROTECT_VDB implementation. Use SA_SIGINFO wherever +possible. +* Remove broken SIGNALS stuff. +* Use size_t instead of word, where appropriate. +* Add .S.o rule to Makefile.am. +* Officially discontinue SunOS4, several old flavors of M68K (SunOS4, +A/UX, HP), IBM PC/RTs and RISCOS/Irix4. (I doubt the old code worked. +If anyone cares, these should be easy to resurrect.) +* Add EXPECT() in some critical places. +* Redefined hb_sz and hb_body to deal with bytes rather than words. +This affected a great deal of code. I would like to consistently use +byte offsets and sizes where there's not a convincing reason to do +otherwise. +* Redefined several other variables (GC_mem_found, GC_words_allocd) +etc. to use units of bytes. Most of these were also renamed to +reflect that fact. +* Killed as many "register" declarations as possible. +* Partially replaced stubborn allocation with manual write barrier. +It's currently broken. +* Restructured mark code, to allow mark bits to be kept either on +a per allocation granule or per object basis. The emphasis is +now on the -DUSE_MARK_BYTES option, since individual bits perform +quite badly on hyper-threaded P4s, and are probably suboptimal on +other architectures. -DUSE_MARK_BITS is currently broken, and may +be resurrected only for the single-threaded case. This significantly +reduced the cache footprint required by auxiliary GC data structures. +It also reduces space overhead for small heaps. It probably slows +things down slightly if interior pointers are very common. +* As part of the above, we now maintain an approximate count of set +mark bits in each heap block. +* As part of the above, the semantics of hb_map changed drastically. +For MARK_BIT_PER_OBJ, it doesn't exist. For MARK_BIT_PER_GRANULE, +it is purely a way to replace a mod instruction with a table lookup. +(Somewhat to my surprise, this still wins on modern hardware.) +* Removed PRINTSTATS, GATHERSTATS, and SILENT macros. Everything is +now controlled by GC_print_stats variable and GC_PRINT_STATS +and new GC_PRINT_VERBOSE_STATS environment variables. +* Add GC_log_printf and use it consistently for logging output. +* Unconditionally count the objects we reclaim in the sweep phase. +For thread local allocation, we need that anyway, and we expect +that's increasingly the only case that matters. And it simplifies +the code. In general expect minor performance hacks that benefit +only the single-threaded case to disappear. +* Remove GC_quiet from gc.h and elsewhere. +* Changed the heap expansion heuristic, and the definition of +GC_free_space_divisor, to refer to live data size, instead of total +heap size. I believe this is much more robust. It wasn't previously +possible, because we didn't have access to live data size. +* Thread local allocation added the extra byte in twice: Once in +thread_local_alloc, and once in malloc_many. +* Removed GC_malloc_words_small and GC_gcj_fast_malloc. A new +mechanism based on the thread local allocation data structures +is expected to be added instead. This should allow inlined code +that is both fast and doesn't rely on collector internals. +* Changed both free lists and reclaim lists to be indexed by granules +instead of words, norming halving their size. +* MERGE_SIZE is now the only option, and the macro was removed. +(Without it, we need a memory reference to GC_all_interior_pointers +anyway. Thus it costs us nothing.) +* Change GC_size_map to map to granules instead of words. Make sure +that every possible size up to TINY_FREELISTS is present. +* Split of macros need for fast inline allocation into gc_tiny_fl.h +in anticipation of a new inline allocator that doesn't rely on GC +internals. +* Changed thread local allocation to use GRANULE_BYTES and TINY_FREELISTS +in anticipation of a merge with the inline allocation code. +* Removed ALIGN_DOUBLE. This is mostly handled by GRANULE_BYTES. +* Make locking on most platforms conditional on GC_need_to_lock. + + +== [6.9] == + +* Fix typo in PREFETCH implementation for X86_64 (thanks to Peter Wang). +* Fix M68K LINUX port (thanks to Debian packagers). +* __GNUC__ was misspelled as __GNUC in new_gc_alloc.h (thanks to Peter Wang). +* Integrated Allan Hsu's patch for OS X VM deallocation problems. +* Applied FreeBSD/X86_64 patch. + + +== [6.8] 2006-07-08 == + +* Added some support for Dragonfly BSD (thanks to Joerg Sonnenberger and +Thomas Klausner). +* Improvements to the HP/UX section of configure.in/configure.ac (thanks +to Andreas Tobler). +* GC_unix_get_mem could neglect to release the malloc lock on Irix, under +extremely unlikely circumstances. (Thanks to Jean-Baptiste Nivois for +some careful code inspection.) +* Added support for kFreeBSD + glibc (thanks to Petr Salinger). +* Fix more MacOS threads memory leaks (thanks to Allan Hsu). +* Added initial Solaris/x64 support (thanks to Rainer Orth). + + +== [6.7] 2006-03-03 == + +* Add "int" to Solaris "end" and "etext" declaration in gc.h. Declared +the symbols with underscores and as arrays, since that's what's actually +used. Perhaps this could all just be removed. (Thanks to John Bowman.) +* Fixed ARM GC_test_and_set code (thanks to Kazu Hirata and Paul Brook). +* Added casts for assignments to hb_last_reclaimed, which truncate the +value. Added a cast to GC_adj_words_allocd. Use GetModuleHandleA +when retrieving a handle to kernel32.dll under win32. +* Added Tandem S-Series support. (Thanks to Craig McDaniel. A modified +version of his patch was applied, and hence breakage is probably not +his fault.) +* Remove spurious gc:: qualifier for operator delete[] in gc_cpp.h (thanks +to Hanno Boeck). +* Changed a test for LINUX in config_macros.h to one for __linux__. +* Add prototypes for GC_finalizer_notifier and GC_thr_init (thanks to +David Ayers). +* Use ld instead of nonexistent ldz instruction in Darwin FindTopOfStack +(thanks to Andreas Tobler). +* Add support for Darwin/x86 (thanks to Geoff Norton and the Mono +developers). +* Merge in some recent gcc fixes. Add ppc64 asm code. (Thanks to +Bryce McKinlay and other GCJ developers.) +* Scan MEM_PRIVATE sections under Windows ME and predecessors. +* Interior pointers with some largish offsets into large objects could +be ignored, if GC_all_interior_pointers was set. (Oddly this worked +correctly for stack references if it was not set. Otherwise it failed +for both stack and heap references. Thanks to Andrew McKinlay for the +critical test case.) +* Integrated Tatsuya Bizenn's NETBSD threads support, with some +untested changes. +* Added GC_strdup and friends to make leak detection work correctly +for strdup clients (thanks to Jon Moore). Fixed the existing strdup +with malloc redirection to handle a null malloc return correctly. + + +== [6.6] 2005-09-09 == + +* Fix CPU count detection for Irix and FreeBSD (thanks to Dan Bonachea). +* Integrate Dan Bonachea's patch for the IBM XLC compiler on Darwin. +* Integrated Andreas Tobler's FreeBSD/PowerPC patch. +* Don't access the GC thread structure from the restart handler. It's +unsafe, since the handler may run too late. (Thanks to Ben Maurer for +tracking this down.) +* Applied Christian Thalinger's patch to change comment syntax in +alpha_mach_dep.S. +* Added test for GC_no_dls in GC_dyld_image_add for DARWIN (thanks to +Juan Jose Garcia-Ripoll). +* Use LINUX_STACKBOTTOM for Linux/SH and LINUX/ARM (thanks to +Sugioka Toshinobu and Christian Thalinger). +* Rewrote GC_parse_map_entry. This assumed a fixed column layout of +/proc/self/maps on Linux. This ceased to be true about 2 years ago. +The old code is probably quite problematic with -DREDIRECT_MALLOC. It +is also used by default for IA64, though I haven't seen actual failures +there. +* More consistently define HBLKSIZE to 4096 on 64 bit architectures with +4K pages (thanks to Andrew Haley). +* With win32 threads, GC_stop_world needs to acquire GC_write_cs (thanks +to Ben Hutchings for the observation and patch). +* Move up struct callinfo declaration to make gcc 4.0.2 happy. + + +== [6.5] 2005-05-22 == + +* Integrated Paolo Molaro's patch to deal with EINTR in sem_wait. +* Make GC_approx_sp() write to dummy location to ensure that stack +is grown here, when sp looks reasonable, rather than later, when +it might look like a bad memory reference. (Problem was never +observed that I know of. But on rereading the code it seemed +dubious.) +* Separate out GC_with_callee_saves_pushed and sometimes call +it from GC_suspend_handler in pthread_stop_world.c. Callee-save +register values sometimes failed to get traced under HP/UX on +PA-RISC. Linux/IA64 had the same problem, though non-stacked +callee-save registers seem to be so rarely used there that nobody +ever noticed. +* Integrated an ancient Darwin powerpc_darwin_machine_dep.s patch +from Andreas Tobler, which I had lost. +* Fix compare_and_exchange implementation for gcc/IA64 to deal with +pickier compiler versions. +* Fixed Itanium 32-bit ABI support (HP/UX). In particular, the +compare_and_exchange implementation didn't consider that possibility. +* Undefine GC_pthread_detach in win32_threads.c (thanks to +Tommaso Tagliapietra). +* Fixed inclusion of frame.h for NETBSD in os_dep.c. +* Applied Dan Bonachea's patch to use mmap on AIX. +* Several fixes to resurrect the Irix port on recent OS versions. +* Change ALPHA to use LINUX_STACKBOTTOM. +* Change SPARC64/LINUX to also use LINUX_STACKBOTTOM. Deal with potential +bad values of __libc_stack_end on that platform (thanks to David Miller). +* Relax gctest to allow larger heap if ALIGN_DOUBLE isn't set. +(Unnecessary in 7.0) +* Force a define of __STDC__=0 for the IBM compiler on AIX, so that +we get prototypes. (Unnecessary in 7.0) +* GC_INIT definition for AIX and CYGWIN referred to DATASTART and DATAEND +which are only defined in private include files. +* Integrated some small gcconfig.h patches from Dan Bonachea. Also +relaxed assertion about FreeBSD stack size in pthread_support.c. +* Integrated Andrew Begel's darwin_stop_world.c patch for 64-bit +support. This may need additional work. +* Avoided potentially infinite recursion in GC_save_callers if +the system backtrace calls malloc. The workaround currently requires +__thread support if this code is used with threads. +* Avoided another similar infinite recursion by conditionally +invoking GC_save_callers in alloc.c (thanks to Matthias Andree +for helping to track down both of these). +* Removed all traces of aix_irix_threads.c. AIX and Irix now use +pthread_support.c and pthread_stop_world.c. The old code appeared +to be unreliable for AIX, and was not regularly maintained. +* On Irix, ignore segments with MA_FETCHOP or MA_NOTCACHED attributed; +they're not always safe to read. +* Fixed a previously vacuous assertion (diagnosed by the SGI compiler) +in GC_remove_from_fl. +* Fix stack_size assertion in GC_pthread_create. +* Fix assertion in GC_steal_mark_stack. + + +== [6.4] 2004-12-21 == + +* Merge gcconfig.h changes from gcc tree. +* Unconditionally include gc_priv.h in solaris_pthreads.c, win32_threads.h, +aix_irix_threads.c, and solaris_threads.c to get thread definitions. +* Start marker threads in GC_thr_init, so that they get started even +if no other threads are ever started. (Oddly enough, the parallel +collector worked correctly, though not well, with no helper threads.) +* Go ahead and split large blocks in GC_allochblk_nth if GC_dont_gc +is set (thanks to Alexander Petrossian). +* GC_PRINT_BACK_HEIGHT would deadlock with thread support. +* Let in_progress_space in backgraph.s grow dynamically. +* Fix README.solaris2. The GC_thr_init() hack doesn't work anymore. +* Convert GC_finalizer_mem_freed to bytes in allchblk.c. +* Add missing declaration for GC_generic_malloc_words_small_inner. +Without it, s390x breaks. (Thanks to Ulrich Weigand.) +* Applied several MacOSX patches to support older tool chains (thanks +to Stefan Ring). +* Bug fix for NetBSD/x64 (thanks to Marc Recht). +* Add NetBSD/sh3 support (thanks to Uchiyama Yasushi). +* Fixed an uninitialized variable in cordprnt.c. +* Eliminated some, but not all, gcc -Wall warnings. +* Changed some old style casts to reinterpret_cast in new_gc_alloc.h +(thanks to Dan Grayson). +* GC_extend_size_map shouldn't adjust for GC_all_interior_pointers if +GC_DONT_ADD_BYTE_AT_END is set. +* Changed some (long) casts to (word) in preparation for Win64 (thanks +to Peter Colson). +* Changed "int stack_size" declaration in pthread_support.c to use +size_t. (Only mattered with GC_ASSERTIONS enabled.) +* Added CRIS (etrax) support (thanks to Simon Posnjak and Hans-Peter Nilsson). +* Removed GC_IGNORE_FB frame buffer recognition, and replaced +it with a check that the mapping type is MEM_IMAGE. +In theory, this should work much better, but it is a high +risk change for win32. (Thanks to Ashley Bone for the crucial +experimental data behind this, and to Rutger Ovidius for +some further experiments.) +* GC_allochblk_nth incremented GC_words_wasted by bytes rather than +words. +* Consider GC_words_wasted in GC_adj_words_allocd only if it is within +reason. (A hack to avoid some extremely unlikely scenarios in which +we manage to allocate only "wasted" space. 7.0 has a better fix.) +* Changed PowerPC GC_clear implementation to use lwsync instead of +eieio, since the documentation recommends against eieio, and +it seems to be incorrect if the preceding memory op is a load. +* Fixed print_block_list to print the correct kind number for +STUBBORN (thanks to Rutger Ovidius). +* Have configure.in generate an error if it is asked to support +pthreads, but doesn't know how to. +* Added Kazuhiro Inaoka's patch for Renesas M32R support. +* Have the GNU build mechanism link with -ldl. Rename THREADLIBS +to THREADDLLIBS to reflect this. (Thanks to Sven Verdoolaege.) +* Added Hannes Mehnert's patch for FreeBSD/SPARC support. +* Merged some FreeBSD specific patches to threadlibs.c and dyn_load.c. +(Thanks to John Merryweather Cooper.) +* Define MPROTECT_VDB on MACOSX only if threads are being used, since the +dirty page tracking mechanism uses threads. (This avoids an undefined +reference to _GC_darwin_register_mach_handler_thread.) +* By popular demand, use __libc symbols only if we are built with +USE_LIBC_PRIVATES, which is off by default, and not otherwise documented. +* Ignore GC_enable_incremental() requests when KEEP_BACK_PTRS is set. +The GC itself will dirty lots of pages in this cases, probably making +it counterproductive on all platforms. And the DARWIN port crashes. + + +== [6.3] 2004-07-08 == + +* Compile test_cpp.cc with CXXCOMPILE instead of COMPILE. +* Very large allocations could cause a collector hang. Correct +calculation of GC_collect_at_heapsize. +* GC_print_hblkfreelist printed some bogus results if USE_MUNMAP +was defined. +* Include gc_config_macros.h in threadlibs.c. +* Correct MacOSX thread stop code (thanks to Dick Porter). +* SMALL_OBJ definition was off by one. This could cause crashes +at startup. (Thanks to Zoltan Varga for narrowing this down to +a trivial test case.) +* Integrate Paolo Molaro's patch to deal with a race in the Darwin +thread stopping code. +* Changed X86_64 implementation to use SA_SIGINFO in the MPROTECT_VDB +implementation. The old approach appears to have been broken by +recent kernels. +* Added GC_ATTR_UNUSED to eliminate a warning in gc_allocator.h (thanks +to Andrew Begel). +* Fix GC_task_self declaration in os_dep.c (thanks to Andrew Pinski). +* Increase INITIAL_BUF_SZ in os_dep.c for Solaris /proc reads. + + +== [6.3alpha6] 2004-05-06 == + +* Define USE_GENERIC_PUSH_REGS for NetBSD/M68K. +* Fixed the x64 PREFETCH macros to correctly handle ia32e (which uses +different prefetch instructions on x64). (Thanks to H.J. Lu.) +* GC_config_macros.h did not correctly define GC_WIN32_THREADS from +GC_THREADS. +* Added simple_example.html. +* Merged Andrew Gray's patch to correctly restore signal handlers on +FreeBSD. +* Merged a patch from Andreas Jaeger to deal with prefetch-related warnings +on x86-64. Added some other casts so that the PREFETCH macros +always get a ptr_t argument. Removed some casts in the PREFETCH +implementations. +* Added a header guard for gc_allocator.h and changed GC_debug_free to +clobber contents of deallocated object (suggested by Jesse Jones). +* The signal masking code in pthread_stop_world.c contained some errors. +In particular SIGSEGV was masked in the handler, in spite of the fact that +it wrote to the heap. This could lead to an uncaught SIGSEGV, which +apparently became much more likely in Linux 2.6. Also fixed some +typos, and reduced code duplication in the same area. +* Remove ltconfig, clean up configure messages for DG/UX (thanks to +Adrian Bunk for the patches). +* Integrated NetBSD/OpenBSD patches from Marc Recht and Matthias Drochner. + + +== [6.3alpha5] 2004-03-30 == + +* Fix & vs && typo in GC_generic_malloc and +GC_generic_malloc_ignore_off_page. (Propagated from the gcc tree.) +* Removed SA_NODEFER hack from NetBSD and Solaris write-protect handler. +(According to Christian Limpach, the NetBSD problem is fixed. +Presumably so is the Solaris 2.3 problem.) +* Removed placement delete from gc_cpp.h for the SGI compiler (thanks +to Simon Gornall for the patch). +* Changed semantics of the GC_IGNORE_FB environment variable, based +on experimentation by Nicolas Cannasse pointing out that the old +interpretation was useless. We still need help in identifying win32 +graphics memory mappings. The current "solution" is a hack. +* Removed "MAKEOVERRIDES =" from Makefile.am and thus Makefile.in. +It probably made more sense in the gcc context. +* Explicitly ensure that NEED_FIND_LIMIT is defined for {Open,Net}BSD/ELF. +* Replaced USE_HPUX_TLS macro by USE_COMPILER_TLS, since gcc often +supports the same extension on various platforms. +* Added some basic (completely untested) defines for Win64, in support +of future work. +* Declared GC_jmp_buf in os_dep.s as JMP_BUF instead of jmp_buf, fixing +a memory overwrite bug on Solaris and perhaps other platforms. +* Added 0 != __libc_stack_end test to GC_linux_stack_base (thanks to +Jakub Jelinek for the patch and explaining the problem). +Otherwise pre-linking could cause the collector to fail. +* Changed default thread local storage implementation to USE_PTHREAD_SPECIFIC +for HP/UX with gcc. The compiler-based implementation appears to work +only with the vendor compiler. +* Export GC_debug_header_size and GC_USR_PTR_FROM_BASE from gc_mark.h, +making client mark code cleaner and less dependent on GC version. +* Export several new procedures and GC_generic_malloc from gc_mark.h +to support user-defined kinds. Use the new procedures to replace existing +code in gcj_mlc.c and typd_mlc.c. +* Added support for GC_BACKTRACES. +* Fixed a remaining problem in CORD_str with signed characters (thanks +to Alexandr Petrosian for the patch). +* Removed supposedly redundant, but very buggy, definitions of finalizer +macros from javaxfc.h. Fortunately this file probably has no users. +The correct declarations were already in gc.h. +* Also need to set GC_in_thread_creation while waiting for GC during +thread termination, since it is also possible to collect from an +unregistered thread in that case. +* Define NO_GETENV for Windows CE, since getenv doesn't appear to exist. +Plus some other minor WinCE fixes (thanks to Alain Novak). +* Added GC_register_describe_type_fn. +* Arrange for debugging finalizer registration to ignore non-heap +registrations, since the regular version of the routine also behaves +that way. +* GC_gcj_malloc and friends need to check for finalizers waiting to be run. +One of the more obscure allocation routines with missing a LOCK() call. +* Fixed cvtres invocations in NT_MAKEFILE and NT_STATIC_THREADS_MAKEFILE +to work with VS.NET. +* Cleaned up GC_INIT calls in test. Updated gc.man to encourage GC_INIT +use in portable code. +* Taught the GC to use libunwind if --enable-full-debug is specified on +IA64 and libunwind is present. +* The USE_MUNMAP code could get confused about the age of a block and +prematurely unmap it. GC_unmap_old had a bug related to wrapping of +GC_gc_no. GC_freehblk and GC_merge_unmapped didn't maintain +hb_last_reclaimed reasonably when blocks were merged. The code was +fixed to reflect original intent, but that may not always be an +improvement. + + +== [6.3alpha4] 2004-01-01 == + +* USE_MMAP was broken by confusion in the code dealing with USE_MMAP_ANON. +* Darwin support was broken in alpha3 as a result of my mis-integration of +Andrew Begel's patches. Fixed with another patch from Andrew Begel. +* A new sanity check in pthread_stop_world.c:GC_push_all_stacks() was +overly aggressive. We may collect from an unregistered thread during +thread creation. Fixed by explicitly checking for that case. (Added +GC_in_thread_creation.) + + +== [6.3alpha3] 2003-12-20 == + +* Removed -DSMALL_CONFIG from BCC_MAKEFILE. +* Changed macros to test for an ARM processor (Patch from Richard Earnshaw.) +* Mostly applied a DJGPP patch from Doug Kaufman. Especially Makefile.dj +had suffered from serious bit rot. +* Rewrote GC_apply_to_maps, eliminating an off-by-one subscript error, +and a call to alloca (for lcc compatibility). +* Changed USE_MUNMAP behavior on POSIX platforms to immediately remap +the memory with PROT_NONE instead of unmapping it. The latter risks +an intervening mmap grabbing the address space out from underneath us. +Updated this code to reflect a cleaner patch from Ulrich Drepper. +* Replaced _T with _Tp in new_gc_alloc.h to avoid a MACOS X conflict. +(Patch from Andrew Begel.) +* Dynamically choose whether or not lock should spin on win32 (thanks +to Maurizio Vairani for the patch). This may be a significant performance +improvement for win32. +* Fix Makefile.direct to actually include NT_STATIC_THREADS_MAKEFILE +in the distribution (thanks to Maurizio Vairani). +* Maybe_install_looping_handler() was accidentally exported, violating +our name space convention. +* Made os_dep.c use sigsetjmp and SA_NODEFER for NetBSD. (Thanks to +Christian Limpach. I generalized the patch to use sigsetjmp on all +UNIX_LIKE platforms, admittedly a slightly risky move. But it may avoid +similar problems on some other platforms. I also cleaned up the definition +of UNIX_LIKE a bit.) +* Integrated Andrew Begel's Darwin threads patch, adjusted according to +some of Fergus Hendersons's comments. (Patch didn't apply cleanly, +errors are possible.) +* Added another test or two for the Intel 8.0 compiler to avoid +confusing it with gcc. The single-threaded collector should now build +with icc, at least on ia64. + + +== [6.3alpha2] 2003-11-04 == + +* Re-enabled I_HOLD_LOCK assertion in aix_irix_threads.h. +* Put back the WINABI qualifier for GC_CreateThread. (Thanks to +Danny Smith for the patch. 6.3alpha1 had the qualifier in one place +but not elsewhere, which was clearly wrong.) +* Sometimes explicitly define __private_extern__ before DARWIN dyld.h +include. (Thanks to Andreas Tobler for posting the patch.) +* Included signal.h from pthread_support.c. Removed GC_looping_handler, +which was dead code. +* GC_find_start was misdeclared by gc_pmark.h if PRINT_BLACK_LIST was +defined (thanks to Glauco Masotti for testing and reporting this). +Changed GC_find_start to never just return 0. According to its +comment it doesn't, and it's unclear that's correct. +* GC_alloc_large had several largely compensating bugs in the +computation of GC_words_wasted. (It was confused about bytes vs. +words in two places.) +* Integrated Slava Sysoltsev's patch to support more recent versions of +the Intel compiler on IA64/Linux. +* Changed win32 spinlock initialization to conditionally set a spin count. +(Emmanual Stumpf pointed out that enabling this makes a large performance +difference on win32 multiprocessors.) Also cleaned up the win32 spinlock +initialization code a bit. +* Fixed thread support for HP/UX/IA64. The register backing store base for +the main thread was sometimes not set correctly. (Thanks to +Laurent Morichetti.) +* Added -DEMPTY_GETENV_RESULTS flag to work around Wine problem. +* Declare GC_stack_alloc and GC_stack_free in solaris_threads.h to +avoid 64-bit size mismatches (thanks to Bernie Solomon). +* Fixed GC_generic_push_regs to avoid a potential and very unfortunate +tail call optimization. This could lead to prematurely reclaimed +objects on configurations that used the generic routine and the new +build infrastructure (which potentially optimizes mach_dep.c). +This was a serious bug, but it's unclear whether it has resulted in +any real failures. +* Fixed CORD_str to deal with signed characters (thanks to Alexandr Petrosian +for noticing the problem and supplying the patch). +* Merged a couple of NOSYS/ECOS tests into os_dep.c from gcj (thanks +to Anthony Green). +* Partially merged a win32 patch from Ben Hutchings, and substantially +revised other parts of win32_threads.c. It had several problems. +Under MinGW with a statically linked library, the main thread was +not registered. Cygwin detached threads leaked thread descriptors. +There were several race conditions. For now, unfortunately the +static threads limit remains, though we increased it, and made table +traversal cost depend on the actual thread count. +There is also still some code duplication with pthread_support.c. +(Thread descriptors did become much smaller, since Ben Hutchings +removed the thread context from them.) +* Integrated a Solaris configure.in patch from Rainer Orth. +* Added GC_IGNORE_FB and associated warning to very partially address +the issue of the collector treating a mapped frame buffer as part +of the root set. (Thanks to David Peroutka for providing some +insight. More would be helpful. Is there anything that can be used +to at least partially identify such memory segments?) + + +== [6.3alpha1] 2003-07-26 == + +* Integrated some NetBSD patches by Marc Recht. These +were already in the NetBSD package. +* GC_pthread_create waited for the semaphore even if pthread_create failed. +(Thanks to Dick Porter for the pthread_support.c patch.) Applied the +analogous fix for aix_irix_threads.c. +* Added Rainer Orth's Tru64 fixes. +* The check for exceeding the thread table size in win32 threadDetach +was incorrect (thanks to Alexandr Petrosian for the patch). +* Applied Andrew Begel's patch to correct some reentrancy issues +with dynamic loading on Darwin. +* GC_CreateThread() was neglecting to duplicate the thread handle in +the table (thanks to Tum Nguyen for the patch). +* Pass +ESdbgasm only on PA-RISC machines with vendor compiler (thanks to +Roger Sayle for the patch). +* Applied more AIX threads patches from Scott Ananian. + + +== [6.2] 2003-06-21 == + +* Integrated a second round of Irix/AIX patches from Dan Bonachea. +Renamed mips_sgi_mach_dep.S back to mips_sgi_mach_dep.s, since it requires +the Irix assembler to do the C preprocessing; gcc -E doesn't work. +* Fixed Makefile.direct for DARWIN (thanks to Manuel Serrano). +* There was a race between GC_pthread_detach and thread exit that could +result in a thread structure being deallocated by GC_pthread_detach +even though it was still needed by the thread exit code (thanks to +Dick Porter for the small test case that allowed this to be debugged). +* Fixed version parsing for non-alpha versions in acinclude.m4 and +version checking in version.h. +* Issues identified (not yet fixed): +- A dynamic libgc.so references dlopen unconditionally, but doesn't link +against libdl. +- GC_proc_fd for Solaris is not correctly updated in response to a +fork() call. Thus incremental collection in the child won't work +correctly. (Thanks to Ben Cottrell for pointing this out.) +- --enable-redirect-malloc is mostly untested and known not to work +on some platforms. +- There seem to be outstanding issues on Solaris/x86, possibly with +finding the data segment starting address. +- Very large root set sizes (> 16 MB or so) could cause the collector +to abort with an unexpected mark stack overflow. (Thanks to +Peter Chubb.) NOT YET FIXED. Workaround is to increase the initial +size. +- The SGI version of the collector marks from mmapped pages, even +if they are not part of dynamic library static data areas. This +causes performance problems with some SGI libraries that use mmap +as a bitmap allocator. NOT YET FIXED. It may be possible to turn +off DYNAMIC_LOADING in the collector as a workaround. It may also +be possible to conditionally intercept mmap and use GC_exclude_static_roots. +The real fix is to walk rld data structures, which looks possible. +- Incremental collector should handle large objects better. Currently, +it looks like the whole object is treated as dirty if any part of it is. + + +== [6.2alpha6] 2003-06-05 == + +* There was an extra underscore in the name of GC_save_registers_in_stack +for NetBSD/SPARC (thanks to Jaap Boender for the patch). +* Integrated Brian Alliet's patch for Darwin. This restructured the +linuxthreads/pthreads support to separate generic pthreads support +from more the system-dependent thread-stopping code. I believe this +should make it easier to eliminate the code duplication between +pthreads platforms in the future. The patch included some other +code cleanups. +* Integrated Dan Bonachea's patch to support AIX threads. This required +substantial manual integration, mostly due to conflicts with other +recent threads changes. It may take another iteration to +get it to work. +* Removed HPUX/PA-RISC support from aix_irix_threads.c. It wasn't used +anyway and it cluttered up the code. And anything we can do to migrate +towards generic pthreads support is a good thing. +* Added a more explicit test for tracing of function arguments to test.c. +* Added Akira Tagoh's PowerPC64 patch. +* Fixed some bit rot in the Cygwin port (thanks to Dan Bonachea for +pointing it out). gc.h now includes just windows.h, not winbase.h. +* Declared GC_save_regs_in_stack() in gc_priv.h. Remove other declarations. +* Changed --enable-cplusplus to use automake consistently. The old way +confused libtool. "Make install" didn't work correctly for the old version. +Previously --enable-cplusplus was broken on cygwin. +* Changed the C version of GC_push_regs to fail at compile time if it is +generated with an empty body. This seems to have been the cause of one +or two subtle failures on unusual platforms. Those failures should +now occur at build time and be easily fixable. + + +== [6.2alpha5] 2003-05-14 == + +* GC_invoke_finalizers could, under rare conditions, set +GC_finalizer_mem_freed to an essentially random value. This could +possibly cause unbounded heap growth for long-running applications +under some conditions. (The bug was introduced in 6.1alpha5, and +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 multi-threaded win32 version only if the +collector is actually built as a dll (thanks to Mohan Embar for +a version of the patch). +* Hide the cygwin threadAttach/Detach functions. They were violating our +namespace rules. +* Fixed an assertion in GC_check_heap_proc. Added GC_STATIC_ASSERT +(thanks again to Ben Hutchings). +* Removed some obsolete definitions for Linux/PowerPC in gcconfig.h. +* CORD_cat was not rebalancing unbalanced trees in some cases, violating +a CORD invariant. Also tweaked the re-balancing rule for +CORD_cat_char_star. (Thanks to Alexandr Petrosian for the bug report +and patch.) +* Added hand-coded structured exception handling support to mark.c. +This should enable support of dynamic libraries under win32 with +gcc-compiled code. (Thanks to Ranjit Mathew for the patch.) +Turned on dynamic library scanning for gcc on Win32. +* Removed some remnants of read wrapping (thanks to Kenneth Schalk). +GC_USE_LD_WRAP was probably broken in recent versions. +* The build could fail on some platforms since gcconfig.h could include +declarations mentioning ptr_t, which was not defined, e.g. when if_mach +was built (thanks to Yann Dirson for pointing this out). Also +cleaned up tests for GC_PRIVATE_H in gcconfig.h a bit. +* The GC_LOOP_ON_ABORT environment variable interfered with incremental +collection, since the write fault handler was erroneously overridden. +Handlers are now set up in the correct order. +* It used to be possible to call GC_mark_thread_local_free_lists() while +the world was not stopped during an incremental GC. This was not safe. +Fortunately, it was also unnecessary. Added GC_world_stopped flag +to avoid it. (This caused occasional crashes in GC_set_fl_marks +with thread local allocation and incremental GC. This probably happened +primarily on old, slow multiprocessors.) +* Allowed overriding of MAX_THREADS in win32_threads.c from the build +command line (thanks to Yannis Bres for the patch). +* Taught the IA64/linux code to determine the register backing store base from +/proc/self/maps after checking the __libc symbol, but before guessing. +(__libc symbols are on the endangered list, and the guess is likely to not +always be right for 2.6 kernels.) Restructured the code to read and parse +/proc/self/maps so it only exists in one place (all platforms). +* The -DUSE_PROC_FOR_LIBRARIES code was broken on Linux. It claimed that it +also registered the main data segment, but didn't actually do so. (I don't +think anyone actually uses this configuration, but ...) +* Made another attempt to get --enablecplusplus to do the right thing. +Since there are unavoidable problems with C programs linking against a +dynamic library that includes C++ code, I separated out the c++ code into +libgccpp. + + +== [6.2alpha4] 2003-03-10 == + +* Use LINUX_STACKBOTTOM for >= glibc2.2 on Linux/MIPS. (See Debian bug +# 177204) +* Integrated Jeff Sturm and Jesse Rosenstock's MACOSX threads patches. +* Integrated Grzegorz Jakacki's substantial GNU build patch. "Make dist" +should now work for the GNU build process. Documentation files +are installed under share/gc. +* Tweaked gc_cpp.h to again support the Borland compiler (thanks to +Rene Girard for pointing out the problems). +* Updated BCC_MAKEFILE (thanks to Rene Girard). +* Added GC_ASSERT check for minimum thread stack size. +* Added --enable-gc-assertions. +* Added some web documentation to the distribution. Updated it in the +process. +* Separate gc_conf_macros.h from gc.h. +* Added generic GC_THREADS client-defined macro to set the appropriate +GC_XXX_THREADS internal macro. (gc_config_macros.h.) +* Add debugging versions of _ignore_off_page allocation primitives. +* Moved declarations of GC_make_closure and GC_debug_invoke_finalizer +from gc.h to gc_priv.h. +* Reset GC_fail_count even if only a small allocation succeeds. +* Integrated Brian Alliet's patch for dynamic library support on Darwin. +* gc_cpp.h's gc_cleanup destructor called GC_REGISTER_FINALIZER_IGNORE_SELF +when it should have called the lower case version, since it was +explicitly computing a base pointer. + + +== [6.2alpha3] 2003-01-30 == + +* Don't include execinfo.h in os_dep.c when it's not needed, and may not +exist. + + +== [6.2alpha2] == + +* Fixed the completely broken FreeBSD code in 6.2alpha1 (thanks to +Hironori Sakamoto for the patch). +* Changed IRIX reference in dbg_mlc.c to IRIX5 (thanks to Marcus Herbert). +* Attempted to work around the problems with .S filenames and the SGI +compiler. (Untested.) +* Worked around an HP/UX make issue with the GNU-style build process. +* Fixed the --enable-cplusplus build machinery to allow builds without +a C++ compiler. (That was always the intent ...) +* Changed the debugging allocation macros to explicitly pass the return +address for Linux and XXXBSD on hardware for which we can't get stack +traces. Use __builtin_return_address(0) to generate it when possible. +Some of the configuration work was cleaned up (good) and moved to gc.h +(bad, but necessary). This should make leak detection more useful +on a number of platforms. (Thanks to Fabian Thylman for the suggestion.) +* Fixed compilation problems in dbg_mlc.c with GC_ADD_CALLER. +* Bumped revision number for dynamic library. + + +== [6.2alpha1] 2003-01-23 == + +* Guard the test for GC_DUMP_REGULARLY in misc.c with +"#ifndef NO_DEBUGGING". Otherwise it fails to build with NO_DEBUGGING +defined. (Thanks to Manuel Serrano.) +* Message about retrying suspend signals was incorrectly generated even when +flag was not set. +* Cleaned up MACOSX/NEXT root registration code. There was apparently a +separate ifdef case in GC_register_data_segments() for no reason. +* Removed MPROTECT_VDB for MACOSX port, based on one negative report. +* Arrange for gc.h and friends to be correctly installed with GNU-style +"make install". +* Enable the GNU-style build facility include C++ support in the library +with --enable-cplusplus (thanks to Thomas Maier for some of the patch). +* Mark from GC_thread_key in linux_threads.c, in case that's allocated +from the garbage collected heap, as it is with our own thread-specific +storage implementation (thanks to Jeff Sturm). +* Mark all free list header blocks if they are heap allocated. This avoids +some unnecessary tracing. And it remains correct if we clear the +root set. (Thanks to Jeff Sturm for identifying the bug.) +* Improved S390/Linux support. Add S390/Linux 64-bit support (thanks to +Ulrich Weigand). +* Corrected the spelling of GC_{M,C}ALLOC_EXPLICTLY_TYPED to +GC_{M,C}ALLOC_EXPLICITLY_TYPED in gc_typed.h. This is technically +an interface change. Based on the fact that nobody reported this, +I suspect/hope there were no clients. +* Cleaned up gc_typed.h so that (1) it adds an extern "C" declaration +when appropriate, (2) doesn't generate references to undefined internal +macros, and (3) allows easier manual construction of descriptors. +* Close the file descriptor used by GC_print_address_map(). +* Set the "close-on-exec" bit for various file descriptors maintained +for the collector's internal use. +* Added a hack to find memory segments owned by the system allocator +under win32. Based on my tests, this tends to eventually find all +segments, though it may take a while. There appear to be cleaner, +but slower solutions under NT/XP. But they rely on an API that's +unsupported under 9X. +* Changed Linux PowerPC stack finding to LINUX_STACKBOTTOM. (Thanks +to Akira Tagoh for pointing out that HEURISTIC1 does not work on +64-bit kernels.) +* Added GC_set_free_space_divisor to avoid some Windows dll issues. +* Added FIXUP_POINTER, POINTER_SHIFT, POINTER_MASK to allow preprocessing +of candidate pointers for tagging, etc. +* Always lock around GC_notify_full_gc(). Simplified code for +invoking GC_notify_full_gc(). +* Changed the way DATASTART is defined on FreeBSD to be robust against +an unmapped page after etext. (Thanks to Hironori Sakamoto for +tracking down the intermittent failure.) +* Made GC_enable() and GC_disable() official. Deprecated direct update +of GC_dont_gc. Changed GC_gcollect to be a noop when garbage collection +is disabled. +* Call GC_register_dynamic_libraries before stopping the world on Linux, +in order to avoid a potential deadlock due to the dl_iterate_phdr lock. +* Introduced a more general mechanism for platform-dependent code to +decide whether the main data segment should be handled separately +from dynamic libraries, or registered by GC_register_dynamic_libraries. +The latter is more reliable and easier on Linux with dl_iterate_phdr. + + +== [6.1] == + +* Added GC_MAXIMUM_HEAP_SIZE environment variable. +* Fix configure.in for MIPS/LINUX (thanks to H.J. Lu). +* Double page hash table size for -DLARGE_CONFIG. +* Integrated Bo Thorsen's x64 support. +* STACKBOTTOM definition for LINUX/MIPS was partially changed back +(thanks to H.J. Lu and Hiroshi Kawashima for resolving this). +* Replaced all occurrences of LINUX_DATA_START in gcconfig.h with +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 +atexit(GC_gcollect) to give us at least one chance to detect leaks. +This may report some very benign leaks, but ... +* Addeded REDIRECT_FREE. It's necessary if we want leak detection with +LD_PRELOAD. +* Defer printing of leaked objects, as for smashed objects. +* Fixed process and descriptor leak in GC_print_callers. Try for +line number even if we got function name.) +* Ported parallel GC support and thread local allocation to Alpha. +Not yet well-tested. +* Added GC_DUMP_REGULARLY and added finalization statistics to GC_dump(). +* Fixed Makefile.am to mention alpha_mach_dep.S instead of the defunct +alpha_mach_dep.s. +* Incorporated a change to new_gc_alloc.h, +which should make it work with gcc3.1. +* Use alpha_mach_dep.S only on Linux. (It's not clear that this is +optimal, but it otherwise didn't build on Tru64. Thanks to Fergus Henderson.) +* Added ifdef to guard free() in os_dep.c. Otherwise we get a +compilation error on Irix (thanks to Dai Sato). +* Added an experimental version of GC_memalign to mallocx.c. This can't +always work, since we don't handle alignment requests in the hblk-level +allocator, and we can't handle arbitrary pointer displacements unless +GC_all_interior_pointers is enabled. But it should work for alignment +requests up to HBLKSIZE. This is not yet documented in the standard +places. +* Finally debugged the OSF1/Tru64 thread support. This needs more testing, +since I needed to add a somewhat unconvincing workaround for signal +delivery issues that I don't yet completely understand. But it does +pass my tests, even in parallel GC mode. Incremental GC support is +disabled if thread support is enabled, due to the signal issues. +* Eliminated name-space-incorrect definition of _cdecl from gc_cpp.h. +* Added GC_debug_malloc_replacement and GC_debug_realloc_replacement +declarations to gc.h. On IA64, this is required for REDIRECT_MALLOC +to work correctly with these. +* Fixed Linux USE_PROC_FOR_LIBRARIES to work with a 64-bit /proc format. + + +== [6.1alpha5] 2002-06-19 == + +* Added GC_finalizer_mem_freed, and changed some of the code that +decided on heap expansion to look at it. Memory explicitly +deallocated by finalizers essentially needs to be counted as reclaimed +by the GC. Otherwise there are cases in which the heap can grow +infinitely. (Thanks to Mark Reichert for the test case.) +* Integrated Adam Megacz patches to not scan dynamic libraries if +we are compiling with gcc on win32. Otherwise we need structured +exception handling to deal with asynchronously unmapped root +segments, and gcc doesn't directly support that. +* Integrated Anthony Green's patch to support Wine. +* GC_OPERATOR_NEW_ARRAY was misspelled OPERATOR_NEW_ARRAY in several +places, including gc_cpp.cc (thanks to Wink Saville for pointing this out). +* Integrated Loren J. Rittle's Alpha FreeBSD patches. These also +changed the declarations of symbols like _end on many platforms to +that they wouldn't mistakenly be declared as short data symbols (suggested by +Richard Henderson). +* Integrated changes from the Debian distribution (thanks to Ryan Murray +for pointing these out). +Fix C++ comments in POWERPC port. Add ARM32 +incremental GC support. Get rid of USE_GENERIC_PUSH_REGS for alpha/Linux, +this time for real. Use va_copy to get rid of cord printf problems +(finally). +* Close file descriptor used to count CPUs (thanks to Jeff Sturm for +pointing out the omission). +* Don't just drop gcj free lists in GC_start_reclaim, since that can +eventually cause the marker to see a bogus mark descriptor in the +dropped objects. The usual symptom was a very intermittent segmentation +fault in the marker. This mattered only if one of the GC_gcj_malloc +variants was used (thanks to Michael Smith, Jeff Sturm, Bryce McKinlay and +Tom Tromey for helping to track this down). +* Fixed Linux and Solaris/64 SPARC configuration (thanks to David Miller, +Jeff Sturm, Tom Tromey, and Christian Joensson). +* Fixed a typo in strdup definition (thanks to Gerard A Allan). +* Changed Makefile.direct to invoke $(CC) to assemble alpha_mach_dep.S. +This is needed on Linux. I'm not sure whether it's better or worse +on Tru64. +* Changed gc_cpp.h once more to declare operator new and friends only in +a Microsoft environment. This may need further fine tuning (thanks to +Johannes Schmidt for pointing out that the older code breaks on gcc3.0.4). +* Don't ever override strdup if it's already macro defined (thanks to +Adnan Ali for pointing out the problem). +* Changed gc_cpp.h yet again to also overload placement new. Due to the +C++ overloading rules, the other overloaded new operations otherwise hide +placement new, which causes many STL uses to break (thanks to Reza Shahidi +for reporting this, and to Matt Austern for proposing a fix). +* Integrated cygwin pthreads support from Dan Bonachea. +* Turn on DYNAMIC_LOADING for NetBSD (thanks to Krister Walfridsson). +* Changed printing code to print more complete GC times. +* Applied Mark Mitchell's Irix patch to correct some bit rot. +* Clarified which object-printing routines in dbg_mlc.c should hold +the allocation lock. Restructured the code to allow reasonable object +printing with -DREDIRECT_MALLOC. +* Fix the Linux mmap code to always start with 0x1000 as the initial hint. +Minor patches for 64-bit AIX, particularly to STACKBOTTOM (thanks to +Jeffrey Mark Siskind). +* Renamed "SUSPENDED" flag for Solaris threads support to avoid a conflict +with a system header (thanks to Philip Brown). +* Cause win32_threads.c to handle an out of range stack pointer correctly, +though currently with a warning. (Thanks to Jonathan Clark for +observing that Windows applications may temporarily use the stack +pointer for other purposes, and suggesting a fix. Unfortunately, it's +not clear that there is a complete solution to this problem.) + + +== [6.1alpha4] 2002-06-16 == + +* Fixed typo in sparc_mach_dep.S, preventing the 64-bit version from +building. Increased 64-bit heap size limit in test.c slightly, since +a functional SPARC collector seems to slightly exceed the old limits. +* Use NPRGREG in solaris_threads.c, thus printing all registers if things +go wrong. +* Added GC_MARKERS environment variable to allow use of a single marker +thread on an MP without confusing the lock implementation. +* Collect much less aggressively in incremental mode with GC_TIME_UNLIMITED. +This is really a purely generational mode, and we can afford to +postpone the collection until the heap is (nearly) full. +* Remove read() wrapper for MPROTECT_VDB. It was causing more harm than +good. It is often no longer needed if system calls avoid writing to +pointerful heap objects. +* Fix MACOSX test in gcconfig.h (thanks to John Clements). +* Change GC_test_and_set so that it consistently has one argument. +Add spaces to ::: in powerpc assembly code in gc_locks.h (thanks to +Ryan Murray). +* Fixed a formatting error in dbg_mlc.c. Added prototype to GC_abort() +declaration (thanks to Michael Smith). +* Removed "source" argument to GC_find_start(). Eliminate GC_FIND_START(). +* Added win32 recognition code in configure.in. Changed some of the +dllimport/export defines in gc.h (thanks to Adam Megacz). +* GC_malloc_many didn't set hb_last_reclaimed when it called +GC_reclaim_generic. (I'm not sure this matters much, but ...) +* 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 (thanks to +Wink Saville for pointing this out). +* Added a bit more assertion checking to make sure that gcj objects +on free lists never have a nonzero second word. +* Replaced BCC_MAKEFILE with an up-to-date one (thanks to Andre Leiradella). +* Upgraded libtool, configure.in and some related files to hopefully +support NetBSD/SPARC (thanks to Adrian Bunk). Unfortunately, +libtool 1.4.2 seemed to be buggy due to missing quotes in several +"test" invocations. Fixed those in the ltmain.sh script. +* Some win32-specific patches, including the introduction of +GC_CreateThread (thanks to Adam Megacz). +* Merged in gcj changes from Anthony Green to support embedded systems. +* Tried to consistently rename preprocessed assembly files with a capital +.S extension. +* Use alpha_mach_dep.S on ALPHA again. It doesn't really matter, but this +makes our distribution consistent with the gcc one, avoiding future merge +problems. +* Move GET_MEM definition into gcconfig.h. Include gcconfig.h slightly +later in gc_priv.h to avoid forward references to ptr_t. +* Add some testing of local allocation to test.c. +* Change definition of INVALID_QTID in specific.h. The -1 value was used +inconsistently, and too likely to collide with a valid stack address. +Some general clean-up of specific.[ch]. Added assertions. (Thanks +to Michael Smith for tracking down an intermittent bug to this +general area. I'm not sure it has been squashed yet, however.) +* On Pthread systems it was not safe to call GC_malloc() between fork() +and exec(). According to the applicable standards, it doesn't appear +to be safe to call malloc() or many other libc functions either, thus +it's not clear this is fixable. Added experimental support for +-DHANDLE_FORK in linux_threads.c which tries to support it. It may +succeed if libc does the right thing. I'm not sure whether it does. +(Thanks to Kenneth Schalk for pointing out this issue.) +* Documented thread local allocation primitives to require an +explicit GC_init call. GC_init_parallel is no longer declared to +be a constructor function, since that isn't portable and often +seems to lead to initialization order problems. +* Changed gc_cpp.cc and gc_cpp.h in one more attempt to make them +compatible with Visual C++ 6 (thanks to Wink Saville for the patch). +* 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 +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 +minimally test this. +* On Linux/x86, retry mmap with a different start argument. That should +allow the collector to use more (closer to 3GB) of the address space. +* Force 64 bit alignment with GCJ support (reflects Bryce McKinlay's +patch to the gcc tree). +* Refined the choice of sa_handler vs. sa_sigaction in GC_dirty_init +to accommodate some glibc5 systems (thanks to Dan Fandrich for the patch). +* Compensated for the fact that current versions of glibc set +__libc_stack_end incorrectly on Linux/IA64 while initialization code +is running. This could cause the collector to miss 16 bytes of +the memory stack if GC_malloc or friends where called before main(). +* Mostly integrated Takis Psarogiannakopoulos' port to DG/UX Inix 86. +This will probably take another iteration to work, since his +patch conflicted with the libtool upgrade. +* Added README.arm.cross containing some information about cross- +compiling to an ARM processor from Margaret Fleck (original code provided by +Bradley D. LaRonde; edited by Andrej Cedilnik using some of solutions by +Tilman Vogel; also ported for iPAQ by Oliver Kurth). + + +== [6.1alpha3] 2002-02-07 == + +* Minor cleanup on the gcconfig.h section for SPARC. +* Minor fix to support Intel compiler for Linux/x86 (thanks to +Sven Hartrumpf). +* Added SPARC V9 (64-bit) support (thanks to Jeff Sturm). +* Restructured the way in which we determine whether or not to keep +call stacks for debug allocation. By default SAVE_CALL_COUNT is +now zero on all platforms. Added SAVE_CALL_NARGS parameters. +If possible, use execinfo.h to capture call stack. (This should +add support for a number of new platforms, though often at +considerable runtime expense.) +* Try to print symbolic information for call stacks. On Linux, we +do this with a combination of execinfo.h and running addr2line in +a separate process. This is both much more expensive and much more +useful. Amazingly, it seems to be fast enough for most purposes. +* Redefined strdup if -DREDIRECT_MALLOC is given. +* Changed incremental collector and MPROTECT_VDB implementation so that, +under favorable conditions, pointer-free objects are not protected. +Added GC_incremental_protection_needs() to determine ahead of time whether +pointer-free objects may be protected. Replaced GC_write_hint() with +GC_remove_protection(). +* Added test for GC_ENABLE_INCREMENTAL environment variable. +* Made GC_time_limit runtime configurable. Added GC_PAUSE_TIME_TARGET +environment variable. +* Eliminated GC_page_sz, a duplicate of GC_page_size. +* Caused the Solaris and Irix thread creation primitives to call +GC_init_inner(). + + +== [6.1alpha2] 2001-12-20 == + +* No longer wrap read by default in multi-threaded applications. It was +pointed out on the libgcj list that this holds the allocation lock for +way too long if the read blocks. For now, reads into the heap are +broken with incremental collection. It's possible to turn this back on +if you make sure that read calls don't block (e.g. by calling select +first). +* Fix ifdef in Solaris_threads.h to refer to GC_SOLARIS_THREADS. +* Added check for environment variable GC_IGNORE_GCJ_INFO. +* Added printing of stop-the-world GC times if GC_PRINT_STATS environment +variable is set. +* The calloc definition in leak_detector.h was missing parentheses, and +realloc was missing a second argument to GC_REALLOC (thanks to +Elvenlord Elrond). +* Added GC_PRINT_BACK_HEIGHT environment variable and associated +code, mostly in the new file backgraph.c. See doc/README.environment. +* Added -DUSE_GLOBAL_ALLOC to work around a Windows NT issue (thanks to +Jonathan Clark). +* Integrated port to NEC EWS4800 (MIPS-based workstation, with somewhat +different address-space layout). This may help for other machines with +holes in the data segment. (Thanks to Hironori Sakamoto.) +* Changed the order in which GC_push_roots and friends push things onto +the mark stack. GC_push_all calls need to come first, since we can't +necessarily recover if those overflow the mark stack. (Thanks to +Matthew Flatt for tracking down the problem.) +* Some minor cleanups to mostly support the Intel compiler on Linux/IA64. + + +== [6.1alpha1] 2001-09-22 == + +* Non-debug, atomic allocations could result in bogus smashed object +reports with debugging on (thanks to Patrick Doyle for the small test case). +* Fixed GC_get_register_stack_base (Itanium only) to work around a glibc +2.2.4 bug. +* Initial port to HP/UX on Itanium. Thread support and both 32 and 64 +bit ABIs appear to work. Parallel mark support doesn't yet, due to +some inline assembly code issues. Thread local allocation does appear +to work. +* ifdef'ed out glibc2.1/Itanium workaround. I suspect nobody is using +that combination anymore. +* Added a patch to make new_gc_alloc.h usable with gcc3.0 (thanks to +Dimitris Vyzovitis for the patch). +* Debugged 64-bit support on HP/UX PA-RISC. +* Turned on dynamic loading support for FreeBSD/ELF (thanks to Peter Housel). +* Unregistering of finalizers with debugging allocation was broken (thanks +to Jani Kajala for the test case). +* Old finalizers were not returned correctly from GC_debug_register_finalizer. +* Disabled MPROTECT_VDB for Linux/M68K based on a report that it doesn't work. +* Cleaned up some statistics gathering code in reclaim.c (thanks to +Walter Bright). +* Added some support for OpenBSD/ELF/Linux (thanks to Suzuki Toshiya). +* 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 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 J. Rittle. +It's likely that the cleanup broke something, since it touched lots of +code. It's also likely that it fixed some unreported bugs in the +less common thread implementations, since some of the original code +didn't stand up to close scrutiny. Support for the next pthreads +implementation should be easier to add. + + +== [6.0] 2001-07-26 == + +* Two more bug fixes for KEEP_BACK_PTRS and DBG_HDRS_ALL. +* Fixed a stack clearing problem that resulted in SIGILL with a +misaligned stack pointer for multi-threaded SPARC builds. +* Integrated another HURD patch (thanks to Igor Khavkine). + + +== [6.0alpha9] == + +* added README.macros. +* Made gc.mak a symbolic link to work around winzip's tendency to ignore +hard links. +* Simplified the setting of NEED_FIND_LIMIT in os_dep.c, possibly breaking +it on untested platforms. +* Integrated initial GNU HURD port (thanks to Chris Lingard and +Igor Khavkine). +* A few more fixes for Digital Mars compiler (by Walter Bright). +* Fixed gcc version recognition. Renamed OPERATOR_NEW_ARRAY to +GC_OPERATOR_NEW_ARRAY. Changed GC_OPERATOR_NEW_ARRAY to be the default. +It can be overridden with -DGC_NO_OPERATOR_NEW_ARRAY (thanks to +Cesar Eduardo Barros). +* Changed the byte size to free-list mapping in thread local allocation +so that size 0 allocations are handled correctly. +* Fixed Linux/MIPS stackbottom for new toolchain (thanks to Ryan Murray). +* Changed finalization registration to invoke GC_oom_fn when it runs out +of memory. +* Removed lvalue cast in finalize.c. This caused some debug configurations +not to build with some non-gcc compilers. + + +== [6.0alpha8] 2001-06-15 == + +* Changed GC_debug_malloc_replacement and GC_debug_realloc_replacement +so that they compile under Irix (thanks to Dave Love). +* Updated powerpc_macosx_mach_dep.s so that it works if the collector +is in a dynamic library (thanks to Andrew Begel). +* Transformed README.debugging into debugging.html, updating and +expanding it in the process. Added gcdescr.html and tree.html +from the web site to the GC distribution. +* Fixed several problems related to PRINT_BLACK_LIST. This involved +restructuring some of the marker macros. +* Fixed some problems with the sizing of objects with debug information. +Finalization was broken KEEP_BACK_PTRS or PRINT_BLACK_LIST. Reduced the +object size with SHORT_DEBUG_HDRS by another word. +* The "Needed to allocate blacklisted ..." warning had inadvertently +been turned off by default, due to a buggy test in allchblk.c. Turned +it back on. +* Removed the marker macros to deal with 2 pointers in interleaved fashion. +They were messy and the performance improvement seemed minimal. We'll +leave such scheduling issues to the compiler. +* Changed Linux/PowerPC test to also check for __powerpc__ in response +to a discussion on the gcc mailing list. +* Removed the "static" from the jmp_buf declaration in GC_generic_push_regs +(suggested by Matthew Flatt). This was causing problems in +systems that register all of their own roots. It looks far more correct +to me without the "static" anyway. +* Fixed several problems with thread local allocation of pointer-free or +typed objects. The collector was reclaiming thread-local free lists, since +it wasn't following the link fields. +* There was apparently a long-standing race condition related to +multi-threaded incremental collection. A collection could be started and +a thread stopped between the memory unprotect system call and the setting of +the corresponding dirt bit. I believe this did not affect Solaris or PCR, +which use a different dirty-bit implementation. Fixed this by installing +signal handlers with sigaction instead of signal, and disabling the thread +suspend signal while in the write-protect handler. (It is unclear +whether this scenario ever actually occurred.) +* Incremental collection did not cooperate correctly with the PARALLEL_MARK +implementation of GC_malloc_many or the local_malloc primitives. It still +doesn't work well, but it shouldn't lose memory anymore. +* Integrated some changes from the gcc source tree that I had previously +missed (thanks to Bryce McKinlay for the reminder and patch). +* Added Makefile.direct as a copy of the default Makefile, which would +normally be overwritten if configure is run. +* Changed the gc.tar target in Makefile.direct to embed the version number +in the gc directory name. This will affect future tar file distributions. +* Changed the Irix dynamic library finding code to no longer try to +eliminate writable text segments under Irix6.x, since that is probably no +longer necessary, and can apparently be unsafe on occasion (thanks to +Shiro Kawai for pointing this out). +* GC_cleanup with GC_DEBUG enabled passed a real object base address to +GC_debug_register_finalizer_ignore_self, which expected a pointer past the +debug header. Call GC_register_finalizer_ignore_self instead, even with +debugging enabled (thanks to Jean-Daniel Fekete for catching this). +* The collector didn't build with call chain saving enabled but NARGS=0. +* Fixed up the GNU-style build files enough so that they work in some +obvious cases (thanks to Maarten Thibaut). +* Added initial port to Digital Mars compiler for win32 (thanks to Walter +Bright). + + +== [6.0alpha7] 2001-04-19 == + +* Added GC_finalizer_notifier. Fixed GC_finalize_on_demand. (The variable +actually wasn't being tested at the right points. The build-time flag +was.) +* Added Tom Tromey's S390 Linux patch. +* Added code to push GC_finalize_now in GC_push_finalizer_structures +(thanks to Matthew Flatt). +* Added GC_push_gc_structures() to push all GC internal roots. +* Integrated some FreeBSD changes from Matthew Flatt. +* It looks like USRSTACK is not always correctly defined under Solaris. +Hacked gcconfig.h to attempt to work around the problem. The result +is not well tested. (Thanks again to Matthew Flatt for pointing this out.) +* Added Ji-Yong Chung's win32 threads and C++ fixes. +* Arranged for hpux_test_and_clear.s to no longer be needed or built. +It was causing build problems with gas, and it's not clear this is +better than the pthreads alternative on this platform. +* Some MINGW32 fixes from Hubert Garavel. +* Added initial Hitachi SH4 port from Kaz Kojima. +* Ported thread-local allocation and parallel mark code to HP/UX on PA_RISC. +* Made include/gc_mark.h more public and separated out the really private +pieces. This is probably still not quite sufficient for clients that +want to supply their own kind of type information. But it's a start. +This involved lots of identifier renaming to make it namespace clean. +* Added GC_dont_precollect for clients that need complete control over +the root set. +* GC_is_visible didn't do the right thing with gcj objects. (Not that +many people are likely to care, but ...) +* Don't redefine read with GC_USE_LD_WRAP. +* Initial port to LINUX/HP_PA. Incremental collection and threads are not +yet supported. (Incremental collection should work if you have the +right kernel. Threads may work with a sufficiently patched pthread +library.) +* Changed gcconfig.h to recognize __i386__ as an alternative to x86 in +many places (thanks to Benjamin Lerman). +* Made win32_threads.c more tolerant of detaching a thread that it didn't +know about (thanks to Paul Nash). +* Added Makefile.am and configure.in from gcc to the distribution, with +minimal changes. For the moment, those are just placeholders. In the +future, we're planning to switch to a GNU-style build environment for +Un*x-like systems, though the old Makefile will remain as a backup. +* Turned off STUBBORN_ALLOC by default, and added it back as a Makefile +option. +* Redistributed some functions between malloc.c and mallocx.c, so that +simple statically linked apps no longer pull in mallocx.o. +* Changed large object allocation to clear the first and last few words +of each block before releasing the lock. Otherwise the marker could see +objects with nonsensical type descriptors. +* Fixed a couple of subtle problems that could result in not recognizing +interior pointers from the stack. (I believe these were introduced +in 6.0alpha6.) +* GC_debug_free_inner called GC_free, which tried to reacquire the +allocator lock, and hence deadlocked. (DBG_HDRS_ALL probably never worked +with threads.) +* Fixed several problems with back traces. Accidental references to a free +list could cause the free list pointer to be overwritten by a back pointer. +There seemed to be some problems with the encoding of root and finalizer +references. + + +== [6.0alpha6] == + +* Changed the definition of DATASTART on ALPHA and IA64, where data_start +and __data_start are not defined by earlier versions of glibc. This might +need to be fixed on other platforms as well. +* Changed the way the stack base and backing store base are found on IA64. +This should now remain reliable on future kernels. But since it relies +on /proc, it will no longer work in the simulated NUE environment. +* Made the call to random() in dbg_mlc.c with -DKEEP_BACK_PTRS dependent +on the OS. On non-Unix systems, rand() should be used instead. Handled +small RAND_MAX (thanks to Peter Ross for pointing this out). +* Fixed the cord make rules to create the cord subdirectory, if necessary +(thanks to Doug Moen). +* Changed fo_object_size calculation in finalize.c. Turned finalization +of non-heap object into a no-op. Removed anachronism from GC_size() +implementation. +* Changed GC_push_dirty call in solaris_threads.c to GC_push_selected. +It was missed in a previous renaming (thanks to Vladimir Tsichevski +for pointing this out). +* Arranged to not mask SIGABRT in linux_threads.c (thanks to Bryce McKinlay). +* Added GC_no_dls hook for applications that want to register their own +roots. +* Integrated Kjetil Matheussen's Amiga changes. +* Added FREEBSD_STACKBOTTOM. Changed the FreeBSD/x86 port to use it +(thanks to Matthew Flatt). +* Added pthread_detach interception for platforms supported by linux_threads.c +and irix_threads.c. +* Changed the USE_MMAP code to check for the case in which we got the +high end of the address space, i.e. mem_ptr + mem_sz == 0. It appears +that this can happen under Solaris 7. It seems to be allowed by what +I would claim is an oversight in the mmap specification. (Thanks to +Toshio Endo for pointing out the problem.) +* Cleanup of linux_threads.c. Some code was originally cloned from +irix_threads.c and now unnecessary. Some comments were obviously wrong. +* (Mostly) fixed a longstanding problem with setting of dirty bits from +a signal handler. In the presence of threads, dirty bits could get lost, +since the getting of a bit in the bit vector was not atomic with respect +to other updates. The fix is 100% correct only for platforms for which +GC_test_and_set is defined. The goal is to make that all platforms with +thread support. Matters only if incremental GC and threads are both +enabled. +* made GC_all_interior_pointers (a.k.a. ALL_INTERIOR_POINTERS) an +initialization time, instead of build-time option. This is a +nontrivial, high risk change. It should slow down the code measurably +only if MERGE_SIZES is not defined, which is a very nonstandard +configuration. +* Added doc/README.environment, and implemented what it describes. This +allows a number of additional configuration options to be set through +the environment. It documents a few previously undocumented options. +* Integrated Eric Benson's leak testing improvements. +* Removed the option to throw away the beginning of each page (DISCARD_WORDS). +This became less and less useful as processors enforce stricter alignment. +And it hadn't been tested in ages, and was thus probably broken anyway. + + +== [6.0alpha5] == + +* Changed the definition of GC_pause in linux_threads.c to use a volatile +asm. Some versions of gcc apparently optimize away writes to local volatile +variables. This caused poor locking behavior starting at about +4 processors. +* Added GC_start_blocking(), GC_end_blocking() calls and wrapper for sleep +to linux_threads.c. +The first two calls could be used to generally avoid sending GC signals to +blocked threads, avoiding both premature wakeups and unnecessary overhead. +* Fixed a serious bug in thread-local allocation. At thread termination, +GC_free could get called on small integers. Changed the code for thread +termination to more efficiently return left-over free-lists. +* Integrated Kjetil Matheussen's BeOS support. +* Rearranged the directory structure to create the doc and tests +subdirectories. +* Sort of integrated Eric Benson's patch for OSF1. This provided basic +OSF1 thread support by suitably extending hpux_irix_threads.c. Based +on earlier email conversations with David Butenhof, I suspect that it +will be more reliable in the long run to base this on linux_threads.c +instead. Thus I attempted to patch up linux_threads.c based on Eric's code. +The result is almost certainly broken, but hopefully close enough that +someone with access to a machine can pick it up. +* Integrated lots of minor changes from the NetBSD distribution. (These +were supplied by David Brownlee. I'm not sure about the original +authors.) +* Hacked a bit more on the HP/UX thread-support in linux_threads.c. It +now appears to work in the absence of incremental collection. Renamed +hpux_irix_threads.c back to irix_threads.c, and removed the attempt to +support HPUX there. +* Changed gc.h to define _REENTRANT in cases in which it should already +have been defined. It is still safer to also define it on the command +line. + + +== [6.0alpha4] == + +* Moved up the detection of mostly full blocks to the initiation of the +sweep phase. This eliminates some lock contention in the PARALLEL_MARK case, +as multiple threads try to look at mostly full blocks concurrently. +* Restored the code in GC_malloc_many that grabs a prefix of the global +free list. This avoids the case in which every GC_malloc_many call +tries and fails to allocate a new heap block, and the returns a single +object from the global free list. +* Some minor fixes in new_hblk.c. (Attempted to build free lists in order +of increasing addresses instead of decreasing addresses for cache performance +reasons. But this seems to be only a very minor gain with -DEAGER_SWEEP, +and a loss in other cases. So the change was backed out.) +* Fixed some of the documentation (thanks in large part to Fergus Henderson). +* Fixed the Linux USE_PROC_FOR_LIBRARIES code to deal with apps that perform +large numbers of mmaps (thanks to Eric Benson). Also fixed that code to +deal with short reads. +* Added GC_get_total_bytes(). +* Fixed leak detection mode to avoid spurious messages under linuxthreads. +(This should also now be easy for the other supported threads packages. +But the code is tricky enough that I'm hesitant to do it without being able +to test. Everything allocated in the GC thread support itself should be +explicitly deallocated.) +* Made it possible (with luck) to redirect malloc to GC_local_malloc. + + +== [6.0alpha3] 2000-09-26 == + +* Fixed the /proc/self/maps code to not seek, since that apparently is not +reliable across all interesting kernels. +* Fixed some compilation problems in the absence of PARALLEL_MARK +(introduced in alpha2). +* Fixed an algorithmic problem with PARALLEL_MARK. If work needs to +be given back to the main mark "stack", the BOTTOM entries of the local +stack should be given away, not the top ones. This has substantial +performance impact, especially for > 2 processors, from what I can tell. +* Extracted gc_lock.h from gc_priv.h. This should eventually make it a +bit easier to avoid including gc_priv.h in clients. +* Moved all include files to include/ and removed duplicate links to the +same file. The old scheme was a bad idea because it was too easy to get the +copies out of sync, and many systems don't support hard links. +Unfortunately, it's likely that I broke some of the non-Unix Makefiles in +the process, although I tried to update them appropriately. +* Removed the partial support for a copied nursery. It's not clear that +this would be a tremendous win, since we don't consistently lose to +generational copying collectors. And it would significantly complicate +many things. May be reintroduced if/when it really turns out to win. +* Removed references to IRIX_JDK_THREADS, since I believe there never +were and never will be any clients. +* Added some code to linux_threads.c to possibly support HPUX threads +using the Linux code. Unfortunately, it doesn't work yet, and is +currently disabled. +* Added support under Linux/x86 for saving the call chain, both in (debug) +objects for client debugging, and in GC_arrays._last_stack for GC +debugging. This was previously supported only under Solaris. It is +not enabled by default under x86, since it requires that code be compiled +to explicitly gave frame pointers on the call stack. (With gcc this +currently happens by default, but is often turned off explicitly.) +To turn it on, define SAVE_CALL_CHAIN. + + +== [6.0alpha2] == + +* Added USE_MARK_BYTES to reduce the need for compare-and-swap on platforms +for which that's expensive. +* Fixed a locking bug ib GC_gcj_malloc and some locking assertion problems. +* Added a missing volatile to OR_WORD and renamed the parameter to +GC_compare_and_swap so it's not a C++ reserved word (thanks to +Toshio Endo for pointing out both of those). +* Changed Linux dynamic library registration code to look at /proc/self/maps +instead of the rld data structures when REDIRECT_MALLOC is defined. +Otherwise some of the rld data data structures may be prematurely garbage +collected. +* Fixed USE_LD_WRAP a bit more, so it should now work without threads. +* Renamed XXX_THREADS macros to GC_XXX_THREADS for namespace correctness. +Temporarily added some backward compatibility definitions. Renamed +USE_LD_WRAP to GC_USE_LD_WRAP. +* Many MACOSX POWERPC changes, some additions to the gctest output, and +a few minor generic bug fixes (thanks to Dietmar Planitzer). + + +== [6.0alpha1] == + +* Added HP/PA prefetch support. +* Added -DDBG_HDRS_ALL and -DSHORT_DBG_HDRS to reduce the cost and improve +the reliability of generating pointer backtrace information, e.g. in +the Bigloo environment. +* Added parallel marking support (-DPARALLEL_MARK). This currently +works only under IA32 and IA64 Linux, but it shouldn't be hard to adapt +to other platforms. This is intended to be a lighter-weight (less +new code, probably not as scalable) solution than the work by Toshio Endo +et al, at the University of Tokyo. A number of their ideas were +reused, though the code wasn't, and the underlying data structure +is significantly different. In particular, we keep the global mark +stack as a single shared data structure, but most of the work is done +on smaller thread-local mark stacks. +* Changed GC_malloc_many to be cheaper, and to require less mutual exclusion +with -DPARALLEL_MARK. +* Added full support for thread local allocation under Linux +(-DTHREAD_LOCAL_ALLOC). This is a thin veneer on GC_malloc_many, and +should be easily portable to other platforms, especially those that +support pthreads. +* CLEAR_DOUBLE was not always getting invoked when it should have been. +* GC_gcj_malloc and friends used different out of memory handling than +everything else, probably because I forgot about one when I implemented +the other. They now both call GC_oom_fn(), not GC_oom_action(). +* Integrated Jakub Jelinek's fixes for Linux/SPARC. +* Moved GC_objfreelist, GC_aobjfreelist, and GC_words_allocd out of +GC_arrays, and separately registered the first two as excluded roots. +This makes code compiled with gc_inl.h less dependent on the +collector version. (It would be nice to remove the inclusion of +gc_priv.h by gc_inl.h completely, but we're not there yet. The +locking definitions in gc_priv.h are still referenced.) +This change was later conditioned on SEPARATE_GLOBALS, which +is not defined by default, since it involves a performance hit. +* Register GC_obj_kinds separately as an excluded root region. The +attempt to register it with GC_arrays was usually failing. (This wasn't +serious, but seemed to generate some confusion.) +* Moved backptr.h to gc_backptr.h. + + +== [5.4] == + +* Fixed a typo that prevented compilation with -DUSE_3DNOW_PREFETCH (thanks to +Shawn Wagner for actually testing this). +* Fixed GC_is_thread_stack in solaris_threads.c. It forgot to return a value +in the common case. +* Fixed another silly syntax problem in GC_double_descr (thanks to +Fergus Henderson for finding it). +* Fixed a GC_gcj_malloc bug: It tended to release the allocator lock twice. + + +== [5.3] 2000-09-24 == + +* Fixed _end declaration for OSF1. +* There were lots of spurious leak reports in leak detection mode, caused +by the fact that some pages were not being swept, and hence unmarked +objects weren't making it onto free lists. (This bug dated back to 5.0.) +* Fixed a typo in the liblinuxgc.so Makefile rule. +* Added the GetExitCodeThread to Win32 GC_stop_world to (mostly) work +around a Windows 95 GetOpenFileName problem (thanks to Jacob Navia). + + +== [5.2] == + +* dyn_load.c declared GC_scratch_last_end_ptr as an extern even if it +was defined as a macro. This prevented the collector from building on +Irix. +* We quietly assumed that indirect mark descriptors were never 0. +Our own typed allocation interface violated that. This could result +in segmentation faults in the marker with typed allocation. +* Fixed a _DUSE_MUNMAP bug in the heap block allocation code (thanks to +Ben Hutchings for the patch). +* Taught the collector about VC++ handling array operator new (thanks to +Ben Hutchings for the patch). +* The two copies of gc_hdrs.h had diverged. Made one a link to the other +again. + + +== [5.1] == + +* Fixed a gc.h header bug which showed up under Irix (thanks to Dan Sullivan). +* Fixed a typo in GC_double_descr in typd_mlc.c. +This probably could result in objects described by array descriptors not +getting traced correctly (thanks to Ben Hutchings for pointing this out). +* The block nearly full tests in reclaim.c were not correct for 64 bit +environments. This could result in unnecessary heap growth under unlikely +conditions. + + +== [5.0] == + +* Fixed threadlibs.c for linux threads. -DUSE_LD_WRAP was broken and +-ldl was omitted. Fixed Linux stack finding code to handle +-DUSE_LD_WRAP correctly. +* Added MSWIN32 exception handler around marker, so that the collector +can recover from root segments that are unmapped during the collection. +This caused occasional failures under Windows 98, and may also be +an issue under Windows NT/2000. + + +== [5.0alpha7] == + +* -DREDIRECT_MALLOC was broken in alpha6. Fixed. +* Cleaned up gc_ccp.h slightly, thus also causing the HP C++ compiler to +accept it. +* Removed accidental reference to dbg_mlc.c, which caused dbg_mlc.o to be +linked into every executable. +* Added PREFETCH to bitmap marker. Changed it to use the header cache. +* GC_push_marked sometimes pushed one object too many, resulting in a +segmentation fault in GC_mark_from_mark_stack. This was probably an old +bug. It finally showed up in gctest on win32. +* Gc_priv.h erroneously #defined GC_incremental to be TRUE instead of FALSE +when SMALL_CONFIG was defined. This was no doubt a major performance bug for +the default win32 configuration. +* Removed -DSMALL_CONFIG from NT_MAKEFILE. It seemed like an anachronism now +that the average PC has 64MB or so. +* Integrated Bryce McKinlay's patches for linux threads and dynamic loading +from the libgcj tree. Turned on dynamic loading support for Linux/PPC. +* Changed the stack finding code to use environ on HP/UX (thanks +to Gustavo Rodriguez-Rivera for the suggestion). This should +probably be done on other platforms, too. Since I can't test those, that'll +wait until after 5.0. + + +== [5.0alpha6] == + +* GC_malloc_explicitly_typed and friends sometimes failed to +initialize first word. +* Added allocation routines and support in the marker for mark descriptors +in a type structure referenced by the first word of an object. This was +introduced to support gcj, but hopefully in a way that makes it +generically useful. +* Added GC_requested_heapsize, and inhibited collections in non-incremental +mode if the actual used heap size is less than what was explicitly +requested. +* The Solaris pthreads version of GC_pthread_create didn't handle a NULL +attribute pointer. Solaris thread support used the wrong default thread +stack size (thanks to Melissa O'Neill for the patch). +* Changed PUSH_CONTENTS macro to no longer modify first parameter. +This usually doesn't matter, but it was certainly an accident waiting +to happen ... +* Added GC_register_finalizer_no_order and friends to gc.h. They're +needed by Java implementations. +* Integrated a fix for a win32 deadlock resulting from clock() calling +malloc (thanks to Chris Dodd). +* Integrated Hiroshi Kawashima's port to Linux/MIPS. This was designed +for a handheld platform, and may or may not be sufficient for other +machines. +* Fixed a va_arg problem with the %c specifier in cordprnt.c. It appears +that this was always broken, but recent versions of gcc are the first to +report the (statically detectable) bug. +* Added an attempt at a more general solution to dlopen races/deadlocks. +GC_dlopen now temporarily disables collection. Still not ideal, but ... +* Added -DUSE_I686_PREFETCH, -DUSE_3DNOW_PREFETCH, and support for IA64 +prefetch instructions. May improve performance measurably, but I'm not +sure the code will run correctly on processors that don't support the +instruction. Won't build except with very recent gcc. +* Added caching for header lookups in the marker. This seems to result +in a barely measurable performance gain. Added support for interleaved +lookups of two pointers, but unconfigured that since the performance +gain is currently near zero, and it adds to code size. +* Changed Linux DATASTART definition to check both data_start and +__data_start, since nothing else seems to be portable. +* Added -DUSE_LD_WRAP to optionally take advantage of the GNU ld function +wrapping mechanism. Probably currently useful only on Linux. +* Moved some variables for the scratch allocator into GC_arrays (suggested +by Martin Hirzel). +* Fixed a win32 threads bug that caused the collector to not look for +interior pointers from one of the thread stacks without +ALL_INTERIOR_POINTERS (thanks to Jeff Sturm). +* Added Mingw32 support (thanks to Jeff Sturm for the patch). +* Changed the alpha port to use the generic register scanning code instead +of alpha_mach_dep.s. Alpha_mach_dep.s doesn't look for pointers in fp +registers, but gcc sometimes spills pointers there (thanks to Manuel Serrano +for helping debug this). Changed the IA64 code to +do something similar for similar reasons. + + +== [5.0alpha4] 1999-10-30 == + +* Added protection fault handling patch for Linux/M68K from Fergus +Henderson and Roman Hodek. +* Removed the tests for SGI_SOURCE in new_gc_alloc.h. This was causing that +interface to fail on non-SGI platforms. +* Changed the Linux stack finding code to use /proc, after changing it +to use HEURISTIC1 (thanks to David Mosberger for pointing out /proc hook). +* Added HP/UX incremental GC support and HP/UX 11 thread support. +Thread support is currently still flaky. +* Added basic Linux/IA64 support. +* Integrated Anthony Green's PicoJava support. +* Integrated Scott Ananian's StrongARM/NetBSD support. +* Fixed some fairly serious performance bugs in the incremental +collector. These have probably been there essentially forever. +(Mark bits were sometimes set before scanning dirty pages. +The reclaim phase unnecessarily dirtied full small object pages.) +* Changed the reclaim phase to ignore nearly full pages to avoid +touching them. +* Limited GC_black_list_spacing to roughly the heap growth increment. +* Changed full collection triggering heuristic to decrease full GC +frequency by default, but to explicitly trigger full GCs during +heap growth. This doesn't always improve things, but on average it's +probably a win. +* GC_debug_free(0, ...) failed (thanks to Fergus Henderson for the +bug report and fix). + + +== [5.0alpha3] 1999-09-15 == + +(Also known as 4.15alpha3.) +* Added some highly incomplete code to support a copied young generation. +Comments on nursery.h are appreciated. +* Changed -DFIND_LEAK, -DJAVA_FINALIZATION, and -DFINALIZE_ON_DEMAND, +so the same effect could be obtained with a runtime switch. This is +a step towards standardizing on a single dynamic GC library. +* Significantly changed the way leak detection is handled, as a consequence +of the above. + + +== [5.0alpha2] 1999-07-23 == + +* Fixed bugs introduced in alpha1 (OpenBSD & large block initialization). +* Added -DKEEP_BACK_PTRS and backptr.h interface. (The implementation +idea came from Alan Demers.) + + +== [5.0alpha1] 1999-06-30 == + +(Also known as 4.15alpha1.) +* Reworked large block allocator. Now uses multiple doubly linked free +lists to approximate best fit. +* Changed heap expansion heuristic. Entirely free blocks are no longer +counted towards the heap size. This seems to have a major impact on +heap size stability; the old version could expand the heap way too +much in the presence of large block fragmentation. +* added -DGC_ASSERTIONS and some simple assertions inside the collector. +This is mainlyt for collector debugging. +* added -DUSE_MUNMAP to allow the heap to shrink. Supported on only +a few UNIX-like platforms for now. +* added GC_dump_regions() for debugging of fragmentation issues. +* Changed PowerPC pointer alignment under Linux to 4. +* Changed the Linux/Alpha port to walk the data segment backwards until +it encounters a SIGSEGV. The old way to find the start of the data +segment broke with a recent release. +* cordxtra.c needed to call GC_REGISTER_FINALIZER instead of +GC_register_finalizer, so that it would continue to work with GC_DEBUG. +* allochblk sometimes cleared the wrong block for debugging purposes +when it dropped blacklisted blocks. This could result in spurious +error reports with GC_DEBUG. +* added MACOS X Server support (thanks to Andrew Stone). +* Changed the Solaris threads code to ignore stack limits > 8 MB with +a warning. Empirically, it is not safe to access arbitrary pages +in such large stacks. And the dirty bit implementation does not +guarantee that none of them will be accessed. +* Integrated Martin Tauchmann's Amiga changes. +* Integrated James Dominy's OpenBSD/SPARC port. + + +== [4.14] 1999-04-16 == + +* changed STACKBOTTOM for DJGPP (thanks to Salvador Eduardo Tropea). + + +== [4.14alpha2] 1999-03-26 == + +* -DSMALL_CONFIG did not work reliably with large (> 4K) pages. +Recycling the mark stack during expansion could result in a size +zero heap segment, which confused things. (This was probably also an +issue with the normal config and huge pages.) +* Did more work to make sure that callee-save registers were scanned +completely, even with the setjmp-based code. Added USE_GENERIC_PUSH_REGS +macro to facilitate testing on machines I have access to. +* Added code to explicitly push register contents for win32 threads. +This seems to be necessary. (Thanks to Pierre de Rop.) + + +== [4.14alpha1] 1999-03-10 == + +* Fixed GC_print_source_ptr to not use a prototype. +* generalized CYGWIN test. +* gc::new did the wrong thing with PointerFreeGC placement (thanks to +Rauli Ruohonen). +* In the ALL_INTERIOR_POINTERS (default) case, some callee-save register +values could fail to be scanned if the register was saved and +reused in a GC frame. This showed up in verbose mode with gctest +compiled with an unreleased SGI compiler. I vaguely recall an old +bug report that may have been related. The bug was probably quite old. +(The problem was that the stack scanning could be deferred until +after the relevant frame was overwritten, and the new save location +might be outside the scanned area. Fixed by more eager stack scanning.) +* PRINT_BLACK_LIST had some problems. A few source addresses were garbage. +* Replaced Makefile.dj and added -I flags to cord make targets (thanks to +Gary Leavens). +* GC_try_to_collect was broken with the non-incremental collector. +* gc_cleanup destructors could pass the wrong address to +GC_register_finalizer_ignore_self in the presence of multiple +inheritance (thanks to Darrell Schiebel). +* Changed PowerPC Linux stack finding code. + + +== [4.13] 1999-02-19 == + +* Fixed a crucial bug in the Watcom port. There was a redundant declaration +of GC_push_one in gc_priv.h. +* Added FINALIZE_ON_DEMAND. +* Fixed some pre-ANSI cc problems in test.c. +* Removed getpagesize() use for Solaris. It seems to be missing in one +or two versions. +* Fixed bool handling for SPARCCompiler version 4.2. +* Fixed some files in include that had gotten unlinked from the main +copy. +* Some RS/6000 fixes (missing casts). (Thanks to Toralf Foerster.) +* Fixed several problems in GC_debug_realloc, affecting mostly the +FIND_LEAK case. +* GC_exclude_static_roots contained a buggy unsigned comparison to +terminate a loop (thanks to Wilson Ho). +* CORD_str failed if the substring occurred at the last possible position. +(Only affects cord users.) +* Fixed Linux code to deal with RedHat 5.0 and integrated Peter Bigot's +os_dep.c code for dealing with various Linux versions. +* Added workaround for Irix pthreads sigaction bug and possible signal +misdirection problems. + + +== [4.13alpha3] 1998-12-08 == + +* Fixed MSWIN32 recognition test, which interfered with cygwin. +* Removed unnecessary gc_watcom.asm from distribution. Removed +some obsolete README.win32 text. +* Added Alpha Linux incremental GC support (thanks to Philipp Tomsich +for code for retrieving the fault address in a signal handler). +Changed Linux signal handler context argument to be a pointer. +* Took care of some new warnings generated by the 7.3 SGI compiler. +* Integrated Phillip Musumeci's FreeBSD/ELF fixes. +* -DIRIX_THREADS was broken with the -o32 ABI (typo in gc_priv.h). + + +== [4.13alpha2] 1998-08-08 == + +* Fixed more Linux threads problems. +* Changed default GC_free_space_divisor to 3 with new large block allocation +(thanks to Matthew Flatt for some measurements that suggest the old +value sometimes favors space too much over time). +* More CYGWIN32 fixes. +* Integrated Tyson Dowd's Linux-M68K port. +* Minor HP PA and DEC UNIX fixes from Fergus Henderson. +* Integrated Christoffe Raffali's Linux-SPARC changes. +* Allowed for one more GC fixup iteration after a full GC in incremental +mode. Some quick measurements suggested that this significantly +reduces pause times even with smaller GC_RATE values. +* Moved some more GC data structures into GC_arrays. This decreases +pause times and GC overhead, but makes debugging slightly less convenient. +* Fixed namespace pollution problem ("excl_table"). +* Made GC_incremental a constant for -DSMALL_CONFIG, hopefully shrinking +that slightly. +* Added some win32 threads fixes. +* Integrated Ivan Demakov and David Stes' Watcom fixes. +* Various other minor fixes contributed by many people. +* Renamed config.h to gcconfig.h, since config.h tends to be used for +many other things. +* Integrated Matthew Flatt's support for 68K MacOS "far globals". +* Fixed up some of the dynamic library Makefile targets for consistency +across platforms. +* Fixed a USE_MMAP typo that caused out-of-memory handling to fail +on Solaris. +* Added code to test.c to test thread creation a bit more. +* Integrated GC_win32_free_heap (as suggested by Ivan Demakov). +* Fixed Solaris 2.7 stack base finding problem. (This may actually +have been done in an earlier alpha release.) + + +== [4.13alpha1] 1998-02-17 == + +* Changed RS6000 STACKBOTTOM. +* Integrated Patrick Beard's Mac changes. +* Alpha1 didn't compile on Irix m.n, m < 6. +* Replaced Makefile.dj with a new one from Gary Leavens. +* Added Andrew Stitcher's changes to support SCO OpenServer. +* Added PRINT_BLACK_LIST, to allow debugging of high densities of false +pointers. +* Added code to debug allocator to keep track of return address +in GC_malloc caller, thus giving a bit more context. +* Changed default behavior of large block allocator to more +aggressively avoid fragmentation. This is likely to slow down the +collector when it succeeds at reducing space cost. +* Integrated Fergus Henderson's CYGWIN32 changes. They are untested, +but needed for newer versions. +* USE_MMAP had some serious bugs. This caused the collector to fail +consistently on Solaris with -DSMALL_CONFIG. +* Added Linux threads support (thanks to Fergus Henderson). + + +== [4.12] 1997-08-26 == + +* Fixed ElfW definition in dyn_load.c. +This prevented the dynamic library support from compiling on some +older ELF Linux systems. +* Fixed UTS4 port (which I apparently mangled during the integration). +(Thanks to Alistair Crooks.) +* "Make C++" failed on Suns with SC4.0, due to a problem with "bool". +Fixed in gc_priv.h. +* Added more pieces for GNU win32 (thanks to Timothy N. Newsham). +The current state of things should suffice for at least some +applications. +* Changed the out of memory retry count handling. (This matters only +if GC_max_retries > 0, which is no longer the default.) +* If a /proc read failed repeatedly, GC_written_pages was not updated +correctly (thanks to Peter Chubb for diagnosing this). +* Under unlikely circumstances, the allocator could infinite loop in +an out of memory situation (thanks to Kenjiro Taura for +identifying the problem and supplying a fix). +* Fixed a syntactic error in the DJGPP code. Also fixed a test program +problem with DJGPP (thanks to Fergus Henderson and Peter Monks). +* Atomic uncollectible objects were not treated correctly by the +incremental collector. This resulted in weird log statistics and +occasional performance problems (thanks to Peter Chubb for pointing this out). +* Fixed some problems resulting from compilers that don't define +__STDC__. In this case void * and char * were used inconsistently +in some cases. (Void * should not have been used at all. If +you have an ANSI superset compiler that does not define __STDC__, +please compile with -D__STDC__=0. Thanks to Manuel Serrano and others +for pointing out the problem.) +* Fixed a compilation problem on Irix with -n32 and -DIRIX_THREADS. +Also fixed some other IRIX_THREADS problems which may or may not have +had observable symptoms. +* Fixed an HP PA compilation problem in dyn_load.c (thanks to +Philippe Queinnec). +* SEGV fault handlers sometimes did not get reset correctly (thanks +to David Pickens). +* Added a fix for SOLARIS_THREADS on Intel (thanks to David Pickens). +This probably needs more work to become functional. +* Fixed struct sigcontext_struct in os_dep.c for compilation under +Linux 2.1.X (thanks to Fergus Henderson). +* Changed the DJGPP STACKBOTTOM and DATASTART values to those ones suggested +(by Kristian Kristensen). These may still not be right, but it is +it is likely to work more often than what was there before. They may +even be exactly right. +* Added a #include to test_cpp.cc. This appears to help +with HP/UX and gcc (thanks to Assar Westerlund). +* Version 4.11 failed to run in incremental mode on recent 64-bit Irix +kernels. This was a problem related to page unaligned heap segments. +Changed the code to page align heap sections on all platforms. +(I had mistakenly identified this as a kernel problem earlier. +It was not.) +* Version 4.11 did not make allocated storage executable, except on +one or two platforms, due to a bug in a #if test (thanks to David Grove +for pointing this out). +* Added sparc_sunos4_mach_dep.s to support Sun's compilers under SunOS4. +* Added GC_exclude_static_roots. +* Fixed the object size mapping algorithm. This shouldn't matter, +but the old code was ugly. +* Heap checking code could die if one of the allocated objects was +larger than its base address. (Unsigned underflow problem. Thanks +to Clay Spence for isolating the problem.) +* Added RS6000 (AIX) dynamic library support and fixed STACK_BOTTOM (thanks +to Fred Stearns). +* Added Fergus Henderson's patches for improved robustness with large +heaps and lots of blacklisting. +* Added Peter Chubb's changes to support Solaris Pthreads, to support +MMAP allocation in Solaris, to allow Solaris to find dynamic libraries +through /proc, to add malloc_typed_ignore_off_page, and a few other +minor features and bug fixes. +* The Solaris 2 port should not use sbrk. I received confirmation from +Sun that the use of sbrk and malloc in the same program is not +supported. The collector now defines USE_MMAP by default on Solaris. +* Replaced the djgpp makefile with Gary Leavens' version. +* Fixed MSWIN32 detection test. +* Added Fergus Henderson's patches to allow putting the collector into +a DLL under GNU win32. +* Added Ivan V. Demakov's port to Watcom C on x86. +* Added Ian Piumarta's Linux/PowerPC port. +* Added PointerFreeGC to the placement options in gc_cpp.h (suggested by +Brian Burton). This is of course unsafe, and may be controversial. +On the other hand, it seems to be needed often enough that it's worth +adding as a standard facility. +* Add Lars Farm's suggestions on building the collector for MacOS. + + +== [4.12alpha2] == + +(Changes not specified.) + + +== [4.11] 1996-12-03 == + +* Rationalized (hopefully) GC_try_to_collect in an incremental collection +environment. It appeared to not handle a call while a collection was in +progress, and was otherwise too conservative. +* Merged GC_reclaim_or_delete_all into GC_reclaim_all to get rid of some +code. +* Added Patrick Beard's Mac fixes, with substantial completely untested +modifications. +* Fixed the MPROTECT_VDB code to deal with large pages and imprecise +fault addresses (as on an UltraSPARC running Solaris 2.5). Note that this +was not a problem in the default configuration, which uses PROC_VDB. +* The DEC Alpha assembly code needed to restore $gp between calls (thanks to +Fergus Henderson for tracking this down and supplying a patch). +* The write command for "de" was completely broken for large files. +I used the easiest portable fix, which involved changing the semantics +so that f.new is written instead of overwriting f. That's safer anyway. +* Added README.solaris2 with a discussion of the possible problems of +mixing the collector's sbrk allocation with malloc/realloc. +* Changed the data segment starting address for SGI machines. The +old code failed under IRIX6. +* Required double word alignment for MIPS. +* Various minor fixes to remove warnings. +* Attempted to fix some Solaris threads problems (reported by Zhiying Chen). +In particular, the collector could try to fork a thread with the +world stopped as part of GC_thr_init. It also failed to deal with +the case in which the original thread terminated before the whole +process did. +* Added -DNO_EXECUTE_PERMISSION. This has a major performance impact +on the incremental collector under Irix, and perhaps under other +operating systems. +* Added some code to support allocating the heap with mmap. This may +be preferable under some circumstances. +* Integrated dynamic library support for HP (thanks to Knut Tvedten). +* Integrated James Clark's win32 threads support, and made a number +of changes to it (many of which suggested by Pontus Rydin). This is still +not 100% solid. +* Integrated Alistair G. Crooks' support for UTS4 running on an Amdahl +370-class machine. +* Fixed a serious bug in explicitly typed allocation. Objects requiring +large descriptors where handled in a way that usually resulted in +a segmentation fault in the marker (thanks to Jeremy Fitzhardinge +for helping to track this down). +* Added partial support for GNU win32 development (thanks to +Fergus Henderson). +* Added optional support for Java-style finalization semantics (thanks to +Patrick Bridges). This is recommended only for Java implementations. +* GC_malloc_uncollectable faulted instead of returning 0 when out of +memory (thanks to Daniel R. Grayson for noticing). +* Calls to GC_base before the collector was initialized failed on a +DEC Alpha (thanks to Matthew Flatt). +* Added base pointer checking to GC_REGISTER_FINALIZER in debugging mode +(thanks to Jeremy Fitzhardinge). +* GC_debug_realloc failed for uncollectible objects (thanks to +Jeremy Fitzhardinge). +* Explicitly typed allocation could crash if it ran out of memory (thanks to +Jeremy Fitzhardinge). +* Added minimal support for a DEC Alpha running Linux. +* Fixed a problem with allocation of objects whose size overflowed +ptrdiff_t. (This now fails unconditionally, as it should.) +* Added the beginning of Irix pthread support. +* Integrated Xiaokun Zhu's fixes for djgpp 2.01. +* Added SGI-style STL allocator support (gc_alloc.h). +* 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 uncollectible objects (thanks +to Peter Chubb for pointing out the problem). +* Added Linux ELF support for dynamic libraries (thanks to Patrick Bridges). +* Changed the Borland cc configuration so that the assembler is not +required. +* Fixed a bug in the C++ test that caused it to fail in 64-bit +environments. + + +== [4.10t3] 1996-11-18 == + +Some changes related to threads support. + + +== [4.10] 1996-02-19 == + +* Fixed a typo around a call to GC_collect_or_expand in alloc.c. It broke +handling of out of memory. (Thanks to Patrick C. Beard for noticing.) + + +== [4.9] 1996-02-12 == + +* More README.debugging fixes. +* Objects ready for finalization, but not finalized in the same GC +cycle, could be prematurely collected. This occasionally happened +in test_cpp. +* Too little memory was obtained from the system for very large +objects. That could cause a heap explosion if these objects were +not contiguous (e.g. under PCR), and too much of them was blacklisted. +* Due to an improper initialization, the collector was too hesitant to +allocate blacklisted objects immediately after system startup. +* Moved GC_arrays from the data into the bss segment by not explicitly +initializing it to zero. This significantly +reduces the size of executables, and probably avoids some disk accesses +on program startup. It's conceivable that it might break a port that I +didn't test. +* Fixed EMX_MAKEFILE to reflect the gc_c++.h to gc_cpp.h renaming which +occurred a while ago. + + +== [4.8] 1995-11-20 == + +* Changed a "comment" in a MacOS specific part of mach_dep.c that caused +gcc to fail on other platforms. + + +== [4.7] 1995-11-18 == + +* Fixed some compilation problems with -DCHECKSUMS (thanks to Ian Searle). +* Updated some Mac specific files (to synchronize with Patrick C. Beard). +* Fixed a serious bug for machines with non-word-aligned pointers. +(Thanks to Patrick C. Beard for pointing out the problem. The collector +should fail almost any conceivable test immediately on such machines.) + + +== [4.6] 1995-11-09 == + +* Added Linux ELF support (thanks to Arrigo Triulzi). +* GC_base crashed if it was called before any other GC_ routines. +This could happen if a gc_cleanup object was allocated outside the heap +before any heap allocation. +* The heap expansion heuristic was not stable if all objects had finalization +enabled. Fixed finalize.c to count memory in finalization queue and +avoid explicit deallocation. Changed alloc.c to also consider this count. +(This is still not recommended. It's expensive if nothing else. Thanks +to John Ellis for pointing this out.) +* GC_malloc_uncollectable(0) was broken (thanks to Phong Vo for pointing +this out). +* The collector didn't compile under Linux 1.3.X (thanks to Fred Gilham for +pointing this out). The current workaround is ugly, but expected to be +temporary. +* Fixed a formatting problem for SPARC stack traces. +* Fixed some '=='s in os_dep.c that should have been assignments. +Fortunately these were in code that should never be executed anyway (thanks +to Fergus Henderson). +* Fixed the heap block allocator to only drop blacklisted blocks in small +chunks. Made BL_LIMIT self adjusting. (Both of these were in response +to heap growth observed by Paul Graham.) +* Fixed the Metrowerks/68K Mac code to also mark from a6 (thanks to +Patrick C. Beard). +* Significantly updated README.debugging. +* Fixed some problems with longjmps out of signal handlers, especially under +Solaris. Added a workaround for the fact that siglongjmp doesn't appear to +do the right thing with -lthread under Solaris. +* Added MSDOS/djgpp port (thanks to Mitch Harris). +* Added "make reserved_namespace" and "make user_namespace". The +first renames ALL "GC_xxx" identifiers as "_GC_xxx". The second is the +inverse transformation. Note that doing this is guaranteed to break all +clients written for the other names. +* descriptor field for kind NORMAL in GC_obj_kinds with ADD_BYTE_AT_END +defined should be -ALIGNMENT not WORDS_TO_BYTES(-1). This is +a serious bug on machines with pointer alignment of less than a word. +* GC_ignore_self_finalize_mark_proc didn't handle pointers to very near the +end of the object correctly. Caused failures of the C++ test on a DEC Alpha +with g++. +* gc_inl.h still had problems. Partially fixed. Added warnings at the +beginning to hopefully specify the remaining dangers. +* Added DATAEND definition to config.h. +* Fixed some of the .h file organization. Fixed "make floppy". + + +== [4.5] 1995-06-14 == + +* Fixed many minor and one major README bugs (thanks to Franklin Chen +for pointing out many of them). +* Fixed ALPHA/OSF/1 dynamic library support (thanks to Jonathan Bachrach). +* Added incremental GC support (MPROTECT_VDB) for Linux (with some +help from Bruno Haible). +* Altered SPARC recognition tests in gc.h and config.h (mostly as +suggested by Fergus Henderson). +* Added basic incremental GC support for win32, as implemented by +Windows NT and Windows 95. GC_enable_incremental is a no-op +under win32s, which doesn't implement enough of the VM interface. +* Added -DLARGE_CONFIG. +* Fixed GC_..._ignore_off_page to also function without +-DALL_INTERIOR_POINTERS. +* (Hopefully) fixed RS/6000 port. (Only the test was broken.) +* Fixed a performance bug in the non-incremental collector running +on machines supporting incremental collection with MPROTECT_VDB +(e.g. SunOS 4, DEC AXP). This turned into a correctness bug under +win32s with win32 incremental collection. (Not all memory protection +was disabled.) +* Fixed some ppcr related bit rot. +* Caused dynamic libraries to be unregistered before re-registering. +The old way turned out to be a performance bug on some machines. +* GC_root_size was not properly maintained under MSWIN32. +* Added -DNO_DEBUGGING and GC_dump. +* Fixed a couple of bugs arising with SOLARIS_THREADS + +REDIRECT_MALLOC. +* Added NetBSD/M68K port (thanks to Peter Seebach). +* Fixed a serious realloc bug. For certain object sizes, the collector +wouldn't scan the expanded part of the object. (Thanks to Clay Spence +for noticing the problem, and helping me to track it down.) + + +== [4.4] 1995-02-18 == + +* ASM_CLEAR_CODE was erroneously defined for HP +PA machines, resulting in a compile error. +* Fixed OS/2 Makefile to create a library (thanks to Mark Boulter). +* Gc_cleanup objects didn't work if they were created on +the stack. Fixed. +* One copy of Gc_cpp.h in the distribution was out of +synch, and failed to document some known compiler +problems with explicit destructor invocation. Partially +fixed. There are probably other compilers on which +gc_cleanup is miscompiled. +* Fixed Makefile to pass C compiler flags to C++ compiler. +* Added Mac fixes. +* Fixed os_dep.c to work around what appears to be +a new and different VirtualQuery bug under newer +versions of win32s. +* GC_non_gc_bytes was not correctly maintained by +GC_free. Fixed (thanks to James Clark). +* Added GC_set_max_heap_size. +* Changed allocation code to ignore blacklisting if it is preventing +use of a very large block of memory. This has the advantage +that naive code allocating very large objects is much more +likely to work. The downside is you might no +longer find out that such code should really use +GC_malloc_ignore_off_page. +* Changed GC_printf under win32 to close and reopen the file +between calls. FAT file systems otherwise make the log file +useless for debugging. +* Added GC_try_to_collect and GC_get_bytes_since_gc. These +allow starting an abortable collection during idle times. +This facility does not require special OS support. (Thanks to +Michael Spertus of Geodesic Systems for suggesting this. It was +actually an easy addition. Kumar Srikantan previously added a similar +facility to a now ancient version of the collector. At the time +this was much harder, and the result was less convincing.) +* Added some support for the Borland development environment (thanks +to John Ellis and Michael Spertus). +* Removed a misfeature from checksums.c that caused unexpected +heap growth (thanks to Scott Schwartz). +* Changed finalize.c to call WARN if it encounters a finalization cycle. +WARN is defined in gc_priv.h to write a message, usually to stdout. +In many environments, this may be inappropriate. +* Renamed NO_PARAMS in gc.h to GC_NO_PARAMS, thus adhering to my own +naming convention. +* Added GC_set_warn_proc to intercept warnings. +* Fixed Amiga port (thanks to Michel Schinz). +* Fixed a bug in mark.c that could result in an access to unmapped +memory from GC_mark_from_mark_stack on machines with unaligned +pointers. +* Fixed a win32 specific performance bug that could result in scanning of +objects allocated with the system malloc. +* Added REDIRECT_MALLOC. + + +== [4.3] 1994-12-23 == + +* Fixed SPARC alignment problem with GC_DEBUG. +* Fixed Solaris threads /proc workaround. The real +problem was an interaction with mprotect. +* Incorporated fix from Patrick Beard for gc_c++.h (now gc_cpp.h). +* Slightly improved allocator space utilization by +fixing the GC_size_map mechanism. +* Integrated some Sony News and MIPS RISCos 4.51 +patches (thanks to Nobuyuki Hikichi at Software Research Associates, +Inc., Japan). +* Fixed HP_PA alignment problem (thanks to Brian F. Dennis). +* Added GC_same_obj and friends. Changed GC_base +to return 0 for pointers past the end of large objects. +Improved GC_base performance with ALL_INTERIOR_POINTERS +on machines with a slow integer mod operation. +Added GC_PTR_ADD, GC_PTR_STORE, etc. to prepare +for preprocessor. +* changed the default on most UNIX machines to be that +signals are not disabled during critical GC operations. +This is still ANSI-conforming, though somewhat dangerous +in the presence of signal handlers. But the performance +cost of the alternative is sometimes problematic. +Can be changed back with a minor Makefile edit. +* renamed IS_STRING in gc.h, to CORD_IS_STRING, thus +following my own naming convention. Added the function +CORD_to_const_char_star. +* Fixed a gross bug in GC_finalize. Symptom: occasional +address faults in that function (thanks to Anselm Baird-Smith). +* Added port to ICL DRS6000 running DRS/NX. Restructured +things a bit to factor out common code, and remove obsolete +code. Collector should now run under SUNOS5 with either +mprotect or /proc dirty bits. (Thanks to Douglas Steel.) +* More bug fixes and workarounds for Solaris 2.X. (These were +mostly related to putting the collector in a dynamic library, +which didn't really work before. Also SOLARIS_THREADS +didn't interact well with dl_open.) (Thanks to Brian Lewis.) +* Fixed a serious performance bug on the DEC Alpha. The text +segment was getting registered as part of the root set. +(Amazingly, the result was still fast enough that the bug +was not conspicuous.) The fix works on OSF/1, version 1.3. +Hopefully it also works on other versions of OSF/1 ... +* Fixed a bug in GC_clear_roots. +* Fixed a bug in GC_generic_malloc_words_small that broke +gc_inl.h (reported by Antoine de Maricourt). +* Fixed some problems with cord/de under Linux. +* Fixed some cord problems, notably with CORD_riter4. +* Added DG/UX port (thanks to Ben A. Mesander). +* Added finalization registration routines with weaker ordering +constraints. (This is necessary for C++ finalization with +multiple inheritance, since the compiler often adds self-cycles.) +* Filled the holes in the SCO port (thanks to Michael Arnoldus). +* Completely rewritten the documentation in the interface gc_c++.h +(later renamed gc_cpp.h) making it both clearer and more precise (done by +John Ellis). +* The definition of accessibility now ignores pointers from a finalizable +object (an object with a clean-up function) to itself (done by John Ellis). +This allows objects with virtual base classes to be finalizable by the +collector. Compilers typically implement virtual base classes using +pointers from an object to itself, which under the old definition of +accessibility prevented objects with virtual base classes from ever +being collected or finalized. +* gc_cleanup now includes gc as a virtual base. This was enabled by +the change in the definition of accessibility (by John Ellis). +* Added support for operator new[] (by John Ellis). Since most compilers +don't yet support operator new[], it is conditionalized on +-DOPERATOR_NEW_ARRAY. The code is untested, but it's trivial and looks +correct. +* The test program test_gc_c++ (later renamed test_cpp.cc) +tries to test for the C++-specific functionality not tested by the +other programs. +* Added unistd.h include to misc.c. (Needed for ppcr.) +* Added PowerMac port (thanks to Patrick C. Beard). +* Fixed "srcdir"-related Makefile problems. Changed things so +that all externally visible include files always appear in the +include subdirectory of the source. Made gc.h directly +includable from C++ code (thanks to Per Bothner). +* Changed Intel code to also mark from ebp (thanks to Kevin Warne). +* Renamed C++ related files so they could live in a FAT +file system (thanks to Charles Fiterman). +* Changed Windows NT Makefile to include C++ support in +gc.lib. Added C++ test as Makefile target. + + +== [4.2] 1994-08-03 == + +* Multiple bug fixes/workarounds in the Solaris threads version. +(It occasionally failed to locate some register contents for +marking. It also turns out that thr_suspend and friends are +unreliable in Solaris 2.3. Dirty bit reads appear +to be unreliable under some weird +circumstances. My stack marking code +contained a serious performance bug. The new code is +extremely defensive, and has not failed in several CPU +hours of testing. But no guarantees ...) +* Added MacOS support. (Thanks to Patrick C. Beard. +David Chase suggested several improvements.) +* Fixed several syntactic bugs in gc_c++.h and friends. (These +didn't bother g++, but did bother most other compilers.) +Fixed gc_c++.h finalization interface. +* 64 bit alignment for allocated objects was not guaranteed in a +few cases in which it should have been. +* Added GC_malloc_atomic_ignore_off_page. +* Added GC_collect_a_little. +* Added some prototypes to gc.h. +* Some other minor bug fixes (notably in Makefile). +* Fixed OS/2 / EMX port (thanks to Ari Huttunen). +* Fixed AmigaDOS port (thanks to Michel Schinz). +* Fixed the DATASTART definition under Solaris. There +was a 1 in 16K chance of the collector missing the first +64K of static data (and thus crashing). +* Fixed some blatant anachronisms in the README file. +* Fixed PCR-Makefile for upcoming PPCR release. + + +== [4.1] 1994-05-20 == + +* Changed finalization implementation to guarantee that +finalization procedures are called outside of the allocation +lock, making direct use of the interface a little less dangerous. +MAY BREAK EXISTING CLIENTS that assume finalizers +are protected by a lock. Since there seem to be few multi-threaded +clients that use finalization, this is hopefully not much of +a problem. +* Fixed a gross bug in CORD_prev. +* Fixed a bug in blacklst.c that could result in unbounded +heap growth during startup on machines that do not clear +memory obtained from the OS (e.g. win32s). +* Ported de editor to Win32 and win32s. (This is now the only +version with a mouse-sensitive UI. Thanks to Rob Haack for the +implementation based on the generic Windows application template.) +* Added GC_malloc_ignore_off_page to allocate large arrays +in the presence of ALL_INTERIOR_POINTERS. +* Changed GC_call_with_alloc_lock to not disable signals in +the single-threaded case. +* Reduced retry count in GC_collect_or_expand for garbage +collecting when out of memory. +* Made uncollectible allocations bypass black-listing, as they +should. +* Fixed a bug in typed_test in test.c that could cause (legitimate) +GC crashes. +* Fixed some potential synchronization problems in finalize.c +* Fixed a real locking problem in typd_mlc.c. +* Worked around an AIX 3.2 compiler feature that results in +out of bounds memory references. +* Partially worked around an IRIX5.2 beta problem (which may +or may not persist to the final release). +* Fixed a bug in the heap integrity checking code that could +result in explicitly deallocated objects being identified as +smashed. Fixed a bug in the dbg_mlc stack saving code +that caused old argument pointers to be considered live. +* Fixed a bug in CORD_ncmp (and hence CORD_str). +* Repaired the OS2 port, which had suffered from bit rot +in 4.0. Worked around what appears to be CSet/2 V1.0 +optimizer bug. +* Fixed a Makefile bug for target "c++". + + +== [4.0] 1994-04-07 == + +* Added support for Solaris threads (which was possible +only by reimplementing some fraction of Solaris threads, +since Sun doesn't currently make the thread debugging +interface available). +* Added non-threads Win32 and win32s support. +* (Grudgingly, with suitable muttering of obscenities) renamed +files so that the collector distribution could live on a FAT +file system. Files that are guaranteed to be useless on +a PC still have long names. Gc_inline.h and gc_private.h +still exist, but now just include gc_inl.h and gc_priv.h. +* Fixed a really obscure bug in finalization that could cause +undetected mark stack overflows. (I would be surprised if +any real code ever tickled this one.) +* Changed finalization code to dynamically resize the hash +tables it maintains. (This probably does not matter for well- +-written code. It no doubt does for C++ code that overuses +destructors.) +* Added typed allocation primitives. Rewrote the marker to +accommodate them with more reasonable efficiency. This +change should also speed up marking for GC_malloc allocated +objects a little. See gc_typed.h for new primitives. (Thanks to +Zhong Shao performed much of the experimentation that led to the +current typed allocation facility.) +* Improved debugging facilities slightly. Allocation time +stack traces are now kept by default on SPARC/SUNOS4. (Thanks to +Scott Schwartz.) +* Added better support for small heap applications. +* Significantly extended cord package. Fixed a bug in the +implementation of lazily read files. Printf and friends now +have cord variants. Cord traversals are a bit faster. +* Made ALL_INTERIOR_POINTERS recognition the default. +* Fixed de so that it can run in constant space, independent +of file size. Added simple string searching to cords and de. +* Added the Hull-Ellis C++ interface (supplied by Jesse Hull and John Ellis). +* Added dynamic library support for OSF/1 (thanks to Alan Dosser and +Tim Bingham at DEC). +* Changed argument to GC_expand_hp to be expressed +in units of bytes instead of heap blocks. (Necessary +since the heap block size now varies depending on +configuration. The old version was never very clean.) +* Added GC_get_heap_size(). The previous "equivalent" +was broken. +* Restructured the Makefile a bit. +* Added FreeBSD port (provided by Jeffrey Hsu). + + +== [3.7] 1994-03-15 == + +* Added a workaround for an HP/UX compiler bug. +* Fixed another stack clearing performance bug. Reworked +that code once more. + + +== [3.6] 1994-01-14 == + +* fixed a bug in the mark stack growth code that was introduced +in 3.4. +* fixed Makefile to work around DEC AXP compiler tail recursion +bug. + + +== [3.5] == + +* Minor collections now mark from roots only once, if that +doesn't cause an excessive pause. +* The stack clearing heuristic was refined to prevent anomalies +with very heavily recursive programs and sparse stacks. +* Fixed a bug that prevented mark stack growth in some cases. +GC_objects_are_marked should be set to TRUE after a call +to GC_push_roots and as part of GC_push_marked, since +both can now set mark bits. I think this is only a performance +bug, but I wouldn't bet on it. It's certainly very hard to argue +that the old version was correct. +* Fixed an incremental collection bug that prevented it from +working at all when HBLKSIZE != getpagesize() +* Changed dynamic_loading.c to include gc_priv.h before testing +DYNAMIC_LOADING. SunOS dynamic library scanning +must have been broken in 3.4. +* Object size rounding now adapts to program behavior. +* Added a workaround (provided by Manuel Serrano and +colleagues) to a long-standing SunOS 4.X (and 3.X) ld bug +that I had incorrectly assumed to have been squished. +The collector was broken if the text segment size was within +32 bytes of a multiple of 8K bytes, and if the beginning of +the data segment contained interesting roots. The workaround +assumes a demand-loadable executable. The original may have +have "worked" in some other cases. +* Added dynamic library support under IRIX5. +* Added support for EMX under OS/2 (thanks to Ari Huttunen). +* Added support of Motorola 88K processor running CX/UX (by Brent Benson). + + +== [3.4] == + +* Fixed a performance bug in GC_realloc. +* Updated the amiga port. +* Added NetBSD and 386BSD ports (supplied by Alistair G. Crooks). +* Added cord library. +* Added trivial performance enhancement for +ALL_INTERIOR_POINTERS (do not scan last word). + + +== [3.3] 1993-10-02 == + +* PCR-specific bugs (thanks to Neil Sharman). +* Missing locking in GC_free, redundant FASTUNLOCK +in GC_malloc_stubborn, and 2 bugs in +GC_unregister_disappearing_link (pointed out by Neil Sharman). +* Common symbols allocated by the SunOS4.X dynamic loader +were not included in the root set. +* Bug in GC_finalize (reported by Brian Beuning and Alan Dosser). +* Merged Amiga port from Jesper Peterson (untested). +* Merged NeXT port from Thomas Funke (significantly +modified and untested). (Also thanks to Brian D. Carlstrom for +the supplied the NeXT ports.) + + +== [3.2] == + +Fixed a serious and not entirely repeatable bug in +the incremental collector. It appeared only when dirty bit info +on the roots was available, which is normally only under Solaris. +It also added GC_general_register_disappearing_link, and some +testing code. Interface.c disappeared. + + +== [3.1] == + +* A workaround for a SunOS 4.X SPARC C compiler +misfeature that caused problems when the collector was turned into +a dynamic library. +* A fix for a bug in GC_base that could result in a memory fault. +* A fix for a performance bug (and several other misfeatures) pointed +out by Dave Detlefs and Alan Dosser. +* Use of dirty bit information for static data under Solaris 2.X. +* DEC Alpha/OSF1 support (thanks to Alan Dosser). +* Incremental collection on more platforms. +* 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. +* 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 +objects with debugging allocation. +* Fixed bogus leak reports of size 4096 blocks with FIND_LEAK. + + +== [3.0] == + +Added generational/incremental collection and stubborn objects. + + +== [2.6] 1993-04-27 == + +(Changes not specified.) + + +== [2.5] == + +* Removed an explicit call to exit(1) +* Fixed calls to GC_printf and GC_err_printf, so the correct number of +arguments are always supplied. The OS/2 C compiler gets confused if +the number of actuals and the number of formals differ. (ANSI C +doesn't require this to work. The ANSI sanctioned way of doing things +causes too many compatibility problems.) + + +== [2.4] 1993-01-26 == + +Added GC_free_space_divisor as a tuning knob, added +support for OS/2 and linux, and fixed the following bugs: +* On machines with unaligned pointers (e.g. Sun 3), every 128th word could +fail to be considered for marking. +* Dynamic_load.c erroneously added 4 bytes to the length of the data and +bss sections of the dynamic library. This could result in a bad memory +reference if the actual length was a multiple of a page. (Observed on +Sun 3. Can probably also happen on a Sun 4.) +(Thanks to Robert Brazile for pointing out that the Sun 3 version +was broken. Dynamic library handling is still broken on Sun 3s +under 4.1.1U1, but apparently not 4.1.1. If you have such a machine, +use -Bstatic.) + + +== [2.3] == + +* Added ALL_INTERIOR_POINTERS. +* Missing declaration of etext in the A/UX version. +* Some PCR root-finding problems. +* Blacklisting was not 100% effective, because the plausible future +heap bounds were being miscalculated. +* GC_realloc didn't handle out-of-memory correctly. +* GC_base could return a nonzero value for addresses inside free blocks. +* test.c wasn't really thread safe, and could erroneously report failure +in a multi-threaded environment. (The locking primitives need to be +replaced for other threads packages.) +* GC_CONS was thoroughly broken. +* On a SPARC with dynamic linking, signals stayed disabled while the +client code was running (thanks to Manuel Serrano). + + +== [2.2] == + +* GC_realloc could fail to extend the size of the object for certain large +object sizes. +* A blatant subscript range error in GC_printf, which unfortunately +wasn't exercised on machines with sufficient stack alignment constraints. +* GC_register_displacement did the wrong thing if it was called after +any allocation had taken place. +* The leak finding code would eventually break after 2048 byte +byte objects leaked. +* interface.c didn't compile. +* The heap size remained much too small for large stacks. +* The stack clearing code behaved badly for large stacks, and perhaps +on HP/PA machines. + + +== [2.1] == + +* The first stable version since 1.9. +* Added support for PPCR. + + +== [2.0] == + +* Introduced a consistent naming convention for collector +routines and added support for registering dynamic library data segments +in the standard mark_roots.c (original code supporting the SunOS dynamic +loader provided by Bill Janssen). Most of the data structures were revamped. +The treatment of interior pointers was completely changed. Finalization +was added. Support for locking was added. Object kinds were added. +We added a black listing facility to avoid allocating at addresses known +to occur as integers somewhere in the address space. Much of this +was accomplished by adapting ideas and code from the PCR collector. +The test program was changed and expanded. + + +== [1.9] 1992-01-29 == + +* fixed a major bug in gc_realloc. + + +== [1.8] == + +* added ULTRIX support in gc_private.h. (Robert Brazile originally supplied +the ULTRIX code. Alan Dosser and Regis Cridlig subsequently provided updates +and information on variation between ULTRIX systems.) + + +== [1.5] == + +* ensure 8 byte alignment for objects allocated on a sparc based machine. + + +== [1.4] == + +* Does not use compile time determined values +for the stack base. This no longer works on Sun 3s, since Sun 3/80s use +a different stack base. We now use a straightforward heuristic on all +machines on which it is known to work (incl. Sun 3s) and compile-time +determined values for the rest. There should really be library calls +to determine such values. + + +== [1.3] == + +* Fixed spurious +assembly language assignments to TMP_SP. Only the assignment in the PC/RT +code is necessary. On other machines, with certain compiler options, +the assignments can lead to an unsaved register being overwritten. +Known to cause problems under SunOS 3.5 WITHOUT the -O option. (With +-O the compiler recognizes it as dead code. It probably shouldn't, +but that's another story.) +The SPARC-specific code was originally contributed by Mark Weiser. +The Encore Multimax modifications were supplied by Kevin Kenny. +The adaptation to the IBM PC/RT is largely +due to Vernon Lee, on machines made available to Rice by IBM. +Much of the HP specific code and a number of good suggestions for improving +the generic code are due to Walter Underwood. +Parag Patel supplied the A/UX code. +Manuel Serrano supplied linux and Sony News specific code. diff --git a/bdwgc/Config.cmake.in b/bdwgc/Config.cmake.in new file mode 100644 index 000000000..354f0ae31 --- /dev/null +++ b/bdwgc/Config.cmake.in @@ -0,0 +1,5 @@ +# The BDWgc CMake configuration file. + +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/BDWgcTargets.cmake") +check_required_components(gc) diff --git a/bdwgc/Makefile.am b/bdwgc/Makefile.am new file mode 100644 index 000000000..ea5d7796e --- /dev/null +++ b/bdwgc/Makefile.am @@ -0,0 +1,249 @@ +# Copyright (c) 1999-2001 by Red Hat, Inc. All rights reserved. +# +# 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. + +## Process this file with automake to produce Makefile.in. + +# Info (current:revision:age) for the Libtool versioning system. +# These numbers should be updated at most once just before the release, +# and, optionally, at most once during the development (after the release). +LIBGC_VER_INFO = 6:2:5 +LIBGCCPP_VER_INFO = 6:0:5 + +## FIXME: `make distcheck' in this directory will not currently work. +## This is most likely to the explicit flags passed to submakes. + +# We currently use the source files directly from libatomic_ops, if we +# use the internal version. This is done since libatomic_ops doesn't +# use libtool, since it has no real use for it. But that seems to make +# it hard to use either the resulting object files or libraries. +# Thus there seems to be no real reason to recursively build in the +# libatomic_ops directory. +# if USE_INTERNAL_LIBATOMICS_OPS +# SUBDIRS = @maybe_libatomic_ops@ +# else +# SUBDIRS = +# endif +SUBDIRS = + +ACLOCAL_AMFLAGS = -I m4 +AM_CPPFLAGS = \ + -I$(top_builddir)/include -I$(top_srcdir)/include \ + $(ATOMIC_OPS_CFLAGS) + +## Initialize variables so that we can declare files locally. +EXTRA_DIST = +lib_LTLIBRARIES = +include_HEADERS = +pkginclude_HEADERS = +dist_noinst_HEADERS = +check_PROGRAMS = +check_LTLIBRARIES = +TESTS = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = bdw-gc.pc + +# C Library +# --------- + +lib_LTLIBRARIES += libgc.la + +if SINGLE_GC_OBJ + +libgc_la_SOURCES = extra/gc.c + +if PTHREAD_START_STANDALONE +AM_CPPFLAGS += -DGC_PTHREAD_START_STANDALONE +libgc_la_SOURCES += pthread_start.c +endif + +else + +EXTRA_DIST += extra/gc.c +libgc_la_SOURCES = \ + allchblk.c alloc.c blacklst.c dbg_mlc.c \ + dyn_load.c finalize.c gc_dlopen.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 ptr_chck.c reclaim.c specific.c typd_mlc.c + +# C Library: Architecture Dependent +# --------------------------------- + +if WIN32_THREADS +libgc_la_SOURCES += win32_threads.c +else +if PTHREADS +# Not Cygwin or MinGW. +libgc_la_SOURCES += pthread_start.c pthread_support.c +if DARWIN_THREADS +libgc_la_SOURCES += darwin_stop_world.c +else +libgc_la_SOURCES += pthread_stop_world.c +endif +endif +endif + +if THREAD_LOCAL_ALLOC +libgc_la_SOURCES += thread_local_alloc.c +endif + +if MAKE_BACK_GRAPH +libgc_la_SOURCES += backgraph.c +endif + +if CHECKSUMS +libgc_la_SOURCES += checksums.c +endif + +if ENABLE_GCJ_SUPPORT +libgc_la_SOURCES += gcj_mlc.c +endif + +if ENABLE_DISCLAIM +libgc_la_SOURCES += fnlz_mlc.c +endif + +## End of !SINGLE_GC_OBJ +endif + +if PTHREADS +pkginclude_HEADERS += include/gc_pthread_redirects.h +endif + +if ENABLE_GCJ_SUPPORT +pkginclude_HEADERS += include/gc_gcj.h +endif + +if ENABLE_DISCLAIM +pkginclude_HEADERS += include/gc_disclaim.h +endif + +if USE_INTERNAL_LIBATOMIC_OPS +nodist_libgc_la_SOURCES = libatomic_ops/src/atomic_ops.c +if NEED_ATOMIC_OPS_ASM +nodist_libgc_la_SOURCES += libatomic_ops/src/atomic_ops_sysdeps.S +endif +endif + +# Include THREADDLLIBS here to ensure that the correct versions of +# linuxthread semaphore (and clock_gettime) functions get linked: +libgc_la_LIBADD = @addobjs@ $(THREADDLLIBS) $(UNWINDLIBS) $(ATOMIC_OPS_LIBS) +libgc_la_DEPENDENCIES = @addobjs@ +libgc_la_LDFLAGS = $(extra_ldflags_libgc) -version-info $(LIBGC_VER_INFO) \ + -no-undefined + +EXTRA_libgc_la_SOURCES = ia64_save_regs_in_stack.s sparc_mach_dep.S \ + sparc_netbsd_mach_dep.s + +if CPLUSPLUS +# C++ Interface +# ------------- +lib_LTLIBRARIES += libgccpp.la +pkginclude_HEADERS += include/gc_allocator.h include/gc_cpp.h +include_HEADERS += include/extra/gc_cpp.h +libgccpp_la_SOURCES = gc_badalc.cc gc_cpp.cc +libgccpp_la_LIBADD = libgc.la +libgccpp_la_LDFLAGS = -version-info $(LIBGCCPP_VER_INFO) -no-undefined +if GC_TBA_LIBRARY +# The same as libgccpp but contains only gc_badalc.o. +lib_LTLIBRARIES += libgctba.la +libgctba_la_SOURCES = gc_badalc.cc +libgctba_la_LIBADD = libgc.la +# Set the same version as for libgccpp. +libgctba_la_LDFLAGS = -version-info $(LIBGCCPP_VER_INFO) -no-undefined +endif +endif + +## FIXME: If Visual C++ users use Makefile.am, this should go into +## pkginclude_HEADERS with proper AM_CONDITIONALization. Otherwise +## delete this comment. +EXTRA_DIST += gc_badalc.cpp gc_cpp.cpp + + +# Misc +# ---- + +AM_CXXFLAGS = @GC_CFLAGS@ +AM_CFLAGS = $(WERROR_CFLAGS) @GC_CFLAGS@ + +CFLAGS += $(CFLAGS_EXTRA) +CXXFLAGS += $(CFLAGS_EXTRA) + +## FIXME: relies on internal code generated by automake. +## FIXME: ./configure --enable-dependency-tracking should be used +#all_objs = @addobjs@ $(libgc_la_OBJECTS) +#$(all_objs) : include/private/gcconfig.h include/private/gc_priv.h \ +#include/private/gc_hdrs.h include/gc.h include/gc_gcj.h \ +#include/gc_pthread_redirects.h include/gc_config_macros.h \ +#include/private/thread_local_alloc.h include/private_support.h \ +#include/private/pthread_stop_world.h \ +#include/gc_mark.h @addincludes@ + +## FIXME: we shouldn't have to do this, but automake forces us to. +## We use -Wp,-P to strip #line directives. Irix `as' chokes on +## these. +if ASM_WITH_CPP_UNSUPPORTED + ASM_CPP_OPTIONS = +else + ASM_CPP_OPTIONS = -Wp,-P -x assembler-with-cpp +endif + +.s.lo: + $(LTCOMPILE) $(ASM_CPP_OPTIONS) -c $< + +.S.lo: + $(LTCOMPILE) $(ASM_CPP_OPTIONS) -c $< + +## We need to add DEFS to assembler flags +## :FIXME: what if assembler does not accept -D... ? +## (use Autoconf to prepare ASDEFS?) + +CCASFLAGS += $(DEFS) + +# headers which are not installed +# (see include/include.am for more) +# + +# documentation which is not installed +# +EXTRA_DIST += README.QUICK + +# other makefiles +EXTRA_DIST += NT_MAKEFILE OS2_MAKEFILE PCR-Makefile digimars.mak \ + Makefile.direct SMakefile.amiga WCC_MAKEFILE autogen.sh CMakeLists.txt \ + Config.cmake.in build/s60v3/bld.inf build/s60v3/libgc.mmp + +# files used by makefiles other than Makefile.am +# +EXTRA_DIST += tools/if_mach.c tools/if_not_there.c tools/setjmp_t.c \ + tools/threadlibs.c extra/MacOS.c extra/AmigaOS.c \ + extra/symbian/global_end.cpp extra/symbian/global_start.cpp \ + extra/symbian/init_global_static_roots.cpp extra/symbian.cpp \ + extra/pcr_interface.c extra/real_malloc.c \ + 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 + +# +# :GOTCHA: GNU make rule for making .s out of .S is flawed, +# it will not remove dest if building fails +.S.s: + if $(CPP) $< >$@ ; then :; else rm -f $@; fi + +include include/include.am +include cord/cord.am +include tests/tests.am +include doc/doc.am +## Putting these at the top causes cord to be built first, and not find +## libgc.a on HP/UX. There may be a better fix. + +# A dummy target for mono build. +test-bundle: diff --git a/bdwgc/Makefile.direct b/bdwgc/Makefile.direct new file mode 100644 index 000000000..10fe88111 --- /dev/null +++ b/bdwgc/Makefile.direct @@ -0,0 +1,430 @@ +# This is the original manually generated Makefile. It may still be used +# to build the collector. +# +# Primary targets: +# all - builds gc.a, gccpp.a, gctba.a and cord.a +# base_lib - builds gc.a only (basic library) +# c++ - builds gccpp.a and gctba.a only (C++ interface to library) +# cords - builds cord.a only (heavyweight strings library) +# check - same as "all" but also prints porting information, and runs some +# tests of collector and cords +# check-cpp - builds gc.a, gccpp.a and gctba.a, runs C++ only test +# cord/de - builds dumb editor based on cords. + +ABI_FLAG= +# ABI_FLAG should be the cc flag that specifies the ABI. On most +# platforms this will be the empty string. Possible values: +# +DD64 for 64-bit executable on HP/UX. +# -n32, -n64, -o32 for SGI/MIPS ABIs. + +AS_ABI_FLAG=$(ABI_FLAG) +# ABI flag for assembler. On HP/UX this is +A64 for 64 bit +# executables. + +CC=cc $(ABI_FLAG) +# Compiler executable name. For EMX, replace to "gcc". + +CXX=g++ $(ABI_FLAG) +# Needed only for "make c++", which builds the C++ interface. + +AS=as $(AS_ABI_FLAG) +# The above doesn't work with gas, which doesn't run cpp. +# Define AS as `gcc -c -x assembler-with-cpp' instead. +# Under Irix 6, you have to specify the ABI (-o32, -n32, or -64) +# if you use something other than the default ABI on your machine. + +LD=ld + +# Redefining srcdir allows object code for the nonPCR version of the collector +# to be generated in different directories. +srcdir= . +VPATH= $(srcdir) + +# Path to atomic_ops source. +AO_SRC_DIR=$(srcdir)/libatomic_ops + +CFLAGS_EXTRA= +# We need CFLAGS_FOR_PIC because we might be building a shared library. +CFLAGS= -O -I$(srcdir)/include -I$(AO_SRC_DIR)/src \ + -DALL_INTERIOR_POINTERS -DENABLE_DISCLAIM -DGC_ATOMIC_UNCOLLECTABLE \ + -DGC_GCJ_SUPPORT -DJAVA_FINALIZATION -DNO_EXECUTE_PERMISSION \ + -DUSE_MMAP -DUSE_MUNMAP $(CFLAGS_FOR_PIC) $(CFLAGS_EXTRA) + +# To build the collector with threads support, add to the above: +# -DGC_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC +# +# To build the preload library that intercepts malloc, add: +# -DGC_USE_DLOPEN_WRAP -DREDIRECT_MALLOC=GC_malloc -fpic + +# To build the collector with fork() support by default, add to the above: +# -DHANDLE_FORK + +# HOSTCC and HOSTCFLAGS are used to build executables that will be run as +# part of the build process, i.e. on the build machine. These will usually +# be the same as CC and CFLAGS, except in a cross-compilation environment. +# Note that HOSTCFLAGS should include any -D flags that affect thread support. +HOSTCC=$(CC) +HOSTCFLAGS=$(CFLAGS) + +# For dynamic library builds, it may be necessary to add flags to generate +# PIC code, e.g. -fPIC on Linux. + +# Setjmp_test may yield overly optimistic results when compiled +# without optimization. + +# Look into doc/README.macros for the description of the "define arguments" +# influencing the collector configuration. + +CXXFLAGS= $(CFLAGS) +AR= ar + +RANLIB= ranlib +# For EMX, replace "ranlib" with "ar s". + +# All .o files of libgc.a except for dyn_load.o. +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o \ + headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o \ + malloc.o checksums.o pthread_support.o pthread_stop_world.o \ + darwin_stop_world.o typd_mlc.o ptr_chck.o mallocx.o gcj_mlc.o specific.o \ + gc_dlopen.o backgraph.o win32_threads.o pthread_start.o \ + thread_local_alloc.o fnlz_mlc.o atomic_ops.o atomic_ops_sysdeps.o + +CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c \ + headers.c mark.c obj_map.c blacklst.c finalize.c \ + new_hblk.c dyn_load.c dbg_mlc.c malloc.c \ + checksums.c pthread_support.c pthread_stop_world.c darwin_stop_world.c \ + typd_mlc.c ptr_chck.c mallocx.c gcj_mlc.c specific.c gc_dlopen.c \ + backgraph.c win32_threads.c pthread_start.c thread_local_alloc.c fnlz_mlc.c + +CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/tests/de.c \ + cord/tests/cordtest.c include/cord.h include/ec.h \ + include/cord_pos.h cord/tests/de_win.c cord/tests/de_win.h \ + cord/tests/de_cmds.h cord/tests/de_win.rc + +CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o + +SRCS= $(CSRCS) \ + include/gc.h include/gc_typed.h include/gc_tiny_fl.h \ + include/gc_version.h include/private/gc_hdrs.h include/private/gc_priv.h \ + include/private/gcconfig.h include/private/gc_pmark.h \ + include/gc_inline.h include/gc_mark.h include/gc_disclaim.h \ + tools/threadlibs.c tools/if_mach.c tools/if_not_there.c gc_badalc.cc \ + gc_cpp.cc include/gc_cpp.h include/private/gc_alloc_ptrs.h \ + include/gc_allocator.h include/javaxfc.h include/gc_backptr.h \ + include/gc_gcj.h include/private/gc_locks.h include/private/dbg_mlc.h \ + include/private/specific.h include/leak_detector.h \ + include/gc_pthread_redirects.h include/private/gc_atomic_ops.h \ + include/gc_config_macros.h include/private/pthread_support.h \ + include/private/pthread_stop_world.h include/private/darwin_semaphore.h \ + include/private/darwin_stop_world.h include/private/thread_local_alloc.h \ + ia64_save_regs_in_stack.s sparc_mach_dep.S \ + sparc_netbsd_mach_dep.s $(CORD_SRCS) + +CORD_INCLUDE_FILES= $(srcdir)/include/gc.h $(srcdir)/include/cord.h \ + $(srcdir)/include/ec.h $(srcdir)/include/cord_pos.h + +# Executable file name extension. For EMX, specify ".exe". +EXEEXT= + +UTILS= if_mach$(EXEEXT) if_not_there$(EXEEXT) threadlibs$(EXEEXT) + +# Libraries needed for curses applications. Only needed for de. +# It might also require -ltermlib on some targets. +CURSES= -lcurses + +# The following is irrelevant on most systems. But a few +# versions of make otherwise fork the shell specified in +# the SHELL environment variable. +SHELL= /bin/sh + +SPECIALCFLAGS= -I$(srcdir)/include -I$(AO_SRC_DIR)/src $(CFLAGS_FOR_PIC) +# Alternative flags to the C compiler for mach_dep.c. +# Mach_dep.c often doesn't like optimization, and it's +# not time-critical anyway. +# Set SPECIALCFLAGS to -q nodirect_code on Encore. + +all: base_lib cords c++ + +atomic_ops.o: $(AO_SRC_DIR)/src/atomic_ops.c + $(CC) $(CFLAGS) -c -o $@ $(AO_SRC_DIR)/src/atomic_ops.c +# For some reason, Solaris make does not handle "$<" properly. + +atomic_ops_sysdeps.o: $(AO_SRC_DIR)/src/atomic_ops_sysdeps.S + $(CC) $(CFLAGS) -c -o $@ $(AO_SRC_DIR)/src/atomic_ops_sysdeps.S + +LEAKFLAGS= $(CFLAGS) -DFIND_LEAK + +BSD-pkg-all: bsd-libgc.a bsd-libleak.a + +bsd-libgc.a bsd-libgccpp.a bsd-libgctba.a: + $(MAKE) -f Makefile.direct CFLAGS="$(CFLAGS)" clean c++-t + mv gc.a bsd-libgc.a + mv gccpp.a bsd-libgccpp.a + mv gctba.a bsd-libgctba.a + +bsd-libleak.a: + $(MAKE) -f Makefile.direct CFLAGS="$(LEAKFLAGS)" clean c++-nt + mv gc.a bsd-libleak.a + +BSD-pkg-install: BSD-pkg-all + ${CP} bsd-libgc.a libgc.a + ${INSTALL_DATA} libgc.a ${PREFIX}/lib + ${CP} bsd-libgccpp.a libgccpp.a + ${INSTALL_DATA} libgccpp.a ${PREFIX}/lib + ${CP} bsd-libgctba.a libgctba.a + ${INSTALL_DATA} libgctba.a ${PREFIX}/lib + ${INSTALL_DATA} gc.h gc_cpp.h ${PREFIX}/include + ${INSTALL_MAN} doc/gc.man ${PREFIX}/man/man3/gc.3 + +pcr: PCR-Makefile include/private/gc_private.h include/private/gc_hdrs.h \ + include/private/gc_locks.h include/gc.h include/private/gcconfig.h \ + mach_dep.o $(SRCS) + $(MAKE) -f PCR-Makefile depend + $(MAKE) -f PCR-Makefile + +$(OBJS) tests/test.o dyn_load.o dyn_load_sunos53.o: \ + $(srcdir)/include/private/gc_priv.h \ + $(srcdir)/include/private/gc_hdrs.h $(srcdir)/include/private/gc_locks.h \ + $(srcdir)/include/gc.h $(srcdir)/include/gc_pthread_redirects.h \ + $(srcdir)/include/private/gcconfig.h $(srcdir)/include/gc_typed.h \ + $(srcdir)/include/gc_config_macros.h + +mark.o typd_mlc.o finalize.o ptr_chck.o: $(srcdir)/include/gc_mark.h \ + $(srcdir)/include/private/gc_pmark.h + +specific.o pthread_support.o thread_local_alloc.o win32_threads.o: \ + $(srcdir)/include/private/specific.h $(srcdir)/include/gc_inline.h \ + $(srcdir)/include/private/thread_local_alloc.h + +dbg_mlc.o gcj_mlc.o: $(srcdir)/include/private/dbg_mlc.h + +tests/test.o: $(srcdir)/tests/test.c + mkdir tests || cat /dev/null + $(CC) $(CFLAGS) -c $(srcdir)/tests/test.c + mv test.o tests/test.o + +base_lib gc.a: $(OBJS) dyn_load.o $(UTILS) + rm -f dont_ar_1 + ./if_mach SPARC SOLARIS touch dont_ar_1 + ./if_mach SPARC SOLARIS $(AR) rus gc.a $(OBJS) dyn_load.o + ./if_mach M68K AMIGA touch dont_ar_1 + ./if_mach M68K AMIGA $(AR) -vrus gc.a $(OBJS) dyn_load.o + ./if_not_there dont_ar_1 || $(AR) ru gc.a $(OBJS) dyn_load.o + ./if_not_there dont_ar_1 || $(RANLIB) gc.a || cat /dev/null + echo > base_lib +# Ignore ranlib failure; that usually means it doesn't exist, and +# isn't needed. + +cords cord.a: $(CORD_OBJS) $(UTILS) + rm -f dont_ar_3 + ./if_mach SPARC SOLARIS touch dont_ar_3 + ./if_mach SPARC SOLARIS $(AR) rus cord.a $(CORD_OBJS) + ./if_mach M68K AMIGA touch dont_ar_3 + ./if_mach M68K AMIGA $(AR) -vrus cord.a $(CORD_OBJS) + ./if_not_there dont_ar_3 || $(AR) ru cord.a $(CORD_OBJS) + ./if_not_there dont_ar_3 || $(RANLIB) cord.a || cat /dev/null + echo > cords + +gc_badalc.o: $(srcdir)/gc_badalc.cc $(srcdir)/include/gc_cpp.h \ + $(srcdir)/include/gc.h + $(CXX) -c $(CXXFLAGS) $(srcdir)/gc_badalc.cc + +gc_cpp.o: $(srcdir)/gc_cpp.cc $(srcdir)/include/gc_cpp.h $(srcdir)/include/gc.h + $(CXX) -c $(CXXFLAGS) $(srcdir)/gc_cpp.cc + +test_cpp$(EXEEXT): $(srcdir)/tests/test_cpp.cc $(srcdir)/include/gc_cpp.h \ + $(srcdir)/include/gc.h c++ base_lib $(UTILS) + rm -f test_cpp$(EXEEXT) + ./if_mach HP_PA HPUX $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/tests/test_cpp.cc gc.a gccpp.a -ldld `./threadlibs` + ./if_not_there test_cpp$(EXEEXT) || $(CXX) $(CXXFLAGS) -DGC_NOT_DLL -o test_cpp$(EXEEXT) $(srcdir)/tests/test_cpp.cc gc.a gccpp.a `./threadlibs` + +check-cpp: test_cpp$(EXEEXT) + ./test_cpp + +c++-t: c++ test_cpp$(EXEEXT) + ./test_cpp 1 + +c++-nt: c++ + @echo "Use ./test_cpp 1 to test the leak library" + +c++ gccpp.a gctba.a: gc_badalc.o gc_cpp.o $(UTILS) + rm -f dont_ar_4 + ./if_mach SPARC SOLARIS touch dont_ar_4 + ./if_mach SPARC SOLARIS $(AR) rus gccpp.a gc_badalc.o gc_cpp.o + ./if_mach SPARC SOLARIS $(AR) rus gctba.a gc_badalc.o + ./if_mach M68K AMIGA touch dont_ar_4 + ./if_mach M68K AMIGA $(AR) -vrus gccpp.a gc_badalc.o gc_cpp.o + ./if_mach M68K AMIGA $(AR) -vrus gctba.a gc_badalc.o + ./if_not_there dont_ar_4 || $(AR) ru gccpp.a gc_badalc.o gc_cpp.o + ./if_not_there dont_ar_4 || $(RANLIB) gccpp.a || cat /dev/null + ./if_not_there dont_ar_4 || $(AR) ru gctba.a gc_badalc.o + ./if_not_there dont_ar_4 || $(RANLIB) gctba.a || cat /dev/null + echo > c++ + +dyn_load_sunos53.o: dyn_load.c + $(CC) $(CFLAGS) -DSUNOS53_SHARED_LIB -c $(srcdir)/dyn_load.c -o $@ + +# SunOS5 shared library version of the collector +sunos5gc.so: $(OBJS) dyn_load_sunos53.o + $(CC) -G -o sunos5gc.so $(OBJS) dyn_load_sunos53.o -ldl + ln sunos5gc.so libgc.so + +# Alpha/OSF shared library version of the collector +libalphagc.so: $(OBJS) dyn_load.o + $(LD) -shared -o libalphagc.so $(OBJS) dyn_load.o -lc + ln libalphagc.so libgc.so + +# IRIX shared library version of the collector +libirixgc.so: $(OBJS) dyn_load.o + $(LD) -shared $(ABI_FLAG) -o libirixgc.so $(OBJS) dyn_load.o -lc + ln libirixgc.so libgc.so + +# Linux shared library version of the collector +liblinuxgc.so: $(OBJS) dyn_load.o + gcc -shared -o liblinuxgc.so $(OBJS) dyn_load.o + ln liblinuxgc.so libgc.so + +# Build gctest with dynamic library +dyn_test: + $(CC) $(CFLAGS) -o gctest$(EXEEXT) tests/test.c libgc.so `./threadlibs` + ./gctest + +# Alternative Linux rule. This is preferable, but is likely to break the +# Makefile for some non-linux platforms. +# LIBOBJS= $(patsubst %.o, %.lo, $(OBJS)) +# +#.SUFFIXES: .lo $(SUFFIXES) +# +#.c.lo: +# $(CC) $(CFLAGS) $(CPPFLAGS) -fPIC -c $< -o $@ +# +# liblinuxgc.so: $(LIBOBJS) dyn_load.lo +# gcc -shared -Wl,-soname=libgc.so.0 -o libgc.so.0 $(LIBOBJS) dyn_load.lo +# touch liblinuxgc.so + +mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/sparc_mach_dep.S \ + $(srcdir)/ia64_save_regs_in_stack.s \ + $(srcdir)/sparc_netbsd_mach_dep.s $(UTILS) + rm -f mach_dep.o + ./if_mach SPARC LINUX $(CC) -c -o mach_dep2.o $(srcdir)/sparc_mach_dep.S + ./if_mach SPARC SOLARIS $(CC) -c -o mach_dep2.o $(srcdir)/sparc_mach_dep.S + ./if_mach SPARC OPENBSD $(CC) -c -o mach_dep2.o $(srcdir)/sparc_mach_dep.S + ./if_mach SPARC NETBSD $(AS) -o mach_dep2.o $(srcdir)/sparc_netbsd_mach_dep.s + ./if_mach SPARC "" $(CC) -c -o mach_dep1.o $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + ./if_mach SPARC "" $(LD) -r -o mach_dep.o mach_dep1.o mach_dep2.o + ./if_mach IA64 "" $(AS) -o ia64_save_regs_in_stack.o $(srcdir)/ia64_save_regs_in_stack.s + ./if_mach IA64 "" $(CC) -c -o mach_dep1.o $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + ./if_mach IA64 "" $(LD) -r -o mach_dep.o mach_dep1.o ia64_save_regs_in_stack.o + -./if_not_there mach_dep.o || $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + -./if_not_there mach_dep.o || `cygpath -w /bin/sh` $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + -./if_not_there mach_dep.o || /bin/sh $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + +mark_rts.o: $(srcdir)/mark_rts.c $(UTILS) + rm -f mark_rts.o + -./if_mach ALPHA OSF1 $(CC) -c $(CFLAGS) -Wo,-notail $(srcdir)/mark_rts.c + -./if_not_there mark_rts.o || $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c + -./if_not_there mark_rts.o || `cygpath -w /bin/sh` $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c + -./if_not_there mark_rts.o || /bin/sh $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c +# Work-around for DEC optimizer tail recursion elimination bug. +# The ALPHA-specific line should be removed if gcc is used. + +alloc.o: include/gc_version.h + +cord/cordbscs.o: $(srcdir)/cord/cordbscs.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordbscs.c + mkdir cord || cat /dev/null + mv cordbscs.o cord/cordbscs.o +# not all compilers understand -o filename + +cord/cordxtra.o: $(srcdir)/cord/cordxtra.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordxtra.c + mkdir cord || cat /dev/null + mv cordxtra.o cord/cordxtra.o + +cord/cordprnt.o: $(srcdir)/cord/cordprnt.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordprnt.c + mkdir cord || cat /dev/null + mv cordprnt.o cord/cordprnt.o + +cordtest$(EXEEXT): $(srcdir)/cord/tests/cordtest.c cords base_lib $(UTILS) + rm -f cordtest$(EXEEXT) + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cordtest $(srcdir)/cord/tests/cordtest.c gc.a cord.a -lucb + ./if_mach HP_PA HPUX $(CC) $(CFLAGS) -o cordtest $(srcdir)/cord/tests/cordtest.c gc.a cord.a -ldld `./threadlibs` + ./if_mach M68K AMIGA $(CC) $(CFLAGS) -UGC_AMIGA_MAKINGLIB -o cordtest $(srcdir)/cord/tests/cordtest.c gc.a cord.a `./threadlibs` + ./if_not_there cordtest$(EXEEXT) || $(CC) $(CFLAGS) -o cordtest$(EXEEXT) $(srcdir)/cord/tests/cordtest.c gc.a cord.a `./threadlibs` + +cord/de: de$(EXEEXT) + +de$(EXEEXT): $(srcdir)/cord/tests/de.c $(srcdir)/cord/tests/de_win.c \ + $(srcdir)/cord/tests/de_win.h cords base_lib $(UTILS) + rm -f de$(EXEEXT) + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o de $(srcdir)/cord/tests/de.c gc.a cord.a -lcurses -ltermlib -lucb `./threadlibs` + ./if_mach HP_PA HPUX $(CC) $(CFLAGS) -o de $(srcdir)/cord/tests/de.c gc.a cord.a -lcurses -ltermlib -ldld `./threadlibs` + ./if_mach POWERPC AIX $(CC) $(CFLAGS) -o de $(srcdir)/cord/tests/de.c gc.a cord.a -lcurses + ./if_mach POWERPC DARWIN $(CC) $(CFLAGS) -o de $(srcdir)/cord/tests/de.c gc.a cord.a + ./if_mach M68K AMIGA $(CC) $(CFLAGS) -UGC_AMIGA_MAKINGLIB -o de $(srcdir)/cord/tests/de.c gc.a cord.a -lcurses + ./if_not_there de$(EXEEXT) || $(CC) $(CFLAGS) -o de$(EXEEXT) $(srcdir)/cord/tests/de.c $(srcdir)/cord/tests/de_win.c gc.a cord.a $(CURSES) `./threadlibs` + +if_mach$(EXEEXT): $(srcdir)/tools/if_mach.c \ + $(srcdir)/include/private/gcconfig.h + $(HOSTCC) $(HOSTCFLAGS) -o if_mach$(EXEEXT) $(srcdir)/tools/if_mach.c + +threadlibs$(EXEEXT): $(srcdir)/tools/threadlibs.c \ + $(srcdir)/include/private/gcconfig.h + $(HOSTCC) $(HOSTCFLAGS) -o threadlibs$(EXEEXT) $(srcdir)/tools/threadlibs.c + +if_not_there$(EXEEXT): $(srcdir)/tools/if_not_there.c + $(HOSTCC) $(HOSTCFLAGS) -o if_not_there$(EXEEXT) $(srcdir)/tools/if_not_there.c + +clean: + rm -f *.a *.i *.o *.exe tests/*.o gctest gctest_dyn_link test_cpp \ + setjmp_test mon.out gmon.out a.out core if_not_there if_mach \ + base_lib c++ $(CORD_OBJS) cordtest de cords dont_ar_* threadlibs + -rm -f *~ + +gctest$(EXEEXT): tests/test.o base_lib $(UTILS) + rm -f gctest$(EXEEXT) + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o gctest tests/test.o gc.a -lucb + ./if_mach HP_PA HPUX $(CC) $(CFLAGS) -o gctest tests/test.o gc.a -ldld `./threadlibs` + ./if_mach M68K AMIGA $(CC) $(CFLAGS) -UGC_AMIGA_MAKINGLIB -o gctest tests/test.o gc.a `./threadlibs` + ./if_not_there gctest$(EXEEXT) || $(CC) $(CFLAGS) -o gctest$(EXEEXT) tests/test.o gc.a `./threadlibs` + +# If an optimized setjmp_test generates a segmentation fault, +# odds are your compiler is broken. Gctest may still work. +# Try compiling setjmp_t.c unoptimized. +setjmp_test$(EXEEXT): $(srcdir)/tools/setjmp_t.c $(srcdir)/include/gc.h \ + $(UTILS) + $(CC) $(CFLAGS) -o setjmp_test$(EXEEXT) $(srcdir)/tools/setjmp_t.c + +check: cordtest$(EXEEXT) gctest$(EXEEXT) setjmp_test$(EXEEXT) test_cpp$(EXEEXT) + ./setjmp_test + ./gctest + ./cordtest + ./test_cpp + +# A synonym to "check" (for compatibility with older GC versions). +test: check + +# BTL: added to test shared library version of collector. +# Currently works only under SunOS5. Requires GC_INIT call from statically +# loaded client code. +ABSDIR= `pwd` +gctest_dyn_link: tests/test.o libgc.so + $(CC) -L$(ABSDIR) -R$(ABSDIR) -o gctest_dyn_link tests/test.o -lgc -ldl -lthread + +gctest_irix_dyn_link: tests/test.o libirixgc.so + $(CC) -L$(ABSDIR) -o gctest_irix_dyn_link tests/test.o -lirixgc + +SYM_PREFIX-libgc=GC + +reserved_namespace: $(SRCS) + for file in $(SRCS) tests/test.c tests/test_cpp.cc; do \ + sed s/GC_/_GC_/g < $$file > tmp; \ + cp tmp $$file; \ + done + +user_namespace: $(SRCS) + for file in $(SRCS) tests/test.c tests/test_cpp.cc; do \ + sed s/_GC_/GC_/g < $$file > tmp; \ + cp tmp $$file; \ + done diff --git a/bdwgc/NT_MAKEFILE b/bdwgc/NT_MAKEFILE new file mode 100644 index 000000000..416081883 --- /dev/null +++ b/bdwgc/NT_MAKEFILE @@ -0,0 +1,186 @@ +# Makefile for Windows (Win32/64). Assumes Microsoft compiler. +# Should be invoked as "nmake -f NT_MAKEFILE []"; the optional arguments +# are: "cpu=AMD64" - to target x64, "cpu=i386" - to target x86, +# "enable_static=1" - to build it as a static library, "nodebug=1" - to produce +# the release variant of the library, "disable_threads=1" - to build the +# library and the tests without threads support. + +cc = cl +link = link +rc = rc + +!IF !DEFINED(CPU) || "$(CPU)" == "" +CPU = $(PROCESSOR_ARCHITECTURE) +!ENDIF +!IF "$(CPU)" == "I386" || "$(CPU)" == "X86" || "$(CPU)" == "x86" +CPU = i386 +!ELSEIF "$(CPU)" == "X64" || "$(CPU)" == "x64" || "$(CPU)" == "amd64" +CPU = AMD64 +!ENDIF + +!IF !DEFINED(NMAKE_WINVER) +NMAKE_WINVER = 0x0600 +!ENDIF + +cflags = $(cflags) -c -DCRTAPI1=_cdecl -DCRTAPI2=_cdecl -GS -D_WINNT -W4 +!IF "$(CPU)" == "i386" +cflags = $(cflags) -D_X86_=1 -DWIN32 -D_WIN32 +!ELSEIF "$(CPU)" == "AMD64" +cflags = $(cflags) -D_AMD64_=1 -DWIN64 -D_WIN64 -DWIN32 -D_WIN32 +!ENDIF +cflags = $(cflags) -D_WIN32_WINNT=$(NMAKE_WINVER) -DWINVER=$(NMAKE_WINVER) + +!IFDEF NODEBUG +cvarsmt = -D_MT -MT +cdebug = -Ox -DNDEBUG +rcvars = -DWIN32 -D_WIN32 -DWINVER=$(NMAKE_WINVER) +ldebug = /RELEASE +!ELSE +cvarsmt = -D_MT -MTd +cdebug = -Zi -Od -DDEBUG +rcvars = -DWIN32 -D_WIN32 -DWINVER=$(NMAKE_WINVER) -DDEBUG -D_DEBUG +ldebug = /DEBUG /DEBUGTYPE:cv +!ENDIF + +!IF "$(CPU)" == "i386" +CVTRES_CPU=X86 +!ELSEIF "$(CPU)" == "AMD64" +CVTRES_CPU=X64 +!ENDIF + +!IFNDEF NODEBUG +CFLAGS_DEBUG=-DGC_ASSERTIONS +!ENDIF + +!IFDEF ENABLE_STATIC +CFLAGS_GCDLL=-DGC_NOT_DLL +CORDFLAG= +!ELSE +CFLAGS_GCDLL=-DGC_DLL +# cord.dll and its clients should not link C library statically otherwise +# FILE-related functions might not work (because own set of opened FILEs +# is maintained by each copy of the C library thus making impossible to pass +# FILE pointer from .exe code to .dll code). +cvarsmt= +!IFDEF NODEBUG +CORDFLAG=-MD +!ELSE +CORDFLAG=-MDd +!ENDIF +!ENDIF + +!IFNDEF DISABLE_THREADS +CFLAGS_MT=$(cvarsmt) -DGC_THREADS -DTHREAD_LOCAL_ALLOC -DPARALLEL_MARK +!ENDIF + +CFLAGS_SPECIFIC=$(CFLAGS_DEBUG) $(CFLAGS_GCDLL) $(CFLAGS_MT) + +CFLAGS_DEFAULT=-DALL_INTERIOR_POINTERS -DENABLE_DISCLAIM -DGC_ATOMIC_UNCOLLECTABLE -DGC_GCJ_SUPPORT -DJAVA_FINALIZATION -DNO_EXECUTE_PERMISSION -DUSE_MUNMAP + +CXXFLAGS_SPECIFIC=/EHsc + +# Make sure that .cc is not viewed as a suffix. It is for VC++2005, but +# not earlier versions. We can deal with either, but not inconsistency. +.SUFFIXES: +.SUFFIXES: .obj .cpp .c + +# Atomic_ops installation directory. For Win32, the source directory +# should do, since we only need the headers. +# We assume this was manually unpacked. +AO_SRC_DIR=libatomic_ops/src +AO_INCLUDE_DIR=$(AO_SRC_DIR) + +!IFDEF ENABLE_STATIC +OBJS= misc.obj win32_threads.obj alloc.obj reclaim.obj allchblk.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj obj_map.obj blacklst.obj finalize.obj new_hblk.obj dbg_mlc.obj fnlz_mlc.obj malloc.obj dyn_load.obj typd_mlc.obj ptr_chck.obj gcj_mlc.obj mallocx.obj extra\msvc_dbg.obj thread_local_alloc.obj +!ELSE +OBJS= extra\gc.obj extra\msvc_dbg.obj +!ENDIF + +COBJS= cord\cordbscs.obj cord\cordxtra.obj cord\cordprnt.obj + +all: gc.lib cord.lib gccpp.lib gctba.lib + +check: gctest.exe test_cpp.exe cordtest.exe de.exe + gctest.exe + cordtest.exe + test_cpp.exe + +.c.obj: + $(cc) $(cdebug) $(cflags) $(CFLAGS_SPECIFIC) $(CORDFLAG) -Iinclude -I$(AO_INCLUDE_DIR) $(CFLAGS_DEFAULT) -D_CRT_SECURE_NO_DEPRECATE $*.c /Fo$*.obj /wd4100 /wd4127 /wd4701 +# 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) $(CFLAGS_SPECIFIC) -Iinclude $(CFLAGS_DEFAULT) $(CXXFLAGS_SPECIFIC) -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 + +!IFDEF ENABLE_STATIC + +gc.lib: $(OBJS) + lib /out:gc.lib /MACHINE:$(CPU) $(OBJS) + +cord.lib: $(COBJS) + lib /out:cord.lib /MACHINE:$(CPU) $(COBJS) + +gccpp.lib: gc_badalc.obj gc_cpp.obj + lib /out:gccpp.lib /MACHINE:$(CPU) gc_badalc.obj gc_cpp.obj + +# The same as gccpp.lib but contains only gc_badalc.obj. +gctba.lib: gc_badalc.obj + lib /out:gctba.lib /MACHINE:$(CPU) gc_badalc.obj + +!ELSE + +gc.lib: $(OBJS) + $(link) $(ldebug) kernel32.lib user32.lib /subsystem:windows /dll /INCREMENTAL:NO /pdb:"gc.pdb" /out:gc.dll /implib:gc.lib /MACHINE:$(CPU) $(OBJS) + +cord.lib: $(COBJS) gc.lib + $(link) $(ldebug) gc.lib /subsystem:windows /dll /INCREMENTAL:NO /pdb:"cord.pdb" /out:cord.dll /implib:cord.lib /MACHINE:$(CPU) $(COBJS) + +gccpp.lib: gc_badalc.obj gc_cpp.obj gc.lib + $(link) $(ldebug) gc.lib /subsystem:windows /dll /INCREMENTAL:NO /pdb:"gccpp.pdb" /out:gccpp.dll /implib:gccpp.lib /MACHINE:$(CPU) gc_badalc.obj gc_cpp.obj + +gctba.lib: gc_badalc.obj gc.lib + $(link) $(ldebug) gc.lib /subsystem:windows /dll /INCREMENTAL:NO /pdb:"gctba.pdb" /out:gctba.dll /implib:gctba.lib /MACHINE:$(CPU) gc_badalc.obj + +!ENDIF + +gctest.exe: gc.lib tests\test.obj + $(link) /MACHINE:$(CPU) /INCREMENTAL:NO $(ldebug) $(lflags) user32.lib -out:$*.exe tests\test.obj gc.lib +# mapsympe -n -o gctest.sym gctest.exe +# This produces a GUI app that opens no window and writes to gctest.gc.log. + +cord\tests\de_win.rbj: cord\tests\de_win.res + cvtres /MACHINE:$(CVTRES_CPU) /OUT:cord\tests\de_win.rbj cord\tests\de_win.res + +cord\tests\de.obj cord\tests\de_win.obj: include\cord.h include\cord_pos.h cord\tests\de_win.h cord\tests\de_cmds.h + +cord\tests\de_win.res: cord\tests\de_win.rc cord\tests\de_win.h cord\tests\de_cmds.h + $(rc) $(rcvars) -r -fo cord\tests\de_win.res cord\tests\de_win.rc + +# Cord/de is a real Windows GUI app. +de.exe: cord\tests\de.obj cord\tests\de_win.obj cord\tests\de_win.rbj gc.lib cord.lib + $(link) /MACHINE:$(CPU) /INCREMENTAL:NO $(ldebug) $(lflags) -out:de.exe cord\tests\de.obj cord\tests\de_win.obj cord\tests\de_win.rbj gc.lib cord.lib gdi32.lib user32.lib + +cordtest.exe: cord\tests\cordtest.obj gc.lib cord.lib + $(link) /subsystem:console /MACHINE:$(CPU) /INCREMENTAL:NO $(ldebug) $(lflags) -out:cordtest.exe cord\tests\cordtest.obj gc.lib cord.lib user32.lib + +gc_badalc.obj: gc_badalc.cc include\gc_cpp.h include\gc.h + +gc_cpp.obj: gc_cpp.cc include\gc_cpp.h include\gc.h + +test_cpp.cpp: tests\test_cpp.cc + copy tests\test_cpp.cc test_cpp.cpp + +# This generates the C++ test executable. The executable expects +# a single numeric argument, which is the number of iterations. +# The output appears in test_cpp.gc.log file. +test_cpp.exe: test_cpp.obj include\gc_cpp.h include\gc.h gc.lib gccpp.lib + $(link) /MACHINE:$(CPU) /INCREMENTAL:NO $(ldebug) $(lflags) user32.lib -out:test_cpp.exe test_cpp.obj gc.lib gccpp.lib + +$(AO_SRC_DIR): + tar xvfz $(AO_SRC_DIR).tar.gz + +clean: + del *.dll *.exe *.exp *.lib *.log *.obj *.pdb cordtst*.tmp cord\*.obj cord\tests\*.rbj cord\tests\*.res cord\tests\*.obj extra\*.obj test_cpp.cpp tests\*.obj 2> nul diff --git a/bdwgc/OS2_MAKEFILE b/bdwgc/OS2_MAKEFILE new file mode 100644 index 000000000..a52163169 --- /dev/null +++ b/bdwgc/OS2_MAKEFILE @@ -0,0 +1,59 @@ +# Makefile for OS/2. Assumes IBM's compiler, static linking, and a single thread. +# Adding dynamic linking support seems easy, but takes a little bit of work. +# Adding thread support may be nontrivial, since we haven't yet figured out how to +# look at another thread's registers. + +# Significantly revised by Mark Boulter (Jan 1994). + +OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj obj_map.obj blacklst.obj finalize.obj new_hblk.obj dbg_mlc.obj fnlz_mlc.obj malloc.obj typd_mlc.obj ptr_chck.obj mallocx.obj gcj_mlc.obj + +CORDOBJS= cord\cordbscs.obj cord\cordxtra.obj cord\cordprnt.obj + +CC= icc +CFLAGS= /O /Q /DALL_INTERIOR_POINTERS /DENABLE_DISCLAIM /DGC_ATOMIC_UNCOLLECTABLE /DGC_GCJ_SUPPORT /DJAVA_FINALIZATION /DNO_EXECUTE_PERMISSION /DSMALL_CONFIG +# Use /Ti instead of /O for debugging +# Setjmp_test may yield overly optimistic results when compiled +# without optimization. + +all: gc.lib cord.lib + +check: gctest.exe cordtest.exe + gctest.exe + cordtest.exe + +$(OBJS) test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h + +## ERASE THE LIB FIRST - if it is already there then this command will fail +## (make sure it is there or erase will fail!) +gc.lib: $(OBJS) + echo . > gc.lib + erase gc.lib + LIB gc.lib $(OBJS), gc.lst + +mach_dep.obj: mach_dep.c + $(CC) $(CFLAGS) /C mach_dep.c + +gctest.exe: test.obj gc.lib + $(CC) $(CFLAGS) /B"/STACK:524288" /Fegctest test.obj gc.lib + +cord\cordbscs.obj: cord\cordbscs.c include\cord.h include\cord_pos.h + $(CC) $(CFLAGS) /C /Focord\cordbscs cord\cordbscs.c + +cord\cordxtra.obj: cord\cordxtra.c include\cord.h include\cord_pos.h include\ec.h + $(CC) $(CFLAGS) /C /Focord\cordxtra cord\cordxtra.c + +cord\cordprnt.obj: cord\cordprnt.c include\cord.h include\cord_pos.h include\ec.h + $(CC) $(CFLAGS) /C /Focord\cordprnt cord\cordprnt.c + +cord.lib: $(CORDOBJS) + echo . > cord.lib + erase cord.lib + LIB cord.lib $(CORDOBJS), cord.lst + +cordtest.exe: cord\tests\cordtest.c include\cord.h include\cord_pos.h include\ec.h gc.lib cord.lib + $(CC) $(CFLAGS) /B"/STACK:65536" /Fecordtest cord\tests\cordtest.c gc.lib cord.lib + +clean: + erase gc.lib cord.lib + erase gctest.exe cordtest.exe + erase $(OBJS) $(CORDOBJS) diff --git a/bdwgc/PCR-Makefile b/bdwgc/PCR-Makefile new file mode 100644 index 000000000..4af42ed4d --- /dev/null +++ b/bdwgc/PCR-Makefile @@ -0,0 +1,47 @@ +# +# Default target +# + +default: gc.o + +include ../config/common.mk + +# +# compilation flags, etc. +# + +CPPFLAGS = $(INCLUDE) $(CONFIG_CPPFLAGS) \ + -DPCR_NO_RENAME -DPCR_NO_HOSTDEP_ERR +#CFLAGS = -DPCR $(CONFIG_CFLAGS) +CFLAGS = -DPCR -DENABLE_DISCLAIM $(CONFIG_CFLAGS) +SPECIALCFLAGS = # For code involving asm's + +ASPPFLAGS = $(INCLUDE) $(CONFIG_ASPPFLAGS) \ + -DPCR_NO_RENAME -DPCR_NO_HOSTDEP_ERR -DASM + +ASFLAGS = $(CONFIG_ASFLAGS) + +LDRFLAGS = $(CONFIG_LDRFLAGS) + +LDFLAGS = $(CONFIG_LDFLAGS) + +# +# BEGIN PACKAGE-SPECIFIC PART +# + +# Fix to point to local pcr installation directory. +PCRDIR= .. + +COBJ= alloc.o reclaim.o allchblk.o misc.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o extra/pcr_interface.o extra/real_malloc.o dyn_load.o dbg_mlc.o fnlz_mlc.o malloc.o checksums.o typd_mlc.o ptr_chck.o mallocx.o + +CSRC= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c blacklst.c finalize.c new_hblk.c extra/pcr_interface.c extra/real_malloc.c dyn_load.c dbg_mlc.c fnlz_mlc.c malloc.c checksums.c typd_mlc.c ptr_chck.c mallocx.c + +SHELL= /bin/sh + +default: gc.o + +gc.o: $(COBJ) mach_dep.o + $(LDR) $(CONFIG_LDRFLAGS) -o gc.o $(COBJ) mach_dep.o + +mach_dep.o: mach_dep.c + $(CC) -c $(SPECIALCFLAGS) mach_dep.c diff --git a/bdwgc/README.QUICK b/bdwgc/README.QUICK new file mode 100644 index 000000000..6e9a92490 --- /dev/null +++ b/bdwgc/README.QUICK @@ -0,0 +1,85 @@ +Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers +Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. +Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. +Copyright (c) 1999-2001 by Hewlett-Packard. All rights reserved. +Copyright (c) 2009-2021 Ivan Maidanski + +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 files have other copyright holders. A few of the files needed +to use the GNU-style build procedure come with a modified GPL license +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.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.md or include/gc_version.h files. + +INSTALLATION: +Under UN*X, Linux: +Alternative 1 (the old way): type "make -f Makefile.direct check". + Link against gc.a. + +Alternative 2 (the new way): type + "./configure --prefix=; make; make check; make install". + Link against /lib/libgc.a or /lib/libgc.so. + 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 check". +(Under Windows, this assumes you have Microsoft command-line tools +installed, and suitably configured.) +Read the machine specific README.XXX in the doc directory if one exists. + +If you need thread support, you should define GC_THREADS as described in +doc/README.macros (configure defines this implicitly unless --disable-threads +option is given). + +If you wish to use the cord (structured string) library with the stand-alone +Makefile.direct, type "make -f Makefile.direct cords". (You may need to +override CC specified in the Makefile. The CORD_printf implementation in +cordprnt.c is known to be less than perfectly portable. The rest of the +package should still work.) See include/cord.h for the API. + +If you wish to use the collector from C++, type "make c++", or use +--enable-cplusplus with the configure script. With Makefile.direct, +"make c++" creates gccpp.a and gctba.a files (you should link with either +gccpp.a or gctba.a). With the alternate (preferred) build process, this +generates libgccpp.a and libgctba.a, and/or libgccpp.so and libgctba.so. +See include/gc_cpp.h and doc/gcinterface.md. + +TYPICAL USE: +Include "gc.h" from the include subdirectory. Link against the +appropriate library ("gc.a" under UN*X). Replace calls to malloc +by calls to GC_MALLOC, and calls to realloc by calls to GC_REALLOC. +If the object is known to never contain pointers, use GC_MALLOC_ATOMIC +instead of GC_MALLOC. + +Define GC_DEBUG before including gc.h for additional checking. + +More documentation on the collector interface can be found in README.md, +doc/gcinterface.md, include/gc.h, and other files in the doc directory. + +WARNINGS: + +Do not store the only pointer to an object in memory allocated +with system malloc, since the collector usually does not scan +memory allocated in this way. + +Use with threads may be supported on your system, but requires the collector +to be built with thread support. See Makefile.am or Makefile.direct. +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. diff --git a/bdwgc/README.md b/bdwgc/README.md new file mode 100644 index 000000000..eeb1450c6 --- /dev/null +++ b/bdwgc/README.md @@ -0,0 +1,575 @@ +# Boehm-Demers-Weiser Garbage Collector + +This is version 8.2.4 of a conservative garbage +collector for C and C++. + + +## Download + +You might find a more recent/stable version on the +[Download](https://github.com/ivmai/bdwgc/wiki/Download) page, or +[BDWGC site](http://www.hboehm.info/gc/). + +Also, the latest bug fixes and new features are available in the +[development repository](https://github.com/ivmai/bdwgc). + + +## Overview + +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., 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., "Reducing Garbage Collector Cache Misses", Proceedings of the + 2000 International Symposium on Memory Management. + +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). + +and + + * Boehm H., "Simple GC-safe Compilation", Proceedings of the ACM SIGPLAN '96 + Conference on Programming Language Design and Implementation. + +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 +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 +to a Strongly-Typed Statically Checked, Concurrent Language" Xerox PARC +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](doc/leak.md) are included, +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 in the +[overview](doc/overview.md). + + +## General Description + +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 +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 +by the routines described below. + +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 +set, as is now the default. + +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 not 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 [here](doc/debugging.md) 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 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.) + +_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 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, +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. +(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 +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 + +The collector operates silently in the default configuration. +In the event of problems, this can usually be changed by defining the +`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 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`). + +Please note that the collector source repository does not contain configure +and similar auto-generated files, thus the full procedure of autoconf-based +build of `master` branch of the collector could look like: + + git clone https://github.com/ivmai/bdwgc + cd bdwgc + git clone https://github.com/ivmai/libatomic_ops + ./autogen.sh + ./configure + make -j + make check + +Cloning of `libatomic_ops` is now optional provided the compiler supports +atomic intrinsics. + +Below we focus on the collector build using classic makefile. +For the Makefile.direct-based process, typing `make check` 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 30 MB of memory. (The multi-threaded version will use more. +64-bit versions may use more.) `make check` 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. +Typing "make cords" will build the cord library (cord.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 +not possible to exercise all combinations of build options this way. + +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 +machines that use a flat 32-bit or 64-bit address space. +That includes the vast majority of Workstations and x86 (i386 or later) PCs. + +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, +(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.) + +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 +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 [here](doc/porting.md). + + +## 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_stackbottom` to a good +approximation of the stack base (bottom). + +Client code may include "gc.h", which defines all of the following, plus many +others. + + 1. `GC_malloc(bytes)` - Allocate an object of a given size. 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 consequence of running out + of space. Other possible consequences are that a function call will fail + due to lack of stack space, or that the collector will fail in other ways + because it cannot maintain its internal data structures, or that a crucial + system process will fail and take down the machine. Most of these + possibilities are independent of the malloc implementation. + + 2. `GC_malloc_atomic(bytes)` - Allocate an object of a given size 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_bytes)` - Change the size of object to be of + a given 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 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. + This is very likely to allocate a new object. + + 4. `GC_free(object)` - Explicitly deallocate an object returned by + `GC_malloc` or `GC_malloc_atomic`, or friends. 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 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 GC + heap block (512 .. 4096 bytes or even more, depending on the configuration) + 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 KB or so. (`GC_malloc` may result in a 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. `GC_register_finalizer(object, proc, data, 0, 0)` and friends - Allow for + registration of finalization code. User supplied finalization code + (`(*proc)(object, data)`) is invoked after object becomes unreachable. + 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 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 +near the top of gc_priv.h. + +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)) + +For small pieces of VERY allocation intensive code, gc_inline.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_`. +To avoid name conflicts, client code should avoid this prefix, except when +accessing garbage collector routines. + +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 Ellis-Hull C++ interface to the collector is included in the collector +distribution. If you intend to use this, type +`./configure --enable-cplusplus; make` (or `make -f Makefile.direct 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 +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 + +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 print a human-readable object description +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 the next section) +then, by default, the human-readable object description will at least contain +the source file and the 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. 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 +sometimes be slightly LESS effective in leak finding mode, since in the latter +`GC_debug_free` actually results in reuse of the object. (Otherwise the +object is simply marked invalid.) Also, note that most GC tests are not +designed to run meaningfully in `FIND_LEAK` mode. + + +## 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 +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 +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 +deallocation of an object without debugging information. Out of +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 this function. 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 +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, +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 +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. + +`GC_debug_malloc`, `GC_debug_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`, +`GC_REGISTER_FINALIZER` and friends 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 +functions, passing the current file name and line number as the two +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 +objects with debugging information are really pointers to a displacement +of 16 bytes from 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 dbg_mlc.c file.) + + +## Incremental/Generational Collection + +The collector normally interrupts client code for the duration of +a garbage collection mark phase. This may be unacceptable if interactive +response is needed for programs with large heaps. The collector +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. + +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 +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 (e.g. 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. The object is considered dirty + after a call to `GC_end_stubborn_change` provided the library has been + compiled suitably. It is typically not worth using for short-lived objects. + Note that bugs caused by a missing `GC_end_stubborn_change` or + `GC_reachable_here` call are likely to be observed very infrequently and + hard to trace. + + +## Bugs + +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 +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, +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 ms +per MB of accessible memory that needs to be scanned and processed. +Your mileage may vary.) The incremental/generational collection facility +may help in some cases. + + +## Feedback, Contribution, Questions and Notifications + +Please address bug reports and new feature ideas to +[GitHub issues](https://github.com/ivmai/bdwgc/issues). Before the +submission please check that it has not been done yet by someone else. + +If you want to contribute, submit +a [pull request](https://github.com/ivmai/bdwgc/pulls) to GitHub. + +If you need help, use +[Stack Overflow](https://stackoverflow.com/questions/tagged/boehm-gc). +Older technical discussions are available in `bdwgc` mailing list archive - it +can be downloaded as a +[compressed file](https://github.com/ivmai/bdwgc/files/1038163/bdwgc-mailing-list-archive-2017_04.tar.gz) +or browsed at [Narkive](http://bdwgc.opendylan.narkive.com). + +To get new release announcements, subscribe to +[RSS feed](https://github.com/ivmai/bdwgc/releases.atom). +(To receive the notifications by email, a 3rd-party free service like +[IFTTT RSS Feed](https://ifttt.com/feed) can be setup.) +To be notified on all issues, please +[watch](https://github.com/ivmai/bdwgc/watchers) the project on +GitHub. + + +## 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. + * Copyright (c) 2008-2021 Ivan Maidanski + +The files pthread_stop_world.c, pthread_support.c and some others are also + + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + +The file include/gc.h is also + + * Copyright (c) 2007 Free Software Foundation, Inc + +The files Makefile.am and configure.ac are + + * Copyright (c) 2001 by Red Hat Inc. All rights reserved. + +The files extra/msvc_dbg.c and include/private/msvc_dbg.h are + + * Copyright (c) 2004-2005 Andrei Polushin + +The file tests/initsecondarythread.c is + + * Copyright (c) 2011 Ludovic Courtes + +The file tests/disclaim_weakmap_test.c is + + * Copyright (c) 2018 Petter A. Urkedal + +Several files supporting GNU-style builds are copyrighted by the Free +Software Foundation, and carry a different license from that given +below. + +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.) diff --git a/bdwgc/SMakefile.amiga b/bdwgc/SMakefile.amiga new file mode 100644 index 000000000..d481f9863 --- /dev/null +++ b/bdwgc/SMakefile.amiga @@ -0,0 +1,172 @@ + +# Rewritten smakefile for amiga / sas/c. -Kjetil M. +# Don't use the cord-package if you define parm=both or parm=reg. + + +#----------------TOOLS-------------------------------- +CC=sc +LINKER=slink +LIBER=oml + +#----------------CPU OPTIONS-------------------------- + +CPU=68060 + +#----------------FPU OPTIONS-------------------------- + +MATH=8 +MATHLIB=LIB:scm881.lib + +#----------------COMPILER OPTIONS--------------------- + +IGNORE= IGNORE=85 IGNORE=154 IGNORE=161 IGNORE=100 + +OPTIMIZE=optimize optimizetime optglobal optimizerdepth=100 optimizerpeephole optloop OPTSCHED optimizerinlocal optimizerrecurdepth=100 +# optimizerinline optimizercomplexity=100 + +OPT= $(OPTIMIZE) CPU=$(CPU) math=$(MATH) NOSTACKCHECK VERBOSE \ +MAPHUNK NOVERSION NOICONS nodebug \ +parm=reg \ +DEFINE __USE_SYSBASE + + +SOPT= $(OPT) $(IGNORE) \ +DEFINE AMIGA_SKIP_SEG \ +DEFINE GC_ATOMIC_UNCOLLECTABLE \ +DEFINE GC_AMIGA_FASTALLOC \ +DEFINE GC_AMIGA_RETRY \ +DEFINE GC_AMIGA_PRINTSTATS \ +DEFINE GC_AMIGA_GC + + +#DEFINE ALL_INTERIOR_POINTERS \ + + +SCOPT= $(SOPT) define GC_AMIGA_MAKINGLIB + +CSCOPT= $(OPT) DEFINE AMIGA IGNORE=100 IGNORE=161 + +#------------------LINKING---------------------------- + + +all: gc.lib cord.lib + +clean: + delete *.lib gctest setjmp_t *.o *.lnk cord/*.o cord/tests/*.o cordtest + smake + +check: setjmp_t gctest cordtest + setjmp_t + gctest + cordtest + +gctest: gc.lib GCAmigaOS$(CPU).lib test.o + $(LINKER) LIB:c.o test.o TO gctest LIB gc.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 + +cordtest: cord/tests/cordtest.o cord.lib gc.lib + $(LINKER) LIB:c.o cord/tests/cordtest.o LIB $(MATHLIB) gc.lib cord.lib LIB:sc.lib TO cordtest + + +#------------------LIBBING---------------------------- + +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 dyn_load.o dbg_mlc.o malloc.o checksums.o typd_mlc.o ptr_chck.o mallocx.o fnlz_mlc.o + +gc.lib: $(OBJS) + $(LIBER) gc.lib r $(OBJS) + + +COBJS = cord/cordbscs.o cord/cordprnt.o cord/cordxtra.o + +cord.lib: $(COBJS) + $(LIBER) cord.lib r $(COBJS) + +#------------------COMPILING-------------------------- + +INC= gc_private.h gc_hdrs.h gc.h gcconfig.h + +alloc.o : alloc.c $(INC) + $(CC) alloc.c $(SCOPT) ignore=7 + +reclaim.o : reclaim.c $(INC) + $(CC) reclaim.c $(SCOPT) + +allchblk.o : allchblk.c $(INC) + $(CC) allchblk.c $(SCOPT) + +misc.o : misc.c $(INC) + $(CC) misc.c $(SCOPT) + +os_dep.o : os_dep.c $(INC) extra/AmigaOS.c + $(CC) os_dep.c $(SCOPT) + +mark_rts.o : mark_rts.c $(INC) + $(CC) mark_rts.c $(SCOPT) + +headers.o : headers.c $(INC) + $(CC) headers.c $(SCOPT) + +mark.o : mark.c $(INC) + $(CC) mark.c $(SCOPT) + +obj_map.o : obj_map.c $(INC) + $(CC) obj_map.c $(SCOPT) + +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. + +new_hblk.o : new_hblk.c $(INC) + $(CC) new_hblk.c $(SCOPT) + +dyn_load.o : dyn_load.c $(INC) + $(CC) dyn_load.c $(SCOPT) + +dbg_mlc.o : dbg_mlc.c $(INC) + $(CC) dbg_mlc.c $(SCOPT) + +fnlz_mlc.o : fnlz_mlc.c $(INC) + $(CC) fnlz_mlc.c $(SCOPT) + +malloc.o : malloc.c $(INC) + $(CC) malloc.c $(SCOPT) + +mallocx.o : mallocx.c $(INC) + $(CC) mallocx.c $(SCOPT) + +checksums.o : checksums.c $(INC) + $(CC) checksums.c $(SCOPT) + +typd_mlc.o: typd_mlc.c $(INC) + $(CC) typd_mlc.c $(SCOPT) + +mach_dep.o : mach_dep.c $(INC) + $(CC) mach_dep.c $(SCOPT) + +ptr_chck.o: ptr_chck.c $(INC) + $(CC) ptr_chck.c $(SCOPT) + +test.o : test.c $(INC) + $(CC) test.c $(SOPT) + +setjmp_t: tools/setjmp_t.c gc.h + $(CC) tools/setjmp_t.c $(SOPT) + +# cords: + +cord/cordbscs.o: cord/cordbscs.c + $(CC) cord/cordbscs.c $(CSCOPT) + +cord/cordprnt.o: cord/cordprnt.c + $(CC) cord/cordprnt.c $(CSCOPT) + +cord/cordxtra.o: cord/cordxtra.c + $(CC) cord/cordxtra.c $(CSCOPT) + +cord/tests/cordtest.o: cord/tests/cordtest.c + $(CC) cord/tests/cordtest.c $(CSCOPT) diff --git a/bdwgc/WCC_MAKEFILE b/bdwgc/WCC_MAKEFILE new file mode 100644 index 000000000..3de104806 --- /dev/null +++ b/bdwgc/WCC_MAKEFILE @@ -0,0 +1,286 @@ +# Makefile for Watcom C/C++ 10.5, 10.6, 11.0 on NT, OS2 and DOS4GW. +# May work with Watcom 10.0. + +# Uncomment one of the lines below for cross compilation. +SYSTEM=MSWIN32 +#SYSTEM=DOS4GW +#SYSTEM=OS2 + +# The collector can be built either as dynamic (the default) or as static +# library. The latter is selected by setting ENABLE_STATIC variable. +#ENABLE_STATIC=1 + +# Select calling conventions. +# Possible choices are r and s. +CALLING=s + +# Select target CPU. +# Possible choices are 3, 4, 5, and 6. +# The last choice available only since version 11.0. +CPU=5 + +# Set optimization options. +# Watcom before 11.0 does not support option "-oh". +OPTIM=-oneatx -s +#OPTIM=-ohneatx -s + +DEFS=-DALL_INTERIOR_POINTERS -DENABLE_DISCLAIM -DGC_ATOMIC_UNCOLLECTABLE -DGC_GCJ_SUPPORT -DJAVA_FINALIZATION -DNO_EXECUTE_PERMISSION #-DSMALL_CONFIG + + +##### + +!ifndef SYSTEM +!ifdef __MSDOS__ +SYSTEM=DOS4GW +!else ifdef __NT__ +SYSTEM=MSWIN32 +!else ifdef __OS2__ +SYSTEM=OS2 +!else +SYSTEM=Unknown +!endif +!endif + +!define $(SYSTEM) + +!ifdef DOS4GW +SYSFLAG=-DDOS4GW -bt=dos +!else ifdef MSWIN32 +SYSFLAG=-DMSWIN32 -bt=nt +DEFS=$(DEFS) -DUSE_MUNMAP +!else ifdef OS2 +SYSFLAG=-DOS2 -bt=os2 +!else +!error undefined or unsupported target platform: $(SYSTEM) +!endif + +!ifdef ENABLE_STATIC +DLLFLAG= +TEST_DLLFLAG=-DGC_NOT_DLL +CORDFLAG= +!else +DLLFLAG=-bd -DGC_DLL +TEST_DLLFLAG=-DGC_DLL +# cord.dll and its clients should not link C library statically otherwise +# FILE-related functions might not work (because own set of opened FILEs +# is maintained by each copy of the C library thus making impossible to pass +# FILE pointer from, e.g., .exe code to .dll one). +CORDFLAG=-br +!endif + +CC=wcc386 +CXX=wpp386 + +CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -iinclude -zp4 -zc $(SYSFLAG) $(DLLFLAG) $(DEFS) +CXXFLAGS= $(CFLAGS) -xs +TEST_CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -iinclude -zp4 -zc $(SYSFLAG) $(TEST_DLLFLAG) $(DEFS) +TEST_CXXFLAGS= $(TEST_CFLAGS) -xs + +COBJS= cordbscs.obj cordxtra.obj cordprnt.obj + +all: gc.lib gccpp.lib gctba.lib cord.lib + +check: gctest.exe test_cpp.exe cordtest.exe .SYMBOLIC + *gctest.exe + *test_cpp.exe + *cordtest.exe + +!ifdef ENABLE_STATIC + +OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj & + mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj & + obj_map.obj blacklst.obj finalize.obj new_hblk.obj & + dbg_mlc.obj malloc.obj dyn_load.obj & + typd_mlc.obj ptr_chck.obj mallocx.obj fnlz_mlc.obj gcj_mlc.obj + +gc.lib: $(OBJS) + @%create $*.lb1 + @for %i in ($(OBJS)) do @%append $*.lb1 +'%i' + *wlib -b -c -n -p=512 $@ @$*.lb1 + +cord.lib: $(COBJS) + @%create $*.lb1 + @for %i in ($(COBJS)) do @%append $*.lb1 +'%i' + *wlib -b -c -n -p=512 $@ @$*.lb1 + +gccpp.lib: gc_badalc.obj gc_cpp.obj + @%create $*.lb1 + @%append $*.lb1 +'gc_badalc.obj' + @%append $*.lb1 +'gc_cpp.obj' + *wlib -b -c -n -p=512 $@ @$*.lb1 + +# The same as gccpp.lib but contains only gc_badalc.obj. +gctba.lib: gc_badalc.obj + @%create $*.lb1 + @%append $*.lb1 +'gc_badalc.obj' + *wlib -b -c -n -p=512 $@ @$*.lb1 + +!else + +gc.obj: extra\gc.c .AUTODEPEND + $(CC) $(CFLAGS) extra\gc.c + +gc.lib: gc.dll + *wlib -b -c -n -p=512 $@ +gc.dll + +gc.dll: gc.obj .AUTODEPEND + @%create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys os2v2_dll +!else ifdef MSWIN32 + @%append $*.lnk sys nt_dll +!else ifdef OS2 + @%append $*.lnk sys os2v2_dll +!endif + @%append $*.lnk op case + @%append $*.lnk name $* + @%append $*.lnk file 'gc.obj' + *wlink @$*.lnk + +cord.lib: cord.dll + *wlib -b -c -n -p=512 $@ +cord.dll + +cord.dll: $(COBJS) gc.lib .AUTODEPEND + @%create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys os2v2_dll +!else ifdef MSWIN32 + @%append $*.lnk sys nt_dll +!else ifdef OS2 + @%append $*.lnk sys os2v2_dll +!endif + @%append $*.lnk op case + @%append $*.lnk name $* + @for %i in ($(COBJS)) do @%append $*.lnk file '%i' + @%append $*.lnk library gc.lib + *wlink @$*.lnk + +gccpp.lib: gccpp.dll + *wlib -b -c -n -p=512 $@ +gccpp.dll + +gccpp.dll: gc_badalc.obj gc_cpp.obj gc.lib .AUTODEPEND + @%create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys os2v2_dll +!else ifdef MSWIN32 + @%append $*.lnk sys nt_dll +!else ifdef OS2 + @%append $*.lnk sys os2v2_dll +!endif + @%append $*.lnk op case + @%append $*.lnk name $* + @%append $*.lnk file 'gc_badalc.obj' + @%append $*.lnk file 'gc_cpp.obj' + @%append $*.lnk library gc.lib + @%append $*.lnk library wr7$(CALLING)dll.lib + *wlink @$*.lnk + +gctba.lib: gctba.dll + *wlib -b -c -n -p=512 $@ +gctba.dll + +gctba.dll: gc_badalc.obj gc.lib .AUTODEPEND + @%create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys os2v2_dll +!else ifdef MSWIN32 + @%append $*.lnk sys nt_dll +!else ifdef OS2 + @%append $*.lnk sys os2v2_dll +!endif + @%append $*.lnk op case + @%append $*.lnk name $* + @%append $*.lnk file 'gc_badalc.obj' + @%append $*.lnk library gc.lib + @%append $*.lnk library wr7$(CALLING)dll.lib + *wlink @$*.lnk + +!endif + +gctest.exe: test.obj gc.lib + %create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys dos4g +!else ifdef MSWIN32 + @%append $*.lnk sys nt +!else ifdef OS2 + @%append $*.lnk sys os2v2 +!endif + @%append $*.lnk op case + @%append $*.lnk op stack=256K + @%append $*.lnk name $* + @%append $*.lnk file test.obj + @%append $*.lnk library gc.lib + *wlink @$*.lnk + +cordtest.exe: cordtest.obj gc.lib cord.lib + %create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys dos4g +!else ifdef MSWIN32 + @%append $*.lnk sys nt +!else ifdef OS2 + @%append $*.lnk sys os2v2 +!endif + @%append $*.lnk op case + @%append $*.lnk op stack=256K + @%append $*.lnk name $* + @%append $*.lnk file cordtest.obj + @%append $*.lnk library gc.lib + @%append $*.lnk library cord.lib + *wlink @$*.lnk + +test_cpp.exe: test_cpp.obj gc.lib gccpp.lib + %create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys dos4g +!else ifdef MSWIN32 + @%append $*.lnk sys nt +!else ifdef OS2 + @%append $*.lnk sys os2v2 +!endif + @%append $*.lnk op case + @%append $*.lnk op stack=256K + @%append $*.lnk name $* + @%append $*.lnk file test_cpp.obj + @%append $*.lnk library gc.lib + @%append $*.lnk library gccpp.lib + *wlink @$*.lnk + +cordbscs.obj: cord\cordbscs.c .AUTODEPEND + $(CC) $(CFLAGS) $(CORDFLAG) cord\cordbscs.c +cordxtra.obj: cord\cordxtra.c .AUTODEPEND + $(CC) $(CFLAGS) $(CORDFLAG) cord\cordxtra.c +cordprnt.obj: cord\cordprnt.c .AUTODEPEND + $(CC) $(CFLAGS) $(CORDFLAG) cord\cordprnt.c + +gc_badalc.obj: gc_badalc.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) $*.cc +gc_cpp.obj: gc_cpp.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) $*.cc + +test.obj: tests\test.c .AUTODEPEND + $(CC) $(TEST_CFLAGS) /wcd=201 tests\test.c +cordtest.obj: cord\tests\cordtest.c .AUTODEPEND + $(CC) $(TEST_CFLAGS) $(CORDFLAG) cord\tests\cordtest.c +test_cpp.obj: tests\test_cpp.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) tests\test_cpp.cc + +.c.obj: .AUTODEPEND + $(CC) $(CFLAGS) $*.c + +.cc.obj: .AUTODEPEND + $(CXX) $(CXXFLAGS) $*.cc + +clean: .SYMBOLIC + @if exist *.obj del *.obj + @if exist *.map del *.map + @if exist *.lnk del *.lnk + @if exist *.lb1 del *.lb1 + @if exist *.sym del *.sym + @if exist *.err del *.err + @if exist *.tmp del *.tmp + @if exist *.lst del *.lst + @if exist *.exe del *.exe + @if exist *.log del *.log + @if exist *.lib del *.lib + @if exist *.dll del *.dll diff --git a/bdwgc/allchblk.c b/bdwgc/allchblk.c new file mode 100644 index 000000000..cc4cfd7f8 --- /dev/null +++ b/bdwgc/allchblk.c @@ -0,0 +1,993 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 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. + * + * 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. + */ + +#include "private/gc_priv.h" + +#include + +#ifdef GC_USE_ENTIRE_HEAP + int GC_use_entire_heap = TRUE; +#else + int GC_use_entire_heap = FALSE; +#endif + +/* + * Free heap blocks are kept on one of several free lists, + * depending on the size of the block. Each free list is doubly linked. + * Adjacent free blocks are coalesced. + */ + + +# define MAX_BLACK_LIST_ALLOC (2*HBLKSIZE) + /* largest block we will allocate starting on a black */ + /* listed block. Must be >= HBLKSIZE. */ + + +# define UNIQUE_THRESHOLD 32 + /* Sizes up to this many HBLKs each have their own free list */ +# define HUGE_THRESHOLD 256 + /* Sizes of at least this many heap blocks are mapped to a */ + /* single free list. */ +# define FL_COMPRESSION 8 + /* In between sizes map this many distinct sizes to a single */ + /* bin. */ + +# define N_HBLK_FLS ((HUGE_THRESHOLD - UNIQUE_THRESHOLD) / FL_COMPRESSION \ + + UNIQUE_THRESHOLD) + +#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. Remains externally visible */ + /* as used by GNU GCJ currently. */ + +#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 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; + + GC_ASSERT(GC_max_large_allocd_bytes <= GC_heapsize); + for (n = N_HBLK_FLS; n >= 0; --n) { + bytes += GC_free_bytes[n]; + if (bytes >= GC_max_large_allocd_bytes) return n; + } + return 0; +} + +/* Map a number of blocks to the appropriate large block free list index. */ +STATIC int GC_hblk_fl_from_blocks(word blocks_needed) +{ + if (blocks_needed <= UNIQUE_THRESHOLD) return (int)blocks_needed; + if (blocks_needed >= HUGE_THRESHOLD) return N_HBLK_FLS; + return (int)(blocks_needed - UNIQUE_THRESHOLD)/FL_COMPRESSION + + UNIQUE_THRESHOLD; + +} + +# 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 +# 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) + { + word total_free = 0; + unsigned i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + 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) +{ + unsigned i; + word total; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h = GC_hblkfreelist[i]; + + if (0 != h) GC_printf("Free list %u (total size %lu):\n", + i, (unsigned long)GC_free_bytes[i]); + while (h /* != NULL */) { /* CPPCHECK */ + hdr * hhdr = HDR(h); + + GC_printf("\t%p size %lu %s black listed\n", + (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; + } + } + 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 */ +/* appears, or -1 if it appears nowhere. */ +static int free_list_index_of(hdr *wanted) +{ + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + for (h = GC_hblkfreelist[i]; h != 0; h = hhdr -> hb_next) { + hhdr = HDR(h); + if (hhdr == wanted) return i; + } + } + return -1; +} + +GC_API void GC_CALL GC_dump_regions(void) +{ + unsigned i; + + for (i = 0; i < GC_n_heap_sects; ++i) { + ptr_t start = GC_heap_sects[i].hs_start; + size_t bytes = GC_heap_sects[i].hs_bytes; + ptr_t end = start + bytes; + ptr_t p; + + /* Merge in contiguous sections. */ + while (i+1 < GC_n_heap_sects && GC_heap_sects[i+1].hs_start == end) { + ++i; + end = GC_heap_sects[i].hs_start + GC_heap_sects[i].hs_bytes; + } + GC_printf("***Section from %p to %p\n", (void *)start, (void *)end); + for (p = start; (word)p < (word)end; ) { + hdr *hhdr = HDR(p); + + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + GC_printf("\t%p Missing header!!(%p)\n", + (void *)p, (void *)hhdr); + p += HBLKSIZE; + continue; + } + if (HBLK_IS_FREE(hhdr)) { + int correct_index = GC_hblk_fl_from_blocks( + divHBLKSZ(hhdr -> hb_sz)); + int actual_index; + + GC_printf("\t%p\tfree block of size 0x%lx bytes%s\n", + (void *)p, (unsigned long)(hhdr -> hb_sz), + IS_MAPPED(hhdr) ? "" : " (unmapped)"); + actual_index = free_list_index_of(hhdr); + if (-1 == actual_index) { + GC_printf("\t\tBlock not on free list %d!!\n", + correct_index); + } else if (correct_index != actual_index) { + GC_printf("\t\tBlock on list %d, should be on %d!!\n", + actual_index, correct_index); + } + p += hhdr -> hb_sz; + } else { + GC_printf("\t%p\tused for blocks of size 0x%lx bytes\n", + (void *)p, (unsigned long)(hhdr -> hb_sz)); + p += HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + } + } + } +} + +# endif /* NO_DEBUGGING */ + +/* Initialize hdr for a block containing the indicated size and */ +/* kind of objects. */ +/* Return FALSE on failure. */ +static GC_bool setup_header(hdr * hhdr, struct hblk *block, size_t byte_sz, + int kind, unsigned flags) +{ + word descr; + +# ifdef MARK_BIT_PER_GRANULE + if (byte_sz > MAXOBJBYTES) + flags |= LARGE_BLOCK; +# endif +# ifdef ENABLE_DISCLAIM + if (GC_obj_kinds[kind].ok_disclaim_proc) + flags |= HAS_DISCLAIM; + if (GC_obj_kinds[kind].ok_mark_unconditionally) + flags |= MARK_UNCONDITIONALLY; +# endif + + /* Set size, kind and mark proc fields */ + hhdr -> hb_sz = byte_sz; + hhdr -> hb_obj_kind = (unsigned char)kind; + hhdr -> hb_flags = (unsigned char)flags; + hhdr -> hb_block = block; + descr = GC_obj_kinds[kind].ok_descriptor; + if (GC_obj_kinds[kind].ok_relocate_descr) descr += byte_sz; + hhdr -> hb_descr = descr; + +# ifdef MARK_BIT_PER_OBJ + /* Set hb_inv_sz as portably as possible. */ + /* We set it to the smallest value such that sz * inv_sz >= 2**32 */ + /* This may be more precision than necessary. */ + if (byte_sz > MAXOBJBYTES) { + hhdr -> hb_inv_sz = LARGE_INV_SZ; + } else { + word inv_sz; + +# if CPP_WORDSZ == 64 + inv_sz = ((word)1 << 32)/byte_sz; + if (((inv_sz*byte_sz) >> 32) == 0) ++inv_sz; +# else /* 32 bit words */ + GC_ASSERT(byte_sz >= 4); + inv_sz = ((unsigned)1 << 31)/byte_sz; + inv_sz *= 2; + while (inv_sz*byte_sz > byte_sz) ++inv_sz; +# endif +# ifdef INV_SZ_COMPUTATION_CHECK + GC_ASSERT(((1ULL << 32) + byte_sz - 1) / byte_sz == inv_sz); +# endif + hhdr -> hb_inv_sz = inv_sz; + } +# endif +# ifdef MARK_BIT_PER_GRANULE + { + size_t granules = BYTES_TO_GRANULES(byte_sz); + + if (EXPECT(!GC_add_map_entry(granules), FALSE)) { + /* Make it look like a valid block. */ + hhdr -> hb_sz = HBLKSIZE; + hhdr -> hb_descr = 0; + hhdr -> hb_flags |= LARGE_BLOCK; + hhdr -> hb_map = 0; + return FALSE; + } + hhdr -> hb_map = GC_obj_map[(hhdr -> hb_flags & LARGE_BLOCK) != 0 ? + 0 : granules]; + } +# endif /* MARK_BIT_PER_GRANULE */ + + /* Clear mark bits */ + GC_clear_hdr_marks(hhdr); + + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; + return(TRUE); +} + +/* Remove hhdr from the free list (it is assumed to specified by index). */ +STATIC void GC_remove_from_fl_at(hdr *hhdr, int index) +{ + GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0); + if (hhdr -> hb_prev == 0) { + GC_ASSERT(HDR(GC_hblkfreelist[index]) == hhdr); + GC_hblkfreelist[index] = hhdr -> hb_next; + } else { + hdr *phdr; + GET_HDR(hhdr -> hb_prev, phdr); + phdr -> hb_next = hhdr -> hb_next; + } + /* 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))); + GET_HDR(hhdr -> hb_next, nhdr); + nhdr -> hb_prev = hhdr -> hb_prev; + } +} + +/* 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 block ending just before h, if any. */ +static struct hblk * get_block_ending_at(struct hblk *h) +{ + struct hblk * p = h - 1; + hdr * phdr; + + GET_HDR(p, phdr); + while (0 != phdr && IS_FORWARDING_ADDR_OR_NIL(phdr)) { + p = FORWARDED_ADDR(p,phdr); + phdr = HDR(p); + } + if (0 != phdr) { + return p; + } + p = GC_prev_block(h - 1); + if (p) { + phdr = HDR(p); + if ((ptr_t)p + phdr -> hb_sz == (ptr_t)h) { + return p; + } + } + return NULL; +} + +/* 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 = get_block_ending_at(h); + + if (p /* != NULL */) { /* CPPCHECK */ + hdr * phdr = HDR(p); + + if (HBLK_IS_FREE(phdr)) { + return p; + } + } + return 0; +} + +/* 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)); + struct hblk *second = GC_hblkfreelist[index]; +# if defined(GC_ASSERTIONS) && !defined(USE_MUNMAP) + struct hblk *next = (struct hblk *)((word)h + hhdr -> hb_sz); + hdr * nexthdr = HDR(next); + struct hblk *prev = GC_free_block_ending_at(h); + hdr * prevhdr = HDR(prev); + + GC_ASSERT(nexthdr == 0 || !HBLK_IS_FREE(nexthdr) + || (GC_heapsize & SIGNB) != 0); + /* In the last case, blocks may be too large to merge. */ + GC_ASSERT(prev == 0 || !HBLK_IS_FREE(prevhdr) + || (GC_heapsize & SIGNB) != 0); +# endif + GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0); + GC_hblkfreelist[index] = h; + 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 (second /* != NULL */) { /* CPPCHECK */ + hdr * second_hdr; + + GET_HDR(second, second_hdr); + second_hdr -> hb_prev = h; + } + hhdr -> hb_flags |= FREE_BLK; +} + +#ifdef USE_MUNMAP + +# ifndef MUNMAP_THRESHOLD +# define MUNMAP_THRESHOLD 6 +# endif + +GC_INNER int GC_unmap_threshold = MUNMAP_THRESHOLD; + +#ifdef COUNT_UNMAPPED_REGIONS + /* GC_unmap_old will avoid creating more than this many unmapped regions, */ + /* but an unmapped region may be split again so exceeding the limit. */ + + /* Return the change in number of unmapped regions if the block h swaps */ + /* from its current state of mapped/unmapped to the opposite state. */ + static int calc_num_unmapped_regions_delta(struct hblk *h, hdr *hhdr) + { + struct hblk * prev = get_block_ending_at(h); + struct hblk * next; + GC_bool prev_unmapped = FALSE; + GC_bool next_unmapped = FALSE; + + next = GC_next_block((struct hblk *)((ptr_t)h + hhdr->hb_sz), TRUE); + /* Ensure next is contiguous with h. */ + if ((ptr_t)next != GC_unmap_end((ptr_t)h, (size_t)hhdr->hb_sz)) { + next = NULL; + } + if (prev != NULL) { + hdr * prevhdr = HDR(prev); + prev_unmapped = !IS_MAPPED(prevhdr); + } + if (next != NULL) { + hdr * nexthdr = HDR(next); + next_unmapped = !IS_MAPPED(nexthdr); + } + + if (prev_unmapped && next_unmapped) { + /* If h unmapped, merge two unmapped regions into one. */ + /* If h remapped, split one unmapped region into two. */ + return IS_MAPPED(hhdr) ? -1 : 1; + } + if (!prev_unmapped && !next_unmapped) { + /* If h unmapped, create an isolated unmapped region. */ + /* If h remapped, remove it. */ + return IS_MAPPED(hhdr) ? 1 : -1; + } + /* If h unmapped, merge it with previous or next unmapped region. */ + /* If h remapped, reduce either previous or next unmapped region. */ + /* In either way, no change to the number of unmapped regions. */ + return 0; + } +#endif /* COUNT_UNMAPPED_REGIONS */ + +/* Update GC_num_unmapped_regions assuming the block h changes */ +/* from its current state of mapped/unmapped to the opposite state. */ +GC_INLINE void GC_adjust_num_unmapped(struct hblk *h GC_ATTR_UNUSED, + hdr *hhdr GC_ATTR_UNUSED) +{ +# ifdef COUNT_UNMAPPED_REGIONS + GC_num_unmapped_regions += calc_num_unmapped_regions_delta(h, hhdr); +# endif +} + +/* Unmap blocks that haven't been recently touched. This is the only */ +/* way blocks are ever unmapped. */ +GC_INNER void GC_unmap_old(void) +{ + int i; + + if (GC_unmap_threshold == 0) + return; /* unmapping disabled */ +# ifdef COUNT_UNMAPPED_REGIONS + /* Skip unmapping if we have already exceeded the soft limit. */ + /* This forgoes any opportunities to merge unmapped regions though. */ + if (GC_num_unmapped_regions >= GC_UNMAPPED_REGIONS_SOFT_LIMIT) + return; +# endif + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + for (h = GC_hblkfreelist[i]; 0 != h; h = hhdr -> hb_next) { + hhdr = HDR(h); + if (!IS_MAPPED(hhdr)) continue; + + /* Check that the interval is larger than the threshold (the */ + /* truncated counter value wrapping is handled correctly). */ + if ((unsigned short)(GC_gc_no - hhdr->hb_last_reclaimed) > + (unsigned short)GC_unmap_threshold) { +# ifdef COUNT_UNMAPPED_REGIONS + /* Continue with unmapping the block only if it will not */ + /* create too many unmapped regions, or if unmapping */ + /* reduces the number of regions. */ + int delta = calc_num_unmapped_regions_delta(h, hhdr); + signed_word regions = GC_num_unmapped_regions + delta; + + if (delta >= 0 && regions >= GC_UNMAPPED_REGIONS_SOFT_LIMIT) { + GC_COND_LOG_PRINTF("Unmapped regions limit reached!\n"); + return; + } + GC_num_unmapped_regions = regions; +# endif + GC_unmap((ptr_t)h, (size_t)hhdr->hb_sz); + hhdr -> hb_flags |= WAS_UNMAPPED; + } + } + } +} + +/* Merge all unmapped blocks that are adjacent to other free */ +/* blocks. This may involve remapping, since all blocks are either */ +/* fully mapped or fully unmapped. */ +GC_INNER void GC_merge_unmapped(void) +{ + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk *h = GC_hblkfreelist[i]; + + while (h != 0) { + struct hblk *next; + hdr *hhdr, *nexthdr; + word size, nextsize; + + GET_HDR(h, hhdr); + size = hhdr->hb_sz; + next = (struct hblk *)((word)h + size); + GET_HDR(next, nexthdr); + /* Coalesce with successor, if possible */ + if (0 != nexthdr && HBLK_IS_FREE(nexthdr) + && (signed_word) (size + (nextsize = nexthdr->hb_sz)) > 0 + /* no pot. overflow */) { + /* Note that we usually try to avoid adjacent free blocks */ + /* that are either both mapped or both unmapped. But that */ + /* isn't guaranteed to hold since we remap blocks when we */ + /* split them, and don't merge at that point. It may also */ + /* not hold if the merged block would be too big. */ + if (IS_MAPPED(hhdr) && !IS_MAPPED(nexthdr)) { + /* make both consistent, so that we can merge */ + if (size > nextsize) { + GC_adjust_num_unmapped(next, nexthdr); + GC_remap((ptr_t)next, nextsize); + } else { + GC_adjust_num_unmapped(h, hhdr); + GC_unmap((ptr_t)h, size); + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize); + hhdr -> hb_flags |= WAS_UNMAPPED; + } + } else if (IS_MAPPED(nexthdr) && !IS_MAPPED(hhdr)) { + if (size > nextsize) { + GC_adjust_num_unmapped(next, nexthdr); + GC_unmap((ptr_t)next, nextsize); + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize); + } else { + GC_adjust_num_unmapped(h, hhdr); + GC_remap((ptr_t)h, size); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + hhdr -> hb_last_reclaimed = nexthdr -> hb_last_reclaimed; + } + } else if (!IS_MAPPED(hhdr) && !IS_MAPPED(nexthdr)) { + /* Unmap any gap in the middle */ + 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_at(hhdr, i); + GC_remove_from_fl(nexthdr); + hhdr -> hb_sz += nexthdr -> hb_sz; + GC_remove_header(next); + GC_add_to_fl(h, hhdr); + /* Start over at beginning of list */ + h = GC_hblkfreelist[i]; + } else /* not mergeable with successor */ { + h = hhdr -> hb_next; + } + } /* while (h != 0) ... */ + } /* for ... */ +} + +#endif /* USE_MUNMAP */ + +/* + * Return a pointer to a block starting at h of length bytes. + * Memory for the block is mapped. + * Remove the block from its free list, and return the remainder (if any) + * to its appropriate free list. + * May fail by returning 0. + * The header for the returned block must be set up by the caller. + * If the return value is not 0, then hhdr is the header for it. + */ +STATIC struct hblk * GC_get_first_part(struct hblk *h, hdr *hhdr, + size_t bytes, int index) +{ + word total_size = hhdr -> hb_sz; + struct hblk * rest; + hdr * rest_hdr; + + GC_ASSERT((total_size & (HBLKSIZE-1)) == 0); + 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); + if (0 == rest_hdr) { + /* FIXME: This is likely to be very bad news ... */ + WARN("Header allocation failed: dropping block\n", 0); + return(0); + } + rest_hdr -> hb_sz = total_size - bytes; + rest_hdr -> hb_flags = 0; +# ifdef GC_ASSERTIONS + /* Mark h not free, to avoid assertion about adjacent free blocks. */ + hhdr -> hb_flags &= ~FREE_BLK; +# endif + GC_add_to_fl(rest, rest_hdr); + return h; +} + +/* + * H is a free block. N points at an address inside it. + * A new header for n has already been set up. Fix up h's header + * to reflect the fact that it is being split, move it to the + * appropriate free list. + * N replaces h in the original free list. + * + * Nhdr is not completely filled in, since it is about to allocated. + * It may in fact end up on the wrong free list for its size. + * That's not a disaster, since n is about to be allocated + * by our caller. + * (Hence adding it to a free list is silly. But this path is hopefully + * rare enough that it doesn't matter. The code is cleaner this way.) + */ +STATIC void GC_split_block(struct hblk *h, hdr *hhdr, struct hblk *n, + hdr *nhdr, int index /* Index of free list */) +{ + word total_size = hhdr -> hb_sz; + word h_size = (word)n - (word)h; + struct hblk *prev = hhdr -> hb_prev; + struct hblk *next = hhdr -> hb_next; + + /* Replace h with n on its freelist */ + nhdr -> hb_prev = prev; + nhdr -> hb_next = next; + nhdr -> hb_sz = total_size - h_size; + nhdr -> hb_flags = 0; + if (prev /* != NULL */) { /* CPPCHECK */ + HDR(prev) -> hb_next = n; + } else { + GC_hblkfreelist[index] = n; + } + if (next /* != NULL */) { + HDR(next) -> hb_prev = n; + } + 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 + hhdr -> hb_sz = h_size; + GC_add_to_fl(h, hhdr); + nhdr -> hb_flags |= FREE_BLK; +} + +STATIC struct hblk * +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 + * for objects of size sz bytes, searching the nth free list. + * + * NOTE: We set obj_map field in header correctly. + * Caller is responsible for building an object freelist in block. + * + * The client is responsible for clearing the block, if necessary. + */ +GC_INNER struct hblk * +GC_allochblk(size_t sz, int kind, unsigned flags/* IGNORE_OFF_PAGE or 0 */) +{ + word blocks; + int start_list; + struct hblk *result; + int may_split; + int split_limit; /* Highest index of free list whose blocks we */ + /* split. */ + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((sz & (GRANULE_BYTES - 1)) == 0); + blocks = OBJ_SZ_TO_BLOCKS_CHECKED(sz); + if ((signed_word)(blocks * HBLKSIZE) < 0) { + return 0; + } + start_list = GC_hblk_fl_from_blocks(blocks); + /* 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 if (GC_finalizer_bytes_freed > (GC_heapsize >> 4)) { + /* If we are deallocating lots of memory from */ + /* finalizers, fail and collect sooner rather */ + /* than later. */ + split_limit = 0; + } else { + /* If we have enough large blocks left to cover any */ + /* previous request for large blocks, we go ahead */ + /* and split. Assuming a steady state, that should */ + /* be safe. It means that we can use the full */ + /* heap if we allocate only small objects. */ + split_limit = GC_enough_large_bytes_left(); +# 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 (; start_list <= split_limit; ++start_list) { + result = GC_allochblk_nth(sz, kind, flags, start_list, may_split); + if (0 != result) + break; + } + 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. 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, int may_split) +{ + struct hblk *hbp; + hdr * hhdr; /* Header corr. to hbp */ + struct hblk *thishbp; + hdr * thishdr; /* Header corr. to thishbp */ + signed_word size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS_CHECKED(sz); + /* number of bytes in requested objects */ + + /* search for a big enough block in free list */ + for (hbp = GC_hblkfreelist[n];; hbp = hhdr -> hb_next) { + signed_word size_avail; /* bytes available in this block */ + + if (hbp /* != NULL */) { + /* CPPCHECK */ + } else { + return NULL; + } + GET_HDR(hbp, hhdr); /* set hhdr value */ + size_avail = (signed_word)hhdr->hb_sz; + if (size_avail < size_needed) continue; + if (size_avail != size_needed) { + if (!may_split) continue; + /* If the next heap block is obviously better, go on. */ + /* This prevents us from disassembling a single large */ + /* block to get tiny blocks. */ + thishbp = hhdr -> hb_next; + if (thishbp /* != NULL */) { /* CPPCHECK */ + signed_word next_size; + + GET_HDR(thishbp, thishdr); + next_size = (signed_word)(thishdr -> hb_sz); + if (next_size < size_avail + && next_size >= size_needed + && !GC_is_black_listed(thishbp, (word)size_needed)) { + continue; + } + } + } + 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; + signed_word orig_avail = size_avail; + signed_word eff_size_needed = (flags & IGNORE_OFF_PAGE) != 0 ? + (signed_word)HBLKSIZE + : size_needed; + + while ((word)lasthbp <= (word)search_end + && (thishbp = GC_is_black_listed(lasthbp, + (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) { +# 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)) { + GC_adjust_num_unmapped(hbp, hhdr); + GC_remap((ptr_t)hbp, (size_t)hhdr->hb_sz); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + } +# endif + /* Split the block at thishbp */ + GC_split_block(hbp, hhdr, thishbp, thishdr, n); + /* Advance to thishbp */ + hbp = thishbp; + 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 + > (signed_word)BL_LIMIT) { + /* Punt, since anything else risks unreasonable heap growth. */ + if (++GC_large_alloc_warn_suppressed + >= GC_large_alloc_warn_interval) { + WARN("Repeated allocation of very large block " + "(appr. size %" WARN_PRIuPTR " KiB):\n" + "\tMay lead to memory leak and poor performance\n", + (word)size_needed >> 10); + GC_large_alloc_warn_suppressed = 0; + } + size_avail = orig_avail; + } else if (size_avail == 0 + && size_needed == (signed_word)HBLKSIZE + && IS_MAPPED(hhdr)) { + if (!GC_find_leak) { + static unsigned count = 0; + + /* The block is completely blacklisted. We need */ + /* to drop some such blocks, since otherwise we spend */ + /* all our time traversing them if pointer-free */ + /* blocks are unpopular. */ + /* A dropped block will be reconsidered at next GC. */ + if ((++count & 3) == 0) { + /* Allocate and drop the block in small chunks, to */ + /* maximize the chance that we will recover some */ + /* later. */ + word total_size = hhdr -> hb_sz; + struct hblk * limit = hbp + divHBLKSZ(total_size); + struct hblk * h; + struct hblk * prev = hhdr -> hb_prev; + + GC_large_free_bytes -= total_size; + GC_bytes_dropped += total_size; + 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); + } + } + } + /* Restore hbp to point at free block */ + hbp = prev; + if (0 == hbp) { + return GC_allochblk_nth(sz, kind, flags, n, may_split); + } + hhdr = HDR(hbp); + } + } + } + } + if( size_avail >= size_needed ) { +# ifdef USE_MUNMAP + if (!IS_MAPPED(hhdr)) { + GC_adjust_num_unmapped(hbp, hhdr); + GC_remap((ptr_t)hbp, (size_t)hhdr->hb_sz); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + /* Note: This may leave adjacent, mapped free blocks. */ + } +# endif + /* hbp may be on the wrong freelist; the parameter n */ + /* is important. */ + hbp = GC_get_first_part(hbp, hhdr, size_needed, n); + break; + } + } + + if (0 == hbp) return 0; + + /* Add it to map of valid blocks */ + if (!GC_install_counts(hbp, (word)size_needed)) return(0); + /* This leaks memory under very rare conditions. */ + + /* Set up header */ + if (!setup_header(hhdr, hbp, sz, kind, flags)) { + GC_remove_counts(hbp, (word)size_needed); + return(0); /* ditto */ + } +# ifndef GC_DISABLE_INCREMENTAL + /* Notify virtual dirty bit implementation that we are about to */ + /* 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); + GC_remove_protection(hbp, divHBLKSZ(size_needed), + (hhdr -> hb_descr == 0) /* pointer-free */); +# endif + /* We just successfully allocated a block. Restart count of */ + /* consecutive failures. */ + GC_fail_count = 0; + + GC_large_free_bytes -= size_needed; + GC_ASSERT(IS_MAPPED(hhdr)); + return( hbp ); +} + +/* + * Free a heap block. + * + * Coalesce the block with its neighbors if possible. + * + * All mark words are assumed to be cleared. + */ +GC_INNER void GC_freehblk(struct hblk *hbp) +{ + struct hblk *next, *prev; + hdr *hhdr, *prevhdr, *nexthdr; + word size; + + GET_HDR(hbp, hhdr); + size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr->hb_sz); + if ((size & SIGNB) != 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, size); + hhdr->hb_sz = size; +# ifdef USE_MUNMAP + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + + /* Check for duplicate deallocation in the easy case */ + if (HBLK_IS_FREE(hhdr)) { + ABORT_ARG1("Duplicate large block deallocation", + " of %p", (void *)hbp); + } + + GC_ASSERT(IS_MAPPED(hhdr)); + hhdr -> hb_flags |= FREE_BLK; + 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); + hhdr -> hb_sz += nexthdr -> hb_sz; + GC_remove_header(next); + } + /* Coalesce with predecessor, if possible. */ + if (prev /* != NULL */) { /* CPPCHECK */ + prevhdr = HDR(prev); + if (IS_MAPPED(prevhdr) + && (signed_word)(hhdr -> hb_sz + prevhdr -> hb_sz) > 0) { + GC_remove_from_fl(prevhdr); + prevhdr -> hb_sz += hhdr -> hb_sz; +# ifdef USE_MUNMAP + prevhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + GC_remove_header(hbp); + hbp = prev; + hhdr = prevhdr; + } + } + /* FIXME: It is not clear we really always want to do these merges */ + /* with USE_MUNMAP, since it updates ages and hence prevents */ + /* unmapping. */ + + GC_large_free_bytes += size; + GC_add_to_fl(hbp, hhdr); +} diff --git a/bdwgc/alloc.c b/bdwgc/alloc.c new file mode 100644 index 000000000..95b0e666d --- /dev/null +++ b/bdwgc/alloc.c @@ -0,0 +1,1817 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2008-2021 Ivan Maidanski + * + * 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. + * + */ + +#include "private/gc_priv.h" + +#include +#if !defined(MACOS) && !defined(MSWINCE) +# include +# if !defined(GC_NO_TYPES) && !defined(SN_TARGET_PSP2) \ + && !defined(__CC_ARM) +# include +# endif +#endif + +/* + * Separate free lists are maintained for different sized objects + * up to MAXOBJBYTES. + * The call GC_allocobj(i,k) ensures that the freelist for + * kind k objects of size i points to a non-empty + * free list. It returns a pointer to the first entry on the free list. + * In a single-threaded world, GC_allocobj may be called to allocate + * an object of small size lb (and NORMAL kind) as follows + * (GC_generic_malloc_inner is a wrapper over GC_allocobj which also + * fills in GC_size_map if needed): + * + * lg = GC_size_map[lb]; + * op = GC_objfreelist[lg]; + * if (NULL == op) { + * op = GC_generic_malloc_inner(lb, NORMAL); + * } else { + * GC_objfreelist[lg] = obj_link(op); + * GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + * } + * + * Note that this is very fast if the free list is non-empty; it should + * only involve the execution of 4 or 5 simple instructions. + * All composite objects on freelists are cleared, except for + * their first word. + */ + +/* + * The allocator uses GC_allochblk to allocate large chunks of objects. + * These chunks all start on addresses which are multiples of + * HBLKSZ. Each allocated chunk has an associated header, + * which can be located quickly based on the address of the chunk. + * (See headers.c for details.) + * This makes it possible to check quickly whether an + * arbitrary address corresponds to an object administered by the + * allocator. + */ + +word GC_non_gc_bytes = 0; /* Number of bytes not intended to be collected */ + +word GC_gc_no = 0; + +#ifndef NO_CLOCK + static unsigned long full_gc_total_time = 0; /* in ms, may wrap */ + static unsigned full_gc_total_ns_frac = 0; /* fraction of 1 ms */ + static GC_bool measure_performance = FALSE; + /* Do performance measurements if set to true (e.g., */ + /* accumulation of the total time of full collections). */ + + GC_API void GC_CALL GC_start_performance_measurement(void) + { + measure_performance = TRUE; + } + + GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void) + { + return full_gc_total_time; + } +#endif /* !NO_CLOCK */ + +#ifndef GC_DISABLE_INCREMENTAL + GC_INNER GC_bool GC_incremental = FALSE; /* By default, stop the world. */ + STATIC GC_bool GC_should_start_incremental_collection = FALSE; +#endif + +GC_API int GC_CALL GC_is_incremental_mode(void) +{ + return (int)GC_incremental; +} + +#ifdef THREADS + int GC_parallel = FALSE; /* By default, parallel GC is off. */ +#endif + +#if defined(GC_FULL_FREQ) && !defined(CPPCHECK) + int GC_full_freq = GC_FULL_FREQ; +#else + int GC_full_freq = 19; /* Every 20th collection is a full */ + /* collection, whether we need it */ + /* or not. */ +#endif + +STATIC GC_bool GC_need_full_gc = FALSE; + /* Need full GC due to heap growth. */ + +#ifdef THREAD_LOCAL_ALLOC + GC_INNER GC_bool GC_world_stopped = FALSE; +#endif + +STATIC GC_bool GC_disable_automatic_collection = FALSE; + +GC_API void GC_CALL GC_set_disable_automatic_collection(int value) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_disable_automatic_collection = (GC_bool)value; + UNLOCK(); +} + +GC_API int GC_CALL GC_get_disable_automatic_collection(void) +{ + int value; + DCL_LOCK_STATE; + + LOCK(); + value = (int)GC_disable_automatic_collection; + UNLOCK(); + return value; +} + +STATIC word GC_used_heap_size_after_full = 0; + +/* GC_copyright symbol is externally visible. */ +EXTERN_C_BEGIN +extern const char * const GC_copyright[]; +EXTERN_C_END +const char * const GC_copyright[] = +{"Copyright 1988, 1989 Hans-J. Boehm and Alan J. Demers ", +"Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. ", +"Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. ", +"Copyright (c) 1999-2009 by Hewlett-Packard Company. All rights reserved. ", +"Copyright (c) 2008-2021 Ivan Maidanski ", +"THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY", +" EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.", +"See source code for details." }; + +/* Version macros are now defined in gc_version.h, which is included by */ +/* gc.h, which is included by gc_priv.h. */ +#ifndef GC_NO_VERSION_VAR + EXTERN_C_BEGIN + extern const unsigned GC_version; + EXTERN_C_END + const unsigned GC_version = ((GC_VERSION_MAJOR << 16) | + (GC_VERSION_MINOR << 8) | GC_VERSION_MICRO); +#endif + +GC_API unsigned GC_CALL GC_get_version(void) +{ + return (GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8) | + GC_VERSION_MICRO; +} + +/* some more variables */ + +#ifdef GC_DONT_EXPAND + int GC_dont_expand = TRUE; +#else + int GC_dont_expand = FALSE; +#endif + +#if defined(GC_FREE_SPACE_DIVISOR) && !defined(CPPCHECK) + word GC_free_space_divisor = GC_FREE_SPACE_DIVISOR; /* must be > 0 */ +#else + word GC_free_space_divisor = 3; +#endif + +GC_INNER int GC_CALLBACK GC_never_stop_func(void) +{ + return(0); +} + +#if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + unsigned long GC_time_limit = GC_TIME_LIMIT; + /* We try to keep pause times from exceeding */ + /* this by much. In milliseconds. */ +#elif defined(PARALLEL_MARK) + unsigned long GC_time_limit = GC_TIME_UNLIMITED; + /* The parallel marker cannot be interrupted for */ + /* now, so the time limit is absent by default. */ +#else + unsigned long GC_time_limit = 50; +#endif + +#ifndef NO_CLOCK + STATIC unsigned long GC_time_lim_nsec = 0; + /* The nanoseconds add-on to GC_time_limit */ + /* value. Not updated by GC_set_time_limit(). */ + /* Ignored if the value of GC_time_limit is */ + /* GC_TIME_UNLIMITED. */ + +# define TV_NSEC_LIMIT (1000UL * 1000) /* amount of nanoseconds in 1 ms */ + + GC_API void GC_CALL GC_set_time_limit_tv(struct GC_timeval_s tv) + { + GC_ASSERT(tv.tv_ms <= GC_TIME_UNLIMITED); + GC_ASSERT(tv.tv_nsec < TV_NSEC_LIMIT); + GC_time_limit = tv.tv_ms; + GC_time_lim_nsec = tv.tv_nsec; + } + + GC_API struct GC_timeval_s GC_CALL GC_get_time_limit_tv(void) + { + struct GC_timeval_s tv; + + tv.tv_ms = GC_time_limit; + tv.tv_nsec = GC_time_lim_nsec; + return tv; + } + + STATIC CLOCK_TYPE GC_start_time = CLOCK_TYPE_INITIALIZER; + /* Time at which we stopped world. */ + /* used only in GC_timeout_stop_func. */ +#endif /* !NO_CLOCK */ + +STATIC int GC_n_attempts = 0; /* Number of attempts at finishing */ + /* collection within GC_time_limit. */ + +STATIC GC_stop_func GC_default_stop_func = GC_never_stop_func; + /* accessed holding the lock. */ + +GC_API void GC_CALL GC_set_stop_func(GC_stop_func stop_func) +{ + DCL_LOCK_STATE; + GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func)); + LOCK(); + GC_default_stop_func = stop_func; + UNLOCK(); +} + +GC_API GC_stop_func GC_CALL GC_get_stop_func(void) +{ + GC_stop_func stop_func; + DCL_LOCK_STATE; + LOCK(); + stop_func = GC_default_stop_func; + UNLOCK(); + return stop_func; +} + +#if defined(GC_DISABLE_INCREMENTAL) || defined(NO_CLOCK) +# define GC_timeout_stop_func GC_default_stop_func +#else + STATIC int GC_CALLBACK GC_timeout_stop_func (void) + { + CLOCK_TYPE current_time; + static unsigned count = 0; + unsigned long time_diff, nsec_diff; + + if ((*GC_default_stop_func)()) + return(1); + + if ((count++ & 3) != 0) return(0); + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time,GC_start_time); + nsec_diff = NS_FRAC_TIME_DIFF(current_time, GC_start_time); +# if defined(CPPCHECK) + GC_noop1((word)&nsec_diff); +# endif + if (time_diff >= GC_time_limit + && (time_diff > GC_time_limit || nsec_diff >= GC_time_lim_nsec)) { + GC_COND_LOG_PRINTF("Abandoning stopped marking after %lu ms %lu ns" + " (attempt %d)\n", + time_diff, nsec_diff, GC_n_attempts); + return 1; + } + return(0); + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +#ifdef THREADS + GC_INNER word GC_total_stacksize = 0; /* updated on every push_all_stacks */ +#endif + +static size_t min_bytes_allocd_minimum = 1; + /* The lowest value returned by min_bytes_allocd(). */ + +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t value) +{ + GC_ASSERT(value > 0); + min_bytes_allocd_minimum = value; +} + +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void) +{ + return min_bytes_allocd_minimum; +} + +/* 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) +{ + word result; + word stack_size; + word total_root_size; /* includes double stack size, */ + /* since the stack is expensive */ + /* to scan. */ + word scan_size; /* Estimate of memory to be scanned */ + /* during normal GC. */ + +# ifdef THREADS + if (GC_need_to_lock) { + /* We are multi-threaded... */ + stack_size = GC_total_stacksize; + /* For now, we just use the value computed during the latest GC. */ +# ifdef DEBUG_THREADS + GC_log_printf("Total stacks size: %lu\n", + (unsigned long)stack_size); +# endif + } else +# endif + /* else*/ { +# ifdef STACK_NOT_SCANNED + stack_size = 0; +# elif defined(STACK_GROWS_UP) + stack_size = GC_approx_sp() - GC_stackbottom; +# else + stack_size = GC_stackbottom - GC_approx_sp(); +# endif + } + + 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) { + result /= 2; + } + return result > min_bytes_allocd_minimum + ? result : min_bytes_allocd_minimum; +} + +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. */ +STATIC word GC_adj_bytes_allocd(void) +{ + signed_word result; + signed_word expl_managed = (signed_word)GC_non_gc_bytes + - (signed_word)GC_non_gc_bytes_at_gc; + + /* Don't count what was explicitly freed, or newly allocated for */ + /* explicit management. Note that deallocating an explicitly */ + /* managed object should not alter result, assuming the client */ + /* is playing by the rules. */ + result = (signed_word)GC_bytes_allocd + + (signed_word)GC_bytes_dropped + - (signed_word)GC_bytes_freed + + (signed_word)GC_finalizer_bytes_freed + - expl_managed; + if (result > (signed_word)GC_bytes_allocd) { + result = GC_bytes_allocd; + /* probably client bug or unfortunate scheduling */ + } + result += GC_bytes_finalized; + /* We count objects enqueued for finalization as though they */ + /* had been reallocated this round. Finalization is user */ + /* visible progress. And if we don't count this, we have */ + /* stability problems for programs that finalize all objects. */ + if (result < (signed_word)(GC_bytes_allocd >> 3)) { + /* Always count at least 1/8 of the allocations. We don't want */ + /* to collect too infrequently, since that would inhibit */ + /* coalescing of free storage blocks. */ + /* This also makes us partially robust against client bugs. */ + return(GC_bytes_allocd >> 3); + } else { + return(result); + } +} + + +/* Clear up a few frames worth of garbage left at the top of the stack. */ +/* This is used to prevent us from accidentally treating garbage left */ +/* on the stack by other parts of the collector as roots. This */ +/* differs from the code in misc.c, which actually tries to keep the */ +/* stack clear of long-lived, client-generated garbage. */ +STATIC void GC_clear_a_few_frames(void) +{ +# ifndef CLEAR_NWORDS +# define CLEAR_NWORDS 64 +# endif + volatile word frames[CLEAR_NWORDS]; + BZERO((word *)frames, CLEAR_NWORDS * sizeof(word)); +} + +/* Heap size at which we need a collection to avoid expanding past */ +/* limits used by blacklisting. */ +STATIC word GC_collect_at_heapsize = GC_WORD_MAX; + +GC_API void GC_CALL GC_start_incremental_collection(void) +{ +# ifndef GC_DISABLE_INCREMENTAL + DCL_LOCK_STATE; + + if (!GC_incremental) return; + LOCK(); + GC_should_start_incremental_collection = TRUE; + if (!GC_dont_gc) { + ENTER_GC(); + GC_collect_a_little_inner(1); + EXIT_GC(); + } + UNLOCK(); +# endif +} + +/* Have we allocated enough to amortize a collection? */ +GC_INNER GC_bool GC_should_collect(void) +{ + static word last_min_bytes_allocd; + static word last_gc_no; + + GC_ASSERT(I_HOLD_LOCK()); + if (last_gc_no != GC_gc_no) { + last_min_bytes_allocd = min_bytes_allocd(); + last_gc_no = GC_gc_no; + } +# ifndef GC_DISABLE_INCREMENTAL + if (GC_should_start_incremental_collection) { + GC_should_start_incremental_collection = FALSE; + return TRUE; + } +# endif + if (GC_disable_automatic_collection) return FALSE; + + return(GC_adj_bytes_allocd() >= last_min_bytes_allocd + || GC_heapsize >= GC_collect_at_heapsize); +} + +/* STATIC */ GC_start_callback_proc GC_start_call_back = 0; + /* Called at start of full collections. */ + /* Not called if 0. Called with the allocation */ + /* lock held. Not used by GC itself. */ + +GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc fn) +{ + DCL_LOCK_STATE; + LOCK(); + GC_start_call_back = fn; + UNLOCK(); +} + +GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void) +{ + GC_start_callback_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_start_call_back; + UNLOCK(); + return fn; +} + +GC_INLINE void GC_notify_full_gc(void) +{ + if (GC_start_call_back != 0) { + (*GC_start_call_back)(); + } +} + +STATIC GC_bool GC_is_full_gc = FALSE; + +STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func); +STATIC void GC_finish_collection(void); + +/* + * Initiate a garbage collection if appropriate. + * Choose judiciously + * between partial, full, and stop-world collections. + */ +STATIC void GC_maybe_gc(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + ASSERT_CANCEL_DISABLED(); + if (GC_should_collect()) { + static int n_partial_gcs = 0; + + if (!GC_incremental) { + /* TODO: If possible, GC_default_stop_func should be used here */ + GC_try_to_collect_inner(GC_never_stop_func); + n_partial_gcs = 0; + return; + } else { +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + if (GC_need_full_gc || n_partial_gcs >= GC_full_freq) { + 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(); + GC_clear_marks(); + n_partial_gcs = 0; + GC_is_full_gc = TRUE; + } else { + n_partial_gcs++; + } + } + /* We try to mark with the world stopped. */ + /* If we run out of time, this turns into */ + /* incremental marking. */ +# ifndef NO_CLOCK + if (GC_time_limit != GC_TIME_UNLIMITED) { GET_TIME(GC_start_time); } +# endif + /* TODO: If possible, GC_default_stop_func should be */ + /* used instead of GC_never_stop_func here. */ + if (GC_stopped_mark(GC_time_limit == GC_TIME_UNLIMITED? + GC_never_stop_func : GC_timeout_stop_func)) { +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + GC_finish_collection(); + } else { + if (!GC_is_full_gc) { + /* Count this as the first attempt */ + GC_n_attempts++; + } + } + } +} + +STATIC GC_on_collection_event_proc GC_on_collection_event = 0; + +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc fn) +{ + /* fn may be 0 (means no event notifier). */ + DCL_LOCK_STATE; + LOCK(); + GC_on_collection_event = fn; + UNLOCK(); +} + +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void) +{ + GC_on_collection_event_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_on_collection_event; + UNLOCK(); + return fn; +} + +/* Stop the world garbage collection. If stop_func is not */ +/* GC_never_stop_func then abort if stop_func returns TRUE. */ +/* Return TRUE if we successfully completed the collection. */ +GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func) +{ +# ifndef NO_CLOCK + CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER; + GC_bool start_time_valid; +# endif + + ASSERT_CANCEL_DISABLED(); + GC_ASSERT(I_HOLD_LOCK()); + if (GC_dont_gc || (*stop_func)()) return FALSE; + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_START); + if (GC_incremental && GC_collection_in_progress()) { + 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)()) { + /* TODO: Notify GC_EVENT_ABANDON */ + return(FALSE); + } + ENTER_GC(); + GC_collect_a_little_inner(1); + EXIT_GC(); + } + } + GC_notify_full_gc(); +# ifndef NO_CLOCK + start_time_valid = FALSE; + if ((GC_print_stats | (int)measure_performance) != 0) { + if (GC_print_stats) + GC_log_printf("Initiating full world-stop collection!\n"); + start_time_valid = TRUE; + GET_TIME(start_time); + } +# endif + GC_promote_black_lists(); + /* Make sure all blocks have been reclaimed, so sweep routines */ + /* don't see cleared mark bits. */ + /* If we're guaranteed to finish, then this is unnecessary. */ + /* In the find_leak case, we have to finish to guarantee that */ + /* previously unmarked objects are not reported as leaks. */ +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + if ((GC_find_leak || stop_func != GC_never_stop_func) + && !GC_reclaim_all(stop_func, FALSE)) { + /* Aborted. So far everything is still consistent. */ + /* TODO: Notify GC_EVENT_ABANDON */ + return(FALSE); + } + GC_invalidate_mark_state(); /* Flush mark stack. */ + GC_clear_marks(); +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + GC_is_full_gc = TRUE; + if (!GC_stopped_mark(stop_func)) { + if (!GC_incremental) { + /* We're partially done and have no way to complete or use */ + /* current work. Reestablish invariants as cheaply as */ + /* possible. */ + GC_invalidate_mark_state(); + GC_unpromote_black_lists(); + } /* else we claim the world is already still consistent. We'll */ + /* finish incrementally. */ + /* TODO: Notify GC_EVENT_ABANDON */ + return(FALSE); + } + GC_finish_collection(); +# ifndef NO_CLOCK + if (start_time_valid) { + CLOCK_TYPE current_time; + unsigned long time_diff, ns_frac_diff; + + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time, start_time); + ns_frac_diff = NS_FRAC_TIME_DIFF(current_time, start_time); + if (measure_performance) { + full_gc_total_time += time_diff; /* may wrap */ + full_gc_total_ns_frac += (unsigned)ns_frac_diff; + if (full_gc_total_ns_frac >= 1000000U) { + /* Overflow of the nanoseconds part. */ + full_gc_total_ns_frac -= 1000000U; + full_gc_total_time++; + } + } + if (GC_print_stats) + GC_log_printf("Complete collection took %lu ms %lu ns\n", + time_diff, ns_frac_diff); + } +# endif + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_END); + return(TRUE); +} + +/* The number of extra calls to GC_mark_some that we have made. */ +STATIC int GC_deficit = 0; + +/* The default value of GC_rate. */ +#ifndef GC_RATE +# define GC_RATE 10 +#endif + +/* When GC_collect_a_little_inner() performs n units of GC work, a unit */ +/* is intended to touch roughly GC_rate pages. (But, every once in */ +/* a while, we do more than that.) This needs to be a fairly large */ +/* number with our current incremental GC strategy, since otherwise we */ +/* allocate too much during GC, and the cleanup gets expensive. */ +STATIC int GC_rate = GC_RATE; + +GC_API void GC_CALL GC_set_rate(int value) +{ + GC_ASSERT(value > 0); + GC_rate = value; +} + +GC_API int GC_CALL GC_get_rate(void) +{ + return GC_rate; +} + +/* The default maximum number of prior attempts at world stop marking. */ +#ifndef MAX_PRIOR_ATTEMPTS +# define MAX_PRIOR_ATTEMPTS 1 +#endif + +/* The maximum number of prior attempts at world stop marking. */ +/* A value of 1 means that we finish the second time, no matter how */ +/* long it takes. Does not count the initial root scan for a full GC. */ +static int max_prior_attempts = MAX_PRIOR_ATTEMPTS; + +GC_API void GC_CALL GC_set_max_prior_attempts(int value) +{ + GC_ASSERT(value >= 0); + max_prior_attempts = value; +} + +GC_API int GC_CALL GC_get_max_prior_attempts(void) +{ + return max_prior_attempts; +} + +GC_INNER void GC_collect_a_little_inner(int n) +{ + IF_CANCEL(int cancel_state;) + + GC_ASSERT(I_HOLD_LOCK()); + DISABLE_CANCEL(cancel_state); + if (GC_incremental && GC_collection_in_progress()) { + int i; + int max_deficit = GC_rate * n; + +# ifdef PARALLEL_MARK + if (GC_time_limit != GC_TIME_UNLIMITED) + GC_parallel_mark_disabled = TRUE; +# endif + for (i = GC_deficit; i < max_deficit; i++) { + if (GC_mark_some(NULL)) + break; + } +# ifdef PARALLEL_MARK + GC_parallel_mark_disabled = FALSE; +# endif + + if (i < max_deficit && !GC_dont_gc) { + /* Need to finish a collection. */ +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + if (GC_n_attempts < max_prior_attempts + && GC_time_limit != GC_TIME_UNLIMITED) { +# ifndef NO_CLOCK + GET_TIME(GC_start_time); +# endif + if (GC_stopped_mark(GC_timeout_stop_func)) { + GC_finish_collection(); + } else { + GC_n_attempts++; + } + } else { + /* TODO: If possible, GC_default_stop_func should be */ + /* used here. */ + (void)GC_stopped_mark(GC_never_stop_func); + GC_finish_collection(); + } + } + if (GC_deficit > 0) { + GC_deficit -= max_deficit; + if (GC_deficit < 0) + GC_deficit = 0; + } + } else if (!GC_dont_gc) { + GC_maybe_gc(); + } + RESTORE_CANCEL(cancel_state); +} + +GC_INNER void (*GC_check_heap)(void) = 0; +GC_INNER void (*GC_print_all_smashed)(void) = 0; + +GC_API int GC_CALL GC_collect_a_little(void) +{ + int result; + DCL_LOCK_STATE; + + LOCK(); + if (!GC_dont_gc) { + ENTER_GC(); + GC_collect_a_little_inner(1); + EXIT_GC(); + } + result = (int)GC_collection_in_progress(); + UNLOCK(); + if (!result && GC_debugging_started) GC_print_all_smashed(); + return(result); +} + +#ifndef NO_CLOCK + /* 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" overflow). */ + static unsigned world_stopped_total_time = 0; + static unsigned world_stopped_total_divisor = 0; +# ifndef MAX_TOTAL_TIME_DIVISOR + /* We shall not use big values here (so "outdated" delay time */ + /* values would have less impact on "average" delay time value than */ + /* newer ones). */ +# define MAX_TOTAL_TIME_DIVISOR 1000 +# endif +#endif /* !NO_CLOCK */ + +#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 + +/* + * We stop the world and mark from all roots. + * If stop_func() ever returns TRUE, we may fail and return FALSE. + * Increment GC_gc_no if we succeed. + */ +STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func) +{ + int i; +# ifndef NO_CLOCK + CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER; +# endif + + GC_ASSERT(I_HOLD_LOCK()); +# if !defined(REDIRECT_MALLOC) && defined(USE_WINALLOC) + GC_add_current_malloc_heap(); +# endif +# if defined(REGISTER_LIBRARIES_EARLY) + GC_cond_register_dynamic_libraries(); +# endif + +# ifndef NO_CLOCK + if (GC_PRINT_STATS_FLAG) + GET_TIME(start_time); +# endif + +# if !defined(GC_NO_FINALIZATION) && !defined(GC_TOGGLE_REFS_NOT_NEEDED) + GC_process_togglerefs(); +# endif +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_PRE_STOP_WORLD); +# endif + STOP_WORLD(); +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_POST_STOP_WORLD); +# endif + +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = TRUE; +# endif + /* Output blank line for convenience here */ + 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(); + } +# endif + + /* Mark from all roots. */ + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_MARK_START); + + /* Minimize junk left in my registers and on the stack */ + GC_clear_a_few_frames(); + GC_noop6(0,0,0,0,0,0); + + GC_initiate_gc(); +# ifdef PARALLEL_MARK + if (stop_func != GC_never_stop_func) + GC_parallel_mark_disabled = TRUE; +# endif + for (i = 0; !(*stop_func)(); i++) { + if (GC_mark_some(GC_approx_sp())) { +# ifdef PARALLEL_MARK + if (GC_parallel && GC_parallel_mark_disabled) { + GC_COND_LOG_PRINTF("Stopped marking done after %d iterations" + " with disabled parallel marker\n", i); + } +# endif + i = -1; + break; + } + } +# ifdef PARALLEL_MARK + GC_parallel_mark_disabled = FALSE; +# endif + + if (i >= 0) { + GC_COND_LOG_PRINTF("Abandoned stopped marking after" + " %d iterations\n", i); + GC_deficit = i; /* Give the mutator a chance. */ +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = FALSE; +# endif + +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_PRE_START_WORLD); +# endif + + START_WORLD(); + +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_POST_START_WORLD); +# endif + + /* TODO: Notify GC_EVENT_MARK_ABANDON */ + return FALSE; + } + + GC_gc_no++; +# ifdef USE_MUNMAP + GC_ASSERT(GC_heapsize >= GC_unmapped_bytes); +# endif + GC_ASSERT(GC_our_mem_bytes >= GC_heapsize); + GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes, heap %lu KiB (" + IF_USE_MUNMAP("+ %lu KiB unmapped ") + "+ %lu KiB internal)\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)), + TO_KiB_UL(GC_our_mem_bytes - GC_heapsize)); + + /* Check all debugged objects for consistency */ + if (GC_debugging_started) { + (*GC_check_heap)(); + } + if (GC_on_collection_event) { + GC_on_collection_event(GC_EVENT_MARK_END); +# ifdef THREADS + GC_on_collection_event(GC_EVENT_PRE_START_WORLD); +# endif + } +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = FALSE; +# endif + + START_WORLD(); + +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_POST_START_WORLD); +# endif + +# ifndef NO_CLOCK + if (GC_PRINT_STATS_FLAG) { + unsigned long time_diff; + unsigned total_time, divisor; + CLOCK_TYPE current_time; + + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time,start_time); + + /* Compute new world-stop delay total time */ + total_time = world_stopped_total_time; + divisor = world_stopped_total_divisor; + if ((int)total_time < 0 || divisor >= MAX_TOTAL_TIME_DIVISOR) { + /* Halve values if overflow occurs */ + total_time >>= 1; + divisor >>= 1; + } + total_time += time_diff < (((unsigned)-1) >> 1) ? + (unsigned)time_diff : ((unsigned)-1) >> 1; + /* Update old world_stopped_total_time and its divisor */ + world_stopped_total_time = total_time; + world_stopped_total_divisor = ++divisor; + + GC_ASSERT(divisor != 0); + GC_log_printf("World-stopped marking took %lu ms %lu ns" + " (%u ms in average)\n", + time_diff, NS_FRAC_TIME_DIFF(current_time, start_time), + total_time / divisor); + } +# endif + return(TRUE); +} + +/* Set all mark bits for the free list whose first entry is q */ +GC_INNER void GC_set_fl_marks(ptr_t q) +{ + if (q /* != NULL */) { /* CPPCHECK */ + struct hblk *h = HBLKPTR(q); + struct hblk *last_h = h; + hdr *hhdr = HDR(h); + IF_PER_OBJ(word sz = hhdr->hb_sz;) + + for (;;) { + word bit_no = MARK_BIT_NO((ptr_t)q - (ptr_t)h, sz); + + if (!mark_bit_from_hdr(hhdr, bit_no)) { + set_mark_bit_from_hdr(hhdr, bit_no); + ++hhdr -> hb_n_marks; + } + + q = (ptr_t)obj_link(q); + if (q == NULL) + break; + + h = HBLKPTR(q); + if (h != last_h) { + last_h = h; + hhdr = HDR(h); + IF_PER_OBJ(sz = hhdr->hb_sz;) + } + } + } +} + +#if defined(GC_ASSERTIONS) && 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) + { + /* TODO: There is a data race with GC_FAST_MALLOC_GRANS (which does */ + /* not do atomic updates to the free-list). The race seems to be */ + /* harmless, and for now we just skip this check in case of TSan. */ +# if defined(AO_HAVE_load_acquire_read) && !defined(THREAD_SANITIZER) + 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 /* 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. */ +STATIC void GC_clear_fl_marks(ptr_t q) +{ + struct hblk *h = HBLKPTR(q); + struct hblk *last_h = h; + hdr *hhdr = HDR(h); + word sz = hhdr->hb_sz; /* Normally set only once. */ + + for (;;) { + word bit_no = MARK_BIT_NO((ptr_t)q - (ptr_t)h, sz); + + if (mark_bit_from_hdr(hhdr, bit_no)) { + size_t n_marks = hhdr -> hb_n_marks; + + GC_ASSERT(n_marks != 0); + clear_mark_bit_from_hdr(hhdr, bit_no); + n_marks--; +# ifdef PARALLEL_MARK + /* Appr. count, don't decrement to zero! */ + if (0 != n_marks || !GC_parallel) { + hhdr -> hb_n_marks = n_marks; + } +# else + hhdr -> hb_n_marks = n_marks; +# endif + } + GC_bytes_found -= sz; + + q = (ptr_t)obj_link(q); + if (q == NULL) + break; + + h = HBLKPTR(q); + if (h != last_h) { + last_h = h; + hhdr = HDR(h); + sz = hhdr->hb_sz; + } + } +} + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + void GC_check_tls(void); +#endif + +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; +# if defined(CPPCHECK) + word limit = (GC_WORD_MAX >> 1) / 50; /* to avoid a false positive */ +# else + const word limit = GC_WORD_MAX / 100; +# endif + + return used >= heap_sz ? 0 : used < limit ? + (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) +{ +# ifndef NO_CLOCK + CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER; + CLOCK_TYPE finalize_time = CLOCK_TYPE_INITIALIZER; +# endif + + GC_ASSERT(I_HOLD_LOCK()); +# if defined(GC_ASSERTIONS) \ + && defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) + /* Check that we marked some of our own data. */ + /* TODO: Add more checks. */ + GC_check_tls(); +# endif + +# ifndef NO_CLOCK + if (GC_print_stats) + GET_TIME(start_time); +# endif + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_RECLAIM_START); + +# 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) { + GC_print_address_map(); + } +# endif + COND_DUMP; + if (GC_find_leak) { + /* Mark all objects on the free list. All objects should be */ + /* marked when we're done. */ + word size; /* current object size */ + unsigned kind; + ptr_t q; + + for (kind = 0; kind < GC_n_kinds; kind++) { + for (size = 1; size <= MAXOBJGRANULES; size++) { + q = (ptr_t)GC_obj_kinds[kind].ok_freelist[size]; + if (q != NULL) + GC_set_fl_marks(q); + } + } + GC_start_reclaim(TRUE); + /* The above just checks; it doesn't really reclaim anything. */ + } + +# ifndef GC_NO_FINALIZATION + GC_finalize(); +# endif +# ifndef NO_CLOCK + if (GC_print_stats) + GET_TIME(finalize_time); +# endif + + if (GC_print_back_height) { +# ifdef MAKE_BACK_GRAPH + GC_traverse_back_graph(); +# elif !defined(SMALL_CONFIG) + GC_err_printf("Back height not available: " + "Rebuild collector with -DMAKE_BACK_GRAPH\n"); +# endif + } + + /* Clear free list mark bits, in case they got accidentally marked */ + /* (or GC_find_leak is set and they were intentionally marked). */ + /* Also subtract memory remaining from GC_bytes_found count. */ + /* Note that composite objects on free list are cleared. */ + /* Thus accidentally marking a free list is not a problem; only */ + /* objects on the list itself will be marked, and that's fixed here. */ + { + word size; /* current object size */ + ptr_t q; /* pointer to current object */ + unsigned kind; + + for (kind = 0; kind < GC_n_kinds; kind++) { + for (size = 1; size <= MAXOBJGRANULES; size++) { + q = (ptr_t)GC_obj_kinds[kind].ok_freelist[size]; + if (q != NULL) + GC_clear_fl_marks(q); + } + } + } + + 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); + 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; + } else { + GC_need_full_gc = USED_HEAP_SIZE - GC_used_heap_size_after_full + > min_bytes_allocd(); + } + + 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; + GC_is_full_gc = FALSE; + GC_bytes_allocd_before_gc += GC_bytes_allocd; + GC_non_gc_bytes_at_gc = GC_non_gc_bytes; + GC_bytes_allocd = 0; + GC_bytes_dropped = 0; + GC_bytes_freed = 0; + GC_finalizer_bytes_freed = 0; + + IF_USE_MUNMAP(GC_unmap_old()); + + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_RECLAIM_END); +# ifndef NO_CLOCK + if (GC_print_stats) { + CLOCK_TYPE done_time; + + GET_TIME(done_time); +# if !defined(SMALL_CONFIG) && !defined(GC_NO_FINALIZATION) + /* A convenient place to output finalization statistics. */ + GC_print_finalization_stats(); +# endif + GC_log_printf("Finalize and initiate sweep took %lu ms %lu ns" + " + %lu ms %lu ns\n", + MS_TIME_DIFF(finalize_time, start_time), + NS_FRAC_TIME_DIFF(finalize_time, start_time), + MS_TIME_DIFF(done_time, finalize_time), + NS_FRAC_TIME_DIFF(done_time, finalize_time)); + } +# elif !defined(SMALL_CONFIG) && !defined(GC_NO_FINALIZATION) + if (GC_print_stats) + GC_print_finalization_stats(); +# endif +} + +STATIC word GC_heapsize_at_forced_unmap = 0; + /* accessed with the allocation lock held */ + +/* If stop_func == 0 then GC_default_stop_func is used instead. */ +STATIC GC_bool GC_try_to_collect_general(GC_stop_func stop_func, + GC_bool force_unmap) +{ + GC_bool result; + IF_USE_MUNMAP(int old_unmap_threshold;) + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + if (GC_debugging_started) GC_print_all_smashed(); + GC_INVOKE_FINALIZERS(); + LOCK(); + if (force_unmap) { + /* Record current heap size to make heap growth more conservative */ + /* afterwards (as if the heap is growing from zero size again). */ + GC_heapsize_at_forced_unmap = GC_heapsize; + } + DISABLE_CANCEL(cancel_state); +# ifdef USE_MUNMAP + old_unmap_threshold = GC_unmap_threshold; + if (force_unmap || + (GC_force_unmap_on_gcollect && old_unmap_threshold > 0)) + GC_unmap_threshold = 1; /* unmap as much as possible */ +# endif + ENTER_GC(); + /* Minimize junk left in my registers */ + 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(); + IF_USE_MUNMAP(GC_unmap_threshold = old_unmap_threshold); /* restore */ + RESTORE_CANCEL(cancel_state); + UNLOCK(); + if (result) { + if (GC_debugging_started) GC_print_all_smashed(); + GC_INVOKE_FINALIZERS(); + } + return(result); +} + +/* Externally callable routines to invoke full, stop-the-world collection. */ + +GC_API int GC_CALL GC_try_to_collect(GC_stop_func stop_func) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func)); + return (int)GC_try_to_collect_general(stop_func, FALSE); +} + +GC_API void GC_CALL GC_gcollect(void) +{ + /* 0 is passed as stop_func to get GC_default_stop_func value */ + /* while holding the allocation lock (to prevent data races). */ + (void)GC_try_to_collect_general(0, FALSE); + if (get_have_errors()) + GC_print_all_errors(); +} + +GC_API void GC_CALL GC_gcollect_and_unmap(void) +{ + /* Collect and force memory unmapping to OS. */ + (void)GC_try_to_collect_general(GC_never_stop_func, TRUE); +} + +#ifdef USE_PROC_FOR_LIBRARIES + /* Add HBLKSIZE aligned, GET_MEM-generated block to GC_our_memory. */ + GC_INNER void GC_add_to_our_memory(ptr_t p, size_t bytes) + { + GC_ASSERT(p != NULL); + if (GC_n_memory >= MAX_HEAP_SECTS) + ABORT("Too many GC-allocated memory sections: Increase MAX_HEAP_SECTS"); + GC_our_memory[GC_n_memory].hs_start = p; + GC_our_memory[GC_n_memory].hs_bytes = bytes; + GC_n_memory++; + GC_our_mem_bytes += bytes; + } +#endif + +/* Use the chunk of memory starting at p of size bytes as part of the heap. */ +/* Assumes p is HBLKSIZE aligned, bytes argument is a multiple of HBLKSIZE. */ +STATIC void GC_add_to_heap(struct hblk *p, size_t bytes) +{ + hdr * phdr; + word endp; + size_t old_capacity = 0; + void *old_heap_sects = NULL; +# ifdef GC_ASSERTIONS + unsigned i; +# endif + + GC_ASSERT((word)p % HBLKSIZE == 0); + GC_ASSERT(bytes % HBLKSIZE == 0); + GC_ASSERT(bytes > 0); + GC_ASSERT(GC_all_nils != NULL); + + if (GC_n_heap_sects == GC_capacity_heap_sects) { + /* Allocate new GC_heap_sects with sufficient capacity. */ +# ifndef INITIAL_HEAP_SECTS +# define INITIAL_HEAP_SECTS 32 +# endif + size_t new_capacity = GC_n_heap_sects > 0 ? + (size_t)GC_n_heap_sects * 2 : INITIAL_HEAP_SECTS; + void *new_heap_sects = + GC_scratch_alloc(new_capacity * sizeof(struct HeapSect)); + + if (EXPECT(NULL == new_heap_sects, FALSE)) { + /* Retry with smaller yet sufficient capacity. */ + new_capacity = (size_t)GC_n_heap_sects + INITIAL_HEAP_SECTS; + new_heap_sects = + GC_scratch_alloc(new_capacity * sizeof(struct HeapSect)); + if (NULL == new_heap_sects) + ABORT("Insufficient memory for heap sections"); + } + old_capacity = GC_capacity_heap_sects; + old_heap_sects = GC_heap_sects; + /* Transfer GC_heap_sects contents to the newly allocated array. */ + if (GC_n_heap_sects > 0) + BCOPY(old_heap_sects, new_heap_sects, + GC_n_heap_sects * sizeof(struct HeapSect)); + GC_capacity_heap_sects = new_capacity; + GC_heap_sects = (struct HeapSect *)new_heap_sects; + GC_COND_LOG_PRINTF("Grew heap sections array to %lu elements\n", + (unsigned long)new_capacity); + } + + while ((word)p <= HBLKSIZE) { + /* Can't handle memory near address zero. */ + ++p; + bytes -= HBLKSIZE; + if (0 == bytes) return; + } + endp = (word)p + bytes; + if (endp <= (word)p) { + /* Address wrapped. */ + bytes -= HBLKSIZE; + if (0 == bytes) return; + endp -= HBLKSIZE; + } + phdr = GC_install_header(p); + if (0 == phdr) { + /* This is extremely unlikely. Can't add it. This will */ + /* almost certainly result in a 0 return from the allocator, */ + /* which is entirely appropriate. */ + return; + } + GC_ASSERT(endp > (word)p && endp == (word)p + bytes); +# ifdef GC_ASSERTIONS + /* Ensure no intersection between sections. */ + for (i = 0; i < GC_n_heap_sects; i++) { + word hs_start = (word)GC_heap_sects[i].hs_start; + word hs_end = hs_start + GC_heap_sects[i].hs_bytes; + word p_e = (word)p + bytes; + + GC_ASSERT(!((hs_start <= (word)p && (word)p < hs_end) + || (hs_start < p_e && p_e <= hs_end) + || ((word)p < hs_start && hs_end < p_e))); + } +# endif + GC_heap_sects[GC_n_heap_sects].hs_start = (ptr_t)p; + GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes; + GC_n_heap_sects++; + phdr -> hb_sz = bytes; + phdr -> hb_flags = 0; + GC_freehblk(p); + GC_heapsize += bytes; + + /* Normally the caller calculates a new GC_collect_at_heapsize, + * but this is also called directly from GC_scratch_recycle_inner, so + * adjust here. It will be recalculated when called from + * GC_expand_hp_inner. + */ + GC_collect_at_heapsize += bytes; + if (GC_collect_at_heapsize < GC_heapsize /* wrapped */) + GC_collect_at_heapsize = GC_WORD_MAX; + + 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 */ + /* us from getting a false hit from the variable */ + /* itself. There's some unintentional reflection */ + /* here. */ + } + if ((word)p + bytes >= (word)GC_greatest_plausible_heap_addr) { + GC_greatest_plausible_heap_addr = (void *)endp; + } +# ifdef SET_REAL_HEAP_BOUNDS + if ((word)p < GC_least_real_heap_addr + || EXPECT(0 == GC_least_real_heap_addr, FALSE)) + GC_least_real_heap_addr = (word)p - sizeof(word); + if (endp > GC_greatest_real_heap_addr) { +# ifdef INCLUDE_LINUX_THREAD_DESCR + /* Avoid heap intersection with the static data roots. */ + GC_exclude_static_roots_inner((void *)p, (void *)endp); +# endif + GC_greatest_real_heap_addr = endp; + } +# endif + if (old_capacity > 0) { +# ifndef GWW_VDB + /* Recycling may call GC_add_to_heap() again but should not */ + /* cause resizing of GC_heap_sects. */ + GC_scratch_recycle_no_gww(old_heap_sects, + old_capacity * sizeof(struct HeapSect)); +# else + /* TODO: implement GWW-aware recycling as in alloc_mark_stack */ + GC_noop1((word)old_heap_sects); +# endif + } +} + +#if !defined(NO_DEBUGGING) + void GC_print_heap_sects(void) + { + unsigned i; + + 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; (word)h < (word)(start + len); h++) { + if (GC_is_black_listed(h, HBLKSIZE)) nbl++; + } + GC_printf("Section %d from %p to %p %u/%lu blacklisted\n", + i, (void *)start, (void *)&start[len], + nbl, (unsigned long)divHBLKSZ(len)); + } + } +#endif + +void * GC_least_plausible_heap_addr = (void *)GC_WORD_MAX; +void * GC_greatest_plausible_heap_addr = 0; + +GC_INLINE word GC_max(word x, word y) +{ + return(x > y? x : y); +} + +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; +} + +GC_word GC_max_retries = 0; + +GC_INNER void GC_scratch_recycle_inner(void *ptr, size_t bytes) +{ + size_t page_offset; + size_t displ = 0; + size_t recycled_bytes; + + if (NULL == ptr) return; + + GC_ASSERT(bytes != 0); + GC_ASSERT(GC_page_size != 0); + /* TODO: Assert correct memory flags if GWW_VDB */ + page_offset = (word)ptr & (GC_page_size - 1); + if (page_offset != 0) + displ = GC_page_size - page_offset; + recycled_bytes = bytes > displ ? (bytes - displ) & ~(GC_page_size - 1) : 0; + GC_COND_LOG_PRINTF("Recycle %lu/%lu scratch-allocated bytes at %p\n", + (unsigned long)recycled_bytes, (unsigned long)bytes, ptr); + if (recycled_bytes > 0) + GC_add_to_heap((struct hblk *)((word)ptr + displ), recycled_bytes); +} + +/* 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) +{ + size_t bytes; + struct hblk * space; + word expansion_slop; /* Number of bytes by which we expect */ + /* the heap to expand soon. */ + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_page_size != 0); + if (n < MINHINCR) n = MINHINCR; + bytes = ROUNDUP_PAGESIZE((size_t)n * HBLKSIZE); + if (GC_max_heapsize != 0 + && (GC_max_heapsize < (word)bytes + || GC_heapsize > GC_max_heapsize - (word)bytes)) { + /* Exceeded self-imposed limit */ + return(FALSE); + } + space = GET_MEM(bytes); + if (EXPECT(NULL == space, FALSE)) { + WARN("Failed to expand heap by %" WARN_PRIuPTR " KiB\n", bytes >> 10); + return(FALSE); + } + GC_add_to_our_memory((ptr_t)space, bytes); + 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 + && (word)GC_last_heap_addr < (word)space)) { + /* Assume the heap is growing up. */ + word new_limit = (word)space + (word)bytes + expansion_slop; + if (new_limit > (word)space) { + GC_greatest_plausible_heap_addr = + (void *)GC_max((word)GC_greatest_plausible_heap_addr, + (word)new_limit); + } + } else { + /* Heap is growing down. */ + word new_limit = (word)space - expansion_slop; + if (new_limit < (word)space) { + GC_least_plausible_heap_addr = + (void *)GC_min((word)GC_least_plausible_heap_addr, + (word)space - expansion_slop); + } + } + GC_last_heap_addr = (ptr_t)space; + + GC_add_to_heap(space, bytes); + + /* Force GC before we are likely to allocate past expansion_slop. */ + GC_collect_at_heapsize = + GC_heapsize + expansion_slop - 2 * MAXHINCR * HBLKSIZE; + if (GC_collect_at_heapsize < GC_heapsize /* wrapped */) + GC_collect_at_heapsize = GC_WORD_MAX; + if (GC_on_heap_resize) + (*GC_on_heap_resize)(GC_heapsize); + + return(TRUE); +} + +/* Really returns a bool, but it's externally visible, so that's clumsy. */ +/* The argument is in bytes. Includes GC_init() call. */ +GC_API int GC_CALL GC_expand_hp(size_t bytes) +{ + int result; + DCL_LOCK_STATE; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + result = (int)GC_expand_hp_inner(divHBLKSZ((word)bytes)); + if (result) GC_requested_heapsize += bytes; + UNLOCK(); + return(result); +} + +GC_INNER unsigned GC_fail_count = 0; + /* How many consecutive GC/expansion failures? */ + /* Reset by GC_allochblk. */ + +/* The minimum value of the ratio of allocated bytes since the latest */ +/* GC to the amount of finalizers created since that GC which triggers */ +/* the collection instead heap expansion. Has no effect in the */ +/* incremental mode. */ +#if defined(GC_ALLOCD_BYTES_PER_FINALIZER) && !defined(CPPCHECK) + STATIC word GC_allocd_bytes_per_finalizer = GC_ALLOCD_BYTES_PER_FINALIZER; +#else + STATIC word GC_allocd_bytes_per_finalizer = 10000; +#endif + +GC_API void GC_CALL GC_set_allocd_bytes_per_finalizer(GC_word value) +{ + GC_allocd_bytes_per_finalizer = value; +} + +GC_API GC_word GC_CALL GC_get_allocd_bytes_per_finalizer(void) +{ + return GC_allocd_bytes_per_finalizer; +} + +static word last_fo_entries = 0; +static word last_bytes_finalized = 0; + +/* Collect or expand heap in an attempt make the indicated number of */ +/* free blocks available. Should be called until the blocks are */ +/* available (setting retry value to TRUE unless this is the first call */ +/* in a loop) or until it fails by returning FALSE. */ +GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, + GC_bool ignore_off_page, + GC_bool retry) +{ + GC_bool gc_not_stopped = TRUE; + word blocks_to_get; + IF_CANCEL(int cancel_state;) + + GC_ASSERT(I_HOLD_LOCK()); + DISABLE_CANCEL(cancel_state); + if (!GC_incremental && !GC_dont_gc && + ((GC_dont_expand && GC_bytes_allocd > 0) + || (GC_fo_entries > last_fo_entries + && (last_bytes_finalized | GC_bytes_finalized) != 0 + && (GC_fo_entries - last_fo_entries) + * GC_allocd_bytes_per_finalizer > GC_bytes_allocd) + || GC_should_collect())) { + /* Try to do a full collection using 'default' stop_func (unless */ + /* nothing has been allocated since the latest collection or heap */ + /* expansion is disabled). */ + gc_not_stopped = GC_try_to_collect_inner( + GC_bytes_allocd > 0 && (!GC_dont_expand || !retry) ? + GC_default_stop_func : GC_never_stop_func); + if (gc_not_stopped == TRUE || !retry) { + /* Either the collection hasn't been aborted or this is the */ + /* first attempt (in a loop). */ + last_fo_entries = GC_fo_entries; + last_bytes_finalized = GC_bytes_finalized; + RESTORE_CANCEL(cancel_state); + return(TRUE); + } + } + + blocks_to_get = (GC_heapsize - GC_heapsize_at_forced_unmap) + / (HBLKSIZE * GC_free_space_divisor) + + needed_blocks; + if (blocks_to_get > MAXHINCR) { + word slop; + + /* Get the minimum required to make it likely that we can satisfy */ + /* the current request in the presence of black-listing. */ + /* This will probably be more than MAXHINCR. */ + if (ignore_off_page) { + slop = 4; + } else { + slop = 2 * divHBLKSZ(BL_LIMIT); + if (slop > needed_blocks) slop = needed_blocks; + } + if (needed_blocks + slop > MAXHINCR) { + blocks_to_get = needed_blocks + slop; + } else { + blocks_to_get = MAXHINCR; + } + if (blocks_to_get > divHBLKSZ(GC_WORD_MAX)) + blocks_to_get = divHBLKSZ(GC_WORD_MAX); + } + + if (!GC_expand_hp_inner(blocks_to_get) + && (blocks_to_get == needed_blocks + || !GC_expand_hp_inner(needed_blocks))) { + if (gc_not_stopped == FALSE) { + /* Don't increment GC_fail_count here (and no warning). */ + GC_gcollect_inner(); + GC_ASSERT(GC_bytes_allocd == 0); + } else if (GC_fail_count++ < GC_max_retries) { + WARN("Out of Memory! Trying to continue...\n", 0); + GC_gcollect_inner(); + } else { +# if !defined(AMIGA) || !defined(GC_AMIGA_FASTALLOC) +# ifdef USE_MUNMAP + GC_ASSERT(GC_heapsize >= GC_unmapped_bytes); +# endif + WARN("Out of Memory! Heap size: %" WARN_PRIuPTR " MiB." + " Returning NULL!\n", (GC_heapsize - GC_unmapped_bytes) >> 20); +# endif + RESTORE_CANCEL(cancel_state); + return(FALSE); + } + } else if (GC_fail_count) { + GC_COND_LOG_PRINTF("Memory available again...\n"); + } + RESTORE_CANCEL(cancel_state); + return(TRUE); +} + +/* + * Make sure the object free list for size gran (in granules) is not empty. + * Return a pointer to the first object on the free list. + * The object MUST BE REMOVED FROM THE FREE LIST BY THE CALLER. + */ +GC_INNER ptr_t GC_allocobj(size_t gran, int kind) +{ + void ** flh = &(GC_obj_kinds[kind].ok_freelist[gran]); + GC_bool tried_minor = FALSE; + GC_bool retry = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + if (gran == 0) return(0); + + while (*flh == 0) { + ENTER_GC(); +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental && GC_time_limit != GC_TIME_UNLIMITED + && !GC_dont_gc) { + /* True incremental mode, not just generational. */ + /* Do our share of marking work. */ + GC_collect_a_little_inner(1); + } +# endif + /* Sweep blocks for objects of this size */ + GC_ASSERT(!GC_is_full_gc + || NULL == GC_obj_kinds[kind].ok_reclaim_list + || NULL == GC_obj_kinds[kind].ok_reclaim_list[gran]); + GC_continue_reclaim(gran, kind); + EXIT_GC(); +# if defined(CPPCHECK) + GC_noop1((word)&flh); +# endif + if (NULL == *flh) { + GC_new_hblk(gran, kind); +# if defined(CPPCHECK) + GC_noop1((word)&flh); +# endif + if (NULL == *flh) { + ENTER_GC(); + if (GC_incremental && GC_time_limit == GC_TIME_UNLIMITED + && !tried_minor && !GC_dont_gc) { + GC_collect_a_little_inner(1); + tried_minor = TRUE; + } else { + if (!GC_collect_or_expand(1, FALSE, retry)) { + EXIT_GC(); + return(0); + } + retry = TRUE; + } + EXIT_GC(); + } + } + } + /* Successful allocation; reset failure count. */ + GC_fail_count = 0; + + return (ptr_t)(*flh); +} diff --git a/bdwgc/autogen.sh b/bdwgc/autogen.sh new file mode 100755 index 000000000..c1500b2dc --- /dev/null +++ b/bdwgc/autogen.sh @@ -0,0 +1,18 @@ +#!/bin/sh +set -e + +# 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 and Libtool 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 + +echo +echo "Ready to run './configure'." diff --git a/bdwgc/backgraph.c b/bdwgc/backgraph.c new file mode 100644 index 000000000..6c6e8a041 --- /dev/null +++ b/bdwgc/backgraph.c @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2001 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. + * + * 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. + * + */ + +#include "private/dbg_mlc.h" + +/* + * This implements a full, though not well-tuned, representation of the + * backwards points-to graph. This is used to test for non-GC-robust + * data structures; the code is not used during normal garbage collection. + * + * One restriction is that we drop all back-edges from nodes with very + * high in-degree, and simply add them add them to a list of such + * nodes. They are then treated as permanent roots. If this by itself + * doesn't introduce a space leak, then such nodes can't contribute to + * a growing space leak. + */ + +#ifdef MAKE_BACK_GRAPH + +#define MAX_IN 10 /* Maximum in-degree we handle directly */ + +/* #include */ + +#if (!defined(DBG_HDRS_ALL) || (ALIGNMENT != CPP_WORDSZ/8) \ + /* || !defined(UNIX_LIKE) */) && !defined(CPPCHECK) +# error The configuration does not support MAKE_BACK_GRAPH +#endif + +/* We store single back pointers directly in the object's oh_bg_ptr field. */ +/* If there is more than one ptr to an object, we store q | FLAG_MANY, */ +/* where q is a pointer to a back_edges object. */ +/* Every once in a while we use a back_edges object even for a single */ +/* pointer, since we need the other fields in the back_edges structure to */ +/* be present in some fraction of the objects. Otherwise we get serious */ +/* performance issues. */ +#define FLAG_MANY 2 + +typedef struct back_edges_struct { + word n_edges; /* Number of edges, including those in continuation */ + /* structures. */ + unsigned short flags; +# define RETAIN 1 /* Directly points to a reachable object; */ + /* retain for next GC. */ + unsigned short height_gc_no; + /* If height > 0, then the GC_gc_no value when it */ + /* was computed. If it was computed this cycle, then */ + /* it is current. If it was computed during the */ + /* last cycle, then it represents the old height, */ + /* which is only saved for live objects referenced by */ + /* dead ones. This may grow due to refs from newly */ + /* dead objects. */ + signed_word height; + /* Longest path through unreachable nodes to this node */ + /* that we found using depth first search. */ +# define HEIGHT_UNKNOWN (-2) +# define HEIGHT_IN_PROGRESS (-1) + + ptr_t edges[MAX_IN]; + struct back_edges_struct *cont; + /* Pointer to continuation structure; we use only the */ + /* edges field in the continuation. */ + /* also used as free list link. */ +} back_edges; + +/* Allocate a new back edge structure. Should be more sophisticated */ +/* if this were production code. */ +#define MAX_BACK_EDGE_STRUCTS 100000 +static back_edges *back_edge_space = 0; +STATIC int GC_n_back_edge_structs = 0; + /* Serves as pointer to never used */ + /* back_edges space. */ +static back_edges *avail_back_edges = 0; + /* Pointer to free list of deallocated */ + /* back_edges structures. */ + +static back_edges * new_back_edges(void) +{ + if (0 == back_edge_space) { + size_t bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(MAX_BACK_EDGE_STRUCTS + * sizeof(back_edges)); + + GC_ASSERT(GC_page_size != 0); + back_edge_space = (back_edges *)GET_MEM(bytes_to_get); + if (NULL == back_edge_space) + ABORT("Insufficient memory for back edges"); + GC_add_to_our_memory((ptr_t)back_edge_space, bytes_to_get); + } + if (0 != avail_back_edges) { + back_edges * result = avail_back_edges; + avail_back_edges = result -> cont; + result -> cont = 0; + return result; + } + if (GC_n_back_edge_structs >= MAX_BACK_EDGE_STRUCTS - 1) { + ABORT("Needed too much space for back edges: adjust " + "MAX_BACK_EDGE_STRUCTS"); + } + return back_edge_space + (GC_n_back_edge_structs++); +} + +/* Deallocate p and its associated continuation structures. */ +static void deallocate_back_edges(back_edges *p) +{ + back_edges *last = p; + + while (0 != last -> cont) last = last -> cont; + last -> cont = avail_back_edges; + avail_back_edges = p; +} + +/* Table of objects that are currently on the depth-first search */ +/* stack. Only objects with in-degree one are in this table. */ +/* Other objects are identified using HEIGHT_IN_PROGRESS. */ +/* FIXME: This data structure NEEDS IMPROVEMENT. */ +#define INITIAL_IN_PROGRESS 10000 +static ptr_t * in_progress_space = 0; +static size_t in_progress_size = 0; +static size_t n_in_progress = 0; + +static void push_in_progress(ptr_t p) +{ + if (n_in_progress >= in_progress_size) { + ptr_t * new_in_progress_space; + + GC_ASSERT(GC_page_size != 0); + if (NULL == in_progress_space) { + in_progress_size = ROUNDUP_PAGESIZE_IF_MMAP(INITIAL_IN_PROGRESS + * sizeof(ptr_t)) + / sizeof(ptr_t); + new_in_progress_space = + (ptr_t *)GET_MEM(in_progress_size * sizeof(ptr_t)); + } else { + in_progress_size *= 2; + new_in_progress_space = (ptr_t *) + GET_MEM(in_progress_size * sizeof(ptr_t)); + if (new_in_progress_space != NULL) + BCOPY(in_progress_space, new_in_progress_space, + n_in_progress * sizeof(ptr_t)); + } + if (EXPECT(new_in_progress_space != NULL, TRUE)) + GC_add_to_our_memory((ptr_t)new_in_progress_space, + in_progress_size * sizeof(ptr_t)); +# ifndef GWW_VDB + GC_scratch_recycle_no_gww(in_progress_space, + n_in_progress * sizeof(ptr_t)); +# elif defined(LINT2) + /* TODO: implement GWW-aware recycling as in alloc_mark_stack */ + GC_noop1((word)in_progress_space); +# endif + in_progress_space = new_in_progress_space; + } + if (in_progress_space == 0) + ABORT("MAKE_BACK_GRAPH: Out of in-progress space: " + "Huge linear data structure?"); + in_progress_space[n_in_progress++] = p; +} + +static GC_bool is_in_progress(ptr_t p) +{ + size_t i; + for (i = 0; i < n_in_progress; ++i) { + if (in_progress_space[i] == p) return TRUE; + } + return FALSE; +} + +GC_INLINE void pop_in_progress(ptr_t p GC_ATTR_UNUSED) +{ + --n_in_progress; + GC_ASSERT(in_progress_space[n_in_progress] == p); +} + +#define GET_OH_BG_PTR(p) \ + (ptr_t)GC_REVEAL_POINTER(((oh *)(p)) -> oh_bg_ptr) +#define SET_OH_BG_PTR(p,q) (((oh *)(p)) -> oh_bg_ptr = GC_HIDE_POINTER(q)) + +/* Ensure that p has a back_edges structure associated with it. */ +static void ensure_struct(ptr_t p) +{ + ptr_t old_back_ptr = GET_OH_BG_PTR(p); + + if (!((word)old_back_ptr & FLAG_MANY)) { + back_edges *be = new_back_edges(); + be -> flags = 0; + if (0 == old_back_ptr) { + be -> n_edges = 0; + } else { + be -> n_edges = 1; + be -> edges[0] = old_back_ptr; + } + be -> height = HEIGHT_UNKNOWN; + be -> height_gc_no = (unsigned short)(GC_gc_no - 1); + GC_ASSERT((word)be >= (word)back_edge_space); + SET_OH_BG_PTR(p, (word)be | FLAG_MANY); + } +} + +/* Add the (forward) edge from p to q to the backward graph. Both p */ +/* q are pointers to the object base, i.e. pointers to an oh. */ +static void add_edge(ptr_t p, ptr_t q) +{ + ptr_t pred = GET_OH_BG_PTR(q); + back_edges * be, *be_cont; + word i; + + GC_ASSERT(p == GC_base(p) && q == GC_base(q)); + if (!GC_HAS_DEBUG_INFO(q) || !GC_HAS_DEBUG_INFO(p)) { + /* This is really a misinterpreted free list link, since we saw */ + /* a pointer to a free list. Don't overwrite it! */ + return; + } + if (NULL == pred) { + static unsigned random_number = 13; +# define GOT_LUCKY_NUMBER (((++random_number) & 0x7f) == 0) + /* A not very random number we use to occasionally allocate a */ + /* back_edges structure even for a single backward edge. This */ + /* prevents us from repeatedly tracing back through very long */ + /* chains, since we will have some place to store height and */ + /* in_progress flags along the way. */ + + SET_OH_BG_PTR(q, p); + if (GOT_LUCKY_NUMBER) ensure_struct(q); + return; + } + + /* Check whether it was already in the list of predecessors. */ + { + back_edges *e = (back_edges *)((word)pred & ~FLAG_MANY); + word n_edges; + word total; + int local = 0; + + if (((word)pred & FLAG_MANY) != 0) { + n_edges = e -> n_edges; + } else if (((word)COVERT_DATAFLOW(pred) & 1) == 0) { + /* A misinterpreted freelist link. */ + n_edges = 1; + local = -1; + } else { + n_edges = 0; + } + for (total = 0; total < n_edges; ++total) { + if (local == MAX_IN) { + e = e -> cont; + local = 0; + } + if (local >= 0) + pred = e -> edges[local++]; + if (pred == p) + return; + } + } + + ensure_struct(q); + be = (back_edges *)((word)GET_OH_BG_PTR(q) & ~FLAG_MANY); + for (i = be -> n_edges, be_cont = be; i > MAX_IN; i -= MAX_IN) + be_cont = be_cont -> cont; + if (i == MAX_IN) { + be_cont -> cont = new_back_edges(); + be_cont = be_cont -> cont; + i = 0; + } + be_cont -> edges[i] = p; + be -> n_edges++; +# ifdef DEBUG_PRINT_BIG_N_EDGES + if (GC_print_stats == VERBOSE && be -> n_edges == 100) { + GC_err_printf("The following object has big in-degree:\n"); + GC_print_heap_obj(q); + } +# endif +} + +typedef void (*per_object_func)(ptr_t p, size_t n_bytes, word gc_descr); + +static void per_object_helper(struct hblk *h, word fn) +{ + hdr * hhdr = HDR(h); + size_t sz = (size_t)hhdr->hb_sz; + word descr = hhdr -> hb_descr; + per_object_func f = (per_object_func)fn; + size_t i = 0; + + do { + f((ptr_t)(h -> hb_body + i), sz, descr); + i += sz; + } while (i + sz <= BYTES_TO_WORDS(HBLKSIZE)); +} + +GC_INLINE void GC_apply_to_each_object(per_object_func f) +{ + GC_apply_to_all_blocks(per_object_helper, (word)f); +} + +static void reset_back_edge(ptr_t p, size_t n_bytes GC_ATTR_UNUSED, + word gc_descr GC_ATTR_UNUSED) +{ + /* Skip any free list links, or dropped blocks */ + if (GC_HAS_DEBUG_INFO(p)) { + ptr_t old_back_ptr = GET_OH_BG_PTR(p); + if ((word)old_back_ptr & FLAG_MANY) { + back_edges *be = (back_edges *)((word)old_back_ptr & ~FLAG_MANY); + if (!(be -> flags & RETAIN)) { + deallocate_back_edges(be); + SET_OH_BG_PTR(p, 0); + } else { + + GC_ASSERT(GC_is_marked(p)); + + /* Back edges may point to objects that will not be retained. */ + /* Delete them for now, but remember the height. */ + /* Some will be added back at next GC. */ + be -> n_edges = 0; + if (0 != be -> cont) { + deallocate_back_edges(be -> cont); + be -> cont = 0; + } + + GC_ASSERT(GC_is_marked(p)); + + /* We only retain things for one GC cycle at a time. */ + be -> flags &= ~RETAIN; + } + } else /* Simple back pointer */ { + /* Clear to avoid dangling pointer. */ + SET_OH_BG_PTR(p, 0); + } + } +} + +static void add_back_edges(ptr_t p, size_t n_bytes, word gc_descr) +{ + word *currentp = (word *)(p + sizeof(oh)); + + /* For now, fix up non-length descriptors conservatively. */ + if((gc_descr & GC_DS_TAGS) != GC_DS_LENGTH) { + gc_descr = n_bytes; + } + while ((word)currentp < (word)(p + gc_descr)) { + word current = *currentp++; + FIXUP_POINTER(current); + if (current >= (word)GC_least_plausible_heap_addr && + current <= (word)GC_greatest_plausible_heap_addr) { + ptr_t target = (ptr_t)GC_base((void *)current); + if (0 != target) { + add_edge(p, target); + } + } + } +} + +/* Rebuild the representation of the backward reachability graph. */ +/* Does not examine mark bits. Can be called before GC. */ +GC_INNER void GC_build_back_graph(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_apply_to_each_object(add_back_edges); +} + +/* Return an approximation to the length of the longest simple path */ +/* through unreachable objects to p. We refer to this as the height */ +/* of p. */ +static word backwards_height(ptr_t p) +{ + word result; + ptr_t pred = GET_OH_BG_PTR(p); + back_edges *be; + + if (NULL == pred) + return 1; + if (((word)pred & FLAG_MANY) == 0) { + if (is_in_progress(p)) return 0; /* DFS back edge, i.e. we followed */ + /* an edge to an object already */ + /* on our stack: ignore */ + push_in_progress(p); + result = backwards_height(pred) + 1; + pop_in_progress(p); + return result; + } + be = (back_edges *)((word)pred & ~FLAG_MANY); + if (be -> height >= 0 && be -> height_gc_no == (unsigned short)GC_gc_no) + return be -> height; + /* Ignore back edges in DFS */ + if (be -> height == HEIGHT_IN_PROGRESS) return 0; + result = (be -> height > 0? be -> height : 1); + be -> height = HEIGHT_IN_PROGRESS; + + { + back_edges *e = be; + word n_edges; + word total; + int local = 0; + + if (((word)pred & FLAG_MANY) != 0) { + n_edges = e -> n_edges; + } else if (((word)pred & 1) == 0) { + /* A misinterpreted freelist link. */ + n_edges = 1; + local = -1; + } else { + n_edges = 0; + } + for (total = 0; total < n_edges; ++total) { + word this_height; + if (local == MAX_IN) { + e = e -> cont; + local = 0; + } + if (local >= 0) + pred = e -> edges[local++]; + + /* Execute the following once for each predecessor pred of p */ + /* in the points-to graph. */ + if (GC_is_marked(pred) && ((word)GET_OH_BG_PTR(p) & FLAG_MANY) == 0) { + GC_COND_LOG_PRINTF("Found bogus pointer from %p to %p\n", + (void *)pred, (void *)p); + /* Reachable object "points to" unreachable one. */ + /* Could be caused by our lax treatment of GC descriptors. */ + this_height = 1; + } else { + this_height = backwards_height(pred); + } + if (this_height >= result) + result = this_height + 1; + } + } + + be -> height = result; + be -> height_gc_no = (unsigned short)GC_gc_no; + return result; +} + +STATIC word GC_max_height = 0; +STATIC ptr_t GC_deepest_obj = NULL; + +/* Compute the maximum height of every unreachable predecessor p of a */ +/* reachable object. Arrange to save the heights of all such objects p */ +/* so that they can be used in calculating the height of objects in the */ +/* next GC. */ +/* Set GC_max_height to be the maximum height we encounter, and */ +/* GC_deepest_obj to be the corresponding object. */ +static void update_max_height(ptr_t p, size_t n_bytes GC_ATTR_UNUSED, + word gc_descr GC_ATTR_UNUSED) +{ + if (GC_is_marked(p) && GC_HAS_DEBUG_INFO(p)) { + word p_height = 0; + ptr_t p_deepest_obj = 0; + ptr_t back_ptr; + back_edges *be = 0; + + /* If we remembered a height last time, use it as a minimum. */ + /* It may have increased due to newly unreachable chains pointing */ + /* to p, but it can't have decreased. */ + back_ptr = GET_OH_BG_PTR(p); + if (0 != back_ptr && ((word)back_ptr & FLAG_MANY)) { + be = (back_edges *)((word)back_ptr & ~FLAG_MANY); + if (be -> height != HEIGHT_UNKNOWN) p_height = be -> height; + } + + { + ptr_t pred = GET_OH_BG_PTR(p); + back_edges *e = (back_edges *)((word)pred & ~FLAG_MANY); + word n_edges; + word total; + int local = 0; + + if (((word)pred & FLAG_MANY) != 0) { + n_edges = e -> n_edges; + } else if (pred != NULL && ((word)pred & 1) == 0) { + /* A misinterpreted freelist link. */ + n_edges = 1; + local = -1; + } else { + n_edges = 0; + } + for (total = 0; total < n_edges; ++total) { + if (local == MAX_IN) { + e = e -> cont; + local = 0; + } + if (local >= 0) + pred = e -> edges[local++]; + + /* Execute the following once for each predecessor pred of p */ + /* in the points-to graph. */ + if (!GC_is_marked(pred) && GC_HAS_DEBUG_INFO(pred)) { + word this_height = backwards_height(pred); + if (this_height > p_height) { + p_height = this_height; + p_deepest_obj = pred; + } + } + } + } + + if (p_height > 0) { + /* Remember the height for next time. */ + if (be == 0) { + ensure_struct(p); + back_ptr = GET_OH_BG_PTR(p); + be = (back_edges *)((word)back_ptr & ~FLAG_MANY); + } + be -> flags |= RETAIN; + be -> height = p_height; + be -> height_gc_no = (unsigned short)GC_gc_no; + } + if (p_height > GC_max_height) { + GC_max_height = p_height; + GC_deepest_obj = p_deepest_obj; + } + } +} + +STATIC word GC_max_max_height = 0; + +GC_INNER void GC_traverse_back_graph(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_max_height = 0; + GC_apply_to_each_object(update_max_height); + if (0 != GC_deepest_obj) + GC_set_mark_bit(GC_deepest_obj); /* Keep it until we can print it. */ +} + +void GC_print_back_graph_stats(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + 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) { + ptr_t obj = GC_deepest_obj; + + GC_max_max_height = GC_max_height; + UNLOCK(); + GC_err_printf( + "The following unreachable object is last in a longest chain " + "of unreachable objects:\n"); + GC_print_heap_obj(obj); + LOCK(); + } + 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; +} + +#endif /* MAKE_BACK_GRAPH */ diff --git a/bdwgc/bdw-gc.pc.in b/bdwgc/bdw-gc.pc.in new file mode 100644 index 000000000..23678f430 --- /dev/null +++ b/bdwgc/bdw-gc.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Boehm-Demers-Weiser Conservative Garbage Collector +Description: A garbage collector for C and C++ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} @ATOMIC_OPS_LIBS@ -lgc @THREADDLLIBS@ +Cflags: -I${includedir} diff --git a/bdwgc/blacklst.c b/bdwgc/blacklst.c new file mode 100644 index 000000000..a345de8e4 --- /dev/null +++ b/bdwgc/blacklst.c @@ -0,0 +1,301 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#include "private/gc_priv.h" + +/* + * We maintain several hash tables of hblks that have had false hits. + * Each contains one bit per hash bucket; If any page in the bucket + * has had a false hit, we assume that all of them have. + * See the definition of page_hash_table in gc_priv.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, even though it does not start on the dangerous block. + */ + +/* + * Externally callable routines are: + + * GC_add_to_black_list_normal + * GC_add_to_black_list_stack + * GC_promote_black_lists + * GC_is_black_listed + * + * All require that the allocator lock is held. + */ + +/* Pointers to individual tables. We replace one table by another by */ +/* switching these pointers. */ +STATIC word * GC_old_normal_bl = NULL; + /* Nonstack false references seen at last full */ + /* collection. */ +STATIC word * GC_incomplete_normal_bl = NULL; + /* Nonstack false references seen since last */ + /* full collection. */ +STATIC word * GC_old_stack_bl = NULL; +STATIC word * GC_incomplete_stack_bl = NULL; + +STATIC word GC_total_stack_black_listed = 0; + /* Number of bytes on stack blacklist. */ + +GC_INNER word GC_black_list_spacing = MINHINCR * HBLKSIZE; + /* Initial rough guess. */ + +STATIC void GC_clear_bl(word *); + +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p) +{ + ptr_t base = (ptr_t)GC_base(p); + int kind = HDR(base)->hb_obj_kind; + + GC_err_printf("object at %p of appr. %lu bytes (%s)\n", + (void *)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_blacklisted_ptr(word p, ptr_t source, + const char *kind_str) + { + ptr_t base = (ptr_t)GC_base(source); + + if (0 == base) { + GC_err_printf("Black listing (%s) %p referenced from %p in %s\n", + kind_str, (void *)p, (void *)source, + NULL != source ? "root set" : "register"); + } else { + /* 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_err_printf("Black listing (%s) %p referenced from %p in" + " object at %p of appr. %lu bytes\n", + kind_str, (void *)p, (void *)source, + (void *)base, (unsigned long)GC_size(base)); + } + } +#endif /* PRINT_BLACK_LIST */ + +GC_INNER void GC_bl_init_no_interiors(void) +{ + if (GC_incomplete_normal_bl == 0) { + GC_old_normal_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + GC_incomplete_normal_bl = (word *)GC_scratch_alloc( + sizeof(page_hash_table)); + if (GC_old_normal_bl == 0 || GC_incomplete_normal_bl == 0) { + GC_err_printf("Insufficient memory for black list\n"); + EXIT(); + } + GC_clear_bl(GC_old_normal_bl); + GC_clear_bl(GC_incomplete_normal_bl); + } +} + +GC_INNER void GC_bl_init(void) +{ + if (!GC_all_interior_pointers) { + GC_bl_init_no_interiors(); + } + GC_ASSERT(NULL == GC_old_stack_bl && NULL == GC_incomplete_stack_bl); + GC_old_stack_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + GC_incomplete_stack_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + if (GC_old_stack_bl == 0 || GC_incomplete_stack_bl == 0) { + GC_err_printf("Insufficient memory for black list\n"); + EXIT(); + } + GC_clear_bl(GC_old_stack_bl); + GC_clear_bl(GC_incomplete_stack_bl); +} + +STATIC void GC_clear_bl(word *doomed) +{ + BZERO(doomed, sizeof(page_hash_table)); +} + +STATIC void GC_copy_bl(word *old, word *dest) +{ + BCOPY(old, dest, sizeof(page_hash_table)); +} + +static word total_stack_black_listed(void); + +/* Signal the completion of a collection. Turn the incomplete black */ +/* lists into new black lists, etc. */ +GC_INNER void GC_promote_black_lists(void) +{ + word * very_old_normal_bl = GC_old_normal_bl; + word * very_old_stack_bl = GC_old_stack_bl; + + GC_old_normal_bl = GC_incomplete_normal_bl; + GC_old_stack_bl = GC_incomplete_stack_bl; + if (!GC_all_interior_pointers) { + GC_clear_bl(very_old_normal_bl); + } + GC_clear_bl(very_old_stack_bl); + 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(); + 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); + } + if (GC_black_list_spacing < 3 * HBLKSIZE) { + GC_black_list_spacing = 3 * HBLKSIZE; + } + if (GC_black_list_spacing > MAXHINCR * HBLKSIZE) { + GC_black_list_spacing = MAXHINCR * HBLKSIZE; + /* Makes it easier to allocate really huge blocks, which otherwise */ + /* may have problems with nonuniform blacklist distributions. */ + /* This way we should always succeed immediately after growing the */ + /* heap. */ + } +} + +GC_INNER void GC_unpromote_black_lists(void) +{ + if (!GC_all_interior_pointers) { + GC_copy_bl(GC_old_normal_bl, GC_incomplete_normal_bl); + } + GC_copy_bl(GC_old_stack_bl, GC_incomplete_stack_bl); +} + +#if defined(PARALLEL_MARK) && defined(THREAD_SANITIZER) +# define backlist_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index_concurrent(db, index) +#else + /* It is safe to set a bit in a blacklist even without */ + /* synchronization, the only drawback is that we might have */ + /* to redo blacklisting sometimes. */ +# define backlist_set_pht_entry_from_index(bl, index) \ + set_pht_entry_from_index(bl, index) +#endif + +/* P is not a valid pointer reference, but it falls inside */ +/* the plausible heap bounds. */ +/* Add it to the normal incomplete black list if appropriate. */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_normal(word p, ptr_t source) +#else + GC_INNER void GC_add_to_black_list_normal(word p) +#endif +{ + if (GC_modws_valid_offsets[p & (sizeof(word)-1)]) { + word index = PHT_HASH((word)p); + + 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_print_blacklisted_ptr(p, source, "normal"); + } +# endif + backlist_set_pht_entry_from_index(GC_incomplete_normal_bl, index); + } /* else this is probably just an interior pointer to an allocated */ + /* object, and isn't worth black listing. */ + } +} + +/* And the same for false pointers from the stack. */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_stack(word p, ptr_t source) +#else + GC_INNER void GC_add_to_black_list_stack(word p) +#endif +{ + word index = PHT_HASH((word)p); + + 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_print_blacklisted_ptr(p, source, "stack"); + } +# endif + backlist_set_pht_entry_from_index(GC_incomplete_stack_bl, index); + } +} + +/* + * Is the block starting at h of size len bytes black listed? If so, + * return the address of the next plausible r such that (r, len) might not + * be black listed. (R may not actually be in the heap. We guarantee only + * that every smaller value of r after h is also black listed.) + * If (h,len) is not black listed, return 0. + * Knows about the structure of the black list hash tables. + */ +struct hblk * GC_is_black_listed(struct hblk *h, word len) +{ + word index = PHT_HASH((word)h); + word i; + word nblocks; + + if (!GC_all_interior_pointers + && (get_pht_entry_from_index(GC_old_normal_bl, index) + || get_pht_entry_from_index(GC_incomplete_normal_bl, index))) { + return (h+1); + } + + nblocks = divHBLKSZ(len); + for (i = 0;;) { + if (GC_old_stack_bl[divWORDSZ(index)] == 0 + && GC_incomplete_stack_bl[divWORDSZ(index)] == 0) { + /* An easy case */ + i += WORDSZ - modWORDSZ(index); + } else { + if (get_pht_entry_from_index(GC_old_stack_bl, index) + || get_pht_entry_from_index(GC_incomplete_stack_bl, index)) { + return(h+i+1); + } + i++; + } + if (i >= nblocks) break; + index = PHT_HASH((word)(h+i)); + } + return(0); +} + +/* Return the number of blacklisted blocks in a given range. */ +/* Used only for statistical purposes. */ +/* Looks only at the GC_incomplete_stack_bl. */ +STATIC word GC_number_stack_black_listed(struct hblk *start, + struct hblk *endp1) +{ + struct hblk * h; + word result = 0; + + 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++; + } + return(result); +} + +/* Return the total number of (stack) black-listed bytes. */ +static word total_stack_black_listed(void) +{ + unsigned i; + word total = 0; + + for (i = 0; i < GC_n_heap_sects; i++) { + struct hblk * start = (struct hblk *) GC_heap_sects[i].hs_start; + struct hblk * endp1 = start + divHBLKSZ(GC_heap_sects[i].hs_bytes); + + total += GC_number_stack_black_listed(start, endp1); + } + return(total * HBLKSIZE); +} diff --git a/bdwgc/build/s60v3/bld.inf b/bdwgc/build/s60v3/bld.inf new file mode 100644 index 000000000..491fcf434 --- /dev/null +++ b/bdwgc/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/bdwgc/build/s60v3/libgc.mmp b/bdwgc/build/s60v3/libgc.mmp new file mode 100644 index 000000000..ada5ad64b --- /dev/null +++ b/bdwgc/build/s60v3/libgc.mmp @@ -0,0 +1,78 @@ +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_ATOMIC_UNCOLLECTABLE +MACRO GC_DONT_REGISTER_MAIN_STATIC_DATA +MACRO GC_DLL +MACRO JAVA_FINALIZATION +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_badalc.cpp +//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 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/bdwgc/checksums.c b/bdwgc/checksums.c new file mode 100644 index 000000000..28e40a887 --- /dev/null +++ b/bdwgc/checksums.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 1992-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#include "private/gc_priv.h" + +#ifdef CHECKSUMS + +/* This is debugging code intended to verify the results of dirty bit */ +/* computations. Works only in a single threaded environment. */ +#define NSUMS 10000 + +#define OFFSET 0x10000 + +typedef struct { + GC_bool new_valid; + word old_sum; + word new_sum; + struct hblk * block; /* Block to which this refers + OFFSET */ + /* to hide it from collector. */ +} page_entry; + +page_entry GC_sums[NSUMS]; + +STATIC word GC_faulted[NSUMS] = { 0 }; + /* Record of pages on which we saw a write fault. */ + +STATIC size_t GC_n_faulted = 0; + +#if defined(MPROTECT_VDB) && !defined(DARWIN) + void GC_record_fault(struct hblk * h) + { + word page = (word)h & ~(GC_page_size - 1); + + GC_ASSERT(GC_page_size != 0); + if (GC_n_faulted >= NSUMS) ABORT("write fault log overflowed"); + GC_faulted[GC_n_faulted++] = page; + } +#endif + +STATIC GC_bool GC_was_faulted(struct hblk *h) +{ + size_t i; + word page = (word)h & ~(GC_page_size - 1); + + for (i = 0; i < GC_n_faulted; ++i) { + if (GC_faulted[i] == page) return TRUE; + } + return FALSE; +} + +STATIC word GC_checksum(struct hblk *h) +{ + word *p = (word *)h; + word *lim = (word *)(h+1); + word result = 0; + + while ((word)p < (word)lim) { + result += *p++; + } + return(result | 0x80000000 /* doesn't look like pointer */); +} + +int GC_n_dirty_errors = 0; +int GC_n_faulted_dirty_errors = 0; +unsigned long GC_n_clean = 0; +unsigned long GC_n_dirty = 0; + +STATIC void GC_update_check_page(struct hblk *h, int index) +{ + page_entry *pe = GC_sums + index; + hdr * hhdr = HDR(h); + struct hblk *b; + + if (pe -> block != 0 && pe -> block != h + OFFSET) ABORT("goofed"); + pe -> old_sum = pe -> new_sum; + pe -> new_sum = GC_checksum(h); +# if !defined(MSWIN32) && !defined(MSWINCE) + if (pe -> new_sum != 0x80000000 && !GC_page_was_ever_dirty(h)) { + GC_err_printf("GC_page_was_ever_dirty(%p) is wrong\n", (void *)h); + } +# endif + if (GC_page_was_dirty(h)) { + GC_n_dirty++; + } else { + GC_n_clean++; + } + b = h; + while (IS_FORWARDING_ADDR_OR_NIL(hhdr) && hhdr != 0) { + b -= (word)hhdr; + hhdr = HDR(b); + } + if (pe -> new_valid + && hhdr != 0 && hhdr -> hb_descr != 0 /* may contain pointers */ + && pe -> old_sum != pe -> new_sum) { + if (!GC_page_was_dirty(h) || !GC_page_was_ever_dirty(h)) { + GC_bool was_faulted = GC_was_faulted(h); + /* Set breakpoint here */GC_n_dirty_errors++; + if (was_faulted) GC_n_faulted_dirty_errors++; + } + } + pe -> new_valid = TRUE; + pe -> block = h + OFFSET; +} + +word GC_bytes_in_used_blocks = 0; + +STATIC void GC_add_block(struct hblk *h, word dummy GC_ATTR_UNUSED) +{ + hdr * hhdr = HDR(h); + + GC_bytes_in_used_blocks += (hhdr->hb_sz + HBLKSIZE-1) & ~(HBLKSIZE-1); +} + +STATIC void GC_check_blocks(void) +{ + word bytes_in_free_blocks = GC_large_free_bytes; + + GC_bytes_in_used_blocks = 0; + GC_apply_to_all_blocks(GC_add_block, (word)0); + 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"); + } +} + +/* Should be called immediately after GC_read_dirty. */ +void GC_check_dirty(void) +{ + int index; + unsigned i; + struct hblk *h; + ptr_t start; + + GC_check_blocks(); + + GC_n_dirty_errors = 0; + GC_n_faulted_dirty_errors = 0; + GC_n_clean = 0; + GC_n_dirty = 0; + + index = 0; + for (i = 0; i < GC_n_heap_sects; i++) { + start = GC_heap_sects[i].hs_start; + for (h = (struct hblk *)start; + (word)h < (word)(start + GC_heap_sects[i].hs_bytes); h++) { + GC_update_check_page(h, index); + index++; + if (index >= NSUMS) goto out; + } + } +out: + GC_COND_LOG_PRINTF("Checked %lu clean and %lu dirty pages\n", + GC_n_clean, 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); + } + for (i = 0; i < GC_n_faulted; ++i) { + GC_faulted[i] = 0; /* Don't expose block pointers to GC */ + } + GC_n_faulted = 0; +} + +#endif /* CHECKSUMS */ diff --git a/bdwgc/configure.ac b/bdwgc/configure.ac new file mode 100644 index 000000000..0a6282616 --- /dev/null +++ b/bdwgc/configure.ac @@ -0,0 +1,1235 @@ +# Copyright (c) 1999-2001 by Red Hat, Inc. All rights reserved. +# Copyright (c) 2005-2009 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2009-2021 Ivan Maidanski +# +# 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. + +dnl Process this file with autoconf to produce configure. + +dnl Initialization. +AC_INIT(gc,8.2.4,https://github.com/ivmai/bdwgc/issues) +dnl Version must conform to: [0-9]+[.][0-9]+[.][0-9]+ + +AC_CONFIG_SRCDIR(gcj_mlc.c) +AC_CONFIG_MACRO_DIR([m4]) +AC_CANONICAL_TARGET +AC_PREREQ(2.61) +GC_SET_VERSION +AM_INIT_AUTOMAKE([foreign nostdinc subdir-objects]) +AC_CONFIG_HEADERS([include/config.h]) +AM_MAINTAINER_MODE + +AC_SUBST(PACKAGE) +AC_SUBST(GC_VERSION) + +AM_PROG_CC_C_O +AC_PROG_CXX +AM_PROG_AS +AC_PROG_INSTALL +LT_INIT([disable-static]) +# Only the shared libraries are produced by default, use "--enable-static" +# option to override it. +dnl Note: If Autoconf reports that LIBTOOL (or AC_ENABLE_SHARED, or +dnl AC_PROG_LIBTOOL) is undefined, Libtool installation should be checked. + +# Special CFLAGS to use when building +gc_cflags="" + +# Set to "yes" on platforms where mmap should be used instead of sbrk. +# This will define USE_MMAP. +gc_use_mmap="" + +# We should set -fexceptions if we are using gcc and might be used +# inside something like gcj. This is the zeroth approximation: +if test :"$GCC": = :yes: ; then + gc_cflags="${gc_cflags} -fexceptions" +else + case "$host" in + hppa*-*-hpux* ) + if test :$GCC: != :"yes": ; then + gc_cflags="${gc_cflags} +ESdbgasm" + fi + # :TODO: actually we should check using Autoconf if + # the compiler supports this option. + ;; + esac +fi + +# target_optspace --enable-target-optspace ("yes", "no", "") +case "${target_optspace}:${host}" in + yes:*) + gc_cflags="${gc_cflags} -Os" + ;; + :m32r-* | :d10v-* | :d30v-*) + gc_cflags="${gc_cflags} -Os" + ;; + no:* | :*) + # Nothing. + ;; +esac + +# Set any host dependent compiler flags. +case "${host}" in + mips-tx39-*|mipstx39-unknown-*) + gc_cflags="${gc_cflags} -G 0" + ;; + *) + ;; +esac + +AC_MSG_CHECKING([for emscripten]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +# ifndef __EMSCRIPTEN__ +# error This is not Emscripten +# endif + ]])], [emscripten=yes], [emscripten=no]) +AM_CONDITIONAL(EMSCRIPTEN, test x$emscripten = xyes) +AC_MSG_RESULT([$emscripten]) + +AC_ARG_ENABLE(emscripten-asyncify, + [AS_HELP_STRING([--enable-emscripten-asyncify], + [use Emscripten asyncify feature])]) +# Use this option if your program is targeting -sASYNCIFY. The latter is +# required to scan the stack, ASYNCIFY_STACK_SIZE is probably needed for +# gctest only. +AS_IF([test "${emscripten}" = yes -a "${enable_emscripten_asyncify}" = yes], + [gc_cflags="${gc_cflags} -DEMSCRIPTEN_ASYNCIFY" + gc_cflags="${gc_cflags} -sASYNCIFY -sASYNCIFY_STACK_SIZE=128000"]) + +GC_CFLAGS=${gc_cflags} +AC_SUBST(GC_CFLAGS) + +dnl Extra user-defined flags to pass both to C and C++ compilers. +AC_SUBST([CFLAGS_EXTRA]) + +AC_ARG_ENABLE(threads, + [AS_HELP_STRING([--enable-threads=TYPE], [choose threading package])], + THREADS=$enableval, + [ AC_MSG_CHECKING([for thread model used by GCC]) + THREADS=`$CC -v 2>&1 | sed -n 's/^Thread model: //p'` + if test -z "$THREADS" -o "x$emscripten" = "xyes"; then + THREADS=no + fi + if test "$THREADS" = "posix"; then + case "$host" in + *-*-mingw*) + # Adjust thread model if cross-compiling for MinGW. + THREADS=win32 + ;; + esac + fi + AC_MSG_RESULT([$THREADS]) ]) + +AC_ARG_ENABLE(parallel-mark, + [AS_HELP_STRING([--disable-parallel-mark], + [do not parallelize marking and free list construction])], + [case "$THREADS" in + no | none | single) + if test "${enable_parallel_mark}" != no; then + AC_MSG_ERROR([Parallel mark requires --enable-threads=x spec]) + fi + ;; + esac] +) + +AC_ARG_ENABLE(thread-local-alloc, + [AS_HELP_STRING([--disable-thread-local-alloc], + [turn off thread-local allocation optimization])], + [case "$THREADS" in + no | none | single) + if test "${enable_thread_local_alloc}" = yes; then + AC_MSG_ERROR( + [Thread-local allocation requires --enable-threads=x spec]) + fi + ;; + esac]) + +AC_ARG_ENABLE(threads-discovery, + [AS_HELP_STRING([--disable-threads-discovery], + [disable threads discovery in GC])]) +if test "${enable_threads_discovery}" = no; then + AC_DEFINE([GC_NO_THREADS_DISCOVERY], 1, + [Disable threads discovery in GC.]) +fi + +AC_ARG_ENABLE(cplusplus, + [AS_HELP_STRING([--enable-cplusplus], [install C++ support])]) + +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 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 + threads.]) +AH_TEMPLATE([GC_WIN32_PTHREADS], + [Define to support pthreads-win32 or winpthreads.]) + +dnl System header feature requests. +AH_TEMPLATE([_POSIX_C_SOURCE], [The POSIX feature macro.]) +AH_TEMPLATE([_PTHREADS], [Indicates the use of pthreads (NetBSD).]) + +dnl Win32-specific API usage controls. +AH_TEMPLATE([GC_UNDERSCORE_STDCALL], + [Explicitly prefix exported/imported WINAPI symbols with '_'.]) +AH_TEMPLATE([UNICODE], + [Use Unicode (W) variant of Win32 API instead of ASCII (A) one.]) + +dnl GC API symbols export control. +AH_TEMPLATE([GC_DLL], + [Define to build dynamic libraries with only API symbols exposed.]) + +dnl Check for a flavor of supported inline keyword. +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $CFLAGS_EXTRA" +AC_C_INLINE +CFLAGS="$old_CFLAGS" + +THREADDLLIBS= +need_atomic_ops_asm=false +need_lib_rt=false +compile_asm=false +use_parallel_mark=no +use_thread_local_alloc=no +# Libraries needed to support dynamic loading and/or threads. +case "$THREADS" in + no | none | single) + THREADS=none + ;; + posix | pthreads) + THREADS=posix + default_threadlibs=false + # Common defines for most POSIX platforms. + case "$host" in + *-*-aix* | *-*-android* | *-*-cygwin* | *-*-darwin* | *-*-dragonfly* | \ + *-*-freebsd* | *-*-haiku* | *-*-hpux11* | *-*-irix* | \ + *-*-kfreebsd*-gnu | *-*-gnu* | *-*-*linux* | *-*-msys* | *-*-nacl* | \ + *-*-netbsd* | *-*-openbsd* | *-*-osf* | *-*-solaris*) + AC_DEFINE(GC_THREADS) + AC_DEFINE([_REENTRANT], [1], + [Required define if using POSIX threads.]) + use_parallel_mark=$enable_parallel_mark + use_thread_local_alloc=$enable_thread_local_alloc + default_threadlibs=true + AC_MSG_WARN("Explicit GC_INIT() calls may be required.") + ;; + esac + AC_CHECK_LIB(pthread, pthread_self, THREADDLLIBS="-lpthread",,) + case "$host" in + *-*-hpux11*) + AC_MSG_WARN("Only HP/UX 11 POSIX threads are supported.") + AC_DEFINE(_POSIX_C_SOURCE,199506L) + THREADDLLIBS="-lpthread" + # HPUX needs REENTRANT for the _r calls. + need_lib_rt=true + ;; + *-*-openbsd*) + AM_CFLAGS="$AM_CFLAGS -pthread" + THREADDLLIBS=-pthread + ;; + *-*-freebsd*) + AM_CFLAGS="$AM_CFLAGS -pthread" + ;; + *-*-kfreebsd*-gnu) + AM_CFLAGS="$AM_CFLAGS -pthread" + THREADDLLIBS=-pthread + ;; + *-*-gnu*) # E.g. linux but excluding kfreebsd. + # The default THREADDLLIBS. + ;; + *-*-netbsd*) + AC_MSG_WARN("Only on NetBSD 2.0 or later.") + AC_DEFINE(_PTHREADS) + THREADDLLIBS="-lpthread" + need_lib_rt=true + ;; + *-*-solaris*) + THREADDLLIBS="-lpthread" + need_lib_rt=true + ;; + *-*-cygwin* | *-*-msys*) + # Cygwin doesn't have a real libpthread, so Libtool can't link + # against it. + THREADDLLIBS="" + win32_threads=true + ;; + *-*-mingw*) + AC_DEFINE(GC_WIN32_PTHREADS) + # Using pthreads-win32 (or other non-Cygwin pthreads) library. + THREADDLLIBS="-lpthread" + use_parallel_mark=$enable_parallel_mark + use_thread_local_alloc=$enable_thread_local_alloc + win32_threads=true + ;; + *-*-darwin*) + darwin_threads=true + ;; + *-*-osf*) + AM_CFLAGS="$AM_CFLAGS -pthread" + THREADDLLIBS="-lpthread" + need_lib_rt=true + ;; + *) + AS_IF([test x$default_threadlibs != xtrue], + [ AC_MSG_ERROR( + [Pthreads not supported by the GC on this platform]) ]) + # The default THREADDLLIBS. + ;; + esac + case "$host" in + sparc*-*-solaris*) + if test "$GCC" != yes; then + need_atomic_ops_asm=true + fi + ;; + esac + ;; + mcf | win32) + AC_DEFINE(GC_THREADS) + use_parallel_mark=$enable_parallel_mark + if test "${enable_parallel_mark}" != no \ + -o "${enable_shared}" != yes -o "${enable_static}" != no; then + # Imply THREAD_LOCAL_ALLOC unless GC_DLL. + use_thread_local_alloc=$enable_thread_local_alloc + fi + if test "${enable_win32_dllmain}" = yes; then + AC_DEFINE(GC_INSIDE_DLL, 1, + [Enable Win32 DllMain-based approach of threads registering.]) + fi + win32_threads=true + AC_DEFINE([EMPTY_GETENV_RESULTS], [1], + [Wine getenv may not return NULL for missing entry.]) + ;; + dgux386) + AC_DEFINE(GC_THREADS) + # Use pthread GCC switch + THREADDLLIBS=-pthread + use_parallel_mark=$enable_parallel_mark + use_thread_local_alloc=$enable_thread_local_alloc + AC_MSG_WARN("Explicit GC_INIT() calls may be required.") + AM_CFLAGS="-pthread $AM_CFLAGS" + ;; + aix) + THREADS=posix + THREADDLLIBS=-lpthread + AC_DEFINE(GC_THREADS) + AC_DEFINE(_REENTRANT) + use_parallel_mark=$enable_parallel_mark + use_thread_local_alloc=$enable_thread_local_alloc + ;; + rtems) + THREADS=posix + AC_DEFINE(GC_THREADS) + use_parallel_mark=$enable_parallel_mark + use_thread_local_alloc=$enable_thread_local_alloc + ;; + decosf1 | irix | mach | os2 | solaris | dce | vxworks) + AC_MSG_ERROR(thread package $THREADS not yet supported) + ;; + *) + AC_MSG_ERROR($THREADS is an unknown thread package) + ;; +esac + +# Check whether -lrt linker option is needed to use clock_gettime. +if test "x$need_lib_rt" != xtrue; then + AC_MSG_CHECKING(for clock_gettime without additional libraries) + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [struct timespec t; clock_gettime(CLOCK_REALTIME, &t)])], + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + AC_CHECK_LIB(rt, clock_gettime, [need_lib_rt=true])]) +fi + +if test "x$need_lib_rt" = xtrue; then + THREADDLLIBS="$THREADDLLIBS -lrt" +fi +AC_SUBST(THREADDLLIBS) + +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) + +compiler_suncc=no +pthread_start_standalone=no +case "$host" in + *-*-*linux*) + # Turn on the workaround described in pthread_start.c. + AS_IF([test "$THREADS" = posix], [pthread_start_standalone=yes]) + ;; + powerpc-*-darwin*) + powerpc_darwin=true + ;; + *-*-solaris*) + if test "$GCC" != yes; then + # Solaris SunCC + compiler_suncc=yes + CFLAGS="-O $CFLAGS" + fi + ;; + *-*-wince*) + if test "$enable_gc_debug" != "no"; then + AC_DEFINE([GC_READ_ENV_FILE], 1, + [Read environment variables from the GC 'env' file.]) + fi + ;; +esac +AM_CONDITIONAL(PTHREAD_START_STANDALONE, + test x$pthread_start_standalone = xyes) + +if test "$GCC" = yes; then + # Output all warnings. + AC_MSG_CHECKING([whether compiler supports -Wextra]) + old_CFLAGS="$CFLAGS" + CFLAGS="-Wextra $CFLAGS" + AC_TRY_COMPILE([],[], [ac_cv_cc_wextra=yes], [ac_cv_cc_wextra=no]) + CFLAGS="$old_CFLAGS" + AC_MSG_RESULT($ac_cv_cc_wextra) + AS_IF([test "$ac_cv_cc_wextra" = yes], [WEXTRA="-Wextra"], [WEXTRA="-W"]) + AC_MSG_CHECKING([whether compiler supports -Wpedantic]) + CFLAGS="-Wpedantic -Wno-long-long $CFLAGS" + AC_TRY_COMPILE([],[ + extern int quiet; + ], [ac_cv_cc_pedantic=yes], [ac_cv_cc_pedantic=no]) + CFLAGS="$old_CFLAGS" + AC_MSG_RESULT($ac_cv_cc_pedantic) + WPEDANTIC= + AS_IF([test "$ac_cv_cc_pedantic" = yes], + [WPEDANTIC="-Wpedantic -Wno-long-long"]) + CFLAGS="-Wall $WEXTRA $WPEDANTIC $CFLAGS" + CXXFLAGS="-Wall $WEXTRA $WPEDANTIC $CXXFLAGS" +fi + +AC_MSG_CHECKING(for xlc) +AC_TRY_COMPILE([],[ + #ifndef __xlC__ + # error + #endif +], [compiler_xlc=yes], [compiler_xlc=no]) +AC_MSG_RESULT($compiler_xlc) +if test $compiler_xlc = yes -a "$powerpc_darwin" = true; then + # the darwin stack-frame-walking code is completely broken on xlc + AC_DEFINE([DARWIN_DONT_PARSE_STACK], 1, [See doc/README.macros.]) +fi + +# XLC neither requires nor tolerates the unnecessary assembler goop. +# Similar for the Sun C compiler. +AM_CONDITIONAL([ASM_WITH_CPP_UNSUPPORTED], + [test $compiler_xlc = yes -o $compiler_suncc = yes]) + +if test "$GCC" = yes; then + # Disable aliasing optimization unless forced to. + AC_MSG_CHECKING([whether compiler supports -fno-strict-aliasing]) + ac_cv_fno_strict_aliasing=no + for cflag in $CFLAGS; do + case "$cflag" in + -fstrict-aliasing) + # Opposite option already present + ac_cv_fno_strict_aliasing=skipped + break + ;; + esac + done + if test "$ac_cv_fno_strict_aliasing" != skipped; then + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fno-strict-aliasing" + AC_TRY_COMPILE([],[], [ac_cv_fno_strict_aliasing=yes], []) + CFLAGS="$old_CFLAGS" + AS_IF([test "$ac_cv_fno_strict_aliasing" = yes], + [CFLAGS="$CFLAGS -fno-strict-aliasing"], []) + fi + AC_MSG_RESULT($ac_cv_fno_strict_aliasing) +fi + +# Check for getcontext (uClibc can be configured without it, for example) +AC_CHECK_FUNC([getcontext], [], + [ AC_DEFINE([NO_GETCONTEXT], [1], [Missing getcontext function.]) ]) + +# Check whether dl_iterate_phdr exists (as a strong symbol). +AC_CHECK_FUNCS([dl_iterate_phdr]) + +case "$host" in +# While IRIX 6 has libdl for the O32 and N32 ABIs, it's missing for N64 +# and unnecessary everywhere. + mips-sgi-irix6*) ;; +# We never want libdl on darwin. It is a fake libdl that just ends up making +# dyld calls anyway. The same applies to Cygwin. + *-*-cygwin* | *-*-darwin* | *-*-msys*) + ;; + *) + AC_CHECK_LIB(dl, dlopen, THREADDLLIBS="$THREADDLLIBS -ldl") + ;; +esac + +case "$host" in + *-*-hpux*) + avoid_cpp_lib=yes;; + *) + avoid_cpp_lib=no; + ;; +esac +AM_CONDITIONAL(AVOID_CPP_LIB,test $avoid_cpp_lib = yes) + +# Check for various headers. +AC_CHECK_HEADER([execinfo.h], [], + [ AC_DEFINE([GC_MISSING_EXECINFO_H], [1], [Missing execinfo.h header.]) ]) + +# extra LD Flags which are required for targets +case "${host}" in + *-*-darwin*) + extra_ldflags_libgc=-Wl,-single_module + ;; +esac +AC_SUBST(extra_ldflags_libgc) + +AC_SUBST(EXTRA_TEST_LIBS) + +target_all=libgc.la +AC_SUBST(target_all) + +dnl If the target is an eCos system, use the appropriate eCos +dnl I/O routines. +dnl FIXME: this should not be a local option but a global target +dnl system; at present there is no eCos target. +TARGET_ECOS="no" +AC_ARG_WITH(ecos, +[ --with-ecos enable runtime eCos target support], +TARGET_ECOS="$with_ecos" +) + +addobjs= +addlibs= +CXXLIBS= + +case "$TARGET_ECOS" in + no) + ;; + *) + AC_DEFINE([ECOS], 1, [Define to enable eCos target support.]) + AM_CPPFLAGS="-I${TARGET_ECOS}/include $AM_CPPFLAGS" + addobjs="$addobjs ecos.lo" + ;; +esac + +AM_CONDITIONAL(CPLUSPLUS, test "${enable_cplusplus}" = yes) + +AC_ARG_ENABLE(throw-bad-alloc-library, + [AS_HELP_STRING([--disable-throw-bad-alloc-library], + [do not build C++ gctba library])]) +AM_CONDITIONAL(GC_TBA_LIBRARY, + test "${enable_cplusplus}" = yes \ + -a "${enable_throw_bad_alloc_library}" != no) + +if test "$GCC" = yes; then + if test "${enable_cplusplus}" = yes; then + case "$host" in + *-*-cygwin* | *-*-mingw* | *-*-msys*) + 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(CXX) +AC_SUBST(AM_CFLAGS) +AC_SUBST(AM_CPPFLAGS) +AC_SUBST(CXXLIBS) + +# Configuration of shared libraries +# +AC_MSG_CHECKING(whether to build shared libraries) +AC_ENABLE_SHARED + +case "$host" in + alpha-*-openbsd*) + enable_shared=no + ;; + *) + ;; +esac + +AC_MSG_RESULT($enable_shared) + +# Compile with GC_DLL defined unless building static libraries. +if test "${enable_shared}" != no -a "${enable_static}" != yes; then + AC_DEFINE(GC_DLL) + if test "$GCC" = yes; then + # Pass -fvisibility=hidden option if supported + AC_MSG_CHECKING([whether compiler supports -fvisibility]) + old_CFLAGS="$CFLAGS" + CFLAGS="-Werror -fvisibility=hidden $CFLAGS" + AC_TRY_COMPILE([],[], [ac_cv_fvisibility_hidden=yes], + [ac_cv_fvisibility_hidden=no]) + CFLAGS="$old_CFLAGS" + AS_IF([test "$ac_cv_fvisibility_hidden" = yes], + [CFLAGS="-DGC_VISIBILITY_HIDDEN_SET -fvisibility=hidden $CFLAGS"], + [CFLAGS="-DGC_NO_VISIBILITY $CFLAGS"]) + AC_MSG_RESULT($ac_cv_fvisibility_hidden) + fi +else + + case "$host" in + *-*-cygwin* | *-*-mingw* | *-*-msys*) + # Do not require the clients to link with "user32" system library. + AC_DEFINE([DONT_USE_USER32_DLL], 1, + [Do not use user32.dll import library (Win32).]) + # Use inline version of GC new and delete operators in test_cpp + # otherwise the system ones might be used instead because of arbitrary + # ordering of object files when linking. + CXXFLAGS="$CXXFLAGS -DGC_NOT_DLL" + ;; + esac +fi + +# Configuration of machine-dependent code +# +AC_MSG_CHECKING(which machine-dependent code should be used) +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.") + fi + ;; + i?86-*-solaris2.[[89]]) + # PROC_VDB appears to work in 2.8 and 2.9 but not in 2.10+ (for now). + AC_DEFINE([SOLARIS25_PROC_VDB_BUG_FIXED], 1, + [See the comment in gcconfig.h.]) + ;; + sparc-*-netbsd*) + machdep="sparc_netbsd_mach_dep.lo" + compile_asm=true + ;; + sparc*-*-linux* | sparc*-*-openbsd* | sparc64-*-freebsd* | sparc64-*-netbsd*) + machdep="sparc_mach_dep.lo" + compile_asm=true + ;; + sparc-sun-solaris2.3) + machdep="sparc_mach_dep.lo" + compile_asm=true + AC_DEFINE(SUNOS53_SHARED_LIB, 1, + [Define to work around a Solaris 5.3 bug (see dyn_load.c).]) + ;; + sparc*-sun-solaris2*) + machdep="sparc_mach_dep.lo" + compile_asm=true + ;; + ia64-*-*) + machdep="ia64_save_regs_in_stack.lo" + ;; +esac +AC_MSG_RESULT($machdep) +addobjs="$addobjs $machdep" +AC_SUBST(addobjs) +AC_SUBST(addlibs) + +AC_PROG_LIBTOOL + +# Suppress "extension used" clang warning (when compiling .S files). +if test x$compile_asm = xtrue -a "$GCC" = yes; then + AC_MSG_CHECKING([whether compiler supports -Wno-language-extension-token]) + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Werror -Wno-language-extension-token" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])], + [ac_cv_lang_ext_token=yes], [ac_cv_lang_ext_token=no]) + CFLAGS="$old_CFLAGS" + AS_IF([test "$ac_cv_lang_ext_token" = yes], + [CFLAGS="$CFLAGS -Wno-language-extension-token"]) + AC_MSG_RESULT($ac_cv_lang_ext_token) +fi + +dnl We use these options to decide which functions to include. +AC_ARG_WITH(target-subdir, +[ --with-target-subdir=SUBDIR + configuring with a cross compiler]) +AC_ARG_WITH(cross-host, +[ --with-cross-host=HOST configuring with a cross compiler]) + +dnl automake wants to see AC_EXEEXT. But we don't need it. And having +dnl it is actually a problem, because the compiler we're passed can't +dnl necessarily do a full link. So we fool automake here. +if false; then + dnl autoconf 2.50 runs AC_EXEEXT by default, and the macro expands + dnl to nothing, so nothing would remain between `then' and `fi' if it + dnl were not for the `:' below. + : + AC_EXEEXT +fi + +dnl The collector might not properly work on IBM AIX when +dnl built with gcc and -O. So we remove -O in the appropriate case. +AC_MSG_CHECKING(whether AIX gcc optimization fix is necessary) +case "$host" in + *aix*) + if test "$GCC" = yes; then + AC_MSG_RESULT(yes) + new_CFLAGS= + for i in $CFLAGS; do + case "$i" in + -O*) + ;; + *) + new_CFLAGS="$new_CFLAGS $i" + ;; + esac + done + CFLAGS="$new_CFLAGS" + else + AC_MSG_RESULT(no) + fi + ;; + *) AC_MSG_RESULT(no) ;; +esac + +dnl Include defines that have become de facto standard. +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.]) +AC_DEFINE([ALL_INTERIOR_POINTERS], [1], + [Define to recognise all pointers to the interior of objects.]) + + +dnl Interface Selection +dnl ------------------- +dnl +dnl By default, make the library as general as possible. +AC_ARG_ENABLE(gcj-support, + [AS_HELP_STRING([--disable-gcj-support], [disable support for gcj])]) +if test x"$enable_gcj_support" != xno; then + AC_DEFINE(GC_GCJ_SUPPORT, 1, [Define to include support for gcj.]) + case "$host" in + *-*-kfreebsd*-gnu) + # FIXME: For a reason, gctest hangs up on kFreeBSD if both of + # THREAD_LOCAL_ALLOC and GC_ENABLE_SUSPEND_THREAD are defined. + if test "${enable_thread_local_alloc}" = no; then + AC_DEFINE(GC_ENABLE_SUSPEND_THREAD) + fi + ;; + *) + AC_DEFINE([GC_ENABLE_SUSPEND_THREAD], 1, + [Define to turn on GC_suspend_thread support.]) + ;; + esac +fi +AM_CONDITIONAL(ENABLE_GCJ_SUPPORT, [test x"enable_gcj_support" != xno]) + +dnl Interaction with other programs that might use signals. +AC_ARG_ENABLE(sigrt-signals, + [AS_HELP_STRING([--enable-sigrt-signals], + [force GC to use SIGRTMIN-based signals for thread suspend/resume])]) +if test x"${enable_sigrt_signals}" = xyes; then + AC_DEFINE([GC_USESIGRT_SIGNALS], 1, + [Force the GC to use signals based on SIGRTMIN+k.]) +fi + + +dnl Debugging +dnl --------- + +AH_TEMPLATE([GC_HAVE_BUILTIN_BACKTRACE], + [Define if backtrace information is supported.]) +AH_TEMPLATE([MAKE_BACK_GRAPH], [See doc/README.macros.]) +AH_TEMPLATE([SAVE_CALL_COUNT], + [The number of caller frames saved when allocating with the + debugging API.]) +UNWINDLIBS= +AC_ARG_ENABLE(gc-debug, + [AS_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_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) + AC_DEFINE(SAVE_CALL_COUNT, 8) + AC_CHECK_LIB(unwind, backtrace, [ + AC_DEFINE(GC_HAVE_BUILTIN_BACKTRACE) + UNWINDLIBS=-lunwind + AC_MSG_WARN("Client code may need to link against libunwind.") + ]) + ;; + x86-*-linux* | i586-*-linux* | i686-*-linux* | x86_64-*-linux* ) + AC_DEFINE(MAKE_BACK_GRAPH) + AC_MSG_WARN("Client must not use -fomit-frame-pointer.") + AC_DEFINE(SAVE_CALL_COUNT, 8) + ;; + i[3456]86-*-dgux*) + AC_DEFINE(MAKE_BACK_GRAPH) + ;; + 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 whether a compiler warning of unsafe __builtin_return_address(1) +# could be suppressed by -Wno-frame-address option. +# __builtin_return_address(1) is used by libgc for debugging purposes only. +AC_MSG_CHECKING([whether -Wno-frame-address works]) +use_wno_error_frame_address=no +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -Werror -Wno-frame-address $CFLAGS_EXTRA" +AC_TRY_COMPILE([], [{ + if (!__builtin_return_address(1)) return 1; +}], [ use_wno_error_frame_address=yes ]) +CFLAGS="$old_CFLAGS" +AC_MSG_RESULT($use_wno_error_frame_address) +if test x"$use_wno_error_frame_address" = xyes; then + CFLAGS="$CFLAGS -Wno-frame-address" +fi + +# Check for dladdr (used for debugging). +AC_MSG_CHECKING(for dladdr) +have_dladdr=no +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $CFLAGS_EXTRA" +AC_TRY_COMPILE([ +#define _GNU_SOURCE 1 +#include ], [{ + Dl_info info; + (void)dladdr("", &info); +}], [ have_dladdr=yes ]) +CFLAGS="$old_CFLAGS" +AC_MSG_RESULT($have_dladdr) +if test x"$have_dladdr" = xyes; then + AC_DEFINE([HAVE_DLADDR], 1, [Define to use 'dladdr' function.]) +fi + +# sigsetjmp could be a macro (thus AC_CHECK_FUNCS cannot be used). +AC_MSG_CHECKING(for sigsetjmp) +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $CFLAGS_EXTRA" +AC_TRY_LINK([#include ], + [sigjmp_buf t; sigsetjmp(t, 0)], + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + AC_DEFINE([GC_NO_SIGSETJMP], [1], [Missing sigsetjmp function.])]) +CFLAGS="$old_CFLAGS" + +# pthread_setname_np, if available, may have 1, 2 or 3 arguments. +AS_IF([test "$THREADS" = posix], + [AC_MSG_CHECKING(for pthread_setname_np) + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $CFLAGS_EXTRA -Werror" + AC_TRY_COMPILE([ +# define _GNU_SOURCE 1 +# include + ], [pthread_setname_np("thread-name")], + [AC_MSG_RESULT([yes (w/o tid)]) + AC_DEFINE([HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID], [1], + [Define to use 'pthread_setname_np(const char*)' function.])], + [AC_TRY_COMPILE([ +# define _GNU_SOURCE 1 +# include + ], [pthread_setname_np(pthread_self(), "thread-name-%u", 0)], + [AC_MSG_RESULT([yes (with tid and arg)]) + AC_DEFINE([HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG], [1], + [Define to use 'pthread_setname_np(pthread_t, const char*, void *)' + function.])], + [AC_TRY_COMPILE([ +# define _GNU_SOURCE 1 +# include + ], [pthread_setname_np(pthread_self(), "thread-name")], + [AC_MSG_RESULT([yes (with tid)]) + AC_DEFINE([HAVE_PTHREAD_SETNAME_NP_WITH_TID], [1], + [Define to use 'pthread_setname_np(pthread_t, const char*)' + function.])], + [AC_MSG_RESULT(no)])])]) + CFLAGS="$old_CFLAGS"]) + +# Check for AViiON Machines running DGUX +ac_is_dgux=no +AC_CHECK_HEADER(sys/dg_sys_info.h, +[ac_is_dgux=yes;]) + +dnl :GOTCHA: we do not check anything but sys/dg_sys_info.h +if test $ac_is_dgux = yes; then + dgux_spec_opts="-DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + CFLAGS="$dgux_spec_opts $CFLAGS" + CXXFLAGS="$dgux_spec_opts $CXXFLAGS" + if test "$enable_gc_debug" = "yes"; then + CFLAGS="-g -mstandard $CFLAGS" + CXXFLAGS="-g -mstandard $CXXFLAGS" + fi + AC_SUBST(CFLAGS) + AC_SUBST(CXXFLAGS) +fi + +AC_ARG_ENABLE(java-finalization, + [AS_HELP_STRING([--disable-java-finalization], + [disable support for java finalization])]) +if test x"$enable_java_finalization" != xno; then + AC_DEFINE([JAVA_FINALIZATION], 1, [See doc/README.macros.]) +fi + +AC_ARG_ENABLE(atomic-uncollectable, + [AS_HELP_STRING([--disable-atomic-uncollectible], + [disable support for atomic uncollectible allocation])]) +if test x"$enable_atomic_uncollectible" != x"no"; then + AC_DEFINE([GC_ATOMIC_UNCOLLECTABLE], 1, + [Define to enable atomic uncollectible allocation.]) +fi + +AC_ARG_ENABLE(redirect-malloc, + [AS_HELP_STRING([--enable-redirect-malloc], + [redirect malloc and friends to GC routines])]) + +if test "${enable_redirect_malloc}" = yes; then + if test "${enable_gc_debug}" = yes; then + AC_DEFINE([REDIRECT_MALLOC], GC_debug_malloc_replacement, + [If defined, redirect malloc to this function.]) + AC_DEFINE([REDIRECT_REALLOC], GC_debug_realloc_replacement, + [If defined, redirect GC_realloc to this function.]) + AC_DEFINE([REDIRECT_FREE], GC_debug_free, + [If defined, redirect free to this function.]) + else + AC_DEFINE(REDIRECT_MALLOC, GC_malloc) + fi + AC_DEFINE([GC_USE_DLOPEN_WRAP], 1, [See doc/README.macros.]) +fi + +AC_ARG_ENABLE(disclaim, + [AS_HELP_STRING([--disable-disclaim], + [disable alternative (more efficient) finalization interface])]) +if test x"$enable_disclaim" != xno; then + AC_DEFINE(ENABLE_DISCLAIM, 1, + [Define to enable alternative finalization interface.]) +fi +AM_CONDITIONAL(ENABLE_DISCLAIM, + [test x"$enable_disclaim" != xno]) + +AC_ARG_ENABLE(large-config, + [AS_HELP_STRING([--enable-large-config], + [optimize for large (> 100 MB) heap or root set])]) +if test "${enable_large_config}" = yes; then + AC_DEFINE(LARGE_CONFIG, 1, + [Define to optimize for large heaps or root sets.]) +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 targeting an embedded system. FIXME. +if test -n "${with_cross_host}"; then + AC_DEFINE([NO_CLOCK], 1, [Define to not use system clock (cross compiling).]) + AC_DEFINE([SMALL_CONFIG], 1, + [Define to tune the collector for small heap sizes.]) +fi + +if test "$enable_gc_debug" = "no"; then + AC_DEFINE([NO_DEBUGGING], 1, + [Disable debugging, like GC_dump and its callees.]) +fi + +AC_SUBST(UNWINDLIBS) + +AC_ARG_ENABLE(gc-assertions, + [AS_HELP_STRING([--enable-gc-assertions], + [collector-internal assertion checking])]) +if test "${enable_gc_assertions}" = yes; then + AC_DEFINE([GC_ASSERTIONS], 1, + [Define to enable internal debug assertions.]) +fi + +AC_ARG_ENABLE(mmap, + [AS_HELP_STRING([--enable-mmap], + [use mmap instead of sbrk to expand the heap])], + gc_use_mmap=$enableval) + +AC_ARG_ENABLE(munmap, + [AS_HELP_STRING([--enable-munmap=N], + [return page to the OS if empty for N collections + (default: 6)])], + MUNMAP_THRESHOLD=$enableval) +if test x$enable_munmap != xno; then + AC_DEFINE([USE_MMAP], 1, + [Define to use mmap instead of sbrk to expand the heap.]) + AH_TEMPLATE([USE_WINALLOC], + [Define to use Win32 VirtualAlloc (instead of sbrk or + mmap) to expand the heap.]) + AC_DEFINE([USE_MUNMAP], 1, + [Define to return memory to OS with munmap calls + (see doc/README.macros).]) + if test x$MUNMAP_THRESHOLD = x -o x$MUNMAP_THRESHOLD = xyes; then + MUNMAP_THRESHOLD=6 + fi + AC_DEFINE_UNQUOTED([MUNMAP_THRESHOLD], [${MUNMAP_THRESHOLD}], + [Number of GC cycles to wait before unmapping an unused block.]) +else + if test "${gc_use_mmap}" = "yes"; then + AC_DEFINE([USE_MMAP], 1, + [Define to use mmap instead of sbrk to expand the heap.]) + fi +fi + +AC_ARG_ENABLE(dynamic-loading, + [AS_HELP_STRING([--disable-dynamic-loading], + [build the collector with disabled tracing + of dynamic library data roots])]) +if test "${enable_dynamic_loading}" = "no"; then + AC_DEFINE([IGNORE_DYNAMIC_LOADING], 1, + [Do not define DYNAMIC_LOADING even if supported (i.e., build the + collector with disabled tracing of dynamic library data roots).]) +fi + +AC_ARG_ENABLE(register-main-static-data, + [AS_HELP_STRING([--disable-register-main-static-data], + [skip the initial guess of data root sets])]) +if test "${enable_register_main_static_data}" = "no"; then + AC_DEFINE([GC_DONT_REGISTER_MAIN_STATIC_DATA], 1, + [Skip the initial guess of data root sets.]) +fi + +AC_ARG_ENABLE(checksums, + [AS_HELP_STRING([--enable-checksums], + [report on erroneously cleared dirty bits at + substantial performance cost; use only for + debugging of the incremental collector])]) +if test x$enable_checksums = xyes; then + if test x$enable_munmap != xno -o x$THREADS != xnone; then + AC_MSG_ERROR([CHECKSUMS not compatible with USE_MUNMAP or threads]) + fi + AC_DEFINE([CHECKSUMS], 1, + [Erroneously cleared dirty bits checking. Use only for + debugging of the incremental collector.]) +fi +AM_CONDITIONAL([CHECKSUMS], test x$enable_checksums = xyes) + +AM_CONDITIONAL(USE_LIBDIR, test -z "$with_cross_host") + +AC_ARG_ENABLE(werror, + [AS_HELP_STRING([--enable-werror], [pass -Werror to the C compiler])], + werror_flag=$enableval, werror_flag=no) +if test x$werror_flag = xyes; then + WERROR_CFLAGS="-Werror" + case "$host" in + # _dyld_bind_fully_image_containing_address is deprecated in OS X 10.5+ + *-*-darwin*) + WERROR_CFLAGS="$WERROR_CFLAGS -Wno-deprecated-declarations" + ;; + esac +fi +AC_SUBST([WERROR_CFLAGS]) + +AC_ARG_ENABLE(single-obj-compilation, + [AS_HELP_STRING([--enable-single-obj-compilation], + [compile all libgc source files into single .o + (default: yes if static libraries are disabled)])], + [], [ AS_IF([test x"$enable_static" = xno], + [enable_single_obj_compilation=yes]) ]) +AM_CONDITIONAL([SINGLE_GC_OBJ], + [test x"$enable_single_obj_compilation" = xyes]) + +AC_ARG_ENABLE(gcov, + [AS_HELP_STRING([--enable-gcov], [turn on code coverage analysis])]) +if test "$enable_gcov" = "yes"; then + CFLAGS="-D NTHREADS=20 $CFLAGS --coverage" + if test "${enable_shared}" = no; then + # FIXME: As of g++-4.8.4/x64, in case of shared library build, test_cpp + # linkage fails with "hidden symbol atexit is referenced by DSO" message. + CXXFLAGS="$CXXFLAGS --coverage" + fi + # Turn off optimization to get accurate line numbers. + CFLAGS=`echo "$CFLAGS" | sed -e 's/-O\(1\|2\|3\|4\|s\|fast\)\?//g'` + CXXFLAGS=`echo "$CXXFLAGS" | sed -e 's/-O\(1\|2\|3\|4\|s\|fast\)\?//g'` +fi + +AC_ARG_ENABLE(docs, + [AS_HELP_STRING([--disable-docs], + [do not build and install documentation])]) +AM_CONDITIONAL(ENABLE_DOCS, test x$enable_docs != xno) + +AM_CONDITIONAL(ENABLE_SHARED, test x$enable_shared = xyes) + +# Atomic Ops +# ---------- + +# Do we want to use an external libatomic_ops? By default use it if it's +# found. +AC_ARG_WITH([libatomic-ops], + [AS_HELP_STRING([--with-libatomic-ops[=yes|no|check|none]], + [use an external libatomic_ops? (default: check; + none: use compiler intrinsics or no thread support)])], + [], [ AS_IF([test x"$THREADS" != xnone], + [with_libatomic_ops=check], [with_libatomic_ops=none]) ]) + +# Check whether compiler atomic intrinsics can be used. +if test x"$with_libatomic_ops" = xcheck; then + AC_MSG_CHECKING(for compiler intrinsics support) + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $CFLAGS_EXTRA -DGC_BUILTIN_ATOMIC" + CFLAGS="$CFLAGS -I${srcdir}/include -I${srcdir}/tests" + AC_TRY_RUN([#include "test_atomic_ops.c"], + [AC_MSG_RESULT(yes) + with_libatomic_ops=none], + [AC_MSG_RESULT(no)], [AC_MSG_RESULT(skipped because cross-compiling)]) + CFLAGS="$old_CFLAGS" +fi + +# Check for an external libatomic_ops if the above answer is "yes" or "check". +# If not found, fail on "yes", and convert "check" to "no". +# First, check that libatomic_ops usage is not disabled explicitly. +missing_libatomic_ops=false +AS_IF([test x"$with_libatomic_ops" != xno -a x"$with_libatomic_ops" != xnone], + [ missing_libatomic_ops=true ]) + +dnl To avoid "syntax error near unexpected token ATOMIC_OPS" configure error +dnl observed by some clients, the following 3 code lines are commented out: +dnl +dnl AS_IF([test x$missing_libatomic_ops = xtrue], +dnl [ PKG_CHECK_MODULES([ATOMIC_OPS], [atomic_ops], +dnl [ missing_libatomic_ops=false ], [ [] ]) ]) + +dnl Retry with AC_CHECK_HEADER if PKG_CHECK_MODULES failed. +AS_IF([test x$missing_libatomic_ops = xtrue], + [ AC_CHECK_HEADER([atomic_ops.h], [missing_libatomic_ops=false]) ]) +AS_IF([test x$missing_libatomic_ops = xtrue], + [ AS_IF([test x"$with_libatomic_ops" != xcheck], + [ 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/src/atomic_ops.h"], + [ 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]) +AS_IF([test x"$with_libatomic_ops" != xno], + [ AS_IF([test x"$with_libatomic_ops" != xnone -a x"$THREADS" != xnone], + [ AC_MSG_RESULT([external]) + ATOMIC_OPS_LIBS="-latomic_ops" + AC_SUBST([ATOMIC_OPS_LIBS]) ], + [ AC_MSG_RESULT([none]) + AS_IF([test x"$THREADS" != xnone], + [ AC_DEFINE([GC_BUILTIN_ATOMIC], [1], + [Use GCC atomic intrinsics instead of + libatomic_ops primitives.]) ]) ]) + AO_TRYLINK_CFLAGS="" ], + [ AC_MSG_RESULT([internal]) + AO_TRYLINK_CFLAGS="-I${srcdir}/libatomic_ops/src" + ATOMIC_OPS_CFLAGS='-I$(top_builddir)/libatomic_ops/src -I$(top_srcdir)/libatomic_ops/src' + ATOMIC_OPS_LIBS="" + AC_SUBST([ATOMIC_OPS_CFLAGS]) + AC_CONFIG_SUBDIRS([libatomic_ops]) + ]) +AM_CONDITIONAL([USE_INTERNAL_LIBATOMIC_OPS], + [test x$with_libatomic_ops = xno -a x"$THREADS" != xnone]) +AM_CONDITIONAL([NEED_ATOMIC_OPS_ASM], + [test x$with_libatomic_ops = xno -a x$need_atomic_ops_asm = xtrue]) + +# Check whether particular AO primitives are emulated with locks. +# The check below is based on the fact that linking with the libatomic_ops +# binary file is not needed in case of absence of the emulation (except for +# Solaris SPARC). +AS_IF([test x$with_libatomic_ops != xnone -a x$need_atomic_ops_asm != xtrue], + [ old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $AO_TRYLINK_CFLAGS $CFLAGS_EXTRA" + AC_MSG_CHECKING([for lock-free AO_or primitive]) + AC_TRY_LINK([#include "atomic_ops.h"], + [AO_t x=0;AO_or(&x,1)], + [ AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_LOCKFREE_AO_OR], [1], + [libatomic_ops AO_or primitive implementation is lock-free.]) ], + [ AC_MSG_RESULT(no) ]) + AC_MSG_CHECKING([for lock-free AO load/store, test-and-set primitives]) + AC_TRY_LINK([#include "atomic_ops.h"], + [AO_t x=0;unsigned char c=0;AO_TS_t z=AO_TS_INITIALIZER; + (void)AO_test_and_set_acquire(&z);AO_CLEAR(&z);AO_compiler_barrier(); + AO_store(&x,AO_load(&x)+1);AO_char_store(&c,AO_char_load(&c)+1); + AO_store_release(&x,AO_load_acquire(&x)+1)], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + use_thread_local_alloc=no + AC_DEFINE([BASE_ATOMIC_OPS_EMULATED], [1], + [AO load, store and/or test-and-set primitives are + implemented in libatomic_ops using locks.]) ]) + AS_IF([test x$use_parallel_mark != xno], + [ AC_MSG_CHECKING( + [for lock-free compare-and-swap and fetch-and-add primitives]) + AC_TRY_LINK( + [#define AO_REQUIRE_CAS + #include "atomic_ops.h"], + [AO_t x=0;(void)AO_fetch_and_add(&x,1);(void)AO_compare_and_swap(&x,1,2)], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + use_parallel_mark=no ]) ]) + CFLAGS="$old_CFLAGS" ]) + +AS_IF([test x$use_parallel_mark != xno], + [ AC_DEFINE(PARALLEL_MARK) ]) +AS_IF([test x$use_thread_local_alloc != xno], + [ AC_DEFINE(THREAD_LOCAL_ALLOC) ]) +AM_CONDITIONAL(THREAD_LOCAL_ALLOC, test x$use_thread_local_alloc != xno) + +AC_ARG_ENABLE(handle-fork, + [AS_HELP_STRING([--enable-handle-fork[=yes|no|auto|manual]], + [attempt to ensure a usable collector after fork() + in multi-threaded programs (default: auto; + manual: GC_atfork_prepare/parent/child should be + called by the client)])]) +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.]) +elif test "${enable_handle_fork}" != manual -a x$THREADS = xposix; then + # If the option is omitted, pthread_atfork handlers are installed + # by default for the targets where pthread_atfork is known to work. + case "$host" in + *-*-darwin*) + # The incremental mode conflicts with fork handling on Darwin. + ;; + *-*-aix* | *-*-android* | *-*-cygwin* | *-*-freebsd* | *-*-haiku* | \ + *-*-hpux11* | *-*-irix* | *-*-kfreebsd*-gnu | \ + *-*-*linux* | *-*-netbsd* | *-*-openbsd* | *-*-osf* | *-*-solaris*) + AC_DEFINE(HANDLE_FORK) + ;; + esac +fi + +dnl Produce the Files +dnl ----------------- + +AC_CONFIG_FILES([Makefile bdw-gc.pc]) + +AC_CONFIG_COMMANDS([default],, + [ srcdir="${srcdir}" + host=${host} + CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + CC="${CC}" + DEFS="$DEFS" ]) + +AC_OUTPUT diff --git a/bdwgc/cord/cord.am b/bdwgc/cord/cord.am new file mode 100644 index 000000000..3deb4043f --- /dev/null +++ b/bdwgc/cord/cord.am @@ -0,0 +1,40 @@ +## This file is processed with automake. + +# Info (current:revision:age) for the Libtool versioning system. +# These numbers should be updated at most once just before the release, +# and, optionally, at most once during the development (after the release). +LIBCORD_VER_INFO = 6:0:5 + +lib_LTLIBRARIES += libcord.la + +libcord_la_LIBADD = libgc.la +libcord_la_LDFLAGS = -version-info $(LIBCORD_VER_INFO) -no-undefined +libcord_la_CPPFLAGS = $(AM_CPPFLAGS) + +libcord_la_SOURCES = \ + cord/cordbscs.c \ + cord/cordprnt.c \ + cord/cordxtra.c + +TESTS += cordtest$(EXEEXT) +check_PROGRAMS += cordtest +cordtest_SOURCES = cord/tests/cordtest.c +cordtest_LDADD = $(top_builddir)/libcord.la + +## In case of static libraries build, libgc.a is already referenced in +## dependency_libs attribute of libcord.la file. +if ENABLE_SHARED +cordtest_LDADD += $(top_builddir)/libgc.la +endif + +EXTRA_DIST += \ + cord/tests/de.c \ + cord/tests/de_cmds.h \ + cord/tests/de_win.c \ + cord/tests/de_win.h \ + cord/tests/de_win.rc + +pkginclude_HEADERS += \ + include/cord.h \ + include/cord_pos.h \ + include/ec.h diff --git a/bdwgc/cord/cordbscs.c b/bdwgc/cord/cordbscs.c new file mode 100644 index 000000000..f2a5a5057 --- /dev/null +++ b/bdwgc/cord/cordbscs.c @@ -0,0 +1,942 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifndef CORD_BUILD +# define CORD_BUILD +#endif + +# include "gc.h" +# include "cord.h" +# include +# include +# include + +/* An implementation of the cord primitives. These are the only */ +/* Functions that understand the representation. We perform only */ +/* minimal checks on arguments to these functions. Out of bounds */ +/* arguments to the iteration functions may result in client functions */ +/* invoked on garbage data. In most cases, client functions should be */ +/* programmed defensively enough that this does not result in memory */ +/* smashes. */ + +typedef void (* oom_fn)(void); + +oom_fn CORD_oom_fn = (oom_fn) 0; + +# define OUT_OF_MEMORY { if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ + ABORT("Out of memory"); } +# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } + +typedef unsigned long word; + + struct Concatenation { + char null; + char header; + char depth; /* concatenation nesting depth. */ + unsigned char left_len; + /* Length of left child if it is sufficiently */ + /* short; 0 otherwise. */ +# define MAX_LEFT_LEN 255 + word len; + CORD left; /* length(left) > 0 */ + CORD right; /* length(right) > 0 */ + }; + + struct Function { + char null; + char header; + char depth; /* always 0 */ + char left_len; /* always 0 */ + word len; + CORD_fn fn; + void * client_data; + }; + + struct Generic { + char null; + char header; + char depth; + char left_len; + word len; + }; + +typedef union { + struct Concatenation concatenation; + struct Function function; + struct Generic generic; + char string[1]; +} CordRep; + +# define CONCAT_HDR 1 + +# define FN_HDR 4 +# define SUBSTR_HDR 6 + /* Substring nodes are a special case of function nodes. */ + /* The client_data field is known to point to a substr_args */ + /* structure, and the function is either CORD_apply_access_fn */ + /* or CORD_index_access_fn. */ + +/* The following may be applied only to function and concatenation nodes: */ +#define IS_CONCATENATION(s) (((CordRep *)s)->generic.header == CONCAT_HDR) + +#define IS_FUNCTION(s) ((((CordRep *)s)->generic.header & FN_HDR) != 0) + +#define IS_SUBSTR(s) (((CordRep *)s)->generic.header == SUBSTR_HDR) + +#define LEN(s) (((CordRep *)s) -> generic.len) +#define DEPTH(s) (((CordRep *)s) -> generic.depth) +#define GEN_LEN(s) (CORD_IS_STRING(s) ? strlen(s) : LEN(s)) + +#define LEFT_LEN(c) ((c) -> left_len != 0? \ + (c) -> left_len \ + : (CORD_IS_STRING((c) -> left) ? \ + (c) -> len - GEN_LEN((c) -> right) \ + : LEN((c) -> left))) + +#define SHORT_LIMIT (sizeof(CordRep) - 1) + /* Cords shorter than this are C strings */ + + +/* Dump the internal representation of x to stdout, with initial */ +/* indentation level n. */ +void CORD_dump_inner(CORD x, unsigned n) +{ + size_t i; + + for (i = 0; i < (size_t)n; i++) { + fputs(" ", stdout); + } + if (x == 0) { + fputs("NIL\n", stdout); + } else if (CORD_IS_STRING(x)) { + for (i = 0; i <= SHORT_LIMIT; i++) { + if (x[i] == '\0') break; + putchar(x[i]); + } + if (x[i] != '\0') fputs("...", stdout); + putchar('\n'); + } else if (IS_CONCATENATION(x)) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + + printf("Concatenation: %p (len: %d, depth: %d)\n", + (void *)x, (int)(conc -> len), (int)(conc -> depth)); + CORD_dump_inner(conc -> left, n+1); + CORD_dump_inner(conc -> right, n+1); + } else /* function */ { + struct Function * func = &(((CordRep *)x) -> function); + + if (IS_SUBSTR(x)) printf("(Substring) "); + printf("Function: %p (len: %d): ", (void *)x, (int)(func -> len)); + for (i = 0; i < 20 && i < func -> len; i++) { + putchar((*(func -> fn))(i, func -> client_data)); + } + if (i < func -> len) fputs("...", stdout); + putchar('\n'); + } +} + +/* Dump the internal representation of x to stdout */ +void CORD_dump(CORD x) +{ + CORD_dump_inner(x, 0); + fflush(stdout); +} + +CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) +{ + size_t result_len; + size_t lenx; + int depth; + + if (x == CORD_EMPTY) return(y); + if (leny == 0) return(x); + if (CORD_IS_STRING(x)) { + lenx = strlen(x); + result_len = lenx + leny; + if (result_len <= SHORT_LIMIT) { + char * result = (char *)GC_MALLOC_ATOMIC(result_len + 1); + + if (result == 0) OUT_OF_MEMORY; +# ifdef LINT2 + memcpy(result, x, lenx + 1); +# else + memcpy(result, x, lenx); + /* No need to copy the terminating zero */ + /* as result[lenx] is written below. */ +# endif + memcpy(result + lenx, y, leny); + result[result_len] = '\0'; + return((CORD) result); + } else { + depth = 1; + } + } else { + CORD right; + CORD left; + char * new_right; + + lenx = LEN(x); + + if (leny <= SHORT_LIMIT/2 + && IS_CONCATENATION(x) + && CORD_IS_STRING(right = ((CordRep *)x) -> concatenation.right)) { + size_t right_len; + + /* Merge y into right part of x. */ + if (!CORD_IS_STRING(left = ((CordRep *)x) -> concatenation.left)) { + right_len = lenx - LEN(left); + } else if (((CordRep *)x) -> concatenation.left_len != 0) { + right_len = lenx - ((CordRep *)x) -> concatenation.left_len; + } else { + right_len = strlen(right); + } + result_len = right_len + leny; /* length of new_right */ + if (result_len <= SHORT_LIMIT) { + new_right = (char *)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'; + y = new_right; + leny = result_len; + x = left; + lenx -= right_len; + /* Now fall through to concatenate the two pieces: */ + } + if (CORD_IS_STRING(x)) { + depth = 1; + } else { + depth = DEPTH(x) + 1; + } + } else { + depth = DEPTH(x) + 1; + } + result_len = lenx + leny; + } + { + /* The general case; lenx, result_len is known: */ + struct Concatenation * result = GC_NEW(struct Concatenation); + + if (NULL == result) OUT_OF_MEMORY; + result->header = CONCAT_HDR; + result->depth = (char)depth; + if (lenx <= MAX_LEFT_LEN) + result->left_len = (unsigned char)lenx; + result->len = (word)result_len; + result->left = x; + GC_PTR_STORE_AND_DIRTY((void *)&result->right, y); + GC_reachable_here(x); + if (depth >= MAX_DEPTH) { + return(CORD_balance((CORD)result)); + } else { + return((CORD) result); + } + } +} + + +CORD CORD_cat(CORD x, CORD y) +{ + size_t result_len; + int depth; + size_t lenx; + + if (x == CORD_EMPTY) return(y); + if (y == CORD_EMPTY) return(x); + if (CORD_IS_STRING(y)) { + return(CORD_cat_char_star(x, y, strlen(y))); + } else if (CORD_IS_STRING(x)) { + lenx = strlen(x); + depth = DEPTH(y) + 1; + } else { + int depthy = DEPTH(y); + + lenx = LEN(x); + depth = DEPTH(x) + 1; + if (depthy >= depth) depth = depthy + 1; + } + result_len = lenx + LEN(y); + { + struct Concatenation * result = GC_NEW(struct Concatenation); + + if (NULL == result) OUT_OF_MEMORY; + result->header = CONCAT_HDR; + result->depth = (char)depth; + if (lenx <= MAX_LEFT_LEN) + result->left_len = (unsigned char)lenx; + result->len = (word)result_len; + result->left = x; + GC_PTR_STORE_AND_DIRTY((void *)&result->right, y); + GC_reachable_here(x); + if (depth >= MAX_DEPTH) { + return(CORD_balance((CORD)result)); + } else { + return((CORD) result); + } + } +} + + +static CordRep *CORD_from_fn_inner(CORD_fn fn, void * client_data, size_t len) +{ + if (len == 0) return(0); + if (len <= SHORT_LIMIT) { + char * result; + size_t i; + char buf[SHORT_LIMIT+1]; + + for (i = 0; i < len; i++) { + char c = (*fn)(i, client_data); + + if (c == '\0') goto gen_case; + buf[i] = c; + } + + result = (char *)GC_MALLOC_ATOMIC(len + 1); + if (result == 0) OUT_OF_MEMORY; + memcpy(result, buf, len); + result[len] = '\0'; + return (CordRep *)result; + } + gen_case: + { + struct Function * result = GC_NEW(struct Function); + + if (NULL == result) OUT_OF_MEMORY; + result->header = FN_HDR; + /* depth is already 0 */ + result->len = (word)len; + result->fn = fn; + GC_PTR_STORE_AND_DIRTY(&result->client_data, client_data); + return (CordRep *)result; + } +} + +CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len) +{ + return (/* const */ CORD) CORD_from_fn_inner(fn, client_data, len); +} + +size_t CORD_len(CORD x) +{ + if (x == 0) { + return(0); + } else { + return(GEN_LEN(x)); + } +} + +struct substr_args { + CordRep * sa_cord; + size_t sa_index; +}; + +char CORD_index_access_fn(size_t i, void * client_data) +{ + struct substr_args *descr = (struct substr_args *)client_data; + + return(((char *)(descr->sa_cord))[i + descr->sa_index]); +} + +char CORD_apply_access_fn(size_t i, void * client_data) +{ + struct substr_args *descr = (struct substr_args *)client_data; + struct Function * fn_cord = &(descr->sa_cord->function); + + return((*(fn_cord->fn))(i + descr->sa_index, fn_cord->client_data)); +} + +/* A version of CORD_substr that simply returns a function node, thus */ +/* postponing its work. The fourth argument is a function that may */ +/* be used for efficient access to the ith character. */ +/* Assumes i >= 0 and i + n < length(x). */ +CORD CORD_substr_closure(CORD x, size_t i, size_t n, CORD_fn f) +{ + struct substr_args * sa = GC_NEW(struct substr_args); + CordRep * result; + + if (sa == 0) OUT_OF_MEMORY; + sa->sa_index = i; + GC_PTR_STORE_AND_DIRTY(&sa->sa_cord, x); + result = CORD_from_fn_inner(f, (void *)sa, n); + if ((CORD)result != CORD_EMPTY && 0 == result -> function.null) + result -> function.header = SUBSTR_HDR; + return (CORD)result; +} + +# define SUBSTR_LIMIT (10 * SHORT_LIMIT) + /* Substrings of function nodes and flat strings shorter than */ + /* this are flat strings. Othewise we use a functional */ + /* representation, which is significantly slower to access. */ + +/* A version of CORD_substr that assumes i >= 0, n > 0, and i + n < length(x).*/ +CORD CORD_substr_checked(CORD x, size_t i, size_t n) +{ + if (CORD_IS_STRING(x)) { + if (n > SUBSTR_LIMIT) { + return(CORD_substr_closure(x, i, n, CORD_index_access_fn)); + } else { + char * result = (char *)GC_MALLOC_ATOMIC(n + 1); + + if (result == 0) OUT_OF_MEMORY; + strncpy(result, x+i, n); + result[n] = '\0'; + return(result); + } + } else if (IS_CONCATENATION(x)) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + size_t left_len = LEFT_LEN(conc); + size_t right_len = conc -> len - left_len; + + if (i >= left_len) { + if (n == right_len) return(conc -> right); + return(CORD_substr_checked(conc -> right, i - left_len, n)); + } else if (i+n <= left_len) { + if (n == left_len) return(conc -> left); + return(CORD_substr_checked(conc -> left, i, n)); + } else { + /* Need at least one character from each side. */ + CORD left_part; + CORD right_part; + size_t left_part_len = left_len - i; + + if (i == 0) { + left_part = conc -> left; + } else { + left_part = CORD_substr_checked(conc -> left, i, left_part_len); + } + if (i + n == right_len + left_len) { + right_part = conc -> right; + } else { + right_part = CORD_substr_checked(conc -> right, 0, + n - left_part_len); + } + return(CORD_cat(left_part, right_part)); + } + } else /* function */ { + if (n > SUBSTR_LIMIT) { + if (IS_SUBSTR(x)) { + /* Avoid nesting substring nodes. */ + struct Function * f = &(((CordRep *)x) -> function); + struct substr_args *descr = + (struct substr_args *)(f -> client_data); + + return(CORD_substr_closure((CORD)descr->sa_cord, + i + descr->sa_index, + n, f -> fn)); + } else { + return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); + } + } else { + char * result; + struct Function * f = &(((CordRep *)x) -> function); + char buf[SUBSTR_LIMIT+1]; + char * p = buf; + size_t j; + size_t lim = i + n; + + for (j = i; j < lim; j++) { + char c = (*(f -> fn))(j, f -> client_data); + + if (c == '\0') { + return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); + } + *p++ = c; + } + result = (char *)GC_MALLOC_ATOMIC(n + 1); + if (result == 0) OUT_OF_MEMORY; + memcpy(result, buf, n); + result[n] = '\0'; + return(result); + } + } +} + +CORD CORD_substr(CORD x, size_t i, size_t n) +{ + size_t len = CORD_len(x); + + if (i >= len || n == 0) return(0); + if (i + n > len) n = len - i; + return(CORD_substr_checked(x, i, n)); +} + +/* See cord.h for definition. We assume i is in range. */ +int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, + CORD_batched_iter_fn f2, void * client_data) +{ + if (x == 0) return(0); + if (CORD_IS_STRING(x)) { + const char *p = x+i; + + if (*p == '\0') ABORT("2nd arg to CORD_iter5 too big"); + if (f2 != CORD_NO_FN) { + return((*f2)(p, client_data)); + } else { + while (*p) { + if ((*f1)(*p, client_data)) return(1); + p++; + } + return(0); + } + } else if (IS_CONCATENATION(x)) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + + if (i > 0) { + size_t left_len = LEFT_LEN(conc); + + if (i >= left_len) { + return(CORD_iter5(conc -> right, i - left_len, f1, f2, + client_data)); + } + } + if (CORD_iter5(conc -> left, i, f1, f2, client_data)) { + return(1); + } + return(CORD_iter5(conc -> right, 0, f1, f2, client_data)); + } else /* function */ { + struct Function * f = &(((CordRep *)x) -> function); + size_t j; + size_t lim = f -> len; + + for (j = i; j < lim; j++) { + if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { + return(1); + } + } + return(0); + } +} + +#undef CORD_iter +int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data) +{ + return(CORD_iter5(x, 0, f1, CORD_NO_FN, client_data)); +} + +int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data) +{ + if (x == 0) return(0); + if (CORD_IS_STRING(x)) { + const char *p = x + i; + + for(;;) { + char c = *p; + + if (c == '\0') ABORT("2nd arg to CORD_riter4 too big"); + if ((*f1)(c, client_data)) return(1); + if (p == x) break; + p--; + } + return(0); + } else if (IS_CONCATENATION(x)) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + CORD left_part = conc -> left; + size_t left_len = LEFT_LEN(conc); + + if (i >= left_len) { + if (CORD_riter4(conc -> right, i - left_len, f1, client_data)) { + return(1); + } + return(CORD_riter4(left_part, left_len - 1, f1, client_data)); + } else { + return(CORD_riter4(left_part, i, f1, client_data)); + } + } else /* function */ { + struct Function * f = &(((CordRep *)x) -> function); + size_t j; + + for (j = i; ; j--) { + if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { + return(1); + } + if (j == 0) return(0); + } + } +} + +int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data) +{ + size_t len = CORD_len(x); + if (len == 0) return(0); + return(CORD_riter4(x, len - 1, f1, 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 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. + * Crucial invariants: + * 1. The concatenation of the forest (in decreasing order) with the + * unscanned part of the rope is equal to the rope being balanced. + * 2. All trees in the forest are balanced. + * 3. forest[i] has depth at most i. + */ + +typedef struct { + CORD c; + size_t len; /* Actual length of c */ +} ForestElement; + +static size_t min_len [ MAX_DEPTH ]; + +static int min_len_init = 0; + +int CORD_max_len; + +typedef ForestElement Forest [ MAX_DEPTH ]; + /* forest[i].len >= fib(i+1) */ + /* The string is the concatenation */ + /* of the forest in order of DECREASING */ + /* indices. */ + +void CORD_init_min_len(void) +{ + int i; + size_t last, previous; + + min_len[0] = previous = 1; + min_len[1] = last = 2; + for (i = 2; i < MAX_DEPTH; i++) { + size_t current = last + previous; + + if (current < last) /* overflow */ current = last; + min_len[i] = current; + previous = last; + last = current; + } + CORD_max_len = (int)last - 1; + min_len_init = 1; +} + + +void CORD_init_forest(ForestElement * forest, size_t max_len) +{ + int i; + + for (i = 0; i < MAX_DEPTH; i++) { + forest[i].c = 0; + if (min_len[i] > max_len) return; + } + ABORT("Cord too long"); +} + +/* Add a leaf to the appropriate level in the forest, cleaning */ +/* out lower levels as necessary. */ +/* Also works if x is a balanced tree of concatenations; however */ +/* in this case an extra concatenation node may be inserted above x; */ +/* This node should not be counted in the statement of the invariants. */ +void CORD_add_forest(ForestElement * forest, CORD x, size_t len) +{ + int i = 0; + CORD sum = CORD_EMPTY; + size_t sum_len = 0; + + while (len > min_len[i + 1]) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + forest[i].c = 0; + } + i++; + } + /* Sum has depth at most 1 greter than what would be required */ + /* for balance. */ + sum = CORD_cat(sum, x); + sum_len += len; + /* If x was a leaf, then sum is now balanced. To see this */ + /* consider the two cases in which forest[i-1] either is or is */ + /* not empty. */ + while (sum_len >= min_len[i]) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + /* This is again balanced, since sum was balanced, and has */ + /* allowable depth that differs from i by at most 1. */ + forest[i].c = 0; + } + i++; + } + i--; + forest[i].c = sum; + forest[i].len = sum_len; +} + +CORD CORD_concat_forest(ForestElement * forest, size_t expected_len) +{ + int i = 0; + CORD sum = 0; + size_t sum_len = 0; + + while (sum_len != expected_len) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + } + i++; + } + return(sum); +} + +/* Insert the frontier of x into forest. Balanced subtrees are */ +/* treated as leaves. This potentially adds one to the depth */ +/* of the final tree. */ +void CORD_balance_insert(CORD x, size_t len, ForestElement * forest) +{ + int depth; + + if (CORD_IS_STRING(x)) { + CORD_add_forest(forest, x, len); + } else if (IS_CONCATENATION(x) + && ((depth = DEPTH(x)) >= MAX_DEPTH + || len < min_len[depth])) { + struct Concatenation * conc = &(((CordRep *)x) -> concatenation); + size_t left_len = LEFT_LEN(conc); + + CORD_balance_insert(conc -> left, left_len, forest); + CORD_balance_insert(conc -> right, len - left_len, forest); + } else /* function or balanced */ { + CORD_add_forest(forest, x, len); + } +} + + +CORD CORD_balance(CORD x) +{ + Forest forest; + size_t len; + + if (x == 0) return(0); + if (CORD_IS_STRING(x)) return(x); + if (!min_len_init) CORD_init_min_len(); + len = LEN(x); + CORD_init_forest(forest, len); + CORD_balance_insert(x, len, forest); + return(CORD_concat_forest(forest, len)); +} + + +/* Position primitives */ + +/* Private routines to deal with the hard cases only: */ + +/* P contains a prefix of the path to cur_pos. Extend it to a full */ +/* path and set up leaf info. */ +/* Return 0 if past the end of cord, 1 o.w. */ +void CORD__extend_path(CORD_pos p) +{ + struct CORD_pe * current_pe = &(p[0].path[p[0].path_len]); + CORD top = current_pe -> pe_cord; + size_t pos = p[0].cur_pos; + size_t top_pos = current_pe -> pe_start_pos; + size_t top_len = GEN_LEN(top); + + /* Fill in the rest of the path. */ + while(!CORD_IS_STRING(top) && IS_CONCATENATION(top)) { + struct Concatenation * conc = &(((CordRep *)top) -> concatenation); + size_t left_len; + + left_len = LEFT_LEN(conc); + current_pe++; + if (pos >= top_pos + left_len) { + current_pe -> pe_cord = top = conc -> right; + current_pe -> pe_start_pos = top_pos = top_pos + left_len; + top_len -= left_len; + } else { + current_pe -> pe_cord = top = conc -> left; + current_pe -> pe_start_pos = top_pos; + top_len = left_len; + } + p[0].path_len++; + } + /* Fill in leaf description for fast access. */ + if (CORD_IS_STRING(top)) { + p[0].cur_leaf = top; + p[0].cur_start = top_pos; + p[0].cur_end = top_pos + top_len; + } else { + p[0].cur_end = 0; + } + if (pos >= top_pos + top_len) p[0].path_len = CORD_POS_INVALID; +} + +char CORD__pos_fetch(CORD_pos p) +{ + /* Leaf is a function node */ + struct CORD_pe * pe; + CORD leaf; + struct Function * f; + + if (!CORD_pos_valid(p)) + ABORT("CORD_pos_fetch: invalid argument"); + pe = &p[0].path[p[0].path_len]; + leaf = pe -> pe_cord; + if (!IS_FUNCTION(leaf)) + ABORT("CORD_pos_fetch: bad leaf"); + f = &((CordRep *)leaf)->function; + return ((*(f -> fn))(p[0].cur_pos - pe -> pe_start_pos, f -> client_data)); +} + +void CORD__next(CORD_pos p) +{ + size_t cur_pos = p[0].cur_pos + 1; + struct CORD_pe * current_pe; + CORD leaf; + + if (!CORD_pos_valid(p)) + ABORT("CORD_next: invalid argument"); + current_pe = &p[0].path[p[0].path_len]; + leaf = current_pe -> pe_cord; + + /* Leaf is not a string or we're at end of leaf */ + p[0].cur_pos = cur_pos; + if (!CORD_IS_STRING(leaf)) { + /* Function leaf */ + struct Function * f = &(((CordRep *)leaf) -> function); + size_t start_pos = current_pe -> pe_start_pos; + size_t end_pos = start_pos + f -> len; + + if (cur_pos < end_pos) { + /* Fill cache and return. */ + size_t i; + size_t limit = FUNCTION_BUF_SZ; + CORD_fn fn = f -> fn; + void * client_data = f -> client_data; + + if (end_pos - cur_pos < FUNCTION_BUF_SZ) { + limit = end_pos - cur_pos; + } + for (i = 0; i < limit; i++) { + p[0].function_buf[i] = (*fn)(i + cur_pos - start_pos, + client_data); + } + p[0].cur_start = cur_pos; + p[0].cur_leaf = p[0].function_buf; + p[0].cur_end = cur_pos + limit; + return; + } + } + /* End of leaf */ + /* Pop the stack until we find two concatenation nodes with the */ + /* same start position: this implies we were in left part. */ + { + while (p[0].path_len > 0 + && current_pe[0].pe_start_pos != current_pe[-1].pe_start_pos) { + p[0].path_len--; + current_pe--; + } + if (p[0].path_len == 0) { + p[0].path_len = CORD_POS_INVALID; + return; + } + } + p[0].path_len--; + CORD__extend_path(p); +} + +void CORD__prev(CORD_pos p) +{ + struct CORD_pe * pe = &(p[0].path[p[0].path_len]); + + if (p[0].cur_pos == 0) { + p[0].path_len = CORD_POS_INVALID; + return; + } + p[0].cur_pos--; + if (p[0].cur_pos >= pe -> pe_start_pos) return; + + /* Beginning of leaf */ + + /* Pop the stack until we find two concatenation nodes with the */ + /* different start position: this implies we were in right part. */ + { + struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); + + while (p[0].path_len > 0 + && current_pe[0].pe_start_pos == current_pe[-1].pe_start_pos) { + p[0].path_len--; + current_pe--; + } + } + p[0].path_len--; + CORD__extend_path(p); +} + +#undef CORD_pos_fetch +#undef CORD_next +#undef CORD_prev +#undef CORD_pos_to_index +#undef CORD_pos_to_cord +#undef CORD_pos_valid + +char CORD_pos_fetch(CORD_pos p) +{ + if (p[0].cur_end != 0) { + return(p[0].cur_leaf[p[0].cur_pos - p[0].cur_start]); + } else { + return(CORD__pos_fetch(p)); + } +} + +void CORD_next(CORD_pos p) +{ + if (p[0].cur_pos + 1 < p[0].cur_end) { + p[0].cur_pos++; + } else { + CORD__next(p); + } +} + +void CORD_prev(CORD_pos p) +{ + if (p[0].cur_end != 0 && p[0].cur_pos > p[0].cur_start) { + p[0].cur_pos--; + } else { + CORD__prev(p); + } +} + +size_t CORD_pos_to_index(CORD_pos p) +{ + return(p[0].cur_pos); +} + +CORD CORD_pos_to_cord(CORD_pos p) +{ + return(p[0].path[0].pe_cord); +} + +int CORD_pos_valid(CORD_pos p) +{ + return(p[0].path_len != CORD_POS_INVALID); +} + +void CORD_set_pos(CORD_pos p, CORD x, size_t i) +{ + if (x == CORD_EMPTY) { + p[0].path_len = CORD_POS_INVALID; + return; + } + p[0].path[0].pe_cord = x; + p[0].path[0].pe_start_pos = 0; + p[0].path_len = 0; + p[0].cur_pos = i; + CORD__extend_path(p); +} diff --git a/bdwgc/cord/cordprnt.c b/bdwgc/cord/cordprnt.c new file mode 100644 index 000000000..048b972d4 --- /dev/null +++ b/bdwgc/cord/cordprnt.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/* An sprintf implementation that understands cords. This is probably */ +/* not terribly portable. It assumes an ANSI stdarg.h. It further */ +/* assumes that I can make copies of va_list variables, and read */ +/* arguments repeatedly by applying va_arg to the copies. This */ +/* could be avoided at some performance cost. */ +/* We also assume that unsigned and signed integers of various kinds */ +/* have the same sizes, and can be cast back and forth. */ +/* We assume that void * and char * have the same size. */ +/* All this cruft is needed because we want to rely on the underlying */ +/* sprintf implementation whenever possible. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifndef CORD_BUILD +# define CORD_BUILD +#endif + +#include "cord.h" +#include "ec.h" + +#include +#include +#include +#include + +#include "gc.h" + +#define CONV_SPEC_LEN 50 /* Maximum length of a single */ + /* conversion specification. */ +#define CONV_RESULT_LEN 50 /* Maximum length of any */ + /* conversion with default */ + /* width and prec. */ +#if defined(CPPCHECK) +# define MACRO_BLKSTMT_BEGIN { +# define MACRO_BLKSTMT_END } +#else +# define MACRO_BLKSTMT_BEGIN do { +# define MACRO_BLKSTMT_END } while (0) +#endif + +#define OUT_OF_MEMORY MACRO_BLKSTMT_BEGIN \ + if (CORD_oom_fn != 0) (*CORD_oom_fn)(); \ + fprintf(stderr, "Out of memory\n"); \ + abort(); \ + MACRO_BLKSTMT_END + +static int ec_len(CORD_ec x) +{ + return (int)(CORD_len(x[0].ec_cord) + (x[0].ec_bufptr - x[0].ec_buf)); +} + +/* Possible non-numeric precision values. */ +# define NONE -1 +# define VARIABLE -2 +/* Copy the conversion specification from CORD_pos into the buffer buf */ +/* Return negative on error. */ +/* Source initially points one past the leading %. */ +/* It is left pointing at the conversion type. */ +/* Assign field width and precision to *width and *prec. */ +/* If width or prec is *, VARIABLE is assigned. */ +/* Set *left to 1 if left adjustment flag is present. */ +/* Set *long_arg to 1 if long flag ('l' or 'L') is present, or to */ +/* -1 if 'h' is present, or to 2 if 'z' is present. */ +static int extract_conv_spec(CORD_pos source, char *buf, + int * width, int *prec, int *left, int * long_arg) +{ + int result = 0; + int current_number = 0; + int saw_period = 0; + int saw_number = 0; + int chars_so_far = 0; + char current; + + *width = NONE; + buf[chars_so_far++] = '%'; + while(CORD_pos_valid(source)) { + if (chars_so_far >= CONV_SPEC_LEN) return(-1); + current = CORD_pos_fetch(source); + buf[chars_so_far++] = current; + switch(current) { + case '*': + saw_number = 1; + current_number = VARIABLE; + break; + case '0': + if (!saw_number) { + /* Zero fill flag; ignore */ + break; + } + current_number *= 10; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + saw_number = 1; + current_number *= 10; + current_number += current - '0'; + break; + case '.': + saw_period = 1; + if(saw_number) { + *width = current_number; + saw_number = 0; + } + current_number = 0; + break; + case 'l': + case 'L': + *long_arg = 1; + current_number = 0; + break; + case 'z': + *long_arg = 2; + current_number = 0; + break; + case 'h': + *long_arg = -1; + current_number = 0; + break; + case ' ': + case '+': + case '#': + current_number = 0; + break; + case '-': + *left = 1; + current_number = 0; + break; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + case 'c': + case 'C': + case 's': + case 'S': + case 'p': + case 'n': + case 'r': + goto done; + default: + return(-1); + } + CORD_next(source); + } + return(-1); + done: + if (saw_number) { + if (saw_period) { + *prec = current_number; + } else { + *prec = NONE; + *width = current_number; + } + } else { + *prec = NONE; + } + buf[chars_so_far] = '\0'; + return(result); +} + +#if defined(__DJGPP__) || defined(__STRICT_ANSI__) + /* vsnprintf is missing in DJGPP (v2.0.3) */ +# define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args) +#elif defined(_MSC_VER) +# if defined(_WIN32_WCE) + /* _vsnprintf is deprecated in WinCE */ +# define GC_VSNPRINTF StringCchVPrintfA +# else +# define GC_VSNPRINTF _vsnprintf +# endif +#else +# define GC_VSNPRINTF vsnprintf +#endif + +int CORD_vsprintf(CORD * out, CORD format, va_list args) +{ + CORD_ec result; + int count; + char current; + CORD_pos pos; + char conv_spec[CONV_SPEC_LEN + 1]; + + CORD_ec_init(result); + for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) { + current = CORD_pos_fetch(pos); + if (current == '%') { + CORD_next(pos); + if (!CORD_pos_valid(pos)) return(-1); + current = CORD_pos_fetch(pos); + if (current == '%') { + CORD_ec_append(result, current); + } else { + int width, prec; + int left_adj = 0; + int long_arg = 0; + CORD arg; + size_t len; + + if (extract_conv_spec(pos, conv_spec, + &width, &prec, + &left_adj, &long_arg) < 0) { + return(-1); + } + current = CORD_pos_fetch(pos); + switch(current) { + case 'n': + /* Assign length to next arg */ + if (long_arg == 0) { + int * pos_ptr; + pos_ptr = va_arg(args, int *); + *pos_ptr = ec_len(result); + } else if (long_arg == 2) { + size_t * pos_ptr = va_arg(args, size_t *); + *pos_ptr = (size_t)(unsigned)ec_len(result); + } else if (long_arg > 0) { + long * pos_ptr; + pos_ptr = va_arg(args, long *); + *pos_ptr = ec_len(result); + } else { + short * pos_ptr; + pos_ptr = va_arg(args, short *); + *pos_ptr = (short)ec_len(result); + } + goto done; + case 'r': + /* Append cord and any padding */ + if (width == VARIABLE) width = va_arg(args, int); + if (prec == VARIABLE) prec = va_arg(args, int); + arg = va_arg(args, CORD); + len = CORD_len(arg); + if (prec != NONE && len > (unsigned)prec) { + if (prec < 0) return(-1); + arg = CORD_substr(arg, 0, (unsigned)prec); + len = (unsigned)prec; + } + if (width != NONE && len < (unsigned)width) { + char * blanks = (char *)GC_MALLOC_ATOMIC( + (unsigned)width - len + 1); + + if (NULL == blanks) OUT_OF_MEMORY; + memset(blanks, ' ', (unsigned)width - len); + blanks[(unsigned)width - len] = '\0'; + if (left_adj) { + arg = CORD_cat(arg, blanks); + } else { + arg = CORD_cat(blanks, arg); + } + } + CORD_ec_append_cord(result, arg); + goto done; + case 'c': + if (width == NONE && prec == NONE) { + char c; + + c = (char)va_arg(args, int); + CORD_ec_append(result, c); + goto done; + } + break; + case 's': + if (width == NONE && prec == NONE) { + char * str = va_arg(args, char *); + char c; + + while ((c = *str++) != '\0') { + CORD_ec_append(result, c); + } + goto done; + } + break; + default: + break; + } + /* Use standard sprintf to perform conversion */ + { + char * buf; + va_list vsprintf_args; + int max_size = 0; + int res = 0; + +# if defined(CPPCHECK) + va_copy(vsprintf_args, args); +# elif defined(__va_copy) + __va_copy(vsprintf_args, args); +# elif defined(__GNUC__) && !defined(__DJGPP__) \ + && !defined(__EMX__) /* and probably in other cases */ + va_copy(vsprintf_args, args); +# else + vsprintf_args = args; +# endif + if (width == VARIABLE) width = va_arg(args, int); + if (prec == VARIABLE) prec = va_arg(args, int); + if (width != NONE) max_size = width; + if (prec != NONE && prec > max_size) max_size = prec; + max_size += CONV_RESULT_LEN; + if (max_size >= CORD_BUFSZ) { + buf = (char *)GC_MALLOC_ATOMIC((unsigned)max_size + 1); + if (NULL == buf) OUT_OF_MEMORY; + } else { + if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf) + < max_size) { + CORD_ec_flush_buf(result); + } + buf = result[0].ec_bufptr; + } + switch(current) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'c': + if (long_arg <= 0) { + (void) va_arg(args, int); + } else if (long_arg == 2) { + (void) va_arg(args, size_t); + } else /* long_arg == 1 */ { + (void) va_arg(args, long); + } + break; + case 's': + case 'p': + (void) va_arg(args, char *); + break; + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + (void) va_arg(args, double); + break; + default: + res = -1; + } + if (0 == res) + res = GC_VSNPRINTF(buf, max_size + 1, conv_spec, + vsprintf_args); +# if defined(CPPCHECK) || defined(__va_copy) \ + || (defined(__GNUC__) && !defined(__DJGPP__) \ + && !defined(__EMX__)) + va_end(vsprintf_args); +# endif + len = (unsigned)res; + if ((char *)(GC_word)res == buf) { + /* old style vsprintf */ + len = strlen(buf); + } else if (res < 0) { + return(-1); + } + if (buf != result[0].ec_bufptr) { + char c; + + while ((c = *buf++) != '\0') { + CORD_ec_append(result, c); + } + } else { + result[0].ec_bufptr = buf + len; + } + } + done:; + } + } else { + CORD_ec_append(result, current); + } + } + count = ec_len(result); + *out = CORD_balance(CORD_ec_to_cord(result)); + return(count); +} + +int CORD_sprintf(CORD * out, CORD format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = CORD_vsprintf(out, format, args); + va_end(args); + return(result); +} + +int CORD_fprintf(FILE * f, CORD format, ...) +{ + va_list args; + int result; + CORD out = CORD_EMPTY; /* initialized to prevent compiler warning */ + + va_start(args, format); + result = CORD_vsprintf(&out, format, args); + va_end(args); + if (result > 0) CORD_put(out, f); + return(result); +} + +int CORD_vfprintf(FILE * f, CORD format, va_list args) +{ + int result; + CORD out = CORD_EMPTY; + + result = CORD_vsprintf(&out, format, args); + if (result > 0) CORD_put(out, f); + return(result); +} + +int CORD_printf(CORD format, ...) +{ + va_list args; + int result; + CORD out = CORD_EMPTY; + + va_start(args, format); + result = CORD_vsprintf(&out, format, args); + va_end(args); + if (result > 0) CORD_put(out, stdout); + return(result); +} + +int CORD_vprintf(CORD format, va_list args) +{ + int result; + CORD out = CORD_EMPTY; + + result = CORD_vsprintf(&out, format, args); + if (result > 0) CORD_put(out, stdout); + return(result); +} diff --git a/bdwgc/cord/cordxtra.c b/bdwgc/cord/cordxtra.c new file mode 100644 index 000000000..bed55eb26 --- /dev/null +++ b/bdwgc/cord/cordxtra.c @@ -0,0 +1,643 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/* + * These are functions on cords that do not need to understand their + * implementation. They serve also serve as example client code for + * cord_basics. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifndef CORD_BUILD +# define CORD_BUILD +#endif + +# include +# include +# include +# include + +# include "cord.h" +# include "ec.h" + +# define I_HIDE_POINTERS /* So we get access to allocation lock. */ + /* We use this for lazy file reading, */ + /* so that we remain independent */ + /* of the threads primitives. */ +# include "gc.h" + +/* For now we assume that pointer reads and writes are atomic, */ +/* i.e. another thread always sees the state before or after */ +/* a write. This might be false on a Motorola M68K with */ +/* pointers that are not 32-bit aligned. But there probably */ +/* aren't too many threads packages running on those. */ +# define ATOMIC_WRITE(x,y) (x) = (y) +# define ATOMIC_READ(x) (*(x)) + +/* The standard says these are in stdio.h, but they aren't always: */ +# ifndef SEEK_SET +# define SEEK_SET 0 +# endif +# ifndef SEEK_END +# define SEEK_END 2 +# endif + +# define BUFSZ 2048 /* Size of stack allocated buffers when */ + /* we want large buffers. */ + +typedef void (* oom_fn)(void); + +# define OUT_OF_MEMORY { if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ + ABORT("Out of memory"); } +# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } + +#if GC_GNUC_PREREQ(3, 4) +# define CORD_ATTR_UNUSED __attribute__((__unused__)) +#else +# define CORD_ATTR_UNUSED /* empty */ +#endif + +CORD CORD_cat_char(CORD x, char c) +{ + char * string; + + if (c == '\0') return(CORD_cat(x, CORD_nul(1))); + string = (char *)GC_MALLOC_ATOMIC(2); + if (string == 0) OUT_OF_MEMORY; + string[0] = c; + string[1] = '\0'; + return(CORD_cat_char_star(x, string, 1)); +} + +CORD CORD_catn(int nargs, ...) +{ + CORD result = CORD_EMPTY; + va_list args; + int i; + + va_start(args, nargs); + for (i = 0; i < nargs; i++) { + CORD next = va_arg(args, CORD); + result = CORD_cat(result, next); + } + va_end(args); + return(result); +} + +typedef struct { + size_t len; + size_t count; + char * buf; +} CORD_fill_data; + +int CORD_fill_proc(char c, void * client_data) +{ + CORD_fill_data * d = (CORD_fill_data *)client_data; + size_t count = d -> count; + + (d -> buf)[count] = c; + d -> count = ++count; + if (count >= d -> len) { + return(1); + } else { + return(0); + } +} + +int CORD_batched_fill_proc(const char * s, void * client_data) +{ + CORD_fill_data * d = (CORD_fill_data *)client_data; + size_t count = d -> count; + size_t max = d -> len; + char * buf = d -> buf; + const char * t = s; + + while((buf[count] = *t++) != '\0') { + count++; + if (count >= max) { + d -> count = count; + return(1); + } + } + d -> count = count; + return(0); +} + +/* Fill buf with len characters starting at i. */ +/* Assumes len characters are available in buf. */ +/* Return 1 if buf is filled fully (and len is */ +/* non-zero), 0 otherwise. */ +int CORD_fill_buf(CORD x, size_t i, size_t len, char * buf) +{ + CORD_fill_data fd; + + fd.len = len; + fd.buf = buf; + fd.count = 0; + return CORD_iter5(x, i, CORD_fill_proc, CORD_batched_fill_proc, &fd); +} + +int CORD_cmp(CORD x, CORD y) +{ + CORD_pos xpos; + CORD_pos ypos; + + if (y == CORD_EMPTY) return(x != CORD_EMPTY); + if (x == CORD_EMPTY) return(-1); + if (CORD_IS_STRING(y) && CORD_IS_STRING(x)) return(strcmp(x,y)); + CORD_set_pos(xpos, x, 0); + CORD_set_pos(ypos, y, 0); + for(;;) { + size_t avail, yavail; + + if (!CORD_pos_valid(xpos)) { + if (CORD_pos_valid(ypos)) { + return(-1); + } else { + return(0); + } + } + if (!CORD_pos_valid(ypos)) { + return(1); + } + avail = CORD_pos_chars_left(xpos); + if (avail == 0 + || (yavail = CORD_pos_chars_left(ypos)) == 0) { + char xcurrent = CORD_pos_fetch(xpos); + char ycurrent = CORD_pos_fetch(ypos); + if (xcurrent != ycurrent) return(xcurrent - ycurrent); + CORD_next(xpos); + CORD_next(ypos); + } else { + /* process as many characters as we can */ + int result; + + if (avail > yavail) avail = yavail; + result = strncmp(CORD_pos_cur_char_addr(xpos), + CORD_pos_cur_char_addr(ypos), avail); + if (result != 0) return(result); + CORD_pos_advance(xpos, avail); + CORD_pos_advance(ypos, avail); + } + } +} + +int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len) +{ + CORD_pos xpos; + CORD_pos ypos; + size_t count; + + CORD_set_pos(xpos, x, x_start); + CORD_set_pos(ypos, y, y_start); + for(count = 0; count < len;) { + long avail, yavail; + + if (!CORD_pos_valid(xpos)) { + if (CORD_pos_valid(ypos)) { + return(-1); + } else { + return(0); + } + } + if (!CORD_pos_valid(ypos)) { + return(1); + } + if ((avail = CORD_pos_chars_left(xpos)) <= 0 + || (yavail = CORD_pos_chars_left(ypos)) <= 0) { + char xcurrent = CORD_pos_fetch(xpos); + char ycurrent = CORD_pos_fetch(ypos); + + if (xcurrent != ycurrent) return(xcurrent - ycurrent); + CORD_next(xpos); + CORD_next(ypos); + count++; + } else { + /* process as many characters as we can */ + int result; + + if (avail > yavail) avail = yavail; + count += avail; + if (count > len) + avail -= (long)(count - len); + result = strncmp(CORD_pos_cur_char_addr(xpos), + CORD_pos_cur_char_addr(ypos), (size_t)avail); + if (result != 0) return(result); + CORD_pos_advance(xpos, (size_t)avail); + CORD_pos_advance(ypos, (size_t)avail); + } + } + return(0); +} + +char * CORD_to_char_star(CORD x) +{ + size_t len = CORD_len(x); + char * result = (char *)GC_MALLOC_ATOMIC(len + 1); + + if (result == 0) OUT_OF_MEMORY; + if (len > 0 && CORD_fill_buf(x, 0, len, result) != 1) + ABORT("CORD_fill_buf malfunction"); + result[len] = '\0'; + return(result); +} + +CORD CORD_from_char_star(const char *s) +{ + char * result; + size_t len = strlen(s); + + if (0 == len) return(CORD_EMPTY); + result = (char *)GC_MALLOC_ATOMIC(len + 1); + if (result == 0) OUT_OF_MEMORY; + memcpy(result, s, len+1); + return(result); +} + +const char * CORD_to_const_char_star(CORD x) +{ + if (x == 0) return(""); + if (CORD_IS_STRING(x)) return((const char *)x); + return(CORD_to_char_star(x)); +} + +char CORD_fetch(CORD x, size_t i) +{ + CORD_pos xpos; + + CORD_set_pos(xpos, x, i); + if (!CORD_pos_valid(xpos)) ABORT("bad index?"); + return(CORD_pos_fetch(xpos)); +} + + +int CORD_put_proc(char c, void * client_data) +{ + FILE * f = (FILE *)client_data; + + return(putc(c, f) == EOF); +} + +int CORD_batched_put_proc(const char * s, void * client_data) +{ + FILE * f = (FILE *)client_data; + + return(fputs(s, f) == EOF); +} + + +int CORD_put(CORD x, FILE * f) +{ + if (CORD_iter5(x, 0, CORD_put_proc, CORD_batched_put_proc, f)) { + return(EOF); + } else { + return(1); + } +} + +typedef struct { + size_t pos; /* Current position in the cord */ + char target; /* Character we're looking for */ +} chr_data; + +int CORD_chr_proc(char c, void * client_data) +{ + chr_data * d = (chr_data *)client_data; + + if (c == d -> target) return(1); + (d -> pos) ++; + return(0); +} + +int CORD_rchr_proc(char c, void * client_data) +{ + chr_data * d = (chr_data *)client_data; + + if (c == d -> target) return(1); + (d -> pos) --; + return(0); +} + +int CORD_batched_chr_proc(const char *s, void * client_data) +{ + chr_data * d = (chr_data *)client_data; + const char * occ = strchr(s, d -> target); + + if (NULL == occ) { + d -> pos += strlen(s); + return(0); + } else { + d -> pos += occ - s; + return(1); + } +} + +size_t CORD_chr(CORD x, size_t i, int c) +{ + chr_data d; + + d.pos = i; + d.target = (char)c; + if (CORD_iter5(x, i, CORD_chr_proc, CORD_batched_chr_proc, &d)) { + return(d.pos); + } else { + return(CORD_NOT_FOUND); + } +} + +size_t CORD_rchr(CORD x, size_t i, int c) +{ + chr_data d; + + d.pos = i; + d.target = (char)c; + if (CORD_riter4(x, i, CORD_rchr_proc, &d)) { + return(d.pos); + } else { + return(CORD_NOT_FOUND); + } +} + +/* 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. */ +/* This has the advantage that we allocate very little, or not at all. */ +/* It's very fast if there are few close misses. */ +size_t CORD_str(CORD x, size_t start, CORD s) +{ + CORD_pos xpos; + size_t xlen = CORD_len(x); + size_t slen; + 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. */ + /* Initialized only to make compilers */ + /* happy. */ + unsigned long mask = 0; + size_t i; + size_t match_pos; + + if (s == CORD_EMPTY) return(start); + if (CORD_IS_STRING(s)) { + s_start = s; + slen = strlen(s); + } else { + s_start = CORD_to_char_star(CORD_substr(s, 0, sizeof(unsigned long))); + slen = CORD_len(s); + } + if (xlen < start || xlen - start < slen) return(CORD_NOT_FOUND); + start_len = slen; + if (start_len > sizeof(unsigned long)) start_len = sizeof(unsigned long); + CORD_set_pos(xpos, x, start); + for (i = 0; i < start_len; i++) { + mask <<= 8; + mask |= 0xff; + s_buf <<= 8; + s_buf |= (unsigned char)s_start[i]; + x_buf <<= 8; + x_buf |= (unsigned char)CORD_pos_fetch(xpos); + CORD_next(xpos); + } + for (match_pos = start; ; match_pos++) { + if ((x_buf & mask) == s_buf) { + if (slen == start_len || + CORD_ncmp(x, match_pos + start_len, + s, start_len, slen - start_len) == 0) { + return(match_pos); + } + } + if ( match_pos == xlen - slen ) { + return(CORD_NOT_FOUND); + } + x_buf <<= 8; + x_buf |= (unsigned char)CORD_pos_fetch(xpos); + CORD_next(xpos); + } +} + +void CORD_ec_flush_buf(CORD_ec x) +{ + size_t len = x[0].ec_bufptr - x[0].ec_buf; + char * s; + + if (len == 0) return; + s = (char *)GC_MALLOC_ATOMIC(len + 1); + if (NULL == s) OUT_OF_MEMORY; + memcpy(s, x[0].ec_buf, len); + s[len] = '\0'; + x[0].ec_cord = CORD_cat_char_star(x[0].ec_cord, s, len); + x[0].ec_bufptr = x[0].ec_buf; +} + +void CORD_ec_append_cord(CORD_ec x, CORD s) +{ + CORD_ec_flush_buf(x); + x[0].ec_cord = CORD_cat(x[0].ec_cord, s); +} + +char CORD_nul_func(size_t i CORD_ATTR_UNUSED, void * client_data) +{ + return (char)(GC_word)client_data; +} + +CORD CORD_chars(char c, size_t i) +{ + return CORD_from_fn(CORD_nul_func, (void *)(GC_word)(unsigned char)c, i); +} + +CORD CORD_from_file_eager(FILE * f) +{ + CORD_ec ecord; + + CORD_ec_init(ecord); + for(;;) { + int c = getc(f); + + if (c == 0) { + /* Append the right number of NULs */ + /* Note that any string of NULs is represented in 4 words, */ + /* independent of its length. */ + size_t count = 1; + + CORD_ec_flush_buf(ecord); + while ((c = getc(f)) == 0) count++; + ecord[0].ec_cord = CORD_cat(ecord[0].ec_cord, CORD_nul(count)); + } + if (c == EOF) break; + CORD_ec_append(ecord, (char)c); + } + (void) fclose(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. */ + +# define LOG_CACHE_SZ 14 +# define CACHE_SZ (1 << LOG_CACHE_SZ) +# define LOG_LINE_SZ 9 +# define LINE_SZ (1 << LOG_LINE_SZ) + +typedef struct { + size_t tag; + char data[LINE_SZ]; + /* data[i%LINE_SZ] = ith char in file if tag = i/LINE_SZ */ +} cache_line; + +typedef struct { + FILE * lf_file; + size_t lf_current; /* Current file pointer value */ + cache_line * volatile lf_cache[CACHE_SZ/LINE_SZ]; +} lf_state; + +# define MOD_CACHE_SZ(n) ((n) & (CACHE_SZ - 1)) +# define DIV_CACHE_SZ(n) ((n) >> LOG_CACHE_SZ) +# define MOD_LINE_SZ(n) ((n) & (LINE_SZ - 1)) +# define DIV_LINE_SZ(n) ((n) >> LOG_LINE_SZ) +# define LINE_START(n) ((n) & ~(LINE_SZ - 1)) + +typedef struct { + lf_state * state; + size_t file_pos; /* Position of needed character. */ + cache_line * new_cache; +} refill_data; + +/* Executed with allocation lock. */ +static void * GC_CALLBACK refill_cache(void * client_data) +{ + lf_state * state = ((refill_data *)client_data) -> state; + size_t file_pos = ((refill_data *)client_data) -> file_pos; + FILE *f = state -> lf_file; + size_t line_start = LINE_START(file_pos); + size_t line_no = DIV_LINE_SZ(MOD_CACHE_SZ(file_pos)); + cache_line * new_cache = ((refill_data *)client_data) -> new_cache; + + if (line_start != state -> lf_current + && fseek(f, (long)line_start, SEEK_SET) != 0) { + ABORT("fseek failed"); + } + if (fread(new_cache -> data, sizeof(char), LINE_SZ, f) + <= file_pos - line_start) { + ABORT("fread failed"); + } + new_cache -> tag = DIV_LINE_SZ(file_pos); + /* Store barrier goes here. */ + ATOMIC_WRITE(state -> lf_cache[line_no], new_cache); + GC_END_STUBBORN_CHANGE((/* no volatile */ void *)(state -> lf_cache + + line_no)); + state -> lf_current = line_start + LINE_SZ; + return (void *)((GC_word)new_cache->data[MOD_LINE_SZ(file_pos)]); +} + +char CORD_lf_func(size_t i, void * client_data) +{ + lf_state * state = (lf_state *)client_data; + cache_line * volatile * cl_addr = + &(state -> lf_cache[DIV_LINE_SZ(MOD_CACHE_SZ(i))]); + cache_line * cl = (cache_line *)ATOMIC_READ(cl_addr); + + if (cl == 0 || cl -> tag != DIV_LINE_SZ(i)) { + /* Cache miss */ + refill_data rd; + + rd.state = state; + rd.file_pos = i; + rd.new_cache = GC_NEW_ATOMIC(cache_line); + if (rd.new_cache == 0) OUT_OF_MEMORY; + return (char)((GC_word)GC_call_with_alloc_lock(refill_cache, &rd)); + } + return(cl -> data[MOD_LINE_SZ(i)]); +} + +#ifndef GC_NO_FINALIZATION + void CORD_lf_close_proc(void * obj, void * client_data CORD_ATTR_UNUSED) + { + if (fclose(((lf_state *)obj) -> lf_file) != 0) { + ABORT("CORD_lf_close_proc: fclose failed"); + } + } +#endif + +CORD CORD_from_file_lazy_inner(FILE * f, size_t len) +{ + lf_state * state = GC_NEW(lf_state); + int i; + + if (state == 0) OUT_OF_MEMORY; + if (len != 0) { + /* Dummy read to force buffer allocation. */ + /* This greatly increases the probability */ + /* of avoiding deadlock if buffer allocation */ + /* is redirected to GC_malloc and the */ + /* world is multi-threaded. */ + char buf[1]; + + if (fread(buf, 1, 1, f) > 1 + || fseek(f, 0l, SEEK_SET) != 0) { + ABORT("Bad f argument or I/O failure"); + } + } + state -> lf_file = f; + for (i = 0; i < CACHE_SZ/LINE_SZ; i++) { + state -> lf_cache[i] = 0; + } + state -> lf_current = 0; +# ifndef GC_NO_FINALIZATION + GC_REGISTER_FINALIZER(state, CORD_lf_close_proc, 0, 0, 0); +# endif + return(CORD_from_fn(CORD_lf_func, state, len)); +} + +CORD CORD_from_file_lazy(FILE * f) +{ + long len; + + if (fseek(f, 0l, SEEK_END) != 0 + || (len = ftell(f)) < 0 + || fseek(f, 0l, SEEK_SET) != 0) { + ABORT("Bad f argument or I/O failure"); + } + return(CORD_from_file_lazy_inner(f, (size_t)len)); +} + +# define LAZY_THRESHOLD (128*1024 + 1) + +CORD CORD_from_file(FILE * f) +{ + long len; + + if (fseek(f, 0l, SEEK_END) != 0 + || (len = ftell(f)) < 0 + || fseek(f, 0l, SEEK_SET) != 0) { + ABORT("Bad f argument or I/O failure"); + } + if (len < LAZY_THRESHOLD) { + return(CORD_from_file_eager(f)); + } else { + return(CORD_from_file_lazy_inner(f, (size_t)len)); + } +} diff --git a/bdwgc/cord/tests/cordtest.c b/bdwgc/cord/tests/cordtest.c new file mode 100644 index 000000000..e81db5c0b --- /dev/null +++ b/bdwgc/cord/tests/cordtest.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +# include "gc.h" /* For GC_INIT() only */ +# include "cord.h" + +# include +# include +# include +# include + +/* This is a very incomplete test of the cord package. It knows about */ +/* a few internals of the package (e.g. when C strings are returned) */ +/* that real clients shouldn't rely on. */ + +# define ABORT(string) \ + { fprintf(stderr, "FAILED: %s\n", string); abort(); } + +#if defined(CPPCHECK) +# undef CORD_iter +# undef CORD_next +# undef CORD_pos_fetch +# undef CORD_pos_to_cord +# undef CORD_pos_to_index +# undef CORD_pos_valid +# undef CORD_prev +#endif + +int count; + +int test_fn(char c, void * client_data) +{ + if (client_data != (void *)(GC_word)13) + ABORT("bad client data"); + if (count < 64*1024+1) { + if ((count & 1) == 0) { + if (c != 'b') ABORT("bad char"); + } else { + if (c != 'a') ABORT("bad char"); + } + count++; + return(0); + } else { + if (c != 'c') ABORT("bad char"); + count++; + return(1); + } +} + +char id_cord_fn(size_t i, void * client_data) +{ + if (client_data != 0) ABORT("id_cord_fn: bad client data"); + return((char)i); +} + +void test_basics(void) +{ + CORD x = CORD_from_char_star("ab"); + size_t i; + CORD y; + CORD_pos p; + + x = CORD_cat(x,x); + if (x == CORD_EMPTY) ABORT("CORD_cat(x,x) returned empty cord"); + if (!CORD_IS_STRING(x)) ABORT("short cord should usually be a string"); + if (strcmp(x, "abab") != 0) ABORT("bad CORD_cat result"); + + for (i = 1; i < 16; i++) { + x = CORD_cat(x,x); + } + x = CORD_cat(x,"c"); + if (CORD_len(x) != 128*1024+1) ABORT("bad length"); + + count = 0; + if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, + (void *)(GC_word)13) == 0) { + ABORT("CORD_iter5 failed"); + } + if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); + + count = 0; + CORD_set_pos(p, x, 64*1024-1); + while(CORD_pos_valid(p)) { + (void)test_fn(CORD_pos_fetch(p), (void *)(GC_word)13); + CORD_next(p); + } + if (count != 64*1024 + 2) ABORT("Position based iteration failed"); + + y = CORD_substr(x, 1023, 5); + if (!y) ABORT("CORD_substr returned NULL"); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); + + y = CORD_substr(x, 1024, 8); + if (!y) ABORT("CORD_substr returned NULL"); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "abababab") != 0) ABORT("bad CORD_substr result"); + + y = CORD_substr(x, 128*1024-1, 8); + if (!y) ABORT("CORD_substr returned NULL"); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "bc") != 0) ABORT("bad CORD_substr result"); + + x = CORD_balance(x); + if (CORD_len(x) != 128*1024+1) ABORT("bad length"); + + count = 0; + if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, + (void *)(GC_word)13) == 0) { + ABORT("CORD_iter5 failed"); + } + if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); + + y = CORD_substr(x, 1023, 5); + if (!y) ABORT("CORD_substr returned NULL"); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); + y = CORD_from_fn(id_cord_fn, 0, 13); + i = 0; + CORD_set_pos(p, y, i); + while(CORD_pos_valid(p)) { + char c = CORD_pos_fetch(p); + + if ((size_t)(unsigned char)c != i) + ABORT("Traversal of function node failed"); + CORD_next(p); + i++; + } + if (i != 13) ABORT("Bad apparent length for function node"); +# if defined(CPPCHECK) + /* TODO: Actually test these functions. */ + CORD_prev(p); + (void)CORD_pos_to_cord(p); + (void)CORD_pos_to_index(p); + (void)CORD_iter(CORD_EMPTY, test_fn, NULL); + (void)CORD_riter(CORD_EMPTY, test_fn, NULL); + CORD_dump(y); +# endif +} + +void test_extras(void) +{ +# define FNAME1 "cordtst1.tmp" /* short name (8+3) for portability */ +# define FNAME2 "cordtst2.tmp" + int i; + CORD y = "abcdefghijklmnopqrstuvwxyz0123456789"; + CORD x = "{}"; + CORD u, w, z; + FILE *f; + FILE *f1a, *f1b, *f2; + + w = CORD_cat(CORD_cat(y,y),y); + z = CORD_catn(3,y,y,y); + if (CORD_cmp(w,z) != 0) ABORT("CORD_catn comparison wrong"); + for (i = 1; i < 100; i++) { + x = CORD_cat(x, y); + } + z = CORD_balance(x); + if (CORD_cmp(x,z) != 0) ABORT("balanced string comparison wrong"); + if (CORD_cmp(x,CORD_cat(z, CORD_nul(13))) >= 0) ABORT("comparison 2"); + if (CORD_cmp(CORD_cat(x, CORD_nul(13)), z) <= 0) ABORT("comparison 3"); + if (CORD_cmp(x,CORD_cat(z, "13")) >= 0) ABORT("comparison 4"); + if ((f = fopen(FNAME1, "w")) == 0) ABORT("open failed"); + if (CORD_put(z,f) == EOF) ABORT("CORD_put failed"); + if (fclose(f) == EOF) ABORT("fclose failed"); + f1a = fopen(FNAME1, "rb"); + if (!f1a) ABORT("Unable to open " FNAME1); + w = CORD_from_file(f1a); + if (CORD_len(w) != CORD_len(z)) ABORT("file length wrong"); + if (CORD_cmp(w,z) != 0) ABORT("file comparison wrong"); + if (CORD_cmp(CORD_substr(w, 50*36+2, 36), y) != 0) + ABORT("file substr wrong"); + f1b = fopen(FNAME1, "rb"); + if (!f1b) ABORT("2nd open failed: " FNAME1); + z = CORD_from_file_lazy(f1b); + if (CORD_cmp(w,z) != 0) ABORT("File conversions differ"); + if (CORD_chr(w, 0, '9') != 37) ABORT("CORD_chr failed 1"); + if (CORD_chr(w, 3, 'a') != 38) ABORT("CORD_chr failed 2"); + if (CORD_rchr(w, CORD_len(w) - 1, '}') != 1) ABORT("CORD_rchr failed"); + x = y; + for (i = 1; i < 14; i++) { + x = CORD_cat(x,x); + } + if ((f = fopen(FNAME2, "w")) == 0) ABORT("2nd open failed"); +# ifdef __DJGPP__ + /* FIXME: DJGPP workaround. Why does this help? */ + if (fflush(f) != 0) ABORT("fflush failed"); +# endif + if (CORD_put(x,f) == EOF) ABORT("CORD_put failed"); + if (fclose(f) == EOF) ABORT("fclose failed"); + f2 = fopen(FNAME2, "rb"); + if (!f2) ABORT("Unable to open " FNAME2); + w = CORD_from_file(f2); + if (CORD_len(w) != CORD_len(x)) ABORT("file length wrong"); + if (CORD_cmp(w,x) != 0) ABORT("file comparison wrong"); + if (CORD_cmp(CORD_substr(w, 1000*36, 36), y) != 0) + ABORT("file substr wrong"); + if (strcmp(CORD_to_char_star(CORD_substr(w, 1000*36, 36)), y) != 0) + ABORT("char * file substr wrong"); + u = CORD_substr(w, 1000*36, 2); + if (!u) ABORT("CORD_substr returned NULL"); + if (strcmp(u, "ab") != 0) + ABORT("short file substr wrong"); + if (CORD_str(x,1,"9a") != 35) ABORT("CORD_str failed 1"); + if (CORD_str(x,0,"9abcdefghijk") != 35) ABORT("CORD_str failed 2"); + 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(); +# ifndef GC_NO_FINALIZATION + GC_invoke_finalizers(); + /* Of course, this does not guarantee the files are closed. */ +# endif + if (remove(FNAME1) != 0) { + /* On some systems, e.g. OS2, this may fail if f1 is still open. */ + /* But we cannot call fclose as it might lead to double close. */ + fprintf(stderr, "WARNING: remove failed: " FNAME1 "\n"); + } + if (remove(FNAME2) != 0) { + fprintf(stderr, "WARNING: remove failed: " FNAME2 "\n"); + } +} + +int wrap_vprintf(CORD format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = CORD_vprintf(format, args); + va_end(args); + return result; +} + +int wrap_vfprintf(FILE * f, CORD format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = CORD_vfprintf(f, format, args); + va_end(args); + return result; +} + +#if defined(__DJGPP__) || defined(__STRICT_ANSI__) + /* snprintf is missing in DJGPP (v2.0.3) */ +#else +# if defined(_MSC_VER) +# if defined(_WIN32_WCE) + /* _snprintf is deprecated in WinCE */ +# define GC_SNPRINTF StringCchPrintfA +# else +# define GC_SNPRINTF _snprintf +# endif +# else +# define GC_SNPRINTF snprintf +# endif +#endif + +/* no static */ /* no const */ char *zu_format = (char*)"%zu"; + +void test_printf(void) +{ + CORD result; + char result2[200]; + long l = -1; + short s = (short)-1; + CORD x; + int res; + + if (CORD_sprintf(&result, "%7.2f%ln", 3.14159F, &l) != 7) + ABORT("CORD_sprintf failed 1"); + if (CORD_cmp(result, " 3.14") != 0) ABORT("CORD_sprintf goofed 1"); + if (l != 7) ABORT("CORD_sprintf goofed 2"); + if (CORD_sprintf(&result, "%-7.2s%hn%c%s", "abcd", &s, 'x', "yz") != 10) + ABORT("CORD_sprintf failed 2"); + if (CORD_cmp(result, "ab xyz") != 0) ABORT("CORD_sprintf goofed 3"); + if (s != 7) ABORT("CORD_sprintf goofed 4"); + x = "abcdefghij"; + x = CORD_cat(x,x); + x = CORD_cat(x,x); + x = CORD_cat(x,x); + if (CORD_sprintf(&result, "->%-120.78r!\n", x) != 124) + ABORT("CORD_sprintf failed 3"); +# ifdef GC_SNPRINTF + (void)GC_SNPRINTF(result2, sizeof(result2), "->%-120.78s!\n", + CORD_to_char_star(x)); +# else + (void)sprintf(result2, "->%-120.78s!\n", CORD_to_char_star(x)); +# endif + result2[sizeof(result2) - 1] = '\0'; + if (CORD_cmp(result, result2) != 0) ABORT("CORD_sprintf goofed 5"); + +# ifdef GC_SNPRINTF + /* Check whether "%zu" specifier is supported; pass the format */ + /* string via a variable to avoid a compiler warning if not. */ + res = GC_SNPRINTF(result2, sizeof(result2), zu_format, (size_t)0); +# else + res = sprintf(result2, zu_format, (size_t)0); +# endif + result2[sizeof(result2) - 1] = '\0'; + if (res == 1) /* is "%z" supported by printf? */ { + if (CORD_sprintf(&result, "%zu %zd 0x%0zx", + (size_t)123, (size_t)4567, (size_t)0x4abc) != 15) + ABORT("CORD_sprintf failed 5"); + if (CORD_cmp(result, "123 4567 0x4abc") != 0) + ABORT("CORD_sprintf goofed 5"); + } else { + (void)CORD_printf("printf lacks support of 'z' modifier\n"); + } + + /* TODO: Better test CORD_[v][f]printf. */ + (void)CORD_printf(CORD_EMPTY); + (void)wrap_vfprintf(stdout, CORD_EMPTY); + (void)wrap_vprintf(CORD_EMPTY); +} + +int main(void) +{ +# ifdef THINK_C + printf("cordtest:\n"); +# endif + GC_INIT(); +# ifndef NO_INCREMENTAL + GC_enable_incremental(); +# endif + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); + test_basics(); + test_extras(); + test_printf(); + CORD_fprintf(stdout, "SUCCEEDED\n"); + return(0); +} diff --git a/bdwgc/cord/tests/de.c b/bdwgc/cord/tests/de.c new file mode 100644 index 000000000..2bdaa8e60 --- /dev/null +++ b/bdwgc/cord/tests/de.c @@ -0,0 +1,640 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * 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 really simple-minded text editor based on cords. + * Things it does right: + * No size bounds. + * Unbounded undo. + * Shouldn't crash no matter what file you invoke it on (e.g. /vmunix) + * (Make sure /vmunix is not writable before you try this.) + * Scrolls horizontally. + * Things it does wrong: + * It doesn't handle tabs reasonably (use "expand" first). + * The command set is MUCH too small. + * The redisplay algorithm doesn't let curses do the scrolling. + * The rule for moving the window over the file is suboptimal. + */ + +#include +#include /* for exit() */ + +#include "gc.h" +#include "cord.h" + +#ifdef THINK_C +#define MACINTOSH +#endif +#include + +#if (defined(__BORLANDC__) || defined(__CYGWIN__) || defined(__MINGW32__) \ + || defined(__NT__) || defined(_WIN32)) && !defined(WIN32) + /* If this is DOS or win16, we'll fail anyway. */ +# define WIN32 +#endif + +#if defined(WIN32) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +# include "de_win.h" +#elif defined(MACINTOSH) +# include +/* curses emulation. */ +# define initscr() +# define endwin() +# define nonl() +# define noecho() csetmode(C_NOECHO, stdout) +# define cbreak() csetmode(C_CBREAK, stdout) +# define refresh() +# define addch(c) putchar(c) +# define standout() cinverse(1, stdout) +# define standend() cinverse(0, stdout) +# define move(line,col) cgotoxy(col + 1, line + 1, stdout) +# define clrtoeol() ccleol(stdout) +# define de_error(s) { fprintf(stderr, s); getchar(); } +# define LINES 25 +# define COLS 80 +#else +# include +# include /* for sleep() */ +# define de_error(s) { fprintf(stderr, s); sleep(2); } +#endif +#include "de_cmds.h" + +#if defined(CPPCHECK) +# define MACRO_BLKSTMT_BEGIN { +# define MACRO_BLKSTMT_END } +#else +# define MACRO_BLKSTMT_BEGIN do { +# define MACRO_BLKSTMT_END } while (0) +#endif + +#define OUT_OF_MEMORY MACRO_BLKSTMT_BEGIN \ + fprintf(stderr, "Out of memory\n"); \ + exit(3); \ + MACRO_BLKSTMT_END + +/* List of line number to position mappings, in descending order. */ +/* There may be holes. */ +typedef struct LineMapRep { + int line; + size_t pos; + struct LineMapRep * previous; +} * line_map; + +/* List of file versions, one per edit operation */ +typedef struct HistoryRep { + CORD file_contents; + struct HistoryRep * previous; + line_map map; /* Invalid for first record "now" */ +} * history; + +history now = 0; +CORD current; /* == now -> file_contents. */ +size_t current_len; /* Current file length. */ +line_map current_map = 0; /* Current line no. to pos. map */ +size_t current_map_size = 0; /* Number of current_map entries. */ + /* Not always accurate, but reset */ + /* by prune_map. */ +# define MAX_MAP_SIZE 3000 + +/* Current display position */ +int dis_line = 0; +int dis_col = 0; + +# define ALL -1 +# define NONE - 2 +int need_redisplay = 0; /* Line that needs to be redisplayed. */ + + +/* Current cursor position. Always within file. */ +int line = 0; +int col = 0; +size_t file_pos = 0; /* Character position corresponding to cursor. */ + +/* Invalidate line map for lines > i */ +void invalidate_map(int i) +{ + for (;;) { + if (NULL == current_map) exit(4); /* for CSA, should not happen */ + if (current_map -> line <= i) break; + current_map = current_map -> previous; + current_map_size--; + } +} + +/* Reduce the number of map entries to save space for huge files. */ +/* This also affects maps in histories. */ +void prune_map(void) +{ + line_map map = current_map; + int start_line = map -> line; + + current_map_size = 0; + do { + current_map_size++; + if (map -> line < start_line - LINES && map -> previous != 0) { + line_map pred = map -> previous -> previous; + + GC_PTR_STORE_AND_DIRTY(&map->previous, pred); + } + map = map -> previous; + } while (map != 0); +} + +/* Add mapping entry */ +void add_map(int line_arg, size_t pos) +{ + line_map new_map = GC_NEW(struct LineMapRep); + line_map cur_map; + + if (NULL == new_map) OUT_OF_MEMORY; + if (current_map_size >= MAX_MAP_SIZE) prune_map(); + new_map -> line = line_arg; + new_map -> pos = pos; + cur_map = current_map; + GC_PTR_STORE_AND_DIRTY(&new_map->previous, cur_map); + current_map = new_map; + current_map_size++; +} + +/* Return position of column *c of ith line in */ +/* current file. Adjust *c to be within the line.*/ +/* A 0 pointer is taken as 0 column. */ +/* Returns CORD_NOT_FOUND if i is too big. */ +/* Assumes i > dis_line. */ +size_t line_pos(int i, int *c) +{ + int j; + size_t cur; + line_map map = current_map; + + while (map -> line > i) map = map -> previous; + if (map -> line < i - 2) /* rebuild */ invalidate_map(i); + for (j = map -> line, cur = map -> pos; j < i;) { + cur = CORD_chr(current, cur, '\n'); + if (cur == current_len-1) return(CORD_NOT_FOUND); + cur++; + if (++j > current_map -> line) add_map(j, cur); + } + if (c != 0) { + size_t next = CORD_chr(current, cur, '\n'); + + if (next == CORD_NOT_FOUND) next = current_len - 1; + if (next < cur + *c) { + *c = (int)(next - cur); + } + cur += *c; + } + return(cur); +} + +void add_hist(CORD s) +{ + history new_file = GC_NEW(struct HistoryRep); + + if (NULL == new_file) OUT_OF_MEMORY; + new_file -> file_contents = current = s; + current_len = CORD_len(s); + new_file -> previous = now; + GC_END_STUBBORN_CHANGE(new_file); + if (now != NULL) { + now -> map = current_map; + GC_END_STUBBORN_CHANGE(now); + } + now = new_file; +} + +void del_hist(void) +{ + now = now -> previous; + current = now -> file_contents; + current_map = now -> map; + current_len = CORD_len(current); +} + +/* Current screen_contents; a dynamically allocated array of CORDs */ +CORD * screen = 0; +int screen_size = 0; + +# ifndef WIN32 +/* Replace a line in the curses stdscr. All control characters are */ +/* displayed as upper case characters in standout mode. This isn't */ +/* terribly appropriate for tabs. */ +void replace_line(int i, CORD s) +{ + CORD_pos p; +# if !defined(MACINTOSH) + size_t len = CORD_len(s); +# endif + + if (screen == 0 || LINES > screen_size) { + screen_size = LINES; + screen = (CORD *)GC_MALLOC(screen_size * sizeof(CORD)); + if (NULL == screen) OUT_OF_MEMORY; + } +# if !defined(MACINTOSH) + /* A gross workaround for an apparent curses bug: */ + if (i == LINES-1 && len == (unsigned)COLS) { + s = CORD_substr(s, 0, len - 1); + } +# endif + if (CORD_cmp(screen[i], s) != 0) { + move(i, 0); clrtoeol(); move(i,0); + + CORD_FOR (p, s) { + int c = CORD_pos_fetch(p) & 0x7f; + + if (iscntrl(c)) { + standout(); addch(c + 0x40); standend(); + } else { + addch(c); + } + } + GC_PTR_STORE_AND_DIRTY(screen + i, s); + } +} +#else +# define replace_line(i,s) invalidate_line(i) +#endif + +/* Return up to COLS characters of the line of s starting at pos, */ +/* returning only characters after the given column. */ +CORD retrieve_line(CORD s, size_t pos, unsigned column) +{ + CORD candidate = CORD_substr(s, pos, column + COLS); + /* avoids scanning very long lines */ + size_t eol = CORD_chr(candidate, 0, '\n'); + int len; + + if (eol == CORD_NOT_FOUND) eol = CORD_len(candidate); + len = (int)eol - (int)column; + if (len < 0) len = 0; + return(CORD_substr(s, pos + column, len)); +} + +# ifdef WIN32 +# define refresh(); + + CORD retrieve_screen_line(int i) + { + size_t pos; + + invalidate_map(dis_line + LINES); /* Prune search */ + pos = line_pos(dis_line + i, 0); + if (pos == CORD_NOT_FOUND) return(CORD_EMPTY); + return(retrieve_line(current, pos, dis_col)); + } +# endif + +/* Display the visible section of the current file */ +void redisplay(void) +{ + int i; + + invalidate_map(dis_line + LINES); /* Prune search */ + for (i = 0; i < LINES; i++) { + if (need_redisplay == ALL || need_redisplay == i) { + size_t pos = line_pos(dis_line + i, 0); + + if (pos == CORD_NOT_FOUND) break; + replace_line(i, retrieve_line(current, pos, dis_col)); + if (need_redisplay == i) goto done; + } + } + for (; i < LINES; i++) replace_line(i, CORD_EMPTY); +done: + refresh(); + need_redisplay = NONE; +} + +int dis_granularity; + +/* Update dis_line, dis_col, and dis_pos to make cursor visible. */ +/* Assumes line, col, dis_line, dis_pos are in bounds. */ +void normalize_display(void) +{ + int old_line = dis_line; + int old_col = dis_col; + + dis_granularity = 1; + if (LINES > 15 && COLS > 15) dis_granularity = 2; + while (dis_line > line) dis_line -= dis_granularity; + while (dis_col > col) dis_col -= dis_granularity; + while (line >= dis_line + LINES) dis_line += dis_granularity; + while (col >= dis_col + COLS) dis_col += dis_granularity; + if (old_line != dis_line || old_col != dis_col) { + need_redisplay = ALL; + } +} + +# if defined(WIN32) +# elif defined(MACINTOSH) +# define move_cursor(x,y) cgotoxy(x + 1, y + 1, stdout) +# else +# define move_cursor(x,y) move(y,x) +# endif + +/* Adjust display so that cursor is visible; move cursor into position */ +/* Update screen if necessary. */ +void fix_cursor(void) +{ + normalize_display(); + if (need_redisplay != NONE) redisplay(); + move_cursor(col - dis_col, line - dis_line); + refresh(); +# ifndef WIN32 + fflush(stdout); +# endif +} + +/* Make sure line, col, and dis_pos are somewhere inside file. */ +/* Recompute file_pos. Assumes dis_pos is accurate or past eof */ +void fix_pos(void) +{ + int my_col = col; + + if ((size_t)line > current_len) + line = (int)current_len; + file_pos = line_pos(line, &my_col); + if (file_pos == CORD_NOT_FOUND) { + for (line = current_map -> line, file_pos = current_map -> pos; + file_pos < current_len; + line++, file_pos = CORD_chr(current, file_pos, '\n') + 1); + line--; + file_pos = line_pos(line, &col); + } else { + col = my_col; + } +} + +#if defined(WIN32) +# define beep() Beep(1000 /* Hz */, 300 /* ms */) +#elif defined(MACINTOSH) +# define beep() SysBeep(1) +#else +/* + * beep() is part of some curses packages and not others. + * We try to match the type of the builtin one, if any. + */ + int beep(void) + { + putc('\007', stderr); + return(0); + } +#endif /* !WIN32 && !MACINTOSH */ + +# define NO_PREFIX -1 +# define BARE_PREFIX -2 +int repeat_count = NO_PREFIX; /* Current command prefix. */ + +int locate_mode = 0; /* Currently between 2 ^Ls */ +CORD locate_string = CORD_EMPTY; /* Current search string. */ + +char * arg_file_name; + +#ifdef WIN32 +/* Change the current position to whatever is currently displayed at */ +/* the given SCREEN coordinates. */ +void set_position(int c, int l) +{ + line = l + dis_line; + col = c + dis_col; + fix_pos(); + move_cursor(col - dis_col, line - dis_line); +} +#endif /* WIN32 */ + +/* Perform the command associated with character c. C may be an */ +/* integer > 256 denoting a windows command, one of the above control */ +/* characters, or another ASCII character to be used as either a */ +/* character to be inserted, a repeat count, or a search string, */ +/* depending on the current state. */ +void do_command(int c) +{ + int i; + int need_fix_pos; + FILE * out; + + if ( c == '\r') c = '\n'; + if (locate_mode) { + size_t new_pos; + + if (c == LOCATE) { + locate_mode = 0; + locate_string = CORD_EMPTY; + return; + } + locate_string = CORD_cat_char(locate_string, (char)c); + new_pos = CORD_str(current, file_pos - CORD_len(locate_string) + 1, + locate_string); + if (new_pos != CORD_NOT_FOUND) { + need_redisplay = ALL; + new_pos += CORD_len(locate_string); + for (;;) { + file_pos = line_pos(line + 1, 0); + if (file_pos > new_pos) break; + line++; + } + col = (int)(new_pos - line_pos(line, 0)); + file_pos = new_pos; + fix_cursor(); + } else { + locate_string = CORD_substr(locate_string, 0, + CORD_len(locate_string) - 1); + beep(); + } + return; + } + if (c == REPEAT) { + repeat_count = BARE_PREFIX; return; + } else if (c < 0x100 && isdigit(c)){ + if (repeat_count == BARE_PREFIX) { + repeat_count = c - '0'; return; + } else if (repeat_count != NO_PREFIX) { + repeat_count = 10 * repeat_count + c - '0'; return; + } + } + if (repeat_count == NO_PREFIX) repeat_count = 1; + if (repeat_count == BARE_PREFIX && (c == UP || c == DOWN)) { + repeat_count = LINES - dis_granularity; + } + if (repeat_count == BARE_PREFIX) repeat_count = 8; + need_fix_pos = 0; + for (i = 0; i < repeat_count; i++) { + switch(c) { + case LOCATE: + locate_mode = 1; + break; + case TOP: + line = col = 0; + file_pos = 0; + break; + case UP: + if (line != 0) { + line--; + need_fix_pos = 1; + } + break; + case DOWN: + line++; + need_fix_pos = 1; + break; + case LEFT: + if (col != 0) { + col--; file_pos--; + } + break; + case RIGHT: + if (CORD_fetch(current, file_pos) == '\n') break; + col++; file_pos++; + break; + case UNDO: + del_hist(); + need_redisplay = ALL; need_fix_pos = 1; + break; + case BS: + if (col == 0) { + beep(); + break; + } + col--; file_pos--; + /* FALLTHRU */ + case DEL: + if (file_pos == current_len-1) break; + /* Can't delete trailing newline */ + if (CORD_fetch(current, file_pos) == '\n') { + need_redisplay = ALL; need_fix_pos = 1; + } else { + need_redisplay = line - dis_line; + } + add_hist(CORD_cat( + CORD_substr(current, 0, file_pos), + CORD_substr(current, file_pos+1, current_len))); + invalidate_map(line); + break; + case WRITE: + { + CORD name = CORD_cat(CORD_from_char_star(arg_file_name), + ".new"); + + if ((out = fopen(CORD_to_const_char_star(name), "wb")) == NULL + || CORD_put(current, out) == EOF) { + de_error("Write failed\n"); + need_redisplay = ALL; + } else { + fclose(out); + } + } + break; + default: + { + CORD left_part = CORD_substr(current, 0, file_pos); + CORD right_part = CORD_substr(current, file_pos, current_len); + + add_hist(CORD_cat(CORD_cat_char(left_part, (char)c), + right_part)); + invalidate_map(line); + if (c == '\n') { + col = 0; line++; file_pos++; + need_redisplay = ALL; + } else { + col++; file_pos++; + need_redisplay = line - dis_line; + } + break; + } + } + } + if (need_fix_pos) fix_pos(); + fix_cursor(); + repeat_count = NO_PREFIX; +} + +/* OS independent initialization */ + +void generic_init(void) +{ + FILE * f; + CORD initial; + + if ((f = fopen(arg_file_name, "rb")) == NULL) { + initial = "\n"; + } else { + size_t len; + + initial = CORD_from_file(f); + len = CORD_len(initial); + if (0 == len || CORD_fetch(initial, len - 1) != '\n') { + initial = CORD_cat(initial, "\n"); + } + } + add_map(0,0); + add_hist(initial); + now -> map = current_map; + now -> previous = now; /* Can't back up further: beginning of the world */ + GC_END_STUBBORN_CHANGE(now); + need_redisplay = ALL; + fix_cursor(); +} + +#ifndef WIN32 + +int main(int argc, char **argv) +{ + int c; + void *buf; + +# if defined(MACINTOSH) + console_options.title = "\pDumb Editor"; + cshow(stdout); + argc = ccommand(&argv); +# endif + GC_set_find_leak(0); /* app is not for testing leak detection mode */ + GC_INIT(); +# ifndef NO_INCREMENTAL + GC_enable_incremental(); +# endif + + if (argc != 2) { + fprintf(stderr, "Usage: %s file\n", argv[0]); + fprintf(stderr, "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n"); + fprintf(stderr, "Undo: ^U Write to .new: ^W"); + fprintf(stderr, "Quit:^D Repeat count: ^R[n]\n"); + fprintf(stderr, "Top: ^T Locate (search, find): ^L text ^L\n"); + exit(1); + } + arg_file_name = argv[1]; + buf = GC_MALLOC_ATOMIC(8192); + if (NULL == buf) OUT_OF_MEMORY; + setvbuf(stdout, (char *)buf, _IOFBF, 8192); + initscr(); + noecho(); nonl(); cbreak(); + generic_init(); + while ((c = getchar()) != QUIT) { + if (c == EOF) break; + do_command(c); + } + move(LINES-1, 0); + clrtoeol(); + refresh(); + nl(); + echo(); + endwin(); + return 0; +} + +#endif /* !WIN32 */ diff --git a/bdwgc/cord/tests/de_cmds.h b/bdwgc/cord/tests/de_cmds.h new file mode 100644 index 000000000..2a69594ef --- /dev/null +++ b/bdwgc/cord/tests/de_cmds.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#ifndef DE_CMDS_H + +# define DE_CMDS_H + +# define UP 16 /* ^P */ +# define DOWN 14 /* ^N */ +# define LEFT 2 /* ^B */ +# define RIGHT 6 /* ^F */ +# define DEL 127 /* ^? */ +# define BS 8 /* ^H */ +# define UNDO 21 /* ^U */ +# define WRITE 23 /* ^W */ +# define QUIT 4 /* ^D */ +# define REPEAT 18 /* ^R */ +# define LOCATE 12 /* ^L */ +# define TOP 20 /* ^T */ + +#endif diff --git a/bdwgc/cord/tests/de_win.c b/bdwgc/cord/tests/de_win.c new file mode 100644 index 000000000..a162ae417 --- /dev/null +++ b/bdwgc/cord/tests/de_win.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/* + * The Windows specific part of de. + * This started as the generic Windows application template + * but significant parts didn't survive to the final version. + */ +#if defined(__BORLANDC__) || defined(__CYGWIN__) || defined(__MINGW32__) \ + || defined(__NT__) || defined(_WIN32) || defined(WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +#endif +#define NOSERVICE +#include + +#include "gc.h" +#include "cord.h" +#include "de_cmds.h" +#include "de_win.h" + +int LINES = 0; +int COLS = 0; + +#define szAppName TEXT("DE") + +HWND hwnd; + +void de_error(const char *s) +{ + (void)MessageBoxA(hwnd, s, "Demonstration Editor", + MB_ICONINFORMATION | MB_OK); + InvalidateRect(hwnd, NULL, TRUE); +} + +int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR command_line, int nCmdShow) +{ + MSG msg; + WNDCLASS wndclass; + HACCEL hAccel; + + GC_set_find_leak(0); + GC_INIT(); +# ifndef NO_INCREMENTAL + GC_enable_incremental(); +# endif +# if defined(CPPCHECK) + GC_noop1((GC_word)&WinMain); +# endif + + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = DLGWINDOWEXTRA; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (hInstance, szAppName); + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = TEXT("DE"); + wndclass.lpszClassName = szAppName; + + if (RegisterClass (&wndclass) == 0) { + de_error("RegisterClass error"); + return(0); + } + } + + /* Empirically, the command line does not include the command name ... + if (command_line != 0) { + while (isspace(*command_line)) command_line++; + while (*command_line != 0 && !isspace(*command_line)) command_line++; + while (isspace(*command_line)) command_line++; + } */ + + if (command_line == 0 || *command_line == 0) { + de_error("File name argument required"); + return( 0 ); + } else { + char *p = command_line; + + while (*p != 0 && !isspace(*(unsigned char *)p)) + p++; + arg_file_name = CORD_to_char_star( + CORD_substr(command_line, 0, p - command_line)); + } + + hwnd = CreateWindow (szAppName, + TEXT("Demonstration Editor"), + WS_OVERLAPPEDWINDOW | WS_CAPTION, /* Window style */ + CW_USEDEFAULT, 0, /* default pos. */ + CW_USEDEFAULT, 0, /* default width, height */ + NULL, /* No parent */ + NULL, /* Window class menu */ + hInstance, NULL); + if (hwnd == NULL) { + de_error("CreateWindow error"); + return(0); + } + + ShowWindow (hwnd, nCmdShow); + + hAccel = LoadAccelerators( hInstance, szAppName ); + + while (GetMessage (&msg, NULL, 0, 0)) + { + if( !TranslateAccelerator( hwnd, hAccel, &msg ) ) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + return (int)msg.wParam; +} + +/* Return the argument with all control characters replaced by blanks. */ +char * plain_chars(char * text, size_t len) +{ + char * result = (char *)GC_MALLOC_ATOMIC(len + 1); + size_t i; + + if (NULL == result) return NULL; + for (i = 0; i < len; i++) { + if (iscntrl(((unsigned char *)text)[i])) { + result[i] = ' '; + } else { + result[i] = text[i]; + } + } + result[len] = '\0'; + return(result); +} + +/* Return the argument with all non-control-characters replaced by */ +/* blank, and all control characters c replaced by c + 64. */ +char * control_chars(char * text, size_t len) +{ + char * result = (char *)GC_MALLOC_ATOMIC(len + 1); + size_t i; + + if (NULL == result) return NULL; + for (i = 0; i < len; i++) { + if (iscntrl(((unsigned char *)text)[i])) { + result[i] = (char)(text[i] + 0x40); + } else { + result[i] = ' '; + } + } + result[len] = '\0'; + return(result); +} + +int char_width; +int char_height; + +void get_line_rect(int line_arg, int win_width, RECT * rectp) +{ + rectp -> top = line_arg * (LONG)char_height; + rectp -> bottom = rectp->top + char_height; + rectp -> left = 0; + rectp -> right = win_width; +} + +int caret_visible = 0; /* Caret is currently visible. */ + +int screen_was_painted = 0;/* Screen has been painted at least once. */ + +void update_cursor(void); + +INT_PTR CALLBACK AboutBoxCallback( HWND hDlg, UINT message, + WPARAM wParam, LPARAM lParam ) +{ + (void)lParam; + switch( message ) + { + case WM_INITDIALOG: + SetFocus( GetDlgItem( hDlg, IDOK ) ); + break; + + case WM_COMMAND: + switch( wParam ) + { + case IDOK: + EndDialog( hDlg, TRUE ); + break; + } + break; + + case WM_CLOSE: + EndDialog( hDlg, TRUE ); + return TRUE; + + } + return FALSE; +} + +LRESULT CALLBACK WndProc (HWND hwnd_arg, UINT message, + WPARAM wParam, LPARAM lParam) +{ + static HINSTANCE hInstance; + HDC dc; + PAINTSTRUCT ps; + RECT client_area; + RECT this_line; + RECT dummy; + TEXTMETRIC tm; + int i; + int id; + + switch (message) + { + case WM_CREATE: + hInstance = ( (LPCREATESTRUCT) lParam)->hInstance; + dc = GetDC(hwnd_arg); + SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT)); + GetTextMetrics(dc, &tm); + ReleaseDC(hwnd_arg, dc); + char_width = tm.tmAveCharWidth; + char_height = tm.tmHeight + tm.tmExternalLeading; + GetClientRect(hwnd_arg, &client_area); + COLS = (client_area.right - client_area.left)/char_width; + LINES = (client_area.bottom - client_area.top)/char_height; + generic_init(); + return(0); + + case WM_CHAR: + if (wParam == QUIT) { + SendMessage(hwnd_arg, WM_CLOSE, 0, 0L); + } else { + do_command((int)wParam); + } + return(0); + + case WM_SETFOCUS: + CreateCaret(hwnd_arg, NULL, char_width, char_height); + ShowCaret(hwnd_arg); + caret_visible = 1; + update_cursor(); + return(0); + + case WM_KILLFOCUS: + HideCaret(hwnd_arg); + DestroyCaret(); + caret_visible = 0; + return(0); + + case WM_LBUTTONUP: + { + unsigned xpos = LOWORD(lParam); /* From left */ + unsigned ypos = HIWORD(lParam); /* from top */ + + set_position(xpos / (unsigned)char_width, + ypos / (unsigned)char_height); + return(0); + } + + case WM_COMMAND: + id = LOWORD(wParam); + if (id & EDIT_CMD_FLAG) { + if (id & REPEAT_FLAG) do_command(REPEAT); + do_command(CHAR_CMD(id)); + return( 0 ); + } else { + switch(id) { + case IDM_FILEEXIT: + SendMessage(hwnd_arg, WM_CLOSE, 0, 0L); + return( 0 ); + + case IDM_HELPABOUT: + if( DialogBox( hInstance, TEXT("ABOUTBOX"), + hwnd_arg, AboutBoxCallback ) ) + InvalidateRect(hwnd_arg, NULL, TRUE); + return( 0 ); + case IDM_HELPCONTENTS: + de_error( + "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n" + "Undo: ^U Write: ^W Quit:^D Repeat count: ^R[n]\n" + "Top: ^T Locate (search, find): ^L text ^L\n"); + return( 0 ); + } + } + break; + + case WM_CLOSE: + DestroyWindow(hwnd_arg); + return 0; + + case WM_DESTROY: + PostQuitMessage (0); + GC_win32_free_heap(); + return 0; + + case WM_PAINT: + dc = BeginPaint(hwnd_arg, &ps); + GetClientRect(hwnd_arg, &client_area); + COLS = (client_area.right - client_area.left)/char_width; + LINES = (client_area.bottom - client_area.top)/char_height; + SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT)); + for (i = 0; i < LINES; i++) { + get_line_rect(i, client_area.right, &this_line); + if (IntersectRect(&dummy, &this_line, &ps.rcPaint)) { + CORD raw_line = retrieve_screen_line(i); + size_t len = CORD_len(raw_line); + char * text = CORD_to_char_star(raw_line); + /* May contain embedded NULLs */ + char * plain = plain_chars(text, len); + char * blanks = CORD_to_char_star(CORD_chars(' ', + COLS - len)); + char * control = control_chars(text, len); + if (NULL == plain || NULL == control) + de_error("Out of memory!"); + +# define RED RGB(255,0,0) + + SetBkMode(dc, OPAQUE); + SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT)); + + if (plain != NULL) + TextOutA(dc, this_line.left, this_line.top, + plain, (int)len); + TextOutA(dc, this_line.left + (int)len * char_width, + this_line.top, + blanks, (int)(COLS - len)); + SetBkMode(dc, TRANSPARENT); + SetTextColor(dc, RED); + if (control != NULL) + TextOutA(dc, this_line.left, this_line.top, + control, (int)strlen(control)); + } + } + EndPaint(hwnd_arg, &ps); + screen_was_painted = 1; + return 0; + } + return DefWindowProc(hwnd_arg, message, wParam, lParam); +} + +int last_col; +int last_line; + +void move_cursor(int c, int l) +{ + last_col = c; + last_line = l; + + if (caret_visible) update_cursor(); +} + +void update_cursor(void) +{ + SetCaretPos(last_col * char_width, last_line * char_height); + ShowCaret(hwnd); +} + +void invalidate_line(int i) +{ + RECT line_r; + + if (!screen_was_painted) return; + /* Invalidating a rectangle before painting seems result in a */ + /* major performance problem. */ + get_line_rect(i, COLS*char_width, &line_r); + InvalidateRect(hwnd, &line_r, FALSE); +} + +#else + + extern int GC_quiet; + /* ANSI C doesn't allow translation units to be empty. */ + /* So we guarantee this one is nonempty. */ + +#endif /* !WIN32 */ diff --git a/bdwgc/cord/tests/de_win.h b/bdwgc/cord/tests/de_win.h new file mode 100644 index 000000000..33ecfa256 --- /dev/null +++ b/bdwgc/cord/tests/de_win.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/* cord.h, de_cmds.h, and windows.h should be included before this. */ + +# define OTHER_FLAG 0x100 +# define EDIT_CMD_FLAG 0x200 +# define REPEAT_FLAG 0x400 + +# define CHAR_CMD(i) ((i) & 0xff) + +/* MENU: DE */ +#define IDM_FILESAVE (EDIT_CMD_FLAG + WRITE) +#define IDM_FILEEXIT (OTHER_FLAG + 1) +#define IDM_HELPABOUT (OTHER_FLAG + 2) +#define IDM_HELPCONTENTS (OTHER_FLAG + 3) + +#define IDM_EDITPDOWN (REPEAT_FLAG + EDIT_CMD_FLAG + DOWN) +#define IDM_EDITPUP (REPEAT_FLAG + EDIT_CMD_FLAG + UP) +#define IDM_EDITUNDO (EDIT_CMD_FLAG + UNDO) +#define IDM_EDITLOCATE (EDIT_CMD_FLAG + LOCATE) +#define IDM_EDITDOWN (EDIT_CMD_FLAG + DOWN) +#define IDM_EDITUP (EDIT_CMD_FLAG + UP) +#define IDM_EDITLEFT (EDIT_CMD_FLAG + LEFT) +#define IDM_EDITRIGHT (EDIT_CMD_FLAG + RIGHT) +#define IDM_EDITBS (EDIT_CMD_FLAG + BS) +#define IDM_EDITDEL (EDIT_CMD_FLAG + DEL) +#define IDM_EDITREPEAT (EDIT_CMD_FLAG + REPEAT) +#define IDM_EDITTOP (EDIT_CMD_FLAG + TOP) + + + + +/* Windows UI stuff */ + +LRESULT CALLBACK WndProc (HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); + +/* Screen dimensions. Maintained by de_win.c. */ +extern int LINES; +extern int COLS; + +/* File being edited. */ +extern char * arg_file_name; + +/* Current display position in file. Maintained by de.c */ +extern int dis_line; +extern int dis_col; + +/* Current cursor position in file. */ +extern int line; +extern int col; + +/* + * Calls from de_win.c to de.c + */ + +CORD retrieve_screen_line(int i); + /* Get the contents of i'th screen line. */ + /* Relies on COLS. */ + +void set_position(int x, int y); + /* Set column, row. Upper left of window = (0,0). */ + +void do_command(int); + /* Execute an editor command. */ + /* Agument is a command character or one */ + /* of the IDM_ commands. */ + +void generic_init(void); + /* OS independent initialization */ + + +/* + * Calls from de.c to de_win.c + */ + +void move_cursor(int column, int line); + /* Physically move the cursor on the display, */ + /* so that it appears at */ + /* (column, line). */ + +void invalidate_line(int line); + /* Invalidate line i on the screen. */ + +void de_error(const char *s); + /* Display error message. */ diff --git a/bdwgc/cord/tests/de_win.rc b/bdwgc/cord/tests/de_win.rc new file mode 100644 index 000000000..746a73dd2 --- /dev/null +++ b/bdwgc/cord/tests/de_win.rc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#include "windows.h" +#include "de_cmds.h" +#include "de_win.h" + +ABOUTBOX DIALOG 19, 21, 163, 47 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Demonstration Text Editor" +BEGIN + LTEXT "Demonstration Text Editor", -1, 44, 8, 118, 8, WS_CHILD | WS_VISIBLE | WS_GROUP + PUSHBUTTON "OK", IDOK, 118, 27, 24, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + +DE MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Save\t^W", IDM_FILESAVE + MENUITEM "E&xit\t^D", IDM_FILEEXIT + END + + POPUP "&Edit" + BEGIN + MENUITEM "Page &Down\t^R^N", IDM_EDITPDOWN + MENUITEM "Page &Up\t^R^P", IDM_EDITPUP + MENUITEM "U&ndo\t^U", IDM_EDITUNDO + MENUITEM "&Locate\t^L ... ^L", IDM_EDITLOCATE + MENUITEM "D&own\t^N", IDM_EDITDOWN + MENUITEM "U&p\t^P", IDM_EDITUP + MENUITEM "Le&ft\t^B", IDM_EDITLEFT + MENUITEM "&Right\t^F", IDM_EDITRIGHT + MENUITEM "Delete &Backward\tBS", IDM_EDITBS + MENUITEM "Delete F&orward\tDEL", IDM_EDITDEL + MENUITEM "&Top\t^T", IDM_EDITTOP + END + + POPUP "&Help" + BEGIN + MENUITEM "&Contents", IDM_HELPCONTENTS + MENUITEM "&About...", IDM_HELPABOUT + END + + MENUITEM "Page_&Down", IDM_EDITPDOWN + MENUITEM "Page_&Up", IDM_EDITPUP +END + +DE ACCELERATORS +BEGIN + "^R", IDM_EDITREPEAT + "^N", IDM_EDITDOWN + "^P", IDM_EDITUP + "^L", IDM_EDITLOCATE + "^B", IDM_EDITLEFT + "^F", IDM_EDITRIGHT + "^T", IDM_EDITTOP + VK_DELETE, IDM_EDITDEL, VIRTKEY + VK_BACK, IDM_EDITBS, VIRTKEY +END diff --git a/bdwgc/darwin_stop_world.c b/bdwgc/darwin_stop_world.c new file mode 100644 index 000000000..f421518d4 --- /dev/null +++ b/bdwgc/darwin_stop_world.c @@ -0,0 +1,780 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +#include "private/pthread_support.h" + +/* This probably needs more porting work to ppc64. */ + +#if defined(GC_DARWIN_THREADS) + +#include +#include +#include + +/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple + Page 49: + "The space beneath the stack pointer, where a new stack frame would normally + be allocated, is called the red zone. This area as shown in Figure 3-2 may + be used for any purpose as long as a new stack frame does not need to be + added to the stack." + + Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then + it must set up a stack frame just like routines that call other routines." +*/ +#ifdef POWERPC +# if CPP_WORDSZ == 32 +# define PPC_RED_ZONE_SIZE 224 +# elif CPP_WORDSZ == 64 +# define PPC_RED_ZONE_SIZE 320 +# endif +#endif + +#ifndef DARWIN_DONT_PARSE_STACK + +typedef struct StackFrame { + unsigned long savedSP; + unsigned long savedCR; + unsigned long savedLR; + unsigned long reserved[2]; + unsigned long savedRTOC; +} StackFrame; + +GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start) +{ + StackFrame *frame = (StackFrame *)stack_start; + + 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 +# elif defined(ARM32) + volatile ptr_t sp_reg; + __asm__ __volatile__ ("mov %0, r7\n" : "=r" (sp_reg)); + frame = (StackFrame *)sp_reg; +# elif defined(AARCH64) + volatile ptr_t sp_reg; + __asm__ __volatile__ ("mov %0, x29\n" : "=r" (sp_reg)); + frame = (StackFrame *)sp_reg; +# else +# if defined(CPPCHECK) + GC_noop1((word)&frame); +# endif + ABORT("GC_FindTopOfStack(0) is not implemented"); +# endif + } + +# ifdef DEBUG_THREADS_EXTRA + GC_log_printf("FindTopOfStack start at sp= %p\n", (void *)frame); +# endif + while (frame->savedSP != 0) { + /* if there are no more stack frames, stop */ + + frame = (StackFrame*)frame->savedSP; + + /* we do these next two checks after going to the next frame + because the LR for the first stack frame in the loop + is not set up on purpose, so we shouldn't check it. */ + if ((frame->savedLR & ~0x3) == 0 || (frame->savedLR & ~0x3) == ~0x3UL) + break; /* if the next LR is bogus, stop */ + } +# ifdef DEBUG_THREADS_EXTRA + GC_log_printf("FindTopOfStack finish at sp= %p\n", (void *)frame); +# endif + return (ptr_t)frame; +} + +#endif /* !DARWIN_DONT_PARSE_STACK */ + +/* GC_query_task_threads controls whether to obtain the list of */ +/* the threads from the kernel or to use GC_threads table. */ +#ifdef GC_NO_THREADS_DISCOVERY +# define GC_query_task_threads FALSE +#elif defined(GC_DISCOVER_TASK_THREADS) +# define GC_query_task_threads TRUE +#else + STATIC GC_bool GC_query_task_threads = FALSE; +#endif /* !GC_NO_THREADS_DISCOVERY */ + +/* Use implicit threads registration (all task threads excluding the GC */ +/* 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) +{ +# if defined(GC_NO_THREADS_DISCOVERY) || defined(DARWIN_DONT_PARSE_STACK) + ABORT("Darwin task-threads-based stop and push unsupported"); +# else +# ifndef GC_ALWAYS_MULTITHREADED + GC_ASSERT(!GC_need_to_lock); +# endif +# ifndef GC_DISCOVER_TASK_THREADS + GC_query_task_threads = TRUE; +# endif + GC_init_parallel(); /* just to be consistent with Win32 one */ +# endif +} + +#ifndef kCFCoreFoundationVersionNumber_iOS_8_0 +# define kCFCoreFoundationVersionNumber_iOS_8_0 1140.1 +#endif + +/* Evaluates the stack range for a given thread. Returns the lower */ +/* bound and sets *phi to the upper one. */ +STATIC ptr_t GC_stack_range_for(ptr_t *phi, thread_act_t thread, GC_thread p, + GC_bool thread_blocked, mach_port_t my_thread, + ptr_t *paltstack_lo, + ptr_t *paltstack_hi GC_ATTR_UNUSED) +{ + ptr_t lo; + if (thread == my_thread) { + GC_ASSERT(!thread_blocked); + lo = GC_approx_sp(); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(0); +# endif + + } else if (thread_blocked) { +# if defined(CPPCHECK) + if (NULL == p) ABORT("Invalid GC_thread passed to GC_stack_range_for"); +# endif + lo = p->stop_info.stack_ptr; +# ifndef DARWIN_DONT_PARSE_STACK + *phi = p->topOfStack; +# endif + + } else { + /* MACHINE_THREAD_STATE_COUNT does not seem to be defined */ + /* everywhere. Hence we use our own version. Alternatively, */ + /* we could use THREAD_STATE_MAX (but seems to be not optimal). */ + kern_return_t kern_result; + GC_THREAD_STATE_T state; + +# if defined(ARM32) && defined(ARM_THREAD_STATE32) + /* Use ARM_UNIFIED_THREAD_STATE on iOS8+ 32-bit targets and on */ + /* 64-bit H/W (iOS7+ 32-bit mode). */ + size_t size; + static cpu_type_t cputype = 0; + + if (cputype == 0) { + sysctlbyname("hw.cputype", &cputype, &size, NULL, 0); + } + if (cputype == CPU_TYPE_ARM64 + || kCFCoreFoundationVersionNumber + >= kCFCoreFoundationVersionNumber_iOS_8_0) { + arm_unified_thread_state_t unified_state; + mach_msg_type_number_t unified_thread_state_count + = ARM_UNIFIED_THREAD_STATE_COUNT; +# if defined(CPPCHECK) +# define GC_ARM_UNIFIED_THREAD_STATE 1 +# else +# define GC_ARM_UNIFIED_THREAD_STATE ARM_UNIFIED_THREAD_STATE +# endif + kern_result = thread_get_state(thread, GC_ARM_UNIFIED_THREAD_STATE, + (natural_t *)&unified_state, + &unified_thread_state_count); +# if !defined(CPPCHECK) + if (unified_state.ash.flavor != ARM_THREAD_STATE32) { + ABORT("unified_state flavor should be ARM_THREAD_STATE32"); + } +# endif + state = unified_state; + } else +# endif + /* else */ { + mach_msg_type_number_t thread_state_count = GC_MACH_THREAD_STATE_COUNT; + + /* Get the thread state (registers, etc.) */ + do { + kern_result = thread_get_state(thread, GC_MACH_THREAD_STATE, + (natural_t *)&state, + &thread_state_count); + } while (kern_result == KERN_ABORTED); + } +# ifdef DEBUG_THREADS + GC_log_printf("thread_get_state returns %d\n", kern_result); +# endif + if (kern_result != KERN_SUCCESS) + ABORT("thread_get_state failed"); + +# if defined(I386) + lo = (ptr_t)state.THREAD_FLD(esp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(esp)); +# endif + GC_push_one(state.THREAD_FLD(eax)); + GC_push_one(state.THREAD_FLD(ebx)); + GC_push_one(state.THREAD_FLD(ecx)); + GC_push_one(state.THREAD_FLD(edx)); + GC_push_one(state.THREAD_FLD(edi)); + GC_push_one(state.THREAD_FLD(esi)); + GC_push_one(state.THREAD_FLD(ebp)); + +# elif defined(X86_64) + lo = (ptr_t)state.THREAD_FLD(rsp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(rsp)); +# endif + GC_push_one(state.THREAD_FLD(rax)); + GC_push_one(state.THREAD_FLD(rbx)); + GC_push_one(state.THREAD_FLD(rcx)); + GC_push_one(state.THREAD_FLD(rdx)); + GC_push_one(state.THREAD_FLD(rdi)); + GC_push_one(state.THREAD_FLD(rsi)); + GC_push_one(state.THREAD_FLD(rbp)); + /* rsp is skipped. */ + GC_push_one(state.THREAD_FLD(r8)); + GC_push_one(state.THREAD_FLD(r9)); + GC_push_one(state.THREAD_FLD(r10)); + GC_push_one(state.THREAD_FLD(r11)); + GC_push_one(state.THREAD_FLD(r12)); + GC_push_one(state.THREAD_FLD(r13)); + GC_push_one(state.THREAD_FLD(r14)); + GC_push_one(state.THREAD_FLD(r15)); + +# elif defined(POWERPC) + lo = (ptr_t)(state.THREAD_FLD(r1) - PPC_RED_ZONE_SIZE); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(r1)); +# endif + GC_push_one(state.THREAD_FLD(r0)); + /* r1 is skipped. */ + GC_push_one(state.THREAD_FLD(r2)); + GC_push_one(state.THREAD_FLD(r3)); + GC_push_one(state.THREAD_FLD(r4)); + GC_push_one(state.THREAD_FLD(r5)); + GC_push_one(state.THREAD_FLD(r6)); + GC_push_one(state.THREAD_FLD(r7)); + GC_push_one(state.THREAD_FLD(r8)); + GC_push_one(state.THREAD_FLD(r9)); + GC_push_one(state.THREAD_FLD(r10)); + GC_push_one(state.THREAD_FLD(r11)); + GC_push_one(state.THREAD_FLD(r12)); + GC_push_one(state.THREAD_FLD(r13)); + GC_push_one(state.THREAD_FLD(r14)); + GC_push_one(state.THREAD_FLD(r15)); + GC_push_one(state.THREAD_FLD(r16)); + GC_push_one(state.THREAD_FLD(r17)); + GC_push_one(state.THREAD_FLD(r18)); + GC_push_one(state.THREAD_FLD(r19)); + GC_push_one(state.THREAD_FLD(r20)); + GC_push_one(state.THREAD_FLD(r21)); + GC_push_one(state.THREAD_FLD(r22)); + GC_push_one(state.THREAD_FLD(r23)); + GC_push_one(state.THREAD_FLD(r24)); + GC_push_one(state.THREAD_FLD(r25)); + GC_push_one(state.THREAD_FLD(r26)); + GC_push_one(state.THREAD_FLD(r27)); + GC_push_one(state.THREAD_FLD(r28)); + GC_push_one(state.THREAD_FLD(r29)); + GC_push_one(state.THREAD_FLD(r30)); + GC_push_one(state.THREAD_FLD(r31)); + +# elif defined(ARM32) + lo = (ptr_t)state.THREAD_FLD(sp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(r[7])); /* fp */ +# endif + { + int j; + for (j = 0; j < 7; j++) + GC_push_one(state.THREAD_FLD(r[j])); + j++; /* "r7" is skipped (iOS uses it as a frame pointer) */ + for (; j <= 12; j++) + GC_push_one(state.THREAD_FLD(r[j])); + } + /* "cpsr", "pc" and "sp" are skipped */ + GC_push_one(state.THREAD_FLD(lr)); + +# elif defined(AARCH64) + lo = (ptr_t)state.THREAD_FLD(sp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(fp)); +# endif + { + int j; + for (j = 0; j <= 28; j++) { + GC_push_one(state.THREAD_FLD(x[j])); + } + } + /* "cpsr", "fp", "pc" and "sp" are skipped */ + GC_push_one(state.THREAD_FLD(lr)); + +# elif defined(CPPCHECK) + lo = NULL; +# else +# error FIXME for non-arm/ppc/x86 architectures +# endif + } /* thread != my_thread */ + +# ifdef DARWIN_DONT_PARSE_STACK + /* p is guaranteed to be non-NULL regardless of GC_query_task_threads. */ + *phi = (p->flags & MAIN_THREAD) != 0 ? GC_stackbottom : p->stack_end; +# endif + + /* TODO: Determine p and handle altstack if !DARWIN_DONT_PARSE_STACK */ +# ifdef DARWIN_DONT_PARSE_STACK + if (p->altstack != NULL && (word)p->altstack <= (word)lo + && (word)lo <= (word)p->altstack + p->altstack_size) { + *paltstack_lo = lo; + *paltstack_hi = p->altstack + p->altstack_size; + lo = p->stack; + *phi = p->stack + p->stack_size; + } else +# endif + /* else */ { + *paltstack_lo = NULL; + } +# if defined(STACKPTR_CORRECTOR_AVAILABLE) && defined(DARWIN_DONT_PARSE_STACK) + if (GC_sp_corrector != 0) + GC_sp_corrector((void **)&lo, (void *)(p -> id)); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("Darwin: Stack for thread %p is [%p,%p)\n", + (void *)(word)thread, (void *)lo, (void *)(*phi)); +# endif + return lo; +} + +GC_INNER void GC_push_all_stacks(void) +{ + ptr_t hi, altstack_lo, altstack_hi; + task_t my_task = current_task(); + mach_port_t my_thread = mach_thread_self(); + GC_bool found_me = FALSE; + int nthreads = 0; + word total_size = 0; + mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ; + if (!EXPECT(GC_thr_initialized, TRUE)) + GC_thr_init(); + +# ifndef DARWIN_DONT_PARSE_STACK + if (GC_query_task_threads) { + int i; + kern_return_t kern_result; + thread_act_array_t act_list = 0; + + /* Obtain the list of the threads from the kernel. */ + kern_result = task_threads(my_task, &act_list, &listcount); + if (kern_result != KERN_SUCCESS) + ABORT("task_threads failed"); + + for (i = 0; i < (int)listcount; i++) { + thread_act_t thread = act_list[i]; + ptr_t lo = GC_stack_range_for(&hi, thread, NULL, FALSE, my_thread, + &altstack_lo, &altstack_hi); + + if (lo) { + GC_ASSERT((word)lo <= (word)hi); + total_size += hi - lo; + GC_push_all_stack(lo, hi); + } + /* TODO: Handle altstack */ + nthreads++; + if (thread == my_thread) + found_me = TRUE; + mach_port_deallocate(my_task, thread); + } /* for (i=0; ...) */ + + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); + } else +# endif /* !DARWIN_DONT_PARSE_STACK */ + /* else */ { + int i; + + for (i = 0; i < (int)listcount; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p->next) + if ((p->flags & FINISHED) == 0) { + thread_act_t thread = (thread_act_t)p->stop_info.mach_thread; + ptr_t lo = GC_stack_range_for(&hi, thread, p, + (GC_bool)p->thread_blocked, + my_thread, &altstack_lo, + &altstack_hi); + + if (lo) { + GC_ASSERT((word)lo <= (word)hi); + total_size += hi - lo; + GC_push_all_stack_sections(lo, hi, p->traced_stack_sect); + } + if (altstack_lo) { + total_size += altstack_hi - altstack_lo; + GC_push_all_stack(altstack_lo, altstack_hi); + } + nthreads++; + if (thread == my_thread) + found_me = TRUE; + } + } /* for (i=0; ...) */ + } + + mach_port_deallocate(my_task, my_thread); + 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; +} + +#ifndef GC_NO_THREADS_DISCOVERY + +# ifdef MPROTECT_VDB + STATIC mach_port_t GC_mach_handler_thread = 0; + STATIC GC_bool GC_use_mach_handler_thread = FALSE; + + GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread) + { + GC_mach_handler_thread = thread; + GC_use_mach_handler_thread = TRUE; + } +# endif /* MPROTECT_VDB */ + +# ifndef GC_MAX_MACH_THREADS +# define GC_MAX_MACH_THREADS THREAD_TABLE_SZ +# endif + + struct GC_mach_thread { + thread_act_t thread; + GC_bool suspended; + }; + + struct GC_mach_thread GC_mach_threads[GC_MAX_MACH_THREADS]; + STATIC int GC_mach_threads_count = 0; + /* FIXME: it is better to implement GC_mach_threads as a hash set. */ + +/* returns true if there's a thread in act_list that wasn't in old_list */ +STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, + thread_act_array_t old_list, + int old_count, task_t my_task, + mach_port_t my_thread) +{ + int i; + int j = -1; + GC_bool changed = FALSE; + + for (i = 0; i < count; i++) { + thread_act_t thread = act_list[i]; + GC_bool found; + kern_return_t kern_result; + + if (thread == my_thread +# ifdef MPROTECT_VDB + || (GC_mach_handler_thread == thread && GC_use_mach_handler_thread) +# endif +# ifdef PARALLEL_MARK + || GC_is_mach_marker(thread) /* ignore the parallel markers */ +# endif + ) { + /* Do not add our one, parallel marker and the handler threads; */ + /* consider it as found (e.g., it was processed earlier). */ + mach_port_deallocate(my_task, thread); + continue; + } + + /* find the current thread in the old list */ + found = FALSE; + { + int last_found = j; /* remember the previous found thread index */ + + /* Search for the thread starting from the last found one first. */ + while (++j < old_count) + if (old_list[j] == thread) { + found = TRUE; + break; + } + if (!found) { + /* If not found, search in the rest (beginning) of the list. */ + for (j = 0; j < last_found; j++) + if (old_list[j] == thread) { + found = TRUE; + break; + } + } + } + + if (found) { + /* It is already in the list, skip processing, release mach port. */ + mach_port_deallocate(my_task, thread); + continue; + } + + /* add it to the GC_mach_threads list */ + if (GC_mach_threads_count == GC_MAX_MACH_THREADS) + ABORT("Too many threads"); + GC_mach_threads[GC_mach_threads_count].thread = thread; + /* default is not suspended */ + GC_mach_threads[GC_mach_threads_count].suspended = FALSE; + changed = TRUE; + +# ifdef DEBUG_THREADS + GC_log_printf("Suspending %p\n", (void *)(word)thread); +# endif + /* Unconditionally suspend the thread. It will do no */ + /* harm if it is already suspended by the client logic. */ + GC_acquire_dirty_lock(); + do { + kern_result = thread_suspend(thread); + } while (kern_result == KERN_ABORTED); + GC_release_dirty_lock(); + if (kern_result != KERN_SUCCESS) { + /* The thread may have quit since the thread_threads() call we */ + /* mark already suspended so it's not dealt with anymore later. */ + GC_mach_threads[GC_mach_threads_count].suspended = FALSE; + } else { + /* Mark the thread as suspended and require resume. */ + GC_mach_threads[GC_mach_threads_count].suspended = TRUE; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, (void *)(word)thread); + } + GC_mach_threads_count++; + } + return changed; +} + +#endif /* !GC_NO_THREADS_DISCOVERY */ + +/* Caller holds allocation lock. */ +GC_INNER void GC_stop_world(void) +{ + task_t my_task = current_task(); + mach_port_t my_thread = mach_thread_self(); + kern_return_t kern_result; + +# ifdef DEBUG_THREADS + GC_log_printf("Stopping the world from thread %p\n", + (void *)(word)my_thread); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) { + /* Make sure all free list construction has stopped before we */ + /* start. No new construction can start, since free list */ + /* construction is required to acquire and release the GC lock */ + /* before it starts, and we have the lock. */ + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + + if (GC_query_task_threads) { +# ifndef GC_NO_THREADS_DISCOVERY + GC_bool changed; + thread_act_array_t act_list, prev_list; + mach_msg_type_number_t listcount, prevcount; + + /* Clear out the mach threads list table. We do not need to */ + /* really clear GC_mach_threads[] as it is used only in the range */ + /* from 0 to GC_mach_threads_count-1, inclusive. */ + GC_mach_threads_count = 0; + + /* Loop stopping threads until you have gone over the whole list */ + /* twice without a new one appearing. thread_create() won't */ + /* return (and thus the thread stop) until the new thread exists, */ + /* so there is no window whereby you could stop a thread, */ + /* recognize it is stopped, but then have a new thread it created */ + /* before stopping show up later. */ + changed = TRUE; + prev_list = NULL; + prevcount = 0; + do { + kern_result = task_threads(my_task, &act_list, &listcount); + + if (kern_result == KERN_SUCCESS) { + changed = GC_suspend_thread_list(act_list, listcount, prev_list, + prevcount, my_task, my_thread); + + if (prev_list != NULL) { + /* Thread ports are not deallocated by list, unused ports */ + /* deallocated in GC_suspend_thread_list, used - kept in */ + /* GC_mach_threads till GC_start_world as otherwise thread */ + /* object change can occur and GC_start_world will not */ + /* find the thread to resume which will cause app to hang. */ + vm_deallocate(my_task, (vm_address_t)prev_list, + sizeof(thread_t) * prevcount); + } + + /* Repeat while having changes. */ + prev_list = act_list; + prevcount = listcount; + } + } while (changed); + + GC_ASSERT(prev_list != 0); + /* The thread ports are not deallocated by list, see above. */ + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); +# endif /* !GC_NO_THREADS_DISCOVERY */ + + } else { + unsigned i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p->next) { + if ((p->flags & FINISHED) == 0 && !p->thread_blocked && + p->stop_info.mach_thread != my_thread) { + GC_acquire_dirty_lock(); + do { + kern_result = thread_suspend(p->stop_info.mach_thread); + } while (kern_result == KERN_ABORTED); + GC_release_dirty_lock(); + if (kern_result != KERN_SUCCESS) + ABORT("thread_suspend failed"); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, + (void *)(word)p->stop_info.mach_thread); + } + } + } + } + +# ifdef MPROTECT_VDB + if (GC_auto_incremental) { + GC_mprotect_stop(); + } +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif + +# ifdef DEBUG_THREADS + GC_log_printf("World stopped from %p\n", (void *)(word)my_thread); +# endif + mach_port_deallocate(my_task, my_thread); +} + +GC_INLINE void GC_thread_resume(thread_act_t thread) +{ + kern_return_t kern_result; +# if defined(DEBUG_THREADS) || defined(GC_ASSERTIONS) + struct thread_basic_info info; + mach_msg_type_number_t outCount = THREAD_BASIC_INFO_COUNT; + +# ifdef CPPCHECK + info.run_state = 0; +# endif + kern_result = thread_info(thread, THREAD_BASIC_INFO, + (thread_info_t)&info, &outCount); + if (kern_result != KERN_SUCCESS) + ABORT("thread_info failed"); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("Resuming thread %p with state %d\n", (void *)(word)thread, + info.run_state); +# endif + /* Resume the thread */ + kern_result = thread_resume(thread); + if (kern_result != KERN_SUCCESS) { + WARN("thread_resume(%p) failed: mach port invalid\n", thread); + } else if (GC_on_thread_event) { + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)(word)thread); + } +} + +/* Caller holds allocation lock, and has held it continuously since */ +/* the world stopped. */ +GC_INNER void GC_start_world(void) +{ + task_t my_task = current_task(); +# ifdef DEBUG_THREADS + GC_log_printf("World starting\n"); +# endif +# ifdef MPROTECT_VDB + if (GC_auto_incremental) { + GC_mprotect_resume(); + } +# endif + + if (GC_query_task_threads) { +# ifndef GC_NO_THREADS_DISCOVERY + int i, j; + kern_return_t kern_result; + thread_act_array_t act_list; + mach_msg_type_number_t listcount; + + kern_result = task_threads(my_task, &act_list, &listcount); + if (kern_result != KERN_SUCCESS) + ABORT("task_threads failed"); + + j = (int)listcount; + for (i = 0; i < GC_mach_threads_count; i++) { + thread_act_t thread = GC_mach_threads[i].thread; + + if (GC_mach_threads[i].suspended) { + int last_found = j; /* The thread index found during the */ + /* previous iteration (count value */ + /* means no thread found yet). */ + + /* Search for the thread starting from the last found one first. */ + while (++j < (int)listcount) { + if (act_list[j] == thread) + break; + } + if (j >= (int)listcount) { + /* If not found, search in the rest (beginning) of the list. */ + for (j = 0; j < last_found; j++) { + if (act_list[j] == thread) + break; + } + } + if (j != last_found) { + /* The thread is alive, resume it. */ + GC_thread_resume(thread); + } + } else { + /* This thread was failed to be suspended by GC_stop_world, */ + /* no action needed. */ +# ifdef DEBUG_THREADS + GC_log_printf("Not resuming thread %p as it is not suspended\n", + (void *)(word)thread); +# endif + } + mach_port_deallocate(my_task, thread); + } + + for (i = 0; i < (int)listcount; i++) + mach_port_deallocate(my_task, act_list[i]); + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); +# endif /* !GC_NO_THREADS_DISCOVERY */ + + } else { + int i; + mach_port_t my_thread = mach_thread_self(); + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + for (p = GC_threads[i]; p != NULL; p = p->next) { + if ((p->flags & FINISHED) == 0 && !p->thread_blocked && + p->stop_info.mach_thread != my_thread) + GC_thread_resume(p->stop_info.mach_thread); + } + } + + mach_port_deallocate(my_task, my_thread); + } + +# ifdef DEBUG_THREADS + GC_log_printf("World started\n"); +# endif +} + +#endif /* GC_DARWIN_THREADS */ diff --git a/bdwgc/dbg_mlc.c b/bdwgc/dbg_mlc.c new file mode 100644 index 000000000..e555da15c --- /dev/null +++ b/bdwgc/dbg_mlc.c @@ -0,0 +1,1221 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2007 Free Software Foundation, Inc + * + * 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. + */ + +#include "private/dbg_mlc.h" + +#ifndef MSWINCE +# include +#endif +#include + +#ifndef SHORT_DBG_HDRS + /* Check whether object with base pointer p has debugging info. */ + /* p is assumed to point to a legitimate object in our part */ + /* 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, 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 */ + /* marked as deallocated. */ + GC_INNER int GC_has_other_debug_info(ptr_t p) + { + ptr_t body = (ptr_t)((oh *)p + 1); + word sz = GC_size(p); + + if (HBLKPTR(p) != HBLKPTR((ptr_t)body) + || sz < DEBUG_BYTES + EXTRA_BYTES) { + return 0; + } + if (((oh *)p) -> oh_sf != (START_FLAG ^ (word)body) + && ((word *)p)[BYTES_TO_WORDS(sz)-1] != (END_FLAG ^ (word)body)) { + return 0; + } + if (((oh *)p)->oh_sz == sz) { + /* Object may have had debug info, but has been deallocated */ + return -1; + } + return 1; + } +#endif /* !SHORT_DBG_HDRS */ + +#ifdef LINT2 + long GC_random(void) + { + static unsigned seed = 1; /* not thread-safe */ + + /* Linear congruential pseudo-random numbers generator. */ + seed = (seed * 1103515245U + 12345) & GC_RAND_MAX; /* overflow is ok */ + return (long)seed; + } +#endif + +#ifdef KEEP_BACK_PTRS + +#ifdef LINT2 +# define RANDOM() GC_random() +#else +# include +# define GC_RAND_MAX RAND_MAX + +# if defined(__GLIBC__) || defined(SOLARIS) \ + || defined(HPUX) || defined(IRIX5) || defined(OSF1) +# define RANDOM() random() +# else +# define RANDOM() (long)rand() +# endif +#endif /* !LINT2 */ + + /* Store back pointer to source in dest, if that appears to be possible. */ + /* This is not completely safe, since we may mistakenly conclude that */ + /* dest has a debugging wrapper. But the error probability is very */ + /* small, and this shouldn't be used in production code. */ + /* We assume that dest is the real base pointer. Source will usually */ + /* be a pointer to the interior of an object. */ + GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest) + { + if (GC_HAS_DEBUG_INFO(dest)) { +# ifdef PARALLEL_MARK + AO_store((volatile AO_t *)&((oh *)dest)->oh_back_ptr, + (AO_t)HIDE_BACK_PTR(source)); +# else + ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source); +# endif + } + } + + GC_INNER void GC_marked_for_finalization(ptr_t dest) + { + GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest); + } + + /* Store information about the object referencing dest in *base_p */ + /* and *offset_p. */ + /* source is root ==> *base_p = address, *offset_p = 0 */ + /* source is heap object ==> *base_p != 0, *offset_p = offset */ + /* Returns 1 on success, 0 if source couldn't be determined. */ + /* Dest can be any address within a heap object. */ + GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void *dest, void **base_p, + size_t *offset_p) + { + oh * hdr = (oh *)GC_base(dest); + ptr_t bp; + ptr_t bp_base; + +# ifdef LINT2 + /* Explicitly instruct the code analysis tool that */ + /* GC_get_back_ptr_info is not expected to be called with an */ + /* incorrect "dest" value. */ + if (!hdr) ABORT("Invalid GC_get_back_ptr_info argument"); +# endif + if (!GC_HAS_DEBUG_INFO((ptr_t) hdr)) return GC_NO_SPACE; + bp = (ptr_t)GC_REVEAL_POINTER(hdr -> oh_back_ptr); + if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD; + if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG; + if (NOT_MARKED == bp) return GC_UNREFERENCED; +# if ALIGNMENT == 1 + /* Heuristically try to fix off by 1 errors we introduced by */ + /* insisting on even addresses. */ + { + ptr_t alternate_ptr = bp + 1; + ptr_t target = *(ptr_t *)bp; + ptr_t alternate_target = *(ptr_t *)alternate_ptr; + + if ((word)alternate_target > GC_least_real_heap_addr + && (word)alternate_target < GC_greatest_real_heap_addr + && ((word)target <= GC_least_real_heap_addr + || (word)target >= GC_greatest_real_heap_addr)) { + bp = alternate_ptr; + } + } +# endif + bp_base = (ptr_t)GC_base(bp); + if (NULL == bp_base) { + *base_p = bp; + *offset_p = 0; + return GC_REFD_FROM_ROOT; + } else { + if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh); + *base_p = bp_base; + *offset_p = bp - bp_base; + return GC_REFD_FROM_HEAP; + } + } + + /* Generate a random heap address. */ + /* The resulting address is in the heap, but */ + /* not necessarily inside a valid object. */ + GC_API void * GC_CALL GC_generate_random_heap_address(void) + { + size_t i; + word heap_offset = RANDOM(); + + if (GC_heapsize > GC_RAND_MAX) { + heap_offset *= GC_RAND_MAX; + heap_offset += RANDOM(); + } + heap_offset %= GC_heapsize; + /* This doesn't yield a uniform distribution, especially if */ + /* e.g. RAND_MAX = 1.5* GC_heapsize. But for typical cases, */ + /* it's not too bad. */ + for (i = 0;; ++i) { + size_t size; + + if (i >= GC_n_heap_sects) + ABORT("GC_generate_random_heap_address: size inconsistency"); + + size = GC_heap_sects[i].hs_bytes; + if (heap_offset < size) { + break; + } else { + heap_offset -= size; + } + } + return GC_heap_sects[i].hs_start + heap_offset; + } + + /* Generate a random address inside a valid marked heap object. */ + GC_API void * GC_CALL GC_generate_random_valid_address(void) + { + ptr_t result; + ptr_t base; + do { + result = (ptr_t)GC_generate_random_heap_address(); + base = (ptr_t)GC_base(result); + } while (NULL == base || !GC_is_marked(base)); + return result; + } + + /* Print back trace for p */ + GC_API void GC_CALL GC_print_backtrace(void *p) + { + void *current = p; + int i; + GC_ref_kind source; + size_t offset; + void *base; + + GC_print_heap_obj((ptr_t)GC_base(current)); + + for (i = 0; ; ++i) { + source = GC_get_back_ptr_info(current, &base, &offset); + if (GC_UNREFERENCED == source) { + GC_err_printf("Reference could not be found\n"); + goto out; + } + if (GC_NO_SPACE == source) { + GC_err_printf("No debug info in object: Can't find reference\n"); + goto out; + } + GC_err_printf("Reachable via %d levels of pointers from ", i); + switch(source) { + case GC_REFD_FROM_ROOT: + GC_err_printf("root at %p\n\n", base); + goto out; + case GC_REFD_FROM_REG: + GC_err_printf("root in register\n\n"); + goto out; + case GC_FINALIZER_REFD: + GC_err_printf("list of finalizable objects\n\n"); + goto out; + case GC_REFD_FROM_HEAP: + 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((ptr_t)GC_base(base)); + break; + default: + GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n"); + goto out; + } + current = base; + } + out:; + } + + GC_API void GC_CALL GC_generate_random_backtrace(void) + { + void *current; + DCL_LOCK_STATE; + + if (GC_try_to_collect(GC_never_stop_func) == 0) { + GC_err_printf("Cannot generate a backtrace: " + "garbage collection is disabled!\n"); + return; + } + + /* Generate/print a backtrace from a random heap address. */ + LOCK(); + current = GC_generate_random_valid_address(); + UNLOCK(); + GC_printf("\n****Chosen address %p in object\n", current); + GC_print_backtrace(current); + } + +#endif /* KEEP_BACK_PTRS */ + +# define CROSSES_HBLK(p, sz) \ + (((word)((p) + sizeof(oh) + (sz) - 1) ^ (word)(p)) >= HBLKSIZE) + +GC_INNER void *GC_store_debug_info_inner(void *p, word sz GC_ATTR_UNUSED, + const char *string, int linenum) +{ + word * result = (word *)((oh *)p + 1); + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); + GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK((ptr_t)p, sz))); +# ifdef KEEP_BACK_PTRS + ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED); +# endif +# ifdef MAKE_BACK_GRAPH + ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0); +# endif + ((oh *)p) -> oh_string = string; + ((oh *)p) -> oh_int = linenum; +# ifndef SHORT_DBG_HDRS + ((oh *)p) -> oh_sz = sz; + ((oh *)p) -> oh_sf = START_FLAG ^ (word)result; + ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] = + result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result; +# endif + return result; +} + +/* Check the allocation is successful, store debugging info into p, */ +/* start the debugging mode (if not yet), and return displaced pointer. */ +static void *store_debug_info(void *p, size_t lb, + const char *fn, GC_EXTRA_PARAMS) +{ + void *result; + DCL_LOCK_STATE; + + if (NULL == p) { + GC_err_printf("%s(%lu) returning NULL (%s:%d)\n", + fn, (unsigned long)lb, s, i); + return NULL; + } + LOCK(); + if (!GC_debugging_started) + GC_start_debugging_inner(); + ADD_CALL_CHAIN(p, ra); + result = GC_store_debug_info_inner(p, (word)lb, s, i); + UNLOCK(); + return result; +} + +#ifndef SHORT_DBG_HDRS + /* Check the object with debugging info at ohdr. */ + /* Return NULL if it's OK. Else return clobbered */ + /* address. */ + STATIC ptr_t GC_check_annotated_obj(oh *ohdr) + { + ptr_t body = (ptr_t)(ohdr + 1); + word gc_sz = GC_size((ptr_t)ohdr); + if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) { + return((ptr_t)(&(ohdr -> oh_sz))); + } + if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) { + return((ptr_t)(&(ohdr -> oh_sf))); + } + if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) { + return (ptr_t)(&((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1]); + } + if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)] + != (END_FLAG ^ (word)body)) { + return (ptr_t)(&((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz)]); + } + return(0); + } +#endif /* !SHORT_DBG_HDRS */ + +STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0}; + +GC_API void GC_CALL GC_register_describe_type_fn(int kind, + GC_describe_type_fn fn) +{ + GC_describe_type_fns[kind] = fn; +} + +#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) +{ + oh * ohdr = (oh *)GC_base(p); + ptr_t q; + hdr * hhdr; + int kind; + const char *kind_str; + char buffer[GC_TYPE_DESCR_LEN + 1]; + + 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])(q, buffer); + GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0); + kind_str = buffer; + } else { + switch(kind) { + case PTRFREE: + kind_str = "PTRFREE"; + break; + case NORMAL: + kind_str = "NORMAL"; + break; + case UNCOLLECTABLE: + kind_str = "UNCOLLECTABLE"; + break; +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + kind_str = "ATOMIC_UNCOLLECTABLE"; + break; +# endif + default: + kind_str = NULL; + /* The alternative is to use snprintf(buffer) but it is */ + /* not quite portable (see vsnprintf in misc.c). */ + } + } + + if (NULL != kind_str) { + GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz= %lu,") " %s)\n", + (void *)((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", + (void *)((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); +} + +STATIC void GC_debug_print_heap_obj_proc(ptr_t p) +{ + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (GC_HAS_DEBUG_INFO(p)) { + GC_print_obj(p); + } else { + GC_default_print_heap_obj_proc(p); + } +} + +#ifndef SHORT_DBG_HDRS + /* Use GC_err_printf and friends to print a description of the object */ + /* whose client-visible address is p, and which was smashed at */ + /* clobbered_addr. */ + STATIC void GC_print_smashed_obj(const char *msg, void *p, + ptr_t clobbered_addr) + { + oh * ohdr = (oh *)GC_base(p); + + GC_ASSERT(I_DONT_HOLD_LOCK()); +# ifdef LINT2 + if (!ohdr) ABORT("Invalid GC_print_smashed_obj argument"); +# endif + if ((word)clobbered_addr <= (word)(&ohdr->oh_sz) + || ohdr -> oh_string == 0) { + GC_err_printf( + "%s %p in or near object at %p(, appr. sz= %lu)\n", + msg, (void *)clobbered_addr, p, + (unsigned long)(GC_size((ptr_t)ohdr) - DEBUG_BYTES)); + } else { + GC_err_printf("%s %p in or near object at %p (%s:%d, sz= %lu)\n", + msg, (void *)clobbered_addr, p, + (word)(ohdr -> oh_string) < HBLKSIZE ? "(smashed string)" : + ohdr -> oh_string[0] == '\0' ? "EMPTY(smashed?)" : + ohdr -> oh_string, + GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz)); + PRINT_CALL_CHAIN(ohdr); + } + } + + STATIC void GC_check_heap_proc (void); + STATIC void GC_print_all_smashed_proc (void); +#else + STATIC void GC_do_nothing(void) {} +#endif + +GC_INNER 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; +# else + GC_check_heap = GC_do_nothing; + GC_print_all_smashed = GC_do_nothing; +# endif + GC_print_heap_obj = GC_debug_print_heap_obj_proc; + GC_debugging_started = TRUE; + GC_register_displacement_inner((word)sizeof(oh)); +# if defined(CPPCHECK) + GC_noop1(GC_debug_header_size); +# endif +} + +const size_t GC_debug_header_size = sizeof(oh); + +GC_API size_t GC_CALL GC_get_debug_header_size(void) { + return sizeof(oh); +} + +GC_API void GC_CALL GC_debug_register_displacement(size_t 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_HAVE_RETURN_ADDR_PARENT) +# include + + 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 GC_ATTR_MALLOC 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. */ +# if defined(_FORTIFY_SOURCE) && !defined(__clang__) + /* Workaround to avoid "exceeds maximum object size" gcc warning. */ + result = GC_malloc(lb < GC_SIZE_MAX - DEBUG_BYTES ? lb + DEBUG_BYTES + : GC_SIZE_MAX >> 1); +# else + result = GC_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES)); +# endif +# ifdef GC_ADD_CALLER + if (s == NULL) { + GC_caller_func_offset(ra, &s, &i); + } +# endif + return store_debug_info(result, lb, "GC_debug_malloc", OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_ignore_off_page(SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_ignore_off_page", + OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_atomic_ignore_off_page( + SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, + "GC_debug_malloc_atomic_ignore_off_page", + OPT_RA s, i); +} + +STATIC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS) +{ + void * result = GC_generic_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES), knd); + + return store_debug_info(result, lb, "GC_debug_generic_malloc", + OPT_RA s, i); +} + +#ifdef DBG_HDRS_ALL + /* An allocation function for internal use. Normally internally */ + /* allocated objects do not have debug information. But in this */ + /* case, we need to make sure that all objects have debug headers. */ + GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k) + { + void * result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), k); + if (NULL == result) { + GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n", + (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)); + } + + GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, + int k) + { + void * result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_generic_malloc_inner_ignore_off_page( + SIZET_SAT_ADD(lb, DEBUG_BYTES), k); + if (NULL == result) { + GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n", + (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)); + } +#endif /* DBG_HDRS_ALL */ + +#ifndef CPPCHECK + GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) + { + return GC_debug_malloc(lb, OPT_RA s, i); + } + + GC_API void GC_CALL GC_debug_change_stubborn( + const void * p GC_ATTR_UNUSED) {} +#endif /* !CPPCHECK */ + +GC_API void GC_CALL GC_debug_end_stubborn_change(const void *p) +{ + const void * q = GC_base_C(p); + + if (NULL == q) { + ABORT_ARG1("GC_debug_end_stubborn_change: bad arg", ": %p", p); + } + GC_end_stubborn_change(q); +} + +GC_API void GC_CALL GC_debug_ptr_store_and_dirty(void *p, const void *q) +{ + *(void **)GC_is_visible(p) = GC_is_valid_displacement((void *)q); + GC_debug_end_stubborn_change(p); + REACHABLE_AFTER_DIRTY(q); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_atomic(size_t lb, + GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_atomic(SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_atomic", + OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strdup(const char *str, + GC_EXTRA_PARAMS) +{ + char *copy; + size_t lb; + if (str == NULL) { + if (GC_find_leak) + GC_err_printf("strdup(NULL) behavior is undefined\n"); + return NULL; + } + + lb = strlen(str) + 1; + copy = (char *)GC_debug_malloc_atomic(lb, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strndup(const char *str, + size_t size, GC_EXTRA_PARAMS) +{ + char *copy; + size_t len = strlen(str); /* str is expected to be non-NULL */ + if (len > size) + len = size; + copy = (char *)GC_debug_malloc_atomic(len + 1, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + if (len > 0) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; +} + +#ifdef GC_REQUIRE_WCSDUP +# include /* for wcslen() */ + + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL GC_debug_wcsdup(const wchar_t *str, + GC_EXTRA_PARAMS) + { + size_t lb = (wcslen(str) + 1) * sizeof(wchar_t); + wchar_t *copy = (wchar_t *)GC_debug_malloc_atomic(lb, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; + } +#endif /* GC_REQUIRE_WCSDUP */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_uncollectable(size_t lb, + GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_uncollectable( + SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_uncollectable", + OPT_RA s, i); +} + +#ifdef GC_ATOMIC_UNCOLLECTABLE + GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS) + { + void * result = GC_malloc_atomic_uncollectable( + SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES)); + + return store_debug_info(result, lb, + "GC_debug_malloc_atomic_uncollectable", + OPT_RA s, i); + } +#endif /* GC_ATOMIC_UNCOLLECTABLE */ + +#ifndef GC_FREED_MEM_MARKER +# if CPP_WORDSZ == 32 +# define GC_FREED_MEM_MARKER 0xdeadbeef +# else +# define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef) +# endif +#endif + +GC_API void GC_CALL GC_debug_free(void * p) +{ + ptr_t base; + if (0 == p) return; + + base = (ptr_t)GC_base(p); + if (NULL == base) { +# if defined(REDIRECT_MALLOC) \ + && ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \ + || defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(MSWIN32)) + /* In some cases, we should ignore objects that do not belong */ + /* to the GC heap. See the comment in GC_free. */ + if (!GC_is_heap_ptr(p)) return; +# endif + ABORT_ARG1("Invalid pointer passed to free()", ": %p", p); + } + if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { +# if defined(REDIRECT_FREE) && defined(USE_PROC_FOR_LIBRARIES) + /* TODO: Suppress the warning if free() caller is in libpthread */ + /* or libdl. */ +# endif + GC_err_printf( + "GC_debug_free called on pointer %p w/o debugging info\n", p); + } else { +# ifndef SHORT_DBG_HDRS + ptr_t clobbered = GC_check_annotated_obj((oh *)base); + word sz = GC_size(base); + if (clobbered != 0) { + GC_SET_HAVE_ERRORS(); /* no "release" barrier is needed */ + if (((oh *)base) -> oh_sz == sz) { + GC_print_smashed_obj( + "GC_debug_free: found previously deallocated (?) object at", + p, clobbered); + return; /* ignore double free */ + } else { + GC_print_smashed_obj("GC_debug_free: found smashed location at", + p, clobbered); + } + } + /* Invalidate size (mark the object as deallocated) */ + ((oh *)base) -> oh_sz = sz; +# endif /* !SHORT_DBG_HDRS */ + } + if (GC_find_leak +# ifndef SHORT_DBG_HDRS + && ((ptr_t)p - (ptr_t)base != sizeof(oh) || !GC_findleak_delay_free) +# endif + ) { + GC_free(base); + } else { + hdr * hhdr = HDR(p); + if (hhdr -> hb_obj_kind == UNCOLLECTABLE +# ifdef GC_ATOMIC_UNCOLLECTABLE + || hhdr -> hb_obj_kind == AUNCOLLECTABLE +# endif + ) { + GC_free(base); + } else { + word i; + word sz = hhdr -> hb_sz; + word obj_sz = BYTES_TO_WORDS(sz - sizeof(oh)); + + for (i = 0; i < obj_sz; ++i) + ((word *)p)[i] = GC_FREED_MEM_MARKER; + GC_ASSERT((word *)p + i == (word *)(base + sz)); + /* Update the counter even though the real deallocation */ + /* is deferred. */ + LOCK(); + GC_bytes_freed += sz; + UNLOCK(); + } + } /* !GC_find_leak */ +} + +#if defined(THREADS) && defined(DBG_HDRS_ALL) + /* Used internally; we assume it's called correctly. */ + GC_INNER void GC_debug_free_inner(void * p) + { + ptr_t base = (ptr_t)GC_base(p); + GC_ASSERT((ptr_t)p - (ptr_t)base == sizeof(oh)); +# ifdef LINT2 + if (!base) ABORT("Invalid GC_debug_free_inner argument"); +# endif +# ifndef SHORT_DBG_HDRS + /* Invalidate size */ + ((oh *)base) -> oh_sz = GC_size(base); +# endif + GC_free_inner(base); + } +#endif + +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 (0 == lb) /* and p != NULL */ { + GC_debug_free(p); + return NULL; + } + +# ifdef GC_ADD_CALLER + if (s == NULL) { + GC_caller_func_offset(ra, &s, &i); + } +# endif + base = GC_base(p); + if (base == 0) { + ABORT_ARG1("Invalid pointer passed to realloc()", ": %p", p); + } + if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { + GC_err_printf( + "GC_debug_realloc called on pointer %p w/o debugging info\n", p); + return(GC_realloc(p, lb)); + } + hhdr = HDR(base); + switch (hhdr -> hb_obj_kind) { + case NORMAL: + result = GC_debug_malloc(lb, OPT_RA s, i); + break; + case PTRFREE: + result = GC_debug_malloc_atomic(lb, OPT_RA s, i); + break; + case UNCOLLECTABLE: + result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i); + break; +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); + break; +# endif + default: + result = NULL; /* initialized to prevent warning. */ + ABORT_RET("GC_debug_realloc: encountered bad kind"); + } + + if (result != NULL) { + size_t old_sz; +# ifdef SHORT_DBG_HDRS + old_sz = GC_size(base) - sizeof(oh); +# else + old_sz = ((oh *)base) -> oh_sz; +# endif + if (old_sz > 0) + BCOPY(p, result, old_sz < lb ? old_sz : lb); + GC_debug_free(p); + } + return(result); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_generic_or_special_malloc(size_t lb, int knd, GC_EXTRA_PARAMS) +{ + switch (knd) { + case PTRFREE: + return GC_debug_malloc_atomic(lb, OPT_RA s, i); + case NORMAL: + return GC_debug_malloc(lb, OPT_RA s, i); + case UNCOLLECTABLE: + return GC_debug_malloc_uncollectable(lb, OPT_RA s, i); +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + return GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); +# endif + default: + return GC_debug_generic_malloc(lb, knd, OPT_RA s, i); + } +} + +#ifndef SHORT_DBG_HDRS + +/* List of smashed (clobbered) locations. We defer printing these, */ +/* since we can't always print them nicely with the allocation lock */ +/* held. We put them here instead of in GC_arrays, since it may be */ +/* useful to be able to look at them with the debugger. */ +#ifndef MAX_SMASHED +# define MAX_SMASHED 20 +#endif +STATIC ptr_t GC_smashed[MAX_SMASHED] = {0}; +STATIC unsigned GC_n_smashed = 0; + +STATIC void GC_add_smashed(ptr_t smashed) +{ + GC_ASSERT(GC_is_marked(GC_base(smashed))); + /* FIXME: Prevent adding an object while printing smashed list. */ + GC_smashed[GC_n_smashed] = smashed; + if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed; + /* In case of overflow, we keep the first MAX_SMASHED-1 */ + /* entries plus the last one. */ + GC_SET_HAVE_ERRORS(); +} + +/* Print all objects on the list. Clear the list. */ +STATIC void GC_print_all_smashed_proc(void) +{ + unsigned i; + + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (GC_n_smashed == 0) return; + GC_err_printf("GC_check_heap_block: found %u smashed heap objects:\n", + GC_n_smashed); + for (i = 0; i < GC_n_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; +} + +/* Check all marked objects in the given block for validity */ +/* Avoid GC_apply_to_each_object for performance reasons. */ +STATIC void GC_check_heap_block(struct hblk *hbp, word dummy GC_ATTR_UNUSED) +{ + struct hblkhdr * hhdr = HDR(hbp); + word sz = hhdr -> hb_sz; + word bit_no; + char *p, *plim; + + p = hbp->hb_body; + if (sz > MAXOBJBYTES) { + plim = p; + } else { + plim = hbp->hb_body + HBLKSIZE - sz; + } + /* go through all words in block */ + 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) + GC_add_smashed(clobbered); + } + } +} + +/* This assumes that all accessible objects are marked, and that */ +/* I hold the allocation lock. Normally called by collector. */ +STATIC void GC_check_heap_proc(void) +{ + GC_STATIC_ASSERT((sizeof(oh) & (GRANULE_BYTES - 1)) == 0); + /* FIXME: Should we check for twice that alignment? */ + GC_apply_to_all_blocks(GC_check_heap_block, 0); +} + +GC_INNER GC_bool GC_check_leaked(ptr_t base) +{ + word i; + word obj_sz; + word *p; + + if ( +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + (*(word *)base & 1) != 0 && +# endif + GC_has_other_debug_info(base) >= 0) + return TRUE; /* object has leaked */ + + /* Validate freed object's content. */ + p = (word *)(base + sizeof(oh)); + obj_sz = BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh)); + for (i = 0; i < obj_sz; ++i) + if (p[i] != GC_FREED_MEM_MARKER) { + GC_set_mark_bit(base); /* do not reclaim it in this cycle */ + GC_add_smashed((ptr_t)(&p[i])); /* alter-after-free detected */ + break; /* don't report any other smashed locations in the object */ + } + + return FALSE; /* GC_debug_free() has been called */ +} + +#endif /* !SHORT_DBG_HDRS */ + +#ifndef GC_NO_FINALIZATION + +struct closure { + GC_finalization_proc cl_fn; + void * cl_data; +}; + +STATIC void * GC_make_closure(GC_finalization_proc fn, void * data) +{ + struct closure * result = +# ifdef DBG_HDRS_ALL + (struct closure *) GC_debug_malloc(sizeof (struct closure), + GC_EXTRAS); +# else + (struct closure *) GC_malloc(sizeof (struct closure)); +# endif + if (result != 0) { + result -> cl_fn = fn; + result -> cl_data = data; + } + return((void *)result); +} + +/* An auxiliary fns to make finalization work correctly with displaced */ +/* pointers introduced by the debugging allocators. */ +STATIC void GC_CALLBACK GC_debug_invoke_finalizer(void * obj, void * data) +{ + struct closure * cl = (struct closure *) data; + (*(cl -> cl_fn))((void *)((char *)obj + sizeof(oh)), cl -> cl_data); +} + +/* Special finalizer_proc value to detect GC_register_finalizer() failure. */ +#define OFN_UNSET ((GC_finalization_proc)~(signed_word)0) + +/* Set ofn and ocd to reflect the values we got back. */ +static void store_old(void *obj, GC_finalization_proc my_old_fn, + struct closure *my_old_cd, GC_finalization_proc *ofn, + void **ocd) +{ + if (0 != my_old_fn) { + if (my_old_fn == OFN_UNSET) { + /* GC_register_finalizer() failed; (*ofn) and (*ocd) are unchanged. */ + return; + } + if (my_old_fn != GC_debug_invoke_finalizer) { + GC_err_printf("Debuggable object at %p had a non-debug finalizer\n", + obj); + /* This should probably be fatal. */ + } else { + if (ofn) *ofn = my_old_cd -> cl_fn; + if (ocd) *ocd = my_old_cd -> cl_data; + } + } else { + if (ofn) *ofn = 0; + if (ocd) *ocd = 0; + } +} + +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 my_old_fn = OFN_UNSET; + void * my_old_cd = NULL; /* to avoid "might be uninitialized" warning */ + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + 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); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory; *ofn and *ocd are unchanged */ + GC_register_finalizer(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +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 my_old_fn = OFN_UNSET; + void * my_old_cd = NULL; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + 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); + } + if (0 == fn) { + GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +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 my_old_fn = OFN_UNSET; + void * my_old_cd = NULL; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + 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); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +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 my_old_fn = OFN_UNSET; + void * my_old_cd = NULL; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + 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); + } + if (0 == fn) { + GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +#endif /* !GC_NO_FINALIZATION */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_replacement(size_t lb) +{ + 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_EXTRAS); +} diff --git a/bdwgc/digimars.mak b/bdwgc/digimars.mak new file mode 100644 index 000000000..4c91dceca --- /dev/null +++ b/bdwgc/digimars.mak @@ -0,0 +1,102 @@ +# Makefile to build Hans Boehm garbage collector using the Digital Mars +# compiler from www.digitalmars.com +# Written by Walter Bright + +DEFINES=-D_WINDOWS -DGC_DLL -DGC_THREADS -DGC_DISCOVER_TASK_THREADS -DALL_INTERIOR_POINTERS -DENABLE_DISCLAIM -DGC_ATOMIC_UNCOLLECTABLE -DGC_GCJ_SUPPORT -DJAVA_FINALIZATION -DNO_EXECUTE_PERMISSION -DUSE_MUNMAP +CFLAGS=-Iinclude -Ilibatomic_ops\src $(DEFINES) -wx -g +LFLAGS=/ma/implib/co +CC=sc + +.c.obj: + $(CC) -c $(CFLAGS) $* + +.cpp.obj: + $(CC) -c $(CFLAGS) -Aa $* + +OBJS= \ + allchblk.obj\ + alloc.obj\ + blacklst.obj\ + checksums.obj\ + dbg_mlc.obj\ + fnlz_mlc.obj\ + dyn_load.obj\ + finalize.obj\ + gc_badalc.obj\ + gc_cpp.obj\ + gcj_mlc.obj\ + headers.obj\ + mach_dep.obj\ + malloc.obj\ + mallocx.obj\ + mark.obj\ + mark_rts.obj\ + misc.obj\ + new_hblk.obj\ + obj_map.obj\ + os_dep.obj\ + ptr_chck.obj\ + reclaim.obj\ + typd_mlc.obj\ + win32_threads.obj + +targets: gc.dll gc.lib + +check: gctest.exe test_cpp.exe + gctest.exe + test_cpp.exe + +gc.lib: gc.dll + +gc.dll: $(OBJS) gc.def digimars.mak + $(CC) -ogc.dll $(OBJS) -L$(LFLAGS) gc.def kernel32.lib user32.lib + +gc.def: digimars.mak + echo LIBRARY GC >gc.def + echo DESCRIPTION "Boehm-Demers-Weiser Garbage Collector" >>gc.def + echo EXETYPE NT >>gc.def + echo EXPORTS >>gc.def + echo GC_is_visible_print_proc >>gc.def + echo GC_is_valid_displacement_print_proc >>gc.def + +clean: + del *.log gc.def gc.dll gc.lib gc.map gctest.map test_cpp.map + del tests\test.obj gctest.exe tests\test_cpp.obj test_cpp.exe + del $(OBJS) + +gctest.exe: gc.lib tests\test.obj + $(CC) -ogctest.exe tests\test.obj gc.lib + +tests\test.obj: tests\test.c + $(CC) -c $(CFLAGS) tests\test.c -otests\test.obj + +test_cpp.exe: gc.lib tests\test_cpp.obj + $(CC) -otest_cpp.exe tests\test_cpp.obj gc.lib + +tests\test_cpp.obj: tests\test_cpp.cc + $(CC) -c $(CFLAGS) -cpp tests\test_cpp.cc -otests\test_cpp.obj + +allchblk.obj: allchblk.c +alloc.obj: alloc.c +blacklst.obj: blacklst.c +checksums.obj: checksums.c +dbg_mlc.obj: dbg_mlc.c +dyn_load.obj: dyn_load.c +finalize.obj: finalize.c +fnlz_mlc.obj: fnlz_mlc.c +gc_badalc.obj: gc_badalc.cc gc_badalc.cpp +gc_cpp.obj: gc_cpp.cc gc_cpp.cpp +headers.obj: headers.c +mach_dep.obj: mach_dep.c +malloc.obj: malloc.c +mallocx.obj: mallocx.c +mark.obj: mark.c +mark_rts.obj: mark_rts.c +misc.obj: misc.c +new_hblk.obj: new_hblk.c +obj_map.obj: obj_map.c +os_dep.obj: os_dep.c +ptr_chck.obj: ptr_chck.c +reclaim.obj: reclaim.c +typd_mlc.obj: typd_mlc.c +win32_threads.obj: win32_threads.c diff --git a/bdwgc/doc/README.DGUX386 b/bdwgc/doc/README.DGUX386 new file mode 100644 index 000000000..98f344c0c --- /dev/null +++ b/bdwgc/doc/README.DGUX386 @@ -0,0 +1,39 @@ + Garbage Collector (parallel version) for x86 DG/UX Release R4.20MU07 + + + *READ* the file README.QUICK. + + You need the GCC-3.0.3 rev (DG/UX) compiler to build this tree. + This compiler has the new "dgux386" threads package implemented. + It also supports the switch "-pthread" needed to link correctly + the DG/UX's -lrte -lthread with -lgcc and the system's -lc. + Finally, we support parallel mark for the SMP DG/UX machines. + To build the garbage collector do: + + ./configure + make + make gctest + + Before you run "gctest" you need to set your LD_LIBRARY_PATH + correctly so that "gctest" can find the shared library libgc. + Alternatively you can do a configuration + + ./configure --disable-shared + + to build only the static version of libgc. + + To enable debugging messages please do: + 1) Add the "--enable-gc-debug" flag during configuration. + 2) Pass "CFLAGS=-DDEBUG_THREADS" to "make". + + In a machine with 4 CPUs (my own machine), parallel mark makes + a BIG difference. + + Takis Psarogiannakopoulos + +Note (HB): + The integration of this patch is currently not complete. + The following patches against 6.1alpha3 where hard to move + to alpha4, and are not integrated. There may also be minor + problems with stylistic corrections made by me. +[The diff for ltconfig and ltmain.sh was removed from this file on 2011-08-22] diff --git a/bdwgc/doc/README.Mac b/bdwgc/doc/README.Mac new file mode 100644 index 000000000..018218eed --- /dev/null +++ b/bdwgc/doc/README.Mac @@ -0,0 +1,332 @@ +The contents of this file are old and pertain to pre-MacOSX versions. +You probably really wanted README.darwin. + +--------------------------------------------- + +Patrick Beard's Notes for building GC with CodeWarrior Pro 2: +---------------------------------------------------------------------------- +The current build environment for the collector is CodeWarrior Pro 2. +Projects for CodeWarrior Pro 2 (and for quite a few older versions) +are distributed in the file Mac_projects.sit.hqx. The project file +:Mac_projects:gc.prj builds static library versions of the collector. +:Mac_projects:gctest.prj builds the GC test suite. + +Configuring the collector is still done by editing the file +:extra:Mac_files:MacOS_config.h. + +Lars Farm's suggestions on building the collector: +---------------------------------------------------------------------------- +Garbage Collection on MacOS - a manual 'MakeFile' +------------------------------------------------- + +Project files and IDE's are great on the Macintosh, but they do have +problems when used as distribution media. This note tries to provide +porting instructions in pure TEXT form to avoid those problems. A manual +'makefile' if you like. + + Codewarrior: CWPro1 + date: 18 July 1997 + +The notes may or may not apply to earlier or later versions of the CWPro. + +This is just to record my experiences. These notes do not mean I now +provide a supported port of the GC to MacOS. It works for me. If it works +for you, great. If it doesn't, sorry, try again...;-) Still, if you find +errors, please let me know. + + +Porting to MacOS is a bit more complex than it first seems. Which MacOS? +68K/PowerPC? Which compiler? Each supports both 68K and PowerPC and offer a +large number of (unique to each environment) compiler settings. Each +combination of compiler/68K/PPC/settings require a unique combination of +standard libraries. And the IDE's does not select them for you. They don't +even check that the library is built with compatible setting and this is +the major source of problems when porting the GC (and otherwise too). + +You will have to make choices when you configure the GC. I've made some +choices here, but there are other combinations of settings and #defines +that work too. + +As for target settings the major obstacles may be: +- 68K Processor: check "4-byte Ints". +- PPC Processor: uncheck "Store Static Data in TOC". + +What you need to do: +1) Build the GC as a library +2) Test that the library works with 'test.c'. +3) Test that the C++ interface 'gc_cpp.cc/h' works with 'test_cpp.cc'. + +== 1. The Libraries == + +I made one project with four targets (68K/PPC tempmem or appheap). One target +will suffice if you're able to decide which one you want. I wasn't... + +Codewarrior allows a large number of compiler/linker settings. I used these: + +Settings shared by all targets: +------------------------------ +o Access Paths: + - User Paths: the GC folder + - System Paths: {Compiler}:Metrowerks Standard Library: + {Compiler}:MacOS Support:Headers: + {Compiler}:MacOS Support:MacHeaders: +o C/C++ language: + - inlining: normal + - direct to SOM: off + - enable/check: exceptions, RTTI, bool (and if you like pool strings) + +PowerPC target settings +----------------------- +o Target Settings: + - name of target + - MacOS PPC Linker +o PPC Target + - name of library +o C/C++ language + - prefix file as described below +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 it is OK +o PPC Linker + - Factory Settings (SYM file with full paths, faster linking, dead-strip + static init, Main: __start) + + +68K target settings +------------------- +o Target Settings: + - name of target + - MacOS 68K Linker +o 68K Target + - name of library + - A5 relative data +o C/C++ language + - prefix file as described below +o 68K Processor + - Code model: smart + - Struct alignment: 68K + - FP: SANE + - enable 4-Byte Ints -- important! + I don't think the others matter. I selected... + - enable: 68020 + - enable: global register allocation +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, + A6 Frames, fast link, Merge compiler glue into segment 1, + dead-strip static init) + +Prefix Files to configure the GC sources +---------------------------------------- +The Codewarrior equivalent of command-line compilers -DNAME=X is to use +prefix-files. A TEXT file that is automatically #included before the first byte +of every source file. I used these: + +---- ( cut here ) ---- gc_prefix_tempmem.h -- 68K and PPC ----- + #include "gc_prefix_common.h" + #undef USE_TEMPORARY_MEMORY + #define USE_TEMPORARY_MEMORY +---- ( cut here ) ---- gc_prefix_appmem.h -- 68K and PPC ----- + #include "gc_prefix_common.h" + #undef USE_TEMPORARY_MEMORY +// #define USE_TEMPORARY_MEMORY + +---- ( cut here ) ---- gc_prefix_common.h -------------------- +// gc_prefix_common.h +// ------------------ +// Codewarrior prefix file to configure the GC libraries +// +// prefix files are the Codewarrior equivalent of the +// command line option -Dname=x frequently seen in makefiles + +#if !__MWERKS__ + #error only tried this with Codewarrior +#endif + +#if macintosh + #define MSL_USE_PRECOMPILED_HEADERS 0 + #include + + // See list of #defines to configure the library in: 'MakeFile' + // see also README + + #define ALL_INTERIOR_POINTERS // follows interior pointers. +//#define DONT_ADD_BYTE_AT_END // disables the padding if defined. +//#define SMALL_CONFIG // whether to use a smaller heap. + #define GC_ATOMIC_UNCOLLECTABLE // GC_malloc_atomic_uncollectable() + + // define either or none as per personal preference + // used in malloc.c + #define REDIRECT_MALLOC GC_malloc +//#define REDIRECT_MALLOC GC_malloc_uncollectable + // if REDIRECT_MALLOC is #defined make sure that the GC library + // is listed before the ANSI/ISO libs in the Codewarrior + // 'Link order' panel +//#define IGNORE_FREE + + // mac specific configs +//#define USE_TEMPORARY_MEMORY // use Macintosh temporary memory. +//#define SHARED_LIBRARY_BUILD // build for use in a shared library. + +#else + // could build Win32 here too, or in the future + // Rhapsody PPC-mach, Rhapsody PPC-MacOS, + // Rhapsody Intel-mach, Rhapsody Intel-Win32,... + // ... ugh this will get messy ... +#endif + +// make sure ints are at least 32-bit +// ( could be set to 16-bit by compiler settings (68K) ) + +struct gc_private_assert_intsize_{ char x[ sizeof(int)>=4 ? 1 : 0 ]; }; + +#if __powerc + #if __option(toc_data) + #error turn off "store static data in TOC" when using GC + // ... or find a way to add TOC to the root set...(?) + #endif +#endif +---- ( cut here ) ---- end of gc_prefix_common.h ----------------- + +Files to build the GC libraries: +-------------------------------- + allchblk.c + alloc.c + blacklst.c + checksums.c + dbg_mlc.c + finalize.c + fnlz_mlc.c + headers.c + mach_dep.c + extra/MacOS.c -- contains MacOS code + malloc.c + mallocx.c + mark.c + mark_rts.c + misc.c + new_hblk.c + obj_map.c + os_dep.c -- contains MacOS code + ptr_chck.c + reclaim.c + typd_mlc.c + gc_badalc.cc + gc_cpp.cc + +== 2. Test that the library works with 'test.c' == + +The test app is just an ordinary ANSI-C console app. Make sure settings +match the library you're testing. + +Files +----- + test.c + the GC library to test -- link order before ANSI libs + suitable Mac+ANSI libraries + +prefix: +------ +---- ( cut here ) ---- gc_prefix_testlib.h -- all libs ----- +#define MSL_USE_PRECOMPILED_HEADERS 0 +#include +#undef NDEBUG + +#define ALL_INTERIOR_POINTERS /* for GC_priv.h */ +---- ( cut here ) ---- + +== 3. Test that the C++ interface 'gc_cpp.cc/h' works with 'test_cpp.cc' == + +The test app is just an ordinary ANSI-C console app. Make sure settings match +the library you're testing. + +Files +----- + test_cpp.cc + the GC library to test -- link order before ANSI libs + suitable Mac+ANSI libraries + +prefix: +------ +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 +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. + +After that, just add the proper GC library to your project and the GC is in +action! malloc will call GC_malloc and free GC_free, new/delete too. You +don't have to call free or delete. You may have to be a bit cautious about +delete if you're freeing other resources than RAM. See gc_cpp.h. You can +also keep coding as always with delete/free. That works too. If you want, +include "gc.h" and tweak its use a bit. + +== Symantec SPM == + +It has been a while since I tried the GC in SPM, but I think that the above +instructions should be sufficient to guide you through in SPM too. SPM +needs to know where the global data is. Use the files 'datastart.c' and +'dataend.c'. Put 'datastart.c' at the top of your project and 'dataend.c' +at the bottom of your project so that all data is surrounded. This is not +needed in Codewarrior because it provides intrinsic variables +__datastart__, __data_end__ that wraps all globals. + + + It worked for me, hope it works for you. + + Lars Farm +---------------------------------------------------------------------------- + + +Patrick Beard's instructions (may be dated): + +The collector should run under Symantec C++/THINK C v7.0.4, and +Metrowerks C/C++ v4.5 both 68K and PowerPC. Project files are provided +to build and test the collector under both development systems. + +Configuration +------------- + +To configure the collector, under both development systems, a prefix file +is used to set preprocessor directives. This file is called "MacOS_config.h". + +Testing +------- + +To test the collector (always a good idea), build one of the gctest projects, +gctest. (Symantec C++/THINK C), mw/gctest.68K, or mw/gctest.PPC. The +test will ask you how many times to run; 1 should be sufficient. + +Building +-------- + +For your convenience project files for the major Macintosh development +systems are provided. + +For Symantec C++/THINK C, you must build the two projects gclib-1 and +gclib-2. It has to be split up because the collector has more than 32 KB +of static data and no library can have more than this in the Symantec +environment. (Future versions will probably fix this.) + +For Metrowerks C/C++ 4.5 you build gc.68K/PPC and the result will +be a library called gc.68K.lib/gc.PPC.lib. + +Using +----- + +Under Symantec C++/THINK C, you can just add the gclib-1 and gclib-2 +projects to your own project. Under Metrowerks, you add gc.68K.lib or +gc.PPC.lib and two additional files. You add the files called datastart.c +and dataend.c to your project, bracketing all files that use the collector. +See mw/gctest for an example. + +Include the projects/libraries you built above into your own project, +#include "gc.h", and call GC_malloc. You don't have to call GC_free. + +Patrick C. Beard diff --git a/bdwgc/doc/README.OS2 b/bdwgc/doc/README.OS2 new file mode 100644 index 000000000..5aaf84c04 --- /dev/null +++ b/bdwgc/doc/README.OS2 @@ -0,0 +1,6 @@ +The code assumes static linking, and a single thread. The editor de has +not been ported. The cord test program has. The supplied OS2_MAKEFILE +assumes the IBM C Set/2 environment, but the code shouldn't. + +Since we haven't figured out how to do partial linking or to build static +libraries, clients currently need to link against a long list of executables. diff --git a/bdwgc/doc/README.amiga b/bdwgc/doc/README.amiga new file mode 100644 index 000000000..53efc9a62 --- /dev/null +++ b/bdwgc/doc/README.amiga @@ -0,0 +1,284 @@ + Kjetil S. Matheussen's notes (28-11-2000) + +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. + + +WHATS NEW: + +1. + Made a pretty big effort in preventing GC allocating-functions from + returning chip-mem. + + 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_ignore_off_page + 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 (e.g., GC_*_typed) can probably be + used without any problems, but beware that the warn hook will not be called. + In case of problems, don't define GC_AMIGA_FASTALLOC. + + Programs using more time actually using the memory allocated + (instead of just allocate and free rapidly) have + the most to earn on this, but even gctest now normally runs twice + as fast and uses less memory, on my poor 8 MB machine. + + The changes have only effect when there is no more + fast-mem left. But with the way GC works, it + could happen quite often. Beware that an atexit handler had to be added, + so using the abort() function will make a big memory-loss. + If you absolutely must call abort() instead of exit(), try calling + the GC_amiga_free_all_mem function before abort(). + + 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 + it will use normal calloc for allocation, and the rest + of the following flags will have no effect. + + GC_AMIGA_ONLYFAST - Makes GC never to return chip-mem. GC_AMIGA_RETRY have + no effect if this flag is set. + + 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 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 + there's either very little fast-memory left or very little + chip-memory left. It's not a very common situation, but gctest + sometimes (very rare) use many minutes because of this. + + GC_AMIGA_RETRY - If gc succeed allocating memory, but it is chip-mem, + try again and see if it is fast-mem. Most of the time, + it will actually return fast-mem for the second try. + I have set max number of retries to 9 or size/5000. You + can change this if you like. (see GC_amiga_rec_alloc()) + + GC_AMIGA_PRINTSTATS - Gather some statistics during the execution of a + program, and prints out the info when the atexit-handler + is called. + + My recommendation is to set all this flags, except GC_AMIGA_PRINTSTATS and + GC_AMIGA_ONLYFAST. + + If your program demands high response-time, you should + not define GC_AMIGA_GC, and possible also define GC_AMIGA_ONLYFAST. + GC_AMIGA_RETRY does not seem to slow down much. + + Also, when compiling up programs, and GC_AMIGA_FASTALLOC was not defined when + compiling gc, you can define GC_AMIGA_MAKINGLIB to avoid having these allocation- + functions wrapped. (see gc.h) + + Note that GC_realloc must not be called before any of + the other above mentioned allocating-functions have been called. (shouldn't be + any programs doing so either, I hope). + + Another note. The allocation-function is wrapped when defining + 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, e.g., like 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_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 + defining, e.g., GC_malloc as GC_amiga_malloc should work too). + + + New Amiga-specific function: + + void GC_amiga_set_toany(void (*func)(void)); + + 'func' is a function that will be called right before gc has to change + allocation-method from MEMF_FAST to MEMF_ANY. I.e., when it is likely + it will return chip-mem. + + +2. A few small compiler-specific additions to make it compile with SAS/C again. + +3. Updated and rewritten the SMakefile.amiga, so that it works again and that + 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 typing "smake -f SMakefile.amiga", both gc.lib and + cord.lib will be made. + + + +STILL MISSING: + +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). An iconx-script solves this problem. + + +BEWARE! + +-To run gctest, set the stack to around 200000 bytes first. +-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.). + + +tested with software: Radium, http://www.stud.ifi.uio.no/~ksvalast/radium/ +tested with hardware: MC68060 + + + Martin Tauchmann's notes (1-Apr-99) + +Works now, also with the GNU-C compiler V2.7.2.1. +Modify the `Makefile` +CC=cc $(ABI_FLAG) +to +CC=gcc $(ABI_FLAG) + +TECHNICAL NOTES + +- `GC_get_stack_base()`, `GC_register_data_segments()` works now with every + C compiler; also Workbench. + +- Removed AMIGA_SKIP_SEG, but the Code-Segment must not be scanned by GC. + + +PROBLEMS +- When the Linker, does`t merge all Code-Segments to an single one. LD of GCC + do it always. + +- With ixemul.library V47.3, when an GC program launched from another program + (example: `Make` or `if_mach M68K AMIGA gctest`), `GC_register_data_segments()` + found the Segment-List of the caller program. + Can be fixed, if the run-time initialization code (for C programs, usually *crt0*) + support `__data` and `__bss`. + +- PowerPC Amiga currently not supported. + +- Dynamic libraries (dyn_load.c) not supported. + + +TESTED WITH SOFTWARE + +`Optimized Oberon 2 C` (oo2c) + + +TESTED WITH HARDWARE + +MC68030 + + + Michel Schinz's notes + +WHO DID WHAT + +The original Amiga port was made by Jesper Peterson. I (Michel Schinz) +modified it slightly to reflect the changes made in the new official +distributions, and to take advantage of the new SAS/C 6.x features. I also +created a makefile to compile the "cord" package (see the cord +subdirectory). + +TECHNICAL NOTES + +In addition to Jesper's notes, I have the following to say: + +- gctest checks to see if the code segment is added to the root set or not, + and complains if it is. The only problem is that, as far as I know, it is + impossible to know which segments are code segments and which are data + segments (there are indeed solutions to this problem, like scanning the + program on disk or patch the LoadSeg functions, but they are rather + complicated). The solution I have chosen (see os_dep.c) is to test whether + the program counter is in the segment we are about to add to the root set, + and if it is, to skip the segment. The problems are that this solution is + rather awkward and that it works only for one code segment. This means that + if your program has more than one code segment, all of them but one will be + added to the root set. This isn't a big problem in fact, since the + collector will continue to work correctly, but it may be slower. + + Anyway, the code which decides whether to skip a segment or not can be + removed simply by not defining AMIGA_SKIP_SEG. But notice that if you do + so, gctest will complain (it will say that "GC_is_visible produced wrong + failure indication"). However, it may be useful if you happen to have + pointers stored in a code segment (you really shouldn't). + + If anyone has a good solution to the problem of finding, when a program + is loaded in memory, whether a segment is a code or a data segment, + please let me know. + + + Jesper Peterson's notes + +ADDITIONAL NOTES FOR AMIGA PORT + +These notes assume some familiarity with Amiga internals. + +WHY I PORTED TO THE AMIGA + +The sole reason why I made this port was as a first step in getting +the Sather(*) language on the Amiga. A port of this language will +be done as soon as the Sather 1.0 sources are made available to me. +Given this motivation, the garbage collection (GC) port is rather +minimal. + +(*) For information on Sather read the comp.lang.sather newsgroup. + +LIMITATIONS + +This port assumes that the startup code linked with target programs +is that supplied with SAS/C versions 6.0 or later. This allows +assumptions to be made about where to find the stack base pointer +and data segments when programs are run from WorkBench, as opposed +to running from the CLI. The compiler dependent code is all in the +GC_get_stack_base() and GC_register_data_segments() functions, but +may spread as I add Amiga specific features. + +Given that SAS/C was assumed, the port is set up to be built with +"smake" using the "SMakefile". Compiler options in "SCoptions" can +be set with "scopts" program. Both "smake" and "scopts" are part of +the SAS/C commercial development system. + +In keeping with the porting philosophy outlined above, this port +will not behave well with Amiga specific code. Especially not inter- +process communications via messages, and setting up public structures like +Intuition objects or anything else in the system lists. For the +time being the use of this library is limited to single threaded +ANSI/POSIX compliant or near-compliant code. (i.e., stick to stdio +for now). Given this limitation there is currently no mechanism for +allocating "CHIP" or "PUBLIC" memory under the garbage collector. +I'll add this after giving it considerable thought. The major +problem is the entire physical address space may have to me scanned, +since there is no telling who we may have passed memory to. + +If you allocate your own stack in client code, you will have to +assign the pointer plus stack size to GC_stackbottom. + +The initial stack size of the target program can be compiled in by +setting the __stack symbol (see SAS documentation). It can be over- +ridden from the CLI by running the AmigaDOS "stack" program, or from +the WorkBench by setting the stack size in the tool types window. + +SAS/C COMPILER OPTIONS (SCoptions) + +You may wish to check the "CPU" code option is appropriate for your +intended target system. + +Under no circumstances set the "StackExtend" code option in either +compiling the library or *ANY* client code. + +All benign compiler warnings have been suppressed. These mainly +involve lack of prototypes in the code, and dead assignments +detected by the optimizer. + +THE GOOD NEWS + +The library as it stands is compatible with the GigaMem commercial +virtual memory software, and probably similar PD software. + +The performance of "gctest" on an Amiga 2630 (68030 @ 25 MHz) +compares favorably with an HP9000 with similar architecture (a 325 +with a 68030 I think). diff --git a/bdwgc/doc/README.arm.cross b/bdwgc/doc/README.arm.cross new file mode 100644 index 000000000..9e60dda77 --- /dev/null +++ b/bdwgc/doc/README.arm.cross @@ -0,0 +1,62 @@ +From: Margaret Fleck + +Here's the key details of what worked for me, in case anyone else needs them. +There may well be better ways to do some of this, but .... + -- Margaret + + +The badge4 has a StrongArm-1110 processor and a StrongArm-1111 coprocessor. + +Assume that the garbage collector distribution is unpacked into /home/arm/gc, +which is visible to both the ARM machine and a linux desktop (e.g. via NFS mounting). + +Assume that you have a file /home/arm/config.site with contents something like the +example attached below. Notice that our local ARM toolchain lives in +/skiff/local. + +Go to /home/arm/gc directory. Do + CONFIG_SITE=/home/arm/config.site ./configure --target=arm-linux +--prefix=/home/arm/gc + +On your desktop, do: + make + make install +The main garbage collector library should now be in ../gc/lib/libgc.so. + +To test the garbage collector, first do the following on your desktop + make gctest + ./gctest +Then do the following on the ARM machine + cd .libs + ./lt-gctest + +Do not try to do "make check" (the usual way of running the test +program). This does not work and seems to erase some of the important +files. + +The gctest program claims to have succeeded. Haven't run any further tests +with it, though I'll be doing so in the near future. + +------------------------------- +# config.site for configure + +HOSTCC=gcc + +# Names of the cross-compilers +CC=/skiff/local/bin/arm-linux-gcc +CXX=/skiff/local/bin/arm-linux-gcc + +# The cross compiler specific options +CFLAGS="-O2 -fno-exceptions" +CXXFLAGS="-O2 -fno-exceptions" +CPPFLAGS="-O2 -fno-exceptions" +LDFLAGS="" + +# Some other programs +AR=/skiff/local/bin/arm-linux-ar +RANLIB=/skiff/local/bin/arm-linux-ranlib +NM=/skiff/local/bin/arm-linux-nm +ac_cv_path_NM=/skiff/local/bin/arm-linux-nm +ac_cv_func_setpgrp_void=yes +x_includes=/skiff/local/arm-linux/include/X11 +x_libraries=/skiff/local/arm-linux/lib/X11 diff --git a/bdwgc/doc/README.autoconf b/bdwgc/doc/README.autoconf new file mode 100644 index 000000000..dd3734020 --- /dev/null +++ b/bdwgc/doc/README.autoconf @@ -0,0 +1,59 @@ +We support GNU-style builds based on automake, autoconf and libtool. +This is based almost entirely on Tom Tromey's work with gcj. + +To build and install libraries use + +configure; make; make install + +The advantages of this process are: + +1) It should eventually do a better job of automatically determining the +right compiler to use, etc. It probably already does in some cases. + +2) It tries to automatically set a good set of default GC parameters for +the platform (e.g. thread support). It provides an easier way to configure +some of the others. + +3) It integrates better with other projects using a GNU-style build process. + +4) It builds both dynamic and static libraries. + +The known disadvantages are: + +1) The build scripts are much more complex and harder to debug (though largely +standard). I don't understand them all, and there's probably lots of redundant +stuff. + +2) It probably doesn't work on all Un*x-like platforms yet. It probably will +never work on the rest. + +3) The scripts are not yet complete. Some of the standard GNU targets don't +yet work. (Corrections/additions are very welcome.) + +The distribution should contain all files needed to run "configure" and "make", +as well as the sources needed to regenerate the derived files. (If I missed +some, please let me know.) + +Note that the distribution comes without "Makefile" which is generated by +"configure". The distribution also contains "Makefile.direct" which is not +always equivalent to the generated one. + +Important options to configure: + + --prefix=PREFIX install architecture-independent files in PREFIX + [/usr/local] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --enable-threads=TYPE choose threading package + --disable-parallel-mark do not parallelize marking and free list + construction + --enable-gc-debug include full support for pointer back-tracing, etc. + + +Unless --prefix is set (or --exec-prefix or one of the more obscure options), +"make install" will install libgc.a and libgc.so in /usr/local/lib and +/usr/local/bin, respectively, which would typically require the "make install" +to be run as root. + +It is not recommended to turn off parallel marking for multiprocessors unless +a poor support of the feature on the platform. diff --git a/bdwgc/doc/README.cmake b/bdwgc/doc/README.cmake new file mode 100644 index 000000000..b38f9e5dd --- /dev/null +++ b/bdwgc/doc/README.cmake @@ -0,0 +1,62 @@ + +CMAKE +----- + +Unix and Win32 binaries (both 32- and 64-bit) can be built using CMake. +CMake is an open-source tool like automake - it generates makefiles. + +CMake v3.14.5 is able to generate: + Borland Makefiles + MSYS Makefiles + MinGW Makefiles + NMake Makefiles + Unix Makefiles + Visual Studio 16 2019 + Visual Studio 15 2017 + Visual Studio 14 2015 + Visual Studio 12 2013 + Visual Studio 11 2012 + Visual Studio 10 2010 + Visual Studio 9 2008 + Watcom WMake + + +BUILD PROCESS +------------- + + . install cmake (cmake.org) + . add directory containing cmake.exe to %PATH% + . run cmake from the gc root directory, passing the target with -G: + e.g., + > cmake -G "Visual Studio 9 2008" . + use the gc.sln file generated by cmake to build gc + . specify -Denable_cplusplus=ON option to build gccpp, gctba (GC C++ support) + . specify -Dbuild_tests=ON option to the tests (and run them by "ctest -V") + . you can also run cmake from a build directory to build outside of + the source tree. Just specify the path to the source tree: + e.g., + > mkdir out + > cd out + > cmake -G "Visual Studio 9 2008" .. + > cmake --build . --config Release + > ctest --build-config Release -V + + +INPUT +----- + +The main input to cmake is CMakeLists.txt file in the GC root directory. For +help, go to cmake.org. + + +HOW TO IMPORT BDWGC +------------------- + +Another project could add bdwgc as one of its dependencies with something like +this in their CMakeLists.txt: + +find_package(BDWgc 8.2.4 REQUIRED) +add_executable(Foo foo.c) +target_link_libraries(Foo BDWgc::gc) + +Other exported libraries are: cord, gccpp, gctba. diff --git a/bdwgc/doc/README.cords b/bdwgc/doc/README.cords new file mode 100644 index 000000000..02477f4da --- /dev/null +++ b/bdwgc/doc/README.cords @@ -0,0 +1,45 @@ +Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + +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. + +This is a string packages that uses a tree-based representation. +See cord.h for a description of the functions provided. Ec.h describes +"extensible cords", which are essentially output streams that write +to a cord. These allow for efficient construction of cords without +requiring a bound on the size of a cord. + +More details on the data structure can be found in + +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 descendants, which include the +GNU C++ library. That uses reference counting by default. +There is a short description of that data structure at +http://www.sgi.com/tech/stl/ropeimpl.html . + +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 +cord representing the file contents. Nonetheless, standard +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 highlighted "I"s. Use the UNIX "expand" program first.) +To build the editor, type "make de" in the gc directory. + +Note that CORD_printf and friends use C functions with variable numbers +of arguments in non-standard-conforming ways. This code is known to +break on some platforms, notably PowerPC. It should be possible to +build the remainder of the library (everything but cordprnt.c) on +any platform that supports the collector. diff --git a/bdwgc/doc/README.darwin b/bdwgc/doc/README.darwin new file mode 100644 index 000000000..32a5fb170 --- /dev/null +++ b/bdwgc/doc/README.darwin @@ -0,0 +1,81 @@ +Darwin/MacOSX Support - December 16, 2003 + +== Build Notes == + +Building can be done with autoconf as normal. If you want to build +a Universal library using autoconf, you need to disable dependency +tracking and specify your desired architectures in CFLAGS: + +CFLAGS="-arch ppc -arch i386 -arch x86_64" ./configure --disable-dependency-tracking + + +== Important Usage Notes == + +GC_INIT() MUST be called before calling any other GC functions. This +is necessary to properly register segments in dynamic libraries. This +call is required even if you code does not use dynamic libraries as the +dyld code handles registering all data segments. + +When your use of the garbage collector is confined to dylibs and you +cannot call GC_INIT() before your libraries' static initializers have +run and perhaps called GC_malloc(), create an initialization routine +for each library to call GC_INIT(), e.g.: + +#include "gc.h" +extern "C" void my_library_init() { GC_INIT(); } + +Compile this code into a my_library_init.o, and link it into your +dylib. When you link the dylib, pass the -init argument with +_my_library_init (e.g. gcc -dynamiclib -o my_library.dylib a.o b.o c.o +my_library_init.o -init _my_library_init). This causes +my_library_init() to be called before any static initializers, and +will initialize the garbage collector properly. + +Note: It doesn't hurt to call GC_INIT() more than once, so it's best, +if you have an application or set of libraries that all use the +garbage collector, to create an initialization routine for each of +them that calls GC_INIT(). Better safe than sorry. + +Thread-local GC allocation will not work with threads that are not +created using the GC-provided override of pthread_create(). Threads +created without the GC-provided pthread_create() do not have the +necessary data structures in the GC to store this data. + + +== Implementation Information == + +Darwin/MacOSX support is nearly complete. Thread support is reliable on +Darwin 6.x (MacOSX 10.2) and there have been reports of success on older +Darwin versions (MacOSX 10.1). Shared library support had also been +added and the gc can be run from a shared library. + +Thread support is implemented in terms of mach thread_suspend and +thread_resume calls. These provide a very clean interface to thread +suspension. This implementation doesn't rely on pthread_kill so the +code works on Darwin < 6.0 (MacOSX 10.1). All the code to stop and +start the world is located in darwin_stop_world.c. + +Since not all uses of the GC enable clients to override pthread_create() +before threads have been created, the code for stopping the world has +been rewritten to look for threads using Mach kernel calls. Each +thread identified in this way is suspended and resumed as above. In +addition, since Mach kernel threads do not contain pointers to their +stacks, a stack-walking function has been written to find the stack +limits. Given an initial stack pointer (for the current thread, a +pointer to a stack-allocated local variable will do; for a non-active +thread, we grab the value of register 1 (on PowerPC)), it +will walk the PPC Mach-O-ABI compliant stack chain until it reaches the +top of the stack. This appears to work correctly for GCC-compiled C, +C++, Objective-C, and Objective-C++ code, as well as for Java +programs that use JNI. If you run code that does not follow the stack +layout or stack pointer conventions laid out in the PPC Mach-O ABI, +then this will likely crash the garbage collector. + +Mach has a very clean interface to exception handing. So, the current +implementation of the incremental collection uses Mach's exception handling. + +Much thanks goes to Andrew Stone, Dietmar Planitzer, Andrew Begel, +Jeff Sturm, and Jesse Rosenstock for all their work on the +Darwin/OS X port. + +-Brian Alliet diff --git a/bdwgc/doc/README.emscripten b/bdwgc/doc/README.emscripten new file mode 100644 index 000000000..c869f19ac --- /dev/null +++ b/bdwgc/doc/README.emscripten @@ -0,0 +1,15 @@ + +The build system (at least autotools-based) should detect and configure +emscripten correctly. + +As of now, gctest almost passes, except for the tests that involve a_get(). + +No thread support for now. No idea how to stop other threads (perhaps we need +support from JS side). + +How to build: + + # source EMSDK first + emconfigure ./configure + emmake make gctest.html + # point your browser at .libs/gctest.html diff --git a/bdwgc/doc/README.environment b/bdwgc/doc/README.environment new file mode 100644 index 000000000..d2237c10f --- /dev/null +++ b/bdwgc/doc/README.environment @@ -0,0 +1,185 @@ +The garbage collector looks at a number of environment variables which are, +then, used to affect its operation. + +GC_INITIAL_HEAP_SIZE= - Initial heap size in bytes. May speed up + process start-up. Optionally, may be + specified with a multiplier ('k', 'M' or 'G') + suffix. + +GC_MAXIMUM_HEAP_SIZE= - Maximum collected heap size. Allows + a multiplier suffix. + +GC_LOOP_ON_ABORT - Causes the collector abort routine to enter a tight loop. + This may make it easier to debug, such a process, especially + for multi-threaded platforms that don't produce usable core + files, or if a core file would be too large. On some + platforms, this also causes SIGSEGV to be caught and + result in an infinite loop in a handler, allowing + similar debugging techniques. + +GC_PRINT_STATS - Turn on GC logging. Not functional with SMALL_CONFIG. + +GC_LOG_FILE - The name of the log file. Stderr by default. Not functional + with SMALL_CONFIG. + +GC_ONLY_LOG_TO_FILE - Turns off redirection of GC stdout and stderr to the log + file specified by GC_LOG_FILE. Has no effect unless + GC_LOG_FILE is set. Not functional with SMALL_CONFIG. + +GC_PRINT_VERBOSE_STATS - Turn on even more logging. Not functional with + SMALL_CONFIG. + +GC_DUMP_REGULARLY - Generate a GC debugging dump (by GC_dump_named) on startup + and during every collection. Very verbose. Useful + if you have a bug to report, but please include only the + last complete dump. + +GC_COLLECT_AT_MALLOC= - 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= - 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 + most objects in the heap were allocated through debug + allocators. This is intended to be only a statistical + sample; individual traces may be erroneous due to + concurrent heap mutation. + +GC_PRINT_ADDRESS_MAP - Linux only. Dump /proc/self/maps, i.e. various address + maps for the process, to stderr on every GC. Useful for + mapping root addresses to source for deciphering leak + reports. + +GC_NPROCS= - Linux w/threads only. Explicitly sets the number of processors + that the GC should expect to use. Note that setting this to 1 + when multiple processors are available will preserve + correctness, but may lead to really horrible performance, + since the lock implementation will immediately yield without + first spinning. + +GC_MARKERS= - Only if compiled with PARALLEL_MARK. Set the number + of marker threads. This is normally set to the number of + processors. It is safer to adjust GC_MARKERS than GC_NPROCS, + since GC_MARKERS has no impact on the lock implementation. + +GC_NO_BLACKLIST_WARNING - Prevents the collector from issuing + warnings about allocations of very large blocks. + Deprecated. Use GC_LARGE_ALLOC_WARN_INTERVAL instead. + +GC_LARGE_ALLOC_WARN_INTERVAL= - Print every nth warning about very large + block allocations, starting with the nth one. Small values + of n are generally benign, in that a bounded number of + such warnings generally indicate at most a bounded leak. + For best results it should be set at 1 during testing. + Default is 5. Very large numbers effectively disable the + warning. + +GC_IGNORE_GCJ_INFO - Ignore the type descriptors implicitly supplied by + GC_gcj_malloc and friends. This is useful for debugging + descriptor generation problems, and possibly for + temporarily working around such problems. It forces a + fully conservative scan of all heap objects except + those known to be pointer-free, and may thus have other + adverse effects. + +GC_PRINT_BACK_HEIGHT - Print max length of chain through unreachable objects + ending in a reachable one. If this number remains + bounded, then the program is "GC robust". This ensures + that a fixed number of misidentified pointers can only + result in a bounded space leak. This currently only + works if debugging allocation is used throughout. + It increases GC space and time requirements appreciably. + This feature is still somewhat experimental, and requires + that the collector have been built with MAKE_BACK_GRAPH + defined. For details, see Boehm, "Bounding Space Usage + of Conservative Garbage Collectors", POPL 2001 + (http://www.hpl.hp.com/techreports/2001/HPL-2001-251.html). + +GC_RETRY_SIGNALS - Try to compensate for lost + thread suspend and restart signals (Pthreads only). + On by default for OSF1 (Tru64) or if the library is + sanitized, off otherwise. Since we've previously seen + similar issues on some other operating systems, it + was turned into a runtime flag to enable last-minute + work-arounds. "0" value means "do not retry signals". + +GC_USE_GETWRITEWATCH= - Only if MPROTECT_VDB and (GWW_VDB or SOFT_VDB) are + both defined (Win32 and Linux only). Explicitly specify + which strategy of keeping track of dirtied pages should + be used. If n=0, then fall back to protecting pages and + catching memory faults strategy), else the collector + tries to use GetWriteWatch-based strategy (GWW_VDB) or + soft-dirty bits strategy (SOFT_VDB) first if available. + +GC_DISABLE_INCREMENTAL - Ignore runtime requests to enable incremental GC. + Useful for debugging. + +The following turn on runtime flags that are also program settable. Checked +only during initialization. We expect that they will usually be set through +other means, but this may help with debugging and testing: + +GC_ENABLE_INCREMENTAL - Turn on incremental collection at startup. Note that, + depending on platform and collector configuration, this + may involve write protecting pieces of the heap to + track modifications. These pieces may include + pointer-free objects or not. Although this is intended + to be transparent, it may cause unintended system call + failures. Use with caution. + +GC_PAUSE_TIME_TARGET - Set the desired garbage collector pause time in + milliseconds (ms). This only has an effect if incremental + collection is enabled. If a collection requires + appreciably more time than this, the client will be + restarted, and the collector will need to do additional + work to compensate. The special value "999999" indicates + that pause time is unlimited, and the incremental + collector will behave completely like a simple + generational collector. Any value, except for the given + special one, disables parallel marker (almost fully) for + now. + +GC_FULL_FREQUENCY - Set the desired number of partial collections between full + collections. Matters only if GC_incremental is set. + Not functional with SMALL_CONFIG. + +GC_FREE_SPACE_DIVISOR - Set GC_free_space_divisor to the indicated value. + Setting it to larger values decreases space consumption + and increases GC frequency. + +GC_UNMAP_THRESHOLD - Set the desired memory blocks unmapping threshold (the + number of sequential garbage collections for which + a candidate block for unmapping should remain free). The + special value "0" completely disables unmapping. + +GC_FORCE_UNMAP_ON_GCOLLECT - Turn "unmap as much as possible on explicit GC" + mode on (overrides the default value). Has no effect on + implicitly-initiated garbage collections. Has no effect if + memory unmapping is disabled (or not compiled in) or if the + unmapping threshold is 1. + +GC_FIND_LEAK - Turns on GC_find_leak and thus leak detection. Forces a + collection at program termination to detect leaks that would + otherwise occur after the last GC. + +GC_FINDLEAK_DELAY_FREE - Turns on deferred freeing of objects in the + leak-finding mode (see the corresponding macro + description for more information). + +GC_ABORT_ON_LEAK - Causes the application to be terminated once leaked or + smashed objects are found. + +GC_ALL_INTERIOR_POINTERS - Turns on GC_all_interior_pointers and thus interior + pointer recognition. + +GC_DONT_GC - Turns off garbage collection. Use cautiously. + +GC_USE_ENTIRE_HEAP - Set desired GC_use_entire_heap value at start-up. See + the similar macro description in README.macros. + +GC_TRACE=addr - Intended for collector debugging. Requires that the collector + have been built with ENABLE_TRACE defined. Causes the debugger + to log information about the tracing of address ranges + containing addr. Typically addr is the address that contains + a pointer to an object that mysteriously failed to get marked. + Addr must be specified as a hexadecimal integer. diff --git a/bdwgc/doc/README.ews4800 b/bdwgc/doc/README.ews4800 new file mode 100644 index 000000000..83e8b1e07 --- /dev/null +++ b/bdwgc/doc/README.ews4800 @@ -0,0 +1,81 @@ +GC on EWS4800 +------------- + +1. About EWS4800 + + EWS4800 is a 32/64-bit workstation. + + Vendor: NEC Corporation + OS: UX/4800 R9.* - R13.* (SystemV R4.2) + CPU: R4000, R4400, R10000 (MIPS) + +2. Compiler + + 32-bit: + Use ANSI C compiler. + CC = /usr/abiccs/bin/cc + + 64-bit: + Use the 64-bit ANSI C compiler. + CC = /usr/ccs64/bin/cc + AR = /usr/ccs64/bin/ar + +3. ELF file format + *** Caution: The following information is empirical. *** + + 32-bit: + ELF file has a unique format. (See a.out(4) and end(3C).) + + &_start + : text segment + &etext + DATASTART + : data segment (initialized) + &edata + DATASTART2 + : data segment (uninitialized) + &end + + Here, DATASTART and DATASTART2 are macros of GC, and are defined as + the following equations. (See include/private/gcconfig.h.) + The algorithm for DATASTART is similar with the function + GC_SysVGetDataStart() in os_dep.c. + + DATASTART = ((&etext + 0x3ffff) & ~0x3ffff) + (&etext & 0xffff) + + Dynamically linked: + DATASTART2 = (&_gp + 0x8000 + 0x3ffff) & ~0x3ffff + + Statically linked: + DATASTART2 = &edata + + GC has to check addresses both between DATASTART and &edata, and + between DATASTART2 and &end. If a program accesses between &etext + and DATASTART, or between &edata and DATASTART2, the segmentation + error occurs and the program stops. + + If a program is statically linked, there is not a gap between + &edata and DATASTART2. The global symbol &_DYNAMIC_LINKING is used + for the detection. + + 64-bit: + ELF file has a simple format. (See end(3C).) + + _ftext + : text segment + _etext + _fdata = DATASTART + : data segment (initialized) + _edata + _fbss + : data segment (uninitialized) + _end = DATAEND + +-- +Hironori SAKAMOTO + + +When using the new "configure; make" build process, please +run configure with the --disable-shared option. "Make check" does not +yet pass with dynamic libraries. The reasons for that are not yet +understood. (HB, paraphrasing message from Hironori SAKAMOTO.) diff --git a/bdwgc/doc/README.hp b/bdwgc/doc/README.hp new file mode 100644 index 000000000..cc31b18de --- /dev/null +++ b/bdwgc/doc/README.hp @@ -0,0 +1,18 @@ +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 collectible objects are +accessible without considering statically allocated variables in dynamic +libraries. + +The collector should compile with either plain cc or cc -Ae. Cc -Aa +fails to define _HPUX_SOURCE and thus will not configure the collector +correctly. + +Incremental collection support was added recently, and should now work. + +In spite of past claims, pthread support under HP/UX 11 should now work. +Define GC_THREADS macro for the build. Incremental collection still does not +work in combination with it. + +The stack finding code can be confused by putenv calls before collector +initialization. Call GC_malloc() or GC_INIT() before any putenv() calls. diff --git a/bdwgc/doc/README.linux b/bdwgc/doc/README.linux new file mode 100644 index 000000000..c9ced4817 --- /dev/null +++ b/bdwgc/doc/README.linux @@ -0,0 +1,127 @@ +See README.alpha for Linux on DEC AXP info. + +This file applies mostly to Linux/Intel IA-32. Ports to Linux on an M68K, +IA-64, 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. + +Incremental GC is generally supported. + +Dynamic libraries are supported on an ELF system. + +The collector appears to work reliably with Linux threads, but beware +of older versions of glibc and gdb. + +The garbage collector uses SIGPWR and SIGXCPU if it is used with +Linux threads. These should not be touched by the client program. + +To use threads, you need to abide by the following requirements: + +1) You need to use LinuxThreads or NPTL (which are included in libc6). + + The collector relies on some implementation details of the LinuxThreads + package. This code may not work on other + pthread implementations (in particular it will *not* work with + MIT pthreads). + +2) You must compile the collector with "-DGC_THREADS -D_REENTRANT" specified + in the Makefile.direct file. + +3a) Every file that makes thread calls should define GC_THREADS, and then + include gc.h. The latter redefines some of the pthread primitives as + macros which also provide the collector with information it requires. + +3b) A new alternative to (3a) is to build the collector and compile GC clients + with -DGC_USE_LD_WRAP, and to link the final program with + + (for ld) --wrap dlopen --wrap pthread_create \ + --wrap pthread_join --wrap pthread_detach \ + --wrap pthread_sigmask --wrap pthread_exit --wrap pthread_cancel + + (for gcc) -Wl,--wrap -Wl,dlopen -Wl,--wrap -Wl,pthread_create \ + -Wl,--wrap -Wl,pthread_join -Wl,--wrap -Wl,pthread_detach \ + -Wl,--wrap -Wl,pthread_sigmask -Wl,--wrap -Wl,pthread_exit \ + -Wl,--wrap -Wl,pthread_cancel + + In any case, _REENTRANT should be defined during compilation. + +4) Dlopen() disables collection during its execution. (It can't run + concurrently with the collector, since the collector looks at its + data structures. It can't acquire the allocator lock, since arbitrary + user startup code may run as part of dlopen().) Under unusual + conditions, this may cause unexpected heap growth. + +5) The combination of GC_THREADS, REDIRECT_MALLOC, and incremental + collection is probably not fully reliable, though it now seems to work + in simple cases. + +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 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. Actually, the + collector probably gets things right, on Linux at least, if there are not + too many tls locations and if dlopen is not used.) + + +M68K LINUX: +(From Richard Zidlicky) +The bad news is that it can crash every linux-m68k kernel on a 68040, +so an additional test is needed somewhere on startup. I have meanwhile +patches to correct the problem in 68040 buserror handler but it is not +yet in any standard kernel. + +Here is a simple test program to detect whether the kernel has the +problem. It could be run as a separate check in configure or tested +upon startup. If it fails (return !0) than mprotect can't be used +on that system. + +/* + * test for bug that may crash 68040 based Linux + */ + +#include +#include +#include +#include +#include + + +char *membase; +int pagesize=4096; +int pageshift=12; +int x_taken=0; + +int sighandler(int sig) +{ + mprotect(membase,pagesize,PROT_READ|PROT_WRITE); + x_taken=1; +} + +main() +{ + long l; + + signal(SIGSEGV,sighandler); + l=(long)mmap(NULL,pagesize,PROT_READ,MAP_PRIVATE | MAP_ANON,-1,0); + if (l==-1) + { + perror("mmap/malloc"); + abort(); + } + membase=(char*)l; + *(long*)(membase+sizeof(long))=123456789; + if (*(long*)(membase+sizeof(long)) != 123456789 ) + { + fprintf(stderr,"writeback failed !\n"); + exit(1); + } + if (!x_taken) + { + fprintf(stderr,"exception not taken !\n"); + exit(1); + } + fprintf(stderr,"vmtest Ok\n"); + exit(0); +} diff --git a/bdwgc/doc/README.macros b/bdwgc/doc/README.macros new file mode 100644 index 000000000..e43321f41 --- /dev/null +++ b/bdwgc/doc/README.macros @@ -0,0 +1,609 @@ +The collector uses a large amount of conditional compilation in order to +deal with platform dependencies. This violates a number of known coding +standards. On the other hand, it seems to be the only practical way to +support this many platforms without excessive code duplication. + +A few guidelines have mostly been followed in order to keep this manageable: + +1) #if and #ifdef directives are properly indented whenever easily possible. +All known C compilers allow whitespace between the "#" and the "if" to make +this possible. ANSI C also allows white space before the "#", though we +avoid that. It has the known disadvantages that it differs from the normal +GNU conventions, and that it makes patches larger than otherwise necessary. +In my opinion, it's still well worth it, for the same reason that we indent +ordinary "if" statements. + +2) Whenever possible, tests are performed on the macros defined in gcconfig.h +instead of directly testing platform-specific predefined macros. This makes +it relatively easy to adapt to new compilers with a different set of +predefined macros. Currently these macros generally identify platforms +instead of features. In many cases, this is a mistake. + +Many of the tested configuration macros are at least somewhat defined in +either include/private/gcconfig.h or in Makefile.direct. Here is an attempt +at documenting these macros: (Thanks to Walter Bright for suggesting +this.) + +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(). + +GC_NO_THREAD_REDIRECTS Tested by gc.h. Prevents redirection of thread + creation routines etc. to GC_ versions. Requires the + programmer to explicitly handle thread registration. + +GC_NO_THREAD_DECLS Tested by gc.h. Windows only. Do not declare + Windows thread creation routines and do not include windows.h. + +GC_DONT_INCLUDE_WINDOWS_H Tested by gc.h. Windows only. Do not include + windows.h from gc.h (but Windows-specific thread creation + routines are declared). + +GC_UNDERSCORE_STDCALL Tested by gc.h. Explicitly prefix exported/imported + WINAPI (__stdcall) symbols with '_' (underscore). Could be + used with MinGW (for x86) compiler (in conjunction with + GC_DLL) to follow MS conventions for __stdcall symbols naming. + +_ENABLE_ARRAYNEW + #define'd by the Digital Mars C++ compiler when + operator new[] and delete[] are separately + overloadable. Used in gc_cpp.h. + +GC_NO_INLINE_STD_NEW Tested by gc_cpp.cc and gc_cpp.h. Windows only. + Define the system-wide new and delete operators in gccpp.dll + instead of providing an inline version of the operators. + +_DLL Tested by gc_config_macros.h. Defined by Visual C++ if runtime + dynamic libraries are in use. Used (only if none of GC_DLL, + GC_NOT_DLL, __GNUC__ are defined) to test whether + __declspec(dllimport) needs to be added to declarations + to support the case in which the collector is in a DLL. + +GC_DLL Defined by user if dynamic libraries are being built + or used. Also set by gc.h if _DLL is defined (except for + mingw) while GC_NOT_DLL and __GNUC__ are both undefined. + This is the macro that is tested internally to determine + whether the GC is in its own dynamic library. May need + to be set by clients before including gc.h. Note that + inside the GC implementation it indicates that the + collector is in its own dynamic library, should export + its symbols, etc. But in clients it indicates that the + GC resides in a different DLL, its entry points should + be referenced accordingly, and precautions may need to + be taken to properly deal with statically allocated + variables in the main program. Used for Windows. + Also used by GCC v4+ (only when the dynamic shared library + is being built) to hide internally used symbols. + +GC_NOT_DLL User-settable macro that overrides _DLL, e.g. if runtime + dynamic libraries are used, but the collector is in a static + library. Tested by gc_config_macros.h. + +GC_MARKERS= Set the desired number of marker threads. If not defined or + defined to zero, then the collector decides based on the + number of CPU cores. Only if compiled with PARALLEL_MARK. + + +These define arguments influence the collector configuration: + +GC_REQUIRE_WCSDUP Force GC to export GC_wcsdup() (the Unicode version + of GC_strdup); could be useful in the leak-finding mode. Clients should + define it before including gc.h if the function is needed. + +FIND_LEAK Causes GC_find_leak to be initially set. This causes the + collector to assume that all inaccessible objects should have been + explicitly deallocated, and reports exceptions. Finalization and the test + program are not usable in this mode. + +GC_FINDLEAK_DELAY_FREE Turns on deferred freeing of objects in the + leak-finding mode letting the collector to detect alter-object-after-free + errors as well as detect leaked objects sooner (instead of only when program + terminates). Has no effect if SHORT_DBG_HDRS. + +GC_ABORT_ON_LEAK Causes the application to be terminated once leaked or + smashed (corrupted on use-after-free) objects are found (after printing the + information about that objects). + +SUNOS5SIGS Solaris-like signal handling. This is probably misnamed, + since it really doesn't guarantee much more than POSIX. Currently set only + for DRSNX, FreeBSD, HP/UX and Solaris. + +PCR Set if the collector is being built as part of the Xerox Portable + Common Runtime. + +GC_THREADS Should set the appropriate one of the below macros, + except GC_WIN32_PTHREADS, which must be set explicitly. Tested by gc.h. + IMPORTANT: GC_THREADS macro (or the relevant platform-specific deprecated + one) must normally also be defined by the client before including gc.h. + This redefines thread primitives to invoke the GC_ wrappers instead. + Alternatively, linker-based symbol interception can be used on a few + platforms. + +GC_SOLARIS_THREADS Enables support for Solaris pthreads. + Must also define _REENTRANT. Deprecated, use GC_THREADS instead. + +GC_IRIX_THREADS Enables support for Irix pthreads. See README.sgi. + Deprecated, use GC_THREADS instead. + +GC_HPUX_THREADS Enables support for HP/UX 11 pthreads. + Also requires _REENTRANT or _POSIX_C_SOURCE=199506L. See README.hp. + Deprecated, use GC_THREADS instead. + +GC_LINUX_THREADS Enables support for Xavier Leroy's Linux threads + or NPTL threads. See README.linux. _REENTRANT may also be required. + Deprecated, use GC_THREADS instead. + +GC_OSF1_THREADS Enables support for Tru64 pthreads. Deprecated, use + GC_THREADS instead. + +GC_FREEBSD_THREADS Enables support for FreeBSD pthreads. Appeared to run + into some underlying thread problems. Deprecated, use GC_THREADS instead. + +GC_NETBSD_THREADS Enables support for NetBSD pthreads. Deprecated, use + GC_THREADS instead. + +GC_OPENBSD_THREADS Enables support for OpenBSD pthreads. Deprecated, + use GC_THREADS instead. + +GC_DARWIN_THREADS Enables support for Mac OS X pthreads. Deprecated, + use GC_THREADS instead. + +GC_AIX_THREADS Enables support for IBM AIX threads. Deprecated, use + GC_THREADS instead. + +GC_DGUX386_THREADS Enables support for DB/UX x86 threads. + See README.DGUX386. (Probably has not been tested recently.) Deprecated, + use GC_THREADS instead. + +GC_WIN32_THREADS Enables support for Win32 threads. Deprecated, + use GC_THREADS instead. + +GC_WIN32_PTHREADS Enables support for pthreads-win32 (or other + non-Cygwin pthreads library for Windows). This cannot be enabled + automatically by GC_THREADS, which would assume Win32 native threads. + +PTW32_STATIC_LIB Causes the static version of the Mingw pthreads + library to be used. Requires GC_WIN32_PTHREADS. + +GC_PTHREADS_PARAMARK Causes pthread-based parallel mark implementation + to be used even if GC_WIN32_PTHREADS is undefined. (Useful for WinCE.) + +ALL_INTERIOR_POINTERS Allows all pointers to the interior of objects to be + recognized. (See gc_priv.h for consequences.) Alternatively, + GC_all_interior_pointers can be set at process initialization time. + +SMALL_CONFIG Tries to tune the collector for small heap sizes, + usually causing it to use less space in such situations. Incremental + collection no longer works in this case. Also, removes some + statistic-printing code. Turns off some optimization algorithms (like data + prefetching in the mark routine). + +NO_CLOCK Do not use system clock. Disables some statistic printing. + +GC_DISABLE_INCREMENTAL Turn off the incremental collection support. + +NO_INCREMENTAL Causes the GC test programs to not invoke the incremental mode + of the collector. This has no impact on the generated library, only on the + test programs. (This is often useful for debugging failures unrelated to + incremental GC.) + +LARGE_CONFIG Tunes the collector for unusually large heaps. + Necessary for heaps larger than about 4 GiB on most (64-bit) machines. + Recommended for heaps larger than about 500 MiB. Not recommended for + embedded systems. Could be used in conjunction with SMALL_CONFIG to + generate smaller code (by disabling incremental collection support, + statistic printing and some optimization algorithms). + +DONT_ADD_BYTE_AT_END Meaningful only with ALL_INTERIOR_POINTERS or + GC_all_interior_pointers = 1. Normally ALL_INTERIOR_POINTERS + causes all objects to be padded so that pointers just past the end of + an object can be recognized. This can be expensive. (The padding + is normally more than one byte due to alignment constraints.) + DONT_ADD_BYTE_AT_END disables the padding. + +NO_EXECUTE_PERMISSION May cause some or all of the heap to not + have execute permission, i.e. it may be impossible to execute + code from the heap. Affects the incremental collector and memory unmapping. + It may greatly improve the performance, since this may avoid some expensive + cache synchronization. Portable clients should call + GC_set_pages_executable(1) at the process initialization time if the + execute permission is required. + +GC_NO_OPERATOR_NEW_ARRAY Declares that the C++ compiler does not + support the new syntax "operator new[]" for allocating and deleting arrays. + See gc_cpp.h for details. No effect on the C part of the collector. + This is defined implicitly in a few environments. Must also be defined + by clients that use gc_cpp.h. + +REDIRECT_MALLOC= Causes malloc to be defined as alias for X. + Unless the following macros are defined, realloc is also redirected + to GC_realloc, and free is redirected to GC_free. + Calloc and str[n]dup are redefined in terms of the new malloc. X should + be either GC_malloc or GC_malloc_uncollectable, or + GC_debug_malloc_replacement. (The latter invokes GC_debug_malloc + with dummy source location information, but still results in + properly remembered call stacks on Linux/x86 and Solaris/SPARC. + It requires that the following two macros also be used.) + The former is occasionally useful for working around leaks in code + you don't want to (or can't) look at. It may not work for + existing code, but it often does. Neither works on all platforms, + since some ports use malloc or calloc to obtain system memory. + (Probably works for UNIX, and Win32.) If you build with DBG_HDRS_ALL, + you should only use GC_debug_malloc_replacement as a malloc + replacement. + +REDIRECT_REALLOC= Causes realloc to be redirected to X. + The canonical use is REDIRECT_REALLOC=GC_debug_realloc_replacement, + together with REDIRECT_MALLOC=GC_debug_malloc_replacement to + generate leak reports with call stacks for both malloc and realloc. + This also requires REDIRECT_FREE. + +REDIRECT_FREE= Causes free to be redirected to X. The canonical use + is REDIRECT_FREE=GC_debug_free. + +IGNORE_FREE Turns calls to free into a no-op. Only useful with + REDIRECT_MALLOC. + +NO_DEBUGGING Removes GC_dump and the debugging routines it calls. + Reduces code size slightly at the expense of debuggability. + +GC_DUMP_REGULARLY Generate regular debugging dumps. + +DEBUG_THREADS Turn on printing additional thread-support debugging + information. + +GC_COLLECT_AT_MALLOC= 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 + in a separate post-pass, and hence their memory won't be reclaimed. + Not recommended unless you are implementing a language that specifies + these semantics. Actually, the macro determines only the initial value + of GC_java_finalization variable. + +FINALIZE_ON_DEMAND Causes finalizers to be run only in response + to explicit GC_invoke_finalizers() calls. Actually, the macro only + determines the initial value of GC_finalize_on_demand. + +GC_NO_FINALIZATION Exclude finalization support (for smaller code size). + +GC_TOGGLE_REFS_NOT_NEEDED Exclude toggle-refs support. + +GC_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. + +MARK_BIT_PER_GRANULE Requests that a mark bit (or often byte) + be allocated for each allocation granule, as opposed to each object. + This often improves speed, possibly at some cost in space and/or + cache footprint. Normally it is best to let this decision be + made automatically depending on platform. + +MARK_BIT_PER_OBJ Requests that a mark bit be allocated for each + object instead of allocation granule. The opposite of + MARK_BIT_PER_GRANULE. + +HBLKSIZE= Explicitly sets the heap block size (where ddd is a power of + 2 between 512 and 16384). Each heap block is devoted to a single size and + kind of object. For the incremental collector it makes sense to match + the most likely page size. Otherwise large values result in more + fragmentation, but generally better performance for large heaps. + +USE_MMAP Use MMAP instead of sbrk to get new memory. + Works for Linux, FreeBSD, Cygwin, Solaris and Irix. + +USE_MUNMAP Causes memory to be returned to the OS under the right + circumstances. 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= Set the desired memory blocks unmapping + threshold (the number of sequential garbage collections for which + a candidate block for unmapping should remain free). + +GC_FORCE_UNMAP_ON_GCOLLECT Set "unmap as much as possible on explicit GC" + mode on by default. The mode could be changed at run-time. Has no effect + unless unmapping is turned on. Has no effect on implicitly-initiated + garbage collections. + +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 + value of the near-bogus-pointer. Can be used to identify regions of + memory that are likely to contribute misidentified pointers. + +KEEP_BACK_PTRS Add code to save back pointers in debugging headers + for objects allocated with the debugging allocator. If all objects + through GC_MALLOC with GC_DEBUG defined, this allows the client + to determine how particular or randomly chosen objects are reachable + for debugging/profiling purposes. The gc_backptr.h interface is + implemented only if this is defined. + +GC_ASSERTIONS Enable some internal GC assertion checking. It is intended + primarily for debugging of the garbage collector itself, but could also + help to identify cases of incorrect GC usage by a client. + +DBG_HDRS_ALL Make sure that all objects have debug headers. Increases + the reliability (from 99.9999% to 100% mod. bugs) of some of the debugging + code (especially KEEP_BACK_PTRS). Makes SHORT_DBG_HDRS possible. + Assumes that all client allocation is done through debugging allocators. + +SHORT_DBG_HDRS Assume that all objects have debug headers. Shorten + the headers to minimize object size, at the expense of checking for + writes past the end of an object. This is intended for environments + in which most client code is written in a "safe" language, such as + Scheme or Java. Assumes that all client allocation is done using + the GC_debug_ functions, or through the macros that expand to these, + or by redirecting malloc to GC_debug_malloc_replacement. + (Also eliminates the field for the requested object size.) + Occasionally could be useful for debugging of client code. Slows down the + collector somewhat, but not drastically. + +SAVE_CALL_COUNT= Set the number of call frames saved with objects + allocated through the debugging interface. Affects the amount of + information generated in leak reports. Only matters on platforms + on which we can quickly generate call stacks, currently Linux/x86, + Linux/SPARC, Solaris/SPARC, and platforms that provide execinfo.h. + Default is zero. On x86, client code should NOT be compiled with + -fomit-frame-pointer. + +SAVE_CALL_NARGS= Set the number of functions arguments to be saved + with each call frame. Default is zero. Ignored if we don't know how to + retrieve arguments on the platform. + +CHECKSUMS Reports on erroneously clear dirty bits (at a substantial + performance cost). Use only for debugging of the incremental collector. + Not compatible with USE_MUNMAP or threads. + +GC_GCJ_SUPPORT Includes support for gcj (and possibly other systems + that include a pointer to a type descriptor in each allocated object). + +USE_I686_PREFETCH Causes the collector to issue Pentium III style + prefetch instructions. No effect except on Linux/x86 platforms. + Empirically the code appears to still run correctly on Pentium II + processors, though with no performance benefit. May not run on other + x86 processors probably. In some cases this improves performance by 15% + or so. + +USE_3DNOW_PREFETCH Causes the collector to issue AMD 3DNow style + prefetch instructions. Same restrictions as USE_I686_PREFETCH. + Minimally tested. Didn't appear to be an obvious win on a K6-2/500. + +USE_PPC_PREFETCH Causes the collector to issue PowerPC style + prefetch instructions. No effect except on PowerPC OS X platforms. + Performance impact untested. + +GC_USE_LD_WRAP In combination with the old flags listed in README.linux + causes the collector some system and pthread calls in a more transparent + fashion than the usual macro-based approach. Requires GNU ld, and + currently probably works only with Linux. + +GC_USE_DLOPEN_WRAP Causes the collector to redefine malloc and + intercepted pthread routines with their real names, and causes it to use + dlopen and dlsym to refer to the original versions. This makes it possible + to build an LD_PRELOADable malloc replacement library. + +THREAD_LOCAL_ALLOC Defines GC_malloc(), GC_malloc_atomic() and + GC_gcj_malloc() to use a per-thread set of free-lists. These then allocate + in a way that usually does not involve acquisition of a global lock. + Recommended for multiprocessors. + +USE_COMPILER_TLS Causes thread local allocation to use + the compiler-supported "__thread" thread-local variables. This is the + default in HP/UX. It may help performance on recent Linux installations. + (It failed for me on RedHat 8, but appears to work on RedHat 9.) + +GC_ATTR_TLS_FAST Use specific attributes for GC_thread_key like + __attribute__((tls_model("local-exec"))). + +PARALLEL_MARK Allows the marker to run in multiple threads. Recommended + for multiprocessors. + +GC_BUILTIN_ATOMIC Use GCC atomic intrinsics instead of libatomic_ops + primitives. + +GC_ALWAYS_MULTITHREADED Force multi-threaded mode at GC initialization. + +GC_ENABLE_SUSPEND_THREAD (Linux only) Turn on thread suspend/resume API +support. + +GC_WINMAIN_REDIRECT (Win32 only) Redirect (rename) an application + WinMain to GC_WinMain; implement the "real" WinMain which starts a new + thread to call GC_WinMain after initializing the GC. Useful for WinCE. + Incompatible with GC_DLL. + +GC_REGISTER_MEM_PRIVATE (Win32 only) Force to register MEM_PRIVATE R/W + sections as data roots. Might be needed for some WinCE 6.0+ custom builds. + (May result in numerous "Data Abort" messages logged to WinCE debugging + console.) Incompatible with GCC toolchains for WinCE. + +NO_GETENV Prevents the collector from looking at environment variables. + These may otherwise alter its configuration, or turn off GC altogether. + I don't know of a reason to disable this, except possibly if the resulting + process runs as a privileged user. (This is on by default for WinCE.) + +EMPTY_GETENV_RESULTS Define to workaround a reputed Wine bug in getenv + (getenv() may return an empty string instead of NULL for a missing entry). + +GC_READ_ENV_FILE (Win32 only) Read environment variables from the GC "env" + file (named as the program name plus ".gc.env" extension). Useful for WinCE + targets (which have no getenv()). In the file, every variable is specified + in a separate line and the format is as "=" (without spaces). + A comment line may start with any character except for the Latin letters, + the digits and the underscore ('_'). The file encoding is Latin-1. + +USE_GLOBAL_ALLOC (Win32 only) Use GlobalAlloc() instead of VirtualAlloc() + to allocate the heap. May be needed to work around a Windows NT/2000 issue. + Incompatible with USE_MUNMAP. See README.win32 for details. + +MAKE_BACK_GRAPH Enable GC_PRINT_BACK_HEIGHT environment variable. + See README.environment for details. Experimental. Limited platform + support. Implies DBG_HDRS_ALL. All allocation should be done using + the debug interface. + +GC_PRINT_BACK_HEIGHT Permanently turn on back-height printing mode + (useful when NO_GETENV). See the similar environment variable description + in README.environment. Requires MAKE_BACK_GRAPH defined. + +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 + set and collected heap to grow significantly if malloc'ed memory is somehow + getting traced by the collector. This has no impact on the generated + library; it only affects the test. + +POINTER_MASK=<0x...> Causes candidate pointers to be AND'ed with the given + mask before being considered. If either this or the following macro is + defined, it will be assumed that all pointers stored in the heap need to be + processed this way. Stack and register pointers will be considered both + with and without processing. These macros are normally needed only to + support systems that use high-order pointer tags. EXPERIMENTAL. + +POINTER_SHIFT= Causes the collector to left shift candidate pointers + by the indicated amount before trying to interpret them. Applied after + POINTER_MASK. EXPERIMENTAL. See also the preceding macro. + +ENABLE_TRACE Enables the GC_TRACE=addr environment setting to do its job. + By default this is not supported in order to keep the marker as fast as + possible. + +DARWIN_DONT_PARSE_STACK Causes the Darwin port to discover thread + stack bounds in the same way as other pthread ports, without trying to + walk the frames on the stack. This is recommended only as a fall-back for + applications that don't support proper stack unwinding. + +GC_NO_THREADS_DISCOVERY (Darwin and Win32+DLL only) Exclude DllMain-based + (on Windows) and task-threads-based (on Darwin) thread registration support. + +GC_INSIDE_DLL (Win32 only) Enable DllMain-based approach of threads + registering even in case GC_DLL is not defined. + +GC_DISCOVER_TASK_THREADS (Darwin and Win32+DLL only) Compile the collector + with the implicitly turned on task-threads-based (on Darwin) or + DllMain-based (on Windows) approach of threads registering. Only for + compatibility and for the case when it is not possible to call + GC_use_threads_discovery() early (before other GC calls). + +USE_PROC_FOR_LIBRARIES Causes the Linux collector to treat writable + memory mappings (as reported by /proc) as roots, if it doesn't have + other information about them. It no longer traverses dynamic loader + data structures to find dynamic library static data. This may be + required for applications that store pointers in mmapped segments without + informing the collector. But it typically performs poorly, especially + since it will scan inactive but cached NPTL thread stacks completely. + +IGNORE_DYNAMIC_LOADING Don't define DYNAMIC_LOADING even if supported by the + platform (that is, build the collector with disabled tracing of dynamic + library data roots). + +NO_PROC_STAT Causes the collector to avoid relying on Linux + "/proc/self/stat". + +NO_GETCONTEXT Causes the collector to not assume the existence of the + getcontext() function on linux-like platforms. This currently happens + implicitly on Darwin, Hurd, or ARM or MIPS hardware. It is explicitly + needed for some old versions of FreeBSD. + +STATIC=static Causes various GC_ symbols that could logically be declared + static to be declared (this is the default if NO_DEBUGGING is specified). + Reduces the number of visible symbols (letting the optimizer do its work + better), which is probably cleaner, but may make some kinds of debugging + and profiling harder. + +GC_DLL Build dynamic-link library (or dynamic shared object). For Unix this + causes the exported symbols to have 'default' visibility (ignored unless + GCC v4+) and the internal ones to have 'hidden' visibility. + +NO_MSGBOX_ON_ERROR (Win32 only) Do not show Windows message box with + "OK" button on a GC fatal error. Otherwise the client application is + terminated only once the user clicks "OK" button. Useful for non-GUI (or + non-interactive) applications. + +DONT_USE_USER32_DLL (Win32 only) Don't use "user32" DLL import library + (containing MessageBox() entry); useful for a static GC library. + +GC_PREFER_MPROTECT_VDB Choose MPROTECT_VDB manually in case of multiple + virtual dirty bit strategies are implemented (at present useful on Win32, + Solaris and Linux to force MPROTECT_VDB strategy instead of the default + GWW_VDB, PROC_VDB or SOFT_VDB ones, respectively). + +GC_IGNORE_GCJ_INFO Disable GCJ-style type information (useful for + debugging on WinCE). + +GC_PRINT_VERBOSE_STATS Permanently turn on verbose logging (useful for + debugging and profiling on WinCE). + +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. + +CONSOLE_LOG (Win32 only) Output error/debug information to stdout and + stderr. + +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 + entire heap before collecting. This sometimes results in more large block + fragmentation, since very large blocks will tend to get broken up during + each GC cycle. It is likely to result in a larger working set, but lower + collection frequencies, and hence fewer instructions executed in the + collector. This macro controls only the default GC_use_entire_heap value. + +GC_INITIAL_HEAP_SIZE= Set the desired default initial heap size + in bytes. + +GC_FREE_SPACE_DIVISOR= Set alternate default GC_free_space_divisor + value. + +GC_ALLOCD_BYTES_PER_FINALIZER= Set alternate default value of + GC_allocd_bytes_per_finalizer. + +GC_TIME_LIMIT= Set alternate default GC_time_limit value + (setting this to GC_TIME_UNLIMITED will essentially disable incremental + collection while leaving generational collection enabled). + +GC_FULL_FREQ= Set alternate default number of partial collections + between full collections (matters only if incremental collection is on). + +NO_CANCEL_SAFE (Posix platforms with threads only) Don't bother trying + to make the collector safe for thread cancellation; cancellation is not + used. (Note that if cancellation is used anyway, threads may end up + getting canceled in unexpected places.) Even without this option, + PTHREAD_CANCEL_ASYNCHRONOUS is never safe with the collector. (We could + argue about its safety without the collector.) + +UNICODE (Win32 only) Use the Unicode variant ('W') of the Win32 API instead + of ANSI/ASCII one ('A'). Useful for WinCE. + +HOST_ANDROID (or __ANDROID__) Compile for Android NDK platform. + +SN_TARGET_PS3 Compile for Sony PS/3. + +USE_GET_STACKBASE_FOR_MAIN (Linux only) Use pthread_attr_getstack() instead + of __libc_stack_end (or instead of any hard-coded value) for getting the + primordial thread stack bottom (useful if the client modifies the program's + address space). + +EMSCRIPTEN_ASYNCIFY (Emscripten only) Use Asyncify feature. Note this is + a relatively rarely used feature of Emscripten, most developers do not use + it, and it does not scale well to moderate-to-large code bases. But the + feature is needed to pass gctest, at least. Requires -sASYNCIFY linker + flag. diff --git a/bdwgc/doc/README.rs6000 b/bdwgc/doc/README.rs6000 new file mode 100644 index 000000000..f5630b20a --- /dev/null +++ b/bdwgc/doc/README.rs6000 @@ -0,0 +1,9 @@ +We have so far failed to find a good way to determine the stack base. +It is highly recommended that GC_stackbottom be set explicitly on program +startup. The supplied value sometimes causes failure under AIX 4.1, though +it appears to work under 3.X. HEURISTIC2 seems to work under 4.1, but +involves a substantial performance penalty, and will fail if there is +no limit on stack size. + +There is no thread support. (I assume recent versions of AIX provide +pthreads? I no longer have access to a machine ...) diff --git a/bdwgc/doc/README.sgi b/bdwgc/doc/README.sgi new file mode 100644 index 000000000..4258dce4f --- /dev/null +++ b/bdwgc/doc/README.sgi @@ -0,0 +1,39 @@ +Performance of the incremental collector can be greatly enhanced with +-DNO_EXECUTE_PERMISSION. + +The collector should run with all of the -32, -n32 and -64 ABIs. Remember to +define the AS macro in the Makefile.direct to be "as -64", or "as -n32". + +If you use -DREDIRECT_MALLOC=GC_malloc with C++ code, your code should make +at least one explicit call to malloc instead of new to ensure that the proper +version of malloc is linked in. + +Sproc threads are not supported. + +Pthreads support is provided. This requires that: + +1) You compile the collector with -DGC_THREADS specified in Makefile.direct. + +2) You have the latest pthreads patches installed. + +(Though the collector makes only documented pthread calls, +it relies on signal/threads interactions working just right in ways +that are not required by the standard. It is unlikely that this code +will run on other pthreads platforms. But please tell me if it does.) + +3) Every file that makes thread calls should define GC_THREADS and then +include gc.h. Gc.h redefines some of the pthread primitives as macros which +also provide the collector with information it requires. + +4) pthread_cond_wait and pthread_cond_timedwait should be prepared for +premature wakeups. (I believe the pthreads and related standards require this +anyway. Irix pthreads often terminate a wait if a signal arrives. +The garbage collector uses signals to stop threads.) + +5) It is expensive to stop a thread waiting in IO at the time the request is +initiated. Applications with many such threads may not exhibit acceptable +performance with the collector. (Increasing the heap size may help.) + +6) The collector should not be compiled with -DREDIRECT_MALLOC. This +confuses some library calls made by the pthreads implementation, which +expect the standard malloc. diff --git a/bdwgc/doc/README.solaris2 b/bdwgc/doc/README.solaris2 new file mode 100644 index 000000000..8eb4b9c2c --- /dev/null +++ b/bdwgc/doc/README.solaris2 @@ -0,0 +1,68 @@ +The collector supports both incremental collection and threads under +Solaris 2. The incremental collector normally retrieves page dirty information +through the appropriate /proc calls. But it can also be configured +(by defining MPROTECT_VDB instead of PROC_VDB in gcconfig.h) to use mprotect +and signals. This may result in shorter pause times, but it is no longer +safe to issue arbitrary system calls that write to the heap. + +Under other UNIX versions, +the collector normally obtains memory through sbrk. There is some reason +to expect that this is not safe if the client program also calls the system +malloc, or especially realloc. The sbrk man page strongly suggests this is +not safe: "Many library routines use malloc() internally, so use brk() +and sbrk() only when you know that malloc() definitely will not be used by +any library routine." This doesn't make a lot of sense to me, since there +seems to be no documentation as to which routines can transitively call malloc. +Nonetheless, under Solaris2, the collector now allocates +memory using mmap by default. (It defines USE_MMAP in gcconfig.h.) +You may want to reverse this decisions if you use -DREDIRECT_MALLOC=... + +Note: +Before you run "make check", you need to set your LD_LIBRARY_PATH correctly +(e.g., to "/usr/local/lib") so that tests can find the shared library +libgcc_s.so.1. Alternatively, you can configure with --disable-shared. + +SOLARIS THREADS: + +Unless --disable-threads option is given, threads support is on by default in +configure. This causes the collector to be compiled with -D GC_THREADS +ensuring thread safety. This assumes use of the pthread_ interface; old-style +Solaris threads are no longer supported. Thread-local allocation is on by +default. Parallel marking is on by default (it could be disabled manually +by configure --disable-parallel-mark option). + +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 +GC bookkeeping, etc. gc.h must be included with GC_THREADS macro defined +first, otherwise these replacements are not visible. A collector built in +this way may only be used by programs that are linked with the threads library. + +Unless USE_PROC_FOR_LIBRARIES is defined, dlopen disables collection +temporarily. In some unlikely cases, this can result in unpleasant heap +growth. But it seems better than the race/deadlock issues we had before. + +If threads are used on an x86 processor with malloc redirected to +GC_malloc, it is necessary to call GC_INIT explicitly before forking the +first thread. (This avoids a deadlock arising from calling GC_thr_init +with the allocation lock held.) + +There could be an issue when using gc_cpp.h in conjunction with Solaris +threads and Sun's C++ runtime. Apparently the overloaded new operator +may be invoked by some iostream initialization code before threads are +correctly initialized. This may cause a SIGSEGV during initialization +of the garbage collector. Currently the only known workaround is to not +invoke the garbage collector from a user defined global operator new, or to +have it invoke the garbage-collector's allocators only after main has started. +(Note that the latter requires a moderately expensive test in operator +delete.) + +I encountered "symbol : offset .... is non-aligned" errors. These +appear to be traceable to the use of the GNU assembler with the Sun linker. +The former appears to generate a relocation not understood by the latter. +The fix appears to be to use a consistent toolchain. (As a non-Solaris-expert +my solution involved hacking the libtool script, but I'm sure you can +do something less ugly.) + +Hans-J. Boehm +(The above contains my personal opinions, which are probably not shared +by anyone else.) diff --git a/bdwgc/doc/README.symbian b/bdwgc/doc/README.symbian new file mode 100644 index 000000000..515a9ccab --- /dev/null +++ b/bdwgc/doc/README.symbian @@ -0,0 +1,11 @@ +Instructions for Symbian: +1. Build: use libgc.mmp +2. Limitations +2.1. No multi-threaded support yet +2.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/bdwgc/doc/README.uts b/bdwgc/doc/README.uts new file mode 100644 index 000000000..6be49667d --- /dev/null +++ b/bdwgc/doc/README.uts @@ -0,0 +1,2 @@ +Alistair Crooks supplied the port. He used Lexa C version 2.1.3 with +-Xa to compile. diff --git a/bdwgc/doc/README.win32 b/bdwgc/doc/README.win32 new file mode 100644 index 000000000..21d141810 --- /dev/null +++ b/bdwgc/doc/README.win32 @@ -0,0 +1,215 @@ +The collector has at various times been compiled under Windows 95 and later, +NT, and XP, with the original Microsoft SDK, with Visual C++ 2.0, 4.0, and 6, +with the GNU Win32 tools, with Borland C++ Builder, with Watcom C, with EMX, +and with the Digital Mars compiler (DMC). + +For historical reasons, +the collector test program "gctest" is linked as a GUI application, +but does not open any windows. Its output normally appears in the file +"gctest.gc.log". It may be started from the file manager. The hour glass +cursor may appear as long as it's running. If it is started from the +command line, it will usually run in the background. Wait a few +minutes (a few seconds on a modern machine) before you check the output. +You should see either a failure indication or a "Collector appears to work" +message. + +A toy editor (de.exe) based on cords (heavyweight +strings represented as trees) has been ported and is included. +It runs fine under either Win32 or win32s. It serves as an example +of a true Windows application, except that it was written by a +nonexpert Windows programmer. (There are some peculiarities +in the way files are displayed. The is displayed explicitly +for standard DOS text files. As in the UNIX version, control +characters are displayed explicitly, but in this case as red text. +This may be suboptimal for some tastes and/or sets of default +window colors.) + +In general -DREDIRECT_MALLOC is unlikely to work unless the +application is completely statically linked. + +The collector normally allocates memory from the OS with VirtualAlloc. +This appears to cause problems under Windows NT and Windows 2000 (but +not Windows 95/98) if the memory is later passed to CreateDIBitmap. +To work around this problem, build the collector with -DUSE_GLOBAL_ALLOC. +This is currently incompatible with -DUSE_MUNMAP. (Thanks to Jonathan +Clark for tracking this down. There's some chance this may be fixed +since we now separate heap sections with an unused page.) + +[Threads and incremental collection are discussed near the end, below.] + +Microsoft Tools +--------------- + +For Microsoft development tools, type +"nmake -f NT_MAKEFILE cpu=i386 disable_threads=1 enable_static=1 nodebug=1" +to build the release variant of the collector as a static library without +threads support. + +In order to use the gc_cpp.h C++ interface, all client code should include +gc_cpp.h. + +[See above for gctest.] + +If you would prefer a VC++ .NET project file, ask Hans Boehm. One has +been contributed, but it seems to contain some absolute paths etc., so +it can presumably only be a starting point, and is not in the standard +distribution. It is unclear (to me, Hans Boehm) whether it is feasible to +change that. + +Clients may need to define GC_NOT_DLL before including gc.h, if the +collector was built as a static library. + +GNU Tools +--------- + +The collector should be buildable under Cygwin with the +"./configure; make check" machinery. + +MinGW builds (including for x64) are available both directly (on a Windows +host) and via cross-compilation, e.g. +"./configure --host=i686-pc-mingw32; make check" + +By default, configure instructs make to build the collector as a DLL (shared +library), adding -D GC_DLL to CFLAGS. + +Parallel marker is enabled by default; it could be disabled by +"--disable-parallel-mark" option. + +Memory unmapping could be turned off by "--disable-munmap" option. + +Borland Tools +------------- + +For Borland tools, use `cmake -G "Borland Makefiles"`. Note that +Borland's compiler defaults to 1 byte alignment in structures (-a1), +whereas Visual C++ appears to default to 8 byte alignment (/Zp8). +The garbage collector in its default configuration EXPECTS AT +LEAST 4 BYTE ALIGNMENT. Thus the BORLAND DEFAULT MUST +BE OVERRIDDEN. (In my opinion, it should usually be anyway. +I expect that -a1 introduces major performance penalties on a +486 or Pentium.) Note that this changes structure layouts. (As a last +resort, gcconfig.h can be changed to allow 1 byte alignment. But +this has significant negative performance implications.) + +Digital Mars compiler +--------------------- + +Same as MS Visual C++ but might require +-DAO_OLD_STYLE_INTERLOCKED_COMPARE_EXCHANGE option to compile with the +parallel marker enabled. + +Watcom compiler +--------------- + +Ivan V. Demakov's README for the Watcom port: + +The collector has been tested with Watcom C 10.6, 11.0 and OpenWatcom 2.0. +It runs under Win32 and win32s, and even under DOS with dos4gw +dos-extender. It should also run under OS/2, though this isn't +tested. Under Win32 the collector can be built either as dll +or as static library. + +Note that all compilations were done under Windows 95 or NT. +For unknown reason compiling under Windows 3.11 for NT (one +attempt has been made) leads to broken executables. + +Incremental collection is supported (except for MSDOS and OS/2). + +Before compiling you may need to edit WCC_MAKEFILE to set target +platform, library type (dynamic or static), calling conventions, and +optimization options. + +To compile the collector use the command: + wmake -f WCC_MAKEFILE + +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 +including "gc.h" (for example, with -DGC_DLL compiler option). It's +important, otherwise resulting programs will not run. + +The alternate way to compile the collector is to use cmake build system: + cmake -G "Watcom WMake" . + cmake --build . + +Special note for OpenWatcom users: the C (unlike the C++) compiler (of the +latest stable release, not sure for older ones) doesn't force pointer global +variables (i.e. not struct fields, not sure for locals) to be aligned unless +optimizing for speed (e.g., "-ot" option is set); the "-zp" option (or align +pragma) only controls alignment for structs; I don't know whether it's a bug or +a feature (see an old report of same kind - +http://bugzilla.openwatcom.org/show_bug.cgi?id=664), so You are warned. + +Incremental Collection +---------------------- + +There is some support for incremental collection. By default, the +collector chooses between explicit page protection, and GetWriteWatch-based +write tracking automatically, depending on the platform. + +The former is slow and interacts poorly with a debugger. +Pages are protected. Protection faults are caught by a handler +installed at the bottom of the handler +stack. Whenever possible, I recommend adding a call to +GC_enable_incremental at the last possible moment, after most +debugging is complete. No system +calls are wrapped by the collector itself. It may be necessary +to wrap ReadFile calls that use a buffer in the heap, so that the +call does not encounter a protection fault while it's running. +(As usual, none of this is an issue unless GC_enable_incremental +is called.) + +Note that incremental collection is disabled with -DSMALL_CONFIG. + +Threads +------- + +The collector by default handles threads similarly to other platforms. +James Clark's code which tracks threads attached to the collector DLL still +exists, but requires that both +- the collector is built in a DLL with GC_DLL defined, and +- GC_use_threads_discovery() is called before GC initialization, which + in turn must happen before creating additional threads. +We generally recommend avoiding this if possible, since it seems to +be less than 100% reliable. + +To build the collector as a dynamic library which handles threads similarly +to other platforms, type "nmake -f NT_MAKEFILE". If automatic tracking of +threads attached to the collector DLL (i.e. support of both kinds of thread +tracking) is needed then delete "-DTHREAD_LOCAL_ALLOC" from NT_MAKEFILE +manually before the build. + +The incremental collection is supported only if it is enabled before any +additional threads are created. + +Threads are also supported in static library builds with Microsoft tools +(e.g., NT_MAKEFILE), as well as with the CMake and GNU tools. The collector +must be built with GC_THREADS defined (this is the default in NT_MAKEFILE, +CMakeLists.txt and configure). + +For the normal, non-dll-based thread tracking to work properly, +threads should be created with GC_CreateThread or GC_beginthreadex, +and exit normally, or call GC_endthreadex or GC_ExitThread. (For Cygwin, the +standard pthread_create/exit calls could be used instead.) As in the pthread +case, including gc.h will redefine CreateThread, _beginthreadex, +_endthreadex, and ExitThread to call the GC_ versions instead. + +Note that, as usual, GC_CreateThread tends to introduce resource leaks (in the +C runtime) that are avoided by GC_beginthreadex. There is currently no +equivalent of _beginthread, and it should not be used. + +GC_INIT should be called from the main thread before other GC calls. + +We strongly advise against using the TerminateThread() Windows API call, +especially with the garbage collector. Any use is likely to provoke a +crash in the GC, since it makes it impossible for the collector to +correctly track threads. + +To build the collector for MinGW pthreads-win32 (or other non-Cygwin pthreads +implementation for Windows), use Makefile.direct and explicitly set +GC_WIN32_PTHREADS (or pass --enable-threads=pthreads to configure). +Use -DPTW32_STATIC_LIB for the static threads library. + +The alternate (better) way to build the library is to use CMake script; +please see README.cmake for the details. diff --git a/bdwgc/doc/README.win64 b/bdwgc/doc/README.win64 new file mode 100644 index 000000000..91d02829f --- /dev/null +++ b/bdwgc/doc/README.win64 @@ -0,0 +1,27 @@ +64-bit Windows on AMD64/Intel EM64T (x64) is supported. A collector can be +built with Microsoft Visual C++ 2005 or with mingw-w64 gcc. + +NT_MAKEFILE has been used in this environment. Type +"nmake -f NT_MAKEFILE cpu=AMD64 nodebug=1" in a Visual C++ command line +window to build the release variant of the dynamic library with threads +support. +To verify that the collector is at least somewhat functional, +type "nmake -f NT_MAKEFILE cpu=AMD64 check" to build and run the usual test +programs. This should create gctest.gc.log after a few seconds. + +Test_cpp.exe might not run correctly in case of dynamic GC linking. (It seems +that we're getting wrong instances of operator new/delete in some cases.) + +This process is completely analogous to NT_MAKEFILE usage +for the 32-bit library version. + +A similar procedure using NT_MAKEFILE is applicable to build the static +library - just pass "enable_static=1" as an extra argument to nmake. +If needed, it is also possible to build the library without threads +support - this could be done by passing "disable_threads=1" argument to nmake. + +Note that some warnings have been explicitly turned off in the makefile. + +VC++ note: to suppress warnings -D_CRT_SECURE_NO_DEPRECATE is used. + +gcc note: -fno-strict-aliasing should be used if optimizing. diff --git a/bdwgc/doc/debugging.md b/bdwgc/doc/debugging.md new file mode 100644 index 000000000..8554f7cc3 --- /dev/null +++ b/bdwgc/doc/debugging.md @@ -0,0 +1,273 @@ +# Debugging Garbage Collector Related Problems + +This page contains some hints on debugging issues specific to the +Boehm-Demers-Weiser conservative garbage collector. It applies both +to debugging issues in client code that manifest themselves as collector +misbehavior, and to debugging the collector itself. + +If you suspect a bug in the collector itself, it is strongly recommended that +you try the latest collector release before proceeding. + +## Bus Errors and Segmentation Violations + +If the fault occurred in `GC_find_limit`, or with incremental collection +enabled, this is probably normal. The collector installs handlers to take care +of these. You will not see these unless you are using a debugger. Your +debugger _should_ allow you to continue. It's often preferable to tell the +debugger to ignore SIGBUS and SIGSEGV ("handle SIGSEGV SIGBUS nostop noprint" +in gdb, "ignore SIGSEGV SIGBUS" in most versions of dbx) and set a breakpoint +in `abort`. The collector will call abort if the signal had another cause, and +there was not other handler previously installed. + +We recommend debugging without incremental collection if possible. (This +applies directly to UNIX systems. Debugging with incremental collection under +Win32 is worse. See README.win32.) + +If the application generates an unhandled SIGSEGV or equivalent, it may often +be easiest to set the environment variable `GC_LOOP_ON_ABORT`. On many +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 multi-threaded +applications, etc. + +## Other Signals + +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. + +## Warning Messages About Needing to Allocate Blacklisted Blocks + +The garbage collector generates warning messages of the form: + + + Repeated allocation of very large block ... + May lead to memory leak and poor performance + + +when it needs to allocate a block at a location that it knows to be referenced +by a false pointer. These false pointers can be either permanent (e.g. +a static integer variable that never changes) or temporary. In the latter +case, the warning is largely spurious, and the block will eventually +be reclaimed normally. In the former case, the program will still run +correctly, but the block will never be reclaimed. Unless the block is intended +to be permanent, the warning indicates a memory leak. + + 1. Ignore these warnings while you are using GC_DEBUG. Some of the routines + mentioned below don't have debugging equivalents. (Alternatively, write the + missing routines and send them to me.) + 2. Replace allocator calls that request large blocks with calls to + `GC_malloc_ignore_off_page` or `GC_malloc_atomic_ignore_off_page`. You may + want to set a breakpoint in `GC_default_warn_proc` to help you identify such + calls. Make sure that a pointer to somewhere near the beginning of the + resulting block is maintained in a (preferably volatile) variable as long + as the block is needed. + 3. If the large blocks are allocated with realloc, we suggest instead + allocating them with something like the following. Note that the realloc + size increment should be fairly large (e.g. a factor of 3/2) for this to + exhibit reasonable performance. But we all know we should do that anyway. + + + void * big_realloc(void *p, size_t new_size) { + size_t old_size = GC_size(p); + void * result; + if (new_size <= 10000) return(GC_realloc(p, new_size)); + if (new_size <= old_size) return(p); + result = GC_malloc_ignore_off_page(new_size); + if (result == 0) return(0); + memcpy(result,p,old_size); + GC_free(p); + return(result); + } + + + 4. In the unlikely case that even relatively small object (<20 KB) + allocations are triggering these warnings, then your address space contains + lots of "bogus pointers", i.e. values that appear to be pointers but aren't. + Usually this can be solved by using `GC_malloc_atomic` or the routines + in `gc_typed.h` to allocate large pointer-free regions of bitmaps, etc. + Sometimes the problem can be solved with trivial changes of encoding + in certain values. It is possible, to identify the source of the bogus + pointers by building the collector with `-DPRINT_BLACK_LIST`, which will + cause it to print the "bogus pointers", along with their location. + 5. If you get only a fixed number of these warnings, you are probably only + introducing a bounded leak by ignoring them. If the data structures being + allocated are intended to be permanent, then it is also safe to ignore them. + The warnings can be turned off by calling `GC_set_warn_proc` with + a procedure that ignores these warnings (e.g. by doing absolutely nothing). + +## The Collector References a Bad Address in GC_malloc + +This typically happens while the collector is trying to remove an entry from +its free list, and the free list pointer is bad because the free list link +in the last allocated object was bad. + +With >99% probability, you wrote past the end of an allocated object. Try +setting `GC_DEBUG` before including `gc.h` and allocating with `GC_MALLOC`. +This will try to detect such overwrite errors. + +## Unexpectedly Large Heap + +Unexpected heap growth can be due to one of the following: + + 1. Data structures that are being unintentionally retained. This is commonly + caused by data structures that are no longer being used, but were not + cleared, or by caches growing without bounds. + 2. Pointer misidentification. The garbage collector is interpreting integers + or other data as pointers and retaining the "referenced" objects. A common + symptom is that GC_dump() shows much of the heap as black-listed. + 3. Heap fragmentation. This should never result in unbounded growth, but + it may account for larger heaps. This is most commonly caused by allocation + of large objects. + 4. Per object overhead. This is usually a relatively minor effect, but + it may be worth considering. If the collector recognizes interior pointers, + object sizes are increased, so that one-past-the-end pointers are correctly + recognized. The collector can be configured not to do this + (`-DDONT_ADD_BYTE_AT_END`). + +The collector rounds up object sizes so the result fits well into the chunk +size (`HBLKSIZE`, normally 4 KB on 32-bit machines, 8 KB on 64-bit ones) used +by the collector. Thus it may be worth avoiding objects of size 2K + 1 bytes +(or exactly 2 KB if a byte is being added at the end.) The last two cases can +often be identified by looking at the output of a call to `GC_dump`. Among +other things, it will print the list of free heap blocks, and a very brief +description of all chunks in the heap, the object sizes they correspond to, +and how many live objects were found in the chunk at the last collection. + +Growing data structures can usually be identified by: + + 1. Building the collector with `-DKEEP_BACK_PTRS`, + 2. Preferably using debugging allocation (defining `GC_DEBUG` before + including `gc.h` and allocating with `GC_MALLOC`), so that objects will + be identified by their allocation site, + 3. Running the application long enough so that most of the heap is composed + of "leaked" memory, and + 4. Then calling `GC_generate_random_backtrace` from gc_backptr.h a few times + to determine why some randomly sampled objects in the heap are being + retained. + +The same technique can often be used to identify problems with false pointers, +by noting whether the reference chains printed +by `GC_generate_random_backtrace` involve any misidentified pointers. +An alternate technique is to build the collector with `-DPRINT_BLACK_LIST` +which will cause it to report values that are almost, but not quite, look like +heap pointers. It is very likely that actual false pointers will come from +similar sources. + +In the unlikely case that false pointers are an issue, it can usually +be resolved using one or more of the following techniques: + + 1. Use `GC_malloc_atomic` for objects containing no pointers. This is + especially important for large arrays containing compressed data, + pseudo-random numbers, and the like. It is also likely to improve GC + performance, perhaps drastically so if the application is paging. + 2. If you allocate large objects containing only one or two pointers at the + beginning, either try the typed allocation primitives in `gc_typed.h`, + or separate out the pointer-free component. + 3. Consider using `GC_malloc_ignore_off_page` to allocate large objects. + (See `gc.h` and above for details. Large means more than 100 KB in most + environments.) + 4. If your heap size is larger than 100 MB or so, build the collector with + `-DLARGE_CONFIG`. This allows the collector to keep more precise black-list + information. + 5. If you are using heaps close to, or larger than, a gigabyte on a 32-bit + machine, you may want to consider moving to a platform with 64-bit pointers. + This is very likely to resolve any false pointer issues. + +## Prematurely Reclaimed Objects + +The usual symptom of this is a segmentation fault, or an obviously overwritten +value in a heap object. This should, of course, be impossible. In practice, +it may happen for reasons like the following: + + 1. The collector did not intercept the creation of threads correctly + in a multi-threaded application, e.g. because the client called + `pthread_create` without including `gc.h`, which redefines it. + 2. The last pointer to an object in the garbage collected heap was stored + somewhere were the collector could not see it, e.g. in an object allocated + with system `malloc`, in certain types of `mmap`ed files, or in some data + structure visible only to the OS. (On some platforms, thread-local storage + is one of these.) + 3. The last pointer to an object was somehow disguised, e.g. by XORing + it with another pointer. + 4. Incorrect use of `GC_malloc_atomic` or typed allocation. + 5. An incorrect `GC_free` call. + 6. The client program overwrote an internal garbage collector data + structure. + 7. A garbage collector bug. + 8. (Empirically less likely than any of the above.) A compiler optimization + that disguised the last pointer. + +The following relatively simple techniques should be tried first to narrow +down the problem: + + 1. If you are using the incremental collector try turning it off for + debugging. + 2. If you are using shared libraries, try linking statically. If that works, + ensure that DYNAMIC_LOADING is defined on your platform. + 3. Try to reproduce the problem with fully debuggable unoptimized code. This + will eliminate the last possibility, as well as making debugging easier. + 4. Try replacing any suspect typed allocation and `GC_malloc_atomic` calls + with calls to `GC_malloc`. + 5. Try removing any `GC_free` calls (e.g. with a suitable `#define`). + 6. Rebuild the collector with `-DGC_ASSERTIONS`. + 7. If the following works on your platform (i.e. if gctest still works if + you do this), try building the collector with + `-DREDIRECT_MALLOC=GC_malloc_uncollectable`. This will cause the collector + to scan memory allocated with malloc. + +If all else fails, you will have to attack this with a debugger. The suggested +steps are: + + 1. Call `GC_dump` from the debugger around the time of the failure. Verify + that the collectors idea of the root set (i.e. static data regions which + it should scan for pointers) looks plausible. If not, i.e. if it does not + include some static variables, report this as a collector bug. Be sure + to describe your platform precisely, since this sort of problem is nearly + always very platform dependent. + 2. Especially if the failure is not deterministic, try to isolate + it to a relatively small test case. + 3. Set a break point in `GC_finish_collection`. This is a good point + to examine what has been marked, i.e. found reachable, by the collector. + 4. If the failure is deterministic, run the process up to the last + collection before the failure. Note that the variable `GC_gc_no` counts + collections and can be used to set a conditional breakpoint in the right + one. It is incremented just before the call to `GC_finish_collection`. + If object `p` was prematurely recycled, it may be helpful to look + at `*GC_find_header(p)` at the failure point. The `hb_last_reclaimed` field + will identify the collection number during which its block was last swept. + 5. Verify that the offending object still has its correct contents at this + point. Then call `GC_is_marked(p)` from the debugger to verify that the + object has not been marked, and is about to be reclaimed. Note that + `GC_is_marked(p)` expects the real address of an object (the address of the + debug header if there is one), and thus it may be more appropriate to call + `GC_is_marked(GC_base(p))` instead. + 6. Determine a path from a root, i.e. static variable, stack, or register + variable, to the reclaimed object. Call `GC_is_marked(q)` for each object + `q` along the path, trying to locate the first unmarked object, say `r`. + 7. If `r` is pointed to by a static root, verify that the location pointing + to it is part of the root set printed by `GC_dump`. If it is on the stack + in the main (or only) thread, verify that `GC_stackbottom` is set correctly + to the base of the stack. If it is in another thread stack, check the + collector's thread data structure (`GC_thread[]` on several platforms) + to make sure that stack bounds are set correctly. + 8. If `r` is pointed to by heap object `s`, check that the collector's + layout description for `s` is such that the pointer field will be scanned. + Call `*GC_find_header(s)` to look at the descriptor for the heap chunk. + The `hb_descr` field specifies the layout of objects in that chunk. + See `gc_mark.h` for the meaning of the descriptor. (If its low order 2 bits + are zero, then it is just the length of the object prefix to be scanned. + This form is always used for objects allocated with `GC_malloc` or + `GC_malloc_atomic`.) + 9. If the failure is not deterministic, you may still be able to apply some + of the above technique at the point of failure. But remember that objects + allocated since the last collection will not have been marked, even if the + collector is functioning properly. On some platforms, the collector can + be configured to save call chains in objects for debugging. Enabling this + feature will also cause it to save the call stack at the point of the last + GC in `GC_arrays._last_stack`. + 10. When looking at GC internal data structures remember that a number + of `GC_xxx` variables are really macro defined to `GC_arrays._xxx`, so that + the collector can avoid scanning them. diff --git a/bdwgc/doc/doc.am b/bdwgc/doc/doc.am new file mode 100644 index 000000000..39322f94a --- /dev/null +++ b/bdwgc/doc/doc.am @@ -0,0 +1,52 @@ +# +# 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. + +## Process this file with automake to produce Makefile.in. + +# installed documentation +if ENABLE_DOCS +dist_doc_DATA = \ + AUTHORS \ + README.md \ + doc/README.DGUX386 \ + doc/README.Mac \ + doc/README.OS2 \ + doc/README.amiga \ + doc/README.arm.cross \ + doc/README.autoconf \ + doc/README.cmake \ + doc/README.cords \ + doc/README.darwin \ + doc/README.emscripten \ + doc/README.environment \ + doc/README.ews4800 \ + doc/README.hp \ + doc/README.linux \ + doc/README.macros \ + doc/README.rs6000 \ + doc/README.sgi \ + doc/README.solaris2 \ + doc/README.symbian \ + doc/README.uts \ + doc/README.win32 \ + doc/README.win64 \ + doc/debugging.md \ + doc/finalization.md \ + doc/gcdescr.md \ + doc/gcinterface.md \ + doc/leak.md \ + doc/overview.md \ + doc/porting.md \ + doc/scale.md \ + doc/simple_example.md \ + doc/tree.md + +dist_man3_MANS = doc/gc.man +endif diff --git a/bdwgc/doc/finalization.md b/bdwgc/doc/finalization.md new file mode 100644 index 000000000..49aee982b --- /dev/null +++ b/bdwgc/doc/finalization.md @@ -0,0 +1,158 @@ +# Finalization + +Many garbage collectors provide a facility for executing user code just before +an object is collected. This can be used to reclaim any system resources +or non-garbage-collected memory associated with the object. Experience has +shown that this can be a useful facility. It is indispensable in cases +in which system resources are embedded in complex data structures (e.g. file +descriptors in the `include/cord.h`). + +Our collector provides the necessary functionality through +`GC_register_finalizer` in `include/gc.h`, or by inheriting from `gc_cleanup` +in `include/gc_cpp.h`). + +However, finalization should not be used in the same way as C++ destructors. +In well-written programs there will typically be very few uses +of finalization. (Garbage collected programs that interact with explicitly +memory-managed libraries may be an exception.) + +In general the following guidelines should be followed: + + * Actions that must be executed promptly do not belong in finalizers. They + should be handled by explicit calls in the code (or C++ destructors if you + prefer). If you expect the action to occur at a specific point, this + is probably not hard. + * Finalizers are intended for resource reclamation. + * Scarce system resources should be managed explicitly whenever convenient. + Use finalizers only as a backup mechanism for the cases that would be hard + to handle explicitly. + * If scarce resources are managed with finalization, the allocation routine + for that resource (e.g. open for file handles) should force a garbage + collection (two if that does not suffice) if it finds itself short of the + resource. + * If extremely scarce resources are managed by finalization (e.g. file + descriptors on systems which have a limit of 20 open files), it may + be necessary to introduce a descriptor caching scheme to hide the resource + limit. (E.g., the program would keep real file descriptors for the 20 most + recently used logically open files. Any other needed files would be closed + after saving their state. They would then be reopened on demand. + Finalization would logically close the file, closing the real descriptor + only if it happened to be cached.) Note that most modern systems allow + thousands of open files, and this is typically not an issue. + * Finalization code may be run anyplace an allocation or other call to the + collector takes place. In multi-threaded programs, finalizers have to obey + the normal locking conventions to ensure safety. Code run directly from + finalizers should not acquire locks that may be held during allocation. + This restriction can be easily circumvented by calling + `GC_set_finalize_on_demand(1)` at program start and creating a separate + thread dedicated to periodic invocation of `GC_invoke_finalizers()`. + +In single-threaded code, it is also often easiest to have finalizers queued +and, then to have them explicitly executed by `GC_invoke_finalizers()`. + +## Topologically ordered finalization + +Our _conservative garbage collector_ supports a form of finalization (with +`GC_register_finalizer`) in which objects are finalized in topological order. +If _A_ points to _B_ and both are registered for finalization, it is +guaranteed the _A_ will be finalized first. This usually guarantees that +finalization procedures see only unfinalized objects. + +This decision is often questioned, particularly since it has an obvious +disadvantage. The current implementation finalizes long chains of finalizable +objects one per collection. This is hard to avoid, since the first finalizer +invoked may store a pointer to the rest of the chain in a global variable, +making it accessible again. Or it may mutate the rest of the chain. + +Cycles involving one or more finalizable objects are never finalized. + +## Why topological ordering? + +It is important to keep in mind that the choice of finalization ordering +matters only in relatively rare cases. In spite of the fact that it has +received a lot of discussion, it is not one of the more important decisions +in designing a system. Many, especially smaller, applications will never +notice the difference. Nonetheless, we believe that topologically ordered +finalization is the right choice. + +To understand the justification, observe that if _A_'s finalization procedure +does not refer to _B_, we could fairly easily have avoided the dependency. +We could have split _A_ into _A'_ and _A''_ such that any references to _A_ +become references to _A'_, _A'_ points to _A''_ but not vice-versa, only +fields needed for finalization are stored in _A''_, and _A''_ is enabled for +finalization. (`GC_register_disappearing_link` provides an alternative +mechanism that does not require breaking up objects.) + +Thus assume that _A_ actually does need access to _B_ during finalization. +To make things concrete, assume that _B_ is finalizable because it holds +a pointer to a C object, which must be explicitly deallocated. (This is likely +to be one of the most common uses of finalization.) If _B_ happens to be +finalized first, _A_ will see a dangling pointer during its finalization. But +a principal goal of garbage collection was to avoid dangling pointers. + +Note that the client program could enforce topological ordering even if the +system did not. A pointer to _B_ could be stored in some globally visible +place, where it is cleared only by _A_'s finalizer. But this puts the burden +to ensure safety back on the programmer. + +With topologically ordered finalization, the programmer can fail to split +an object, thus leaving an accidental cycle. This results in a leak, which +is arguably less dangerous than a dangling pointer. More importantly, it is +_much_ easier to diagnose, since the garbage collector would have to go out of +its way not to notice finalization cycles. It can trivially report them. + +Furthermore unordered finalization does not really solve the problem +of cycles. Consider the above case in which _A_'s finalization procedure +depends on _B_, and thus a pointer to _B_ is stored in a global data +structure, to be cleared by _A_'s finalizer. If there is an accidental pointer +from _B_ back to _A_, and thus a cycle, neither _B_ nor _A_ will become +unreachable. The leak is there, just as in the topologically ordered case, but +it is hidden from easy diagnosis. + +A number of alternative finalization orderings have been proposed, e.g. based +on statically assigned priorities. In our opinion, these are much more likely +to require complex programming discipline to use in a large modular system. +(Some of them, e.g. Guardians proposed by Dybvig, Bruggeman, and Eby, do avoid +some problems which arise in combination with certain other collection +algorithms.) + +Fundamentally, a garbage collector assumes that objects reachable via pointer +chains may be accessed, and thus should be preserved. Topologically ordered +finalization simply extends this to object finalization; an finalizable object +reachable from another finalizer via a pointer chain is presumed to be +accessible by the finalizer, and thus should not be finalized. + +## Programming with topological finalization + +Experience with Cedar has shown that cycles or long chains of finalizable +objects are typically not a problem. Finalizable objects are typically rare. +There are several ways to reduce spurious dependencies between finalizable +objects. Splitting objects as discussed above is one technique. The collector +also provides `GC_register_disappearing_link`, which explicitly nils a pointer +before determining finalization ordering. + +Some so-called "operating systems" fail to clean up some resources associated +with a process. These resources must be deallocated at all cost before process +exit whether or not they are still referenced. Probably the best way to deal +with those is by not relying exclusively on finalization. They should +be registered in a table of weak pointers (implemented as disguised pointers +cleared by the finalization procedure that deallocates the resource). If any +references are still left at process exit, they can be explicitly deallocated +then. + +## Getting around topological finalization ordering + +There are certain situations in which cycles between finalizable objects are +genuinely unavoidable. Most notably, C++ compilers introduce self-cycles +to represent inheritance. `GC_register_finalizer_ignore_self` tells the +finalization part of the collector to ignore self cycles. This is used by the +C++ interface. + +Finalize.c actually contains an intentionally undocumented mechanism for +registering a finalizable object with user-defined dependencies. The problem +is that this dependency information is also used for memory reclamation, not +just finalization ordering. Thus misuse can result in dangling pointers even +if finalization does not create any. The risk of dangling pointers can be +eliminated by building the collector with `-DJAVA_FINALIZATION`. This forces +objects reachable from finalizers to be marked, even though this dependency +is not considered for finalization ordering. diff --git a/bdwgc/doc/gc.man b/bdwgc/doc/gc.man new file mode 100644 index 000000000..6725d889e --- /dev/null +++ b/bdwgc/doc/gc.man @@ -0,0 +1,155 @@ +.TH BDWGC 3 "26 Mar 2019" +.SH NAME +GC_malloc, GC_malloc_atomic, GC_free, GC_realloc, GC_enable_incremental, +GC_register_finalizer, GC_malloc_ignore_off_page, +GC_malloc_atomic_ignore_off_page, GC_set_warn_proc \- Garbage collecting +malloc replacement +.SH SYNOPSIS +#include +.br +void * GC_malloc(size_t size); +.br +void * GC_malloc_atomic(size_t size); +.br +void GC_free(void *ptr); +.br +void * GC_realloc(void *ptr, size_t size); +.br +void GC_enable_incremental(void); +.br +void * GC_malloc_ignore_off_page(size_t size); +.br +void * GC_malloc_atomic_ignore_off_page(size_t size); +.br +void GC_set_warn_proc(void (*proc)(char *, GC_word)); +.br +.sp +cc ... -lgc +.LP +.SH DESCRIPTION +.I GC_malloc +and +.I GC_free +are plug-in replacements for standard malloc and free. However, +.I +GC_malloc +will attempt to reclaim inaccessible space automatically by invoking +a conservative garbage collector at appropriate points. The collector +traverses all data structures accessible by following pointers from the +machines registers, stack(s), data, and bss segments. Inaccessible structures +will be reclaimed. A machine word is considered to be a valid pointer if +it is an address inside an object allocated by +.I +GC_malloc +or friends. +.LP +In most cases it is preferable to call the macros GC_MALLOC, GC_FREE, etc. +instead of calling GC_malloc and friends directly. This allows debugging +versions of the routines to be substituted by defining GC_DEBUG before +including gc.h. +.LP +See the documentation in the include files gc_cpp.h and gc_allocator.h, +as well as the gcinterface.md file in the distribution, +for an alternate, C++ specific interface to the garbage collector. +Note that C++ programs generally +need to be careful to ensure that all allocated memory (whether via new, +malloc, or STL allocators) that may point to garbage collected memory +is either itself garbage collected, or at least traced by the collector. +.LP +Unlike the standard implementations of malloc, +.I +GC_malloc +clears the newly allocated storage. +.I +GC_malloc_atomic +does not. Furthermore, it informs the collector that the resulting object +will never contain any pointers, and should therefore not be scanned by the +collector. +.LP +.I +GC_free +can be used to deallocate objects, but its use is optional, and generally +discouraged. +.I +GC_realloc +has the standard realloc semantics. It preserves pointer-free-ness. +.I +GC_register_finalizer +allows for registration of functions that are invoked when an object becomes +inaccessible. +.LP +The garbage collector tries to avoid allocating memory at locations that +already appear to be referenced before allocation. (Such apparent +``pointers'' are usually large integers and the like that just happen to look +like an address.) This may make it hard to allocate very large objects. +An attempt to do so may generate a warning. +.LP +.I +GC_malloc_ignore_off_page +and +.I +GC_malloc_atomic_ignore_off_page +inform the collector that the client code will always maintain a pointer to +near the beginning (i.e. within the first heap block) of the object, and that +pointers beyond that can be ignored by the collector. This makes it much +easier for the collector to place large objects. These are recommended for +large object allocation. (Objects expected to be > ~100 KB should be +allocated this way.) +.LP +It is also possible to use the collector to find storage leaks in programs +destined to be run with standard malloc/free. The collector can be compiled +for thread-safe operation. Unlike standard malloc, it is safe to call malloc +after a previous malloc call was interrupted by a signal, provided the +original malloc call is not resumed. +.LP +The collector may, on rare occasion, produce warning messages. On UNIX +machines these appear on stderr. Warning messages can be filtered, +redirected, or ignored with +.I +GC_set_warn_proc +This is recommended for production code. See gc.h for details. +.LP +Fully portable code should call +.I +GC_INIT +from the primordial thread of the main program before making any other +GC calls. On most platforms this does nothing and the collector is +initialized on first use. On a few platforms explicit initialization is +necessary. And it can never hurt. +.LP +Debugging versions of many of the above routines are provided as macros. +Their names are identical to the above, but consist of all capital letters. +If GC_DEBUG is defined before gc.h is included, these routines do additional +checking, and allow the leak detecting version of the collector to produce +slightly more useful output. Without GC_DEBUG defined, they behave exactly +like the lower-case versions. +.LP +On some machines, collection will be performed incrementally after a call to +.I +GC_enable_incremental. +This may temporarily write protect pages in the heap. See the README file for +more information on how this interacts with system calls that write to the +heap. +.LP +Other facilities not discussed here include limited facilities to support +incremental collection on machines without appropriate VM support, provisions +for providing more explicit object layout information to the garbage +collector, more direct support for ``weak'' pointers, support for +``abortable'' garbage collections during idle time, etc. +.LP +.SH "SEE ALSO" +The README and gc.h files in the distribution. More detailed definitions of +the functions exported by the collector are given there. (The above list is +not complete.) +.LP +The web site at http://www.hboehm.info/gc/ (or https://github.com/ivmai/bdwgc/). +.LP +Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative Environment", +"Software Practice & Experience", September 1988, pp. 807-820. +.LP +The malloc(3) man page. +.LP +.SH AUTHOR +Hans-J. Boehm (boehm@acm.org). +Some of the code was written by others (see the AUTHORS file for the details), +most notably by Alan Demers, and, recently, Ivan Maidanski. diff --git a/bdwgc/doc/gcdescr.md b/bdwgc/doc/gcdescr.md new file mode 100644 index 000000000..4fa269879 --- /dev/null +++ b/bdwgc/doc/gcdescr.md @@ -0,0 +1,546 @@ +# Conservative GC Algorithmic Overview + +This is a description of the algorithms and data structures used in our +conservative garbage collector. I expect the level of detail to increase with +time. For a survey of GC algorithms, e.g. see Paul Wilson's +["Uniprocessor Garbage Collection Techniques"](ftp://ftp.cs.utexas.edu/pub/garbage/gcsurvey.ps) +excellent paper. For an overview of the collector interface, see +[here](gcinterface.md). + +This description is targeted primarily at someone trying to understand the +source code. It specifically refers to variable and function names. It may +also be useful for understanding the algorithms at a higher level. + +The description here assumes that the collector is used in default mode. +In particular, we assume that it used as a garbage collector, and not just +a leak detector. We initially assume that it is used in stop-the-world, +non-incremental mode, though the presence of the incremental collector will +be apparent in the design. We assume the default finalization model, but the +code affected by that is very localized. + +Table of Contents + * [Introduction](#introduction) + * [Allocation](#allocation) + * [Mark phase](#mark-phase) + * [Sweep phase](#sweep-phase) + * [Finalization](#finalization) + * [Generational Collection and Dirty Bits](#generational-collection-and-dirty-bits) + * [Black-listing](#black-listing) + * [Thread support](#thread-support) + * [Thread-local allocation](#thread-local-allocation) + +## Introduction + +The garbage collector uses a modified mark-sweep algorithm. Conceptually +it operates roughly in four phases, which are performed occasionally as part +of a memory allocation: + + 1. _Preparation phase_ + + Each object has an associated mark bit. Clear all mark + bits, indicating that all objects are potentially unreachable. + + 2. _Mark phase_ + + Marks all objects that can be reachable via chains of pointers from + variables. Often the collector has no real information about the location + of pointer variables in the heap, so it views all static data areas, + stacks and registers as potentially containing pointers. Any bit + patterns that represent addresses inside heap objects managed by the + collector are viewed as pointers. Unless the client program has made heap + object layout information available to the collector, any heap objects + found to be reachable from variables are again scanned similarly. + + 3. _Sweep phase_ + + Scans the heap for inaccessible, and hence unmarked, + objects, and returns them to an appropriate free list for reuse. This is + not really a separate phase; even in non-incremental mode this operation + is usually performed on demand during an allocation that discovers an + empty free list. Thus the sweep phase is very unlikely to touch a page + that would not have been touched shortly thereafter anyway. + + 4. _Finalization phase_ + + Unreachable objects which had been registered for + finalization are enqueued for finalization outside the collector. + +The remaining sections describe the memory allocation data structures, and +then the last 3 collection phases in more detail. We conclude by outlining +some of the additional features implemented in the collector. + +## Allocation + +The collector includes its own memory allocator. The allocator obtains memory +from the system in a platform-dependent way. Under UNIX, it uses either +`malloc`, `sbrk`, or `mmap`. + +Most static data used by the allocator, as well as that needed by the rest +of the garbage collector is stored inside the `_GC_arrays` structure. This +allows the garbage collector to easily ignore the collectors own data +structures when it searches for root pointers. Other allocator and collector +internal data structures are allocated dynamically with `GC_scratch_alloc`. +The latter does not allow for deallocation, and is therefore used only for +permanent data structures. + +The allocator returns objects of different _kinds_. 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 +uncollectible. In spite of that, it is very likely that most C clients of the +collector currently use at most two kinds: `NORMAL` and `PTRFREE` objects. The +[GCJ](https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcj/) runtime also makes heavy +use of a kind (allocated with `GC_gcj_malloc`) that stores type information +at a known offset in method tables. + +The collector uses a two level allocator. A large block is defined to be one +larger than half of `HBLKSIZE`, which is a power of 2, typically on the order +of the page size. + +Large block sizes are rounded up to the next multiple of `HBLKSIZE` and then +allocated by `GC_allochblk`. The collector use an approximate best fit +algorithm by keeping free lists for several large block sizes. The actual +implementation of `GC_allochblk` is significantly complicated by black-listing +issues (see below). + +Small blocks are allocated in chunks of size `HBLKSIZE`. Each chunk +is dedicated to only one object size and kind. + +The allocator maintains separate free lists for each size and kind of object. +Associated with each kind is an array of free list pointers, with entry +`freelist[i]` pointing to a free list of size 'i' objects. Index `i` is +expressed in granules, which are the minimum allocatable unit, typically 8 or +16 bytes. The free lists themselves are linked through the first word in each +object (see `obj_link` macro). + +Once a large block is split for use in smaller objects, it can only be used +for objects of that size, unless the collector discovers a completely empty +chunk. Completely empty chunks are restored to the appropriate large block +free list. + +In order to avoid allocating blocks for too many distinct object sizes, the +collector normally does not directly allocate objects of every possible +request size. Instead, the request is rounded up to one of a smaller number +of allocated sizes, for which free lists are maintained. The exact allocated +sizes are computed on demand, but subject to the constraint that they increase +roughly in geometric progression. Thus objects requested early in the +execution are likely to be allocated with exactly the requested size, subject +to alignment constraints. See `GC_init_size_map` for details. + +The actual size rounding operation during small object allocation +is implemented as a table lookup in `GC_size_map` which maps a requested +allocation size in bytes to a number of granules. + +Both collector initialization and computation of allocated sizes are handled +carefully so that they do not slow down the small object fast allocation path. +An attempt to allocate before the collector is initialized, or before the +appropriate `GC_size_map` entry is computed, will take the same path as an +allocation attempt with an empty free list. This results in a call to the slow +path code (`GC_generic_malloc_inner`) which performs the appropriate +initialization checks. + +In non-incremental mode, we make a decision about whether to garbage collect +whenever an allocation would otherwise have failed with the current heap size. +If the total amount of allocation since the last collection is less than the +heap size divided by `GC_free_space_divisor`, we try to expand the heap. +Otherwise, we initiate a garbage collection. This ensures that the amount +of garbage collection work per allocated byte remains constant. + +The above is in fact an oversimplification of the real heap expansion and GC +triggering heuristic, which adjusts slightly for root size and certain kinds +of fragmentation. In particular: + + * Programs with a large root set size and little live heap memory will + expand the heap to amortize the cost of scanning the roots. + * GC actually collects more frequently in non-incremental mode. The large + block allocator usually refuses to split large heap blocks once the garbage + collection threshold is reached. This often has the effect of collecting + well before the heap fills up, thus reducing fragmentation and working set + size at the expense of GC time. (If the collector is configured not to unmap + unused pages, GC chooses an intermediate strategy depending on how much + large object allocation has taken place in the past.) + * In calculating the amount of allocation since the last collection we give + partial credit for objects we expect to be explicitly deallocated. Even + if all objects are explicitly managed, it is often desirable to collect + on rare occasion, since that is our only mechanism for coalescing completely + empty chunks. + +It has been suggested that this should be adjusted so that we favor expansion +if the resulting heap still fits into physical memory. In many cases, that +would no doubt help. But it is tricky to do this in a way that remains robust +if multiple application are contending for a single pool of physical memory. + +## Mark phase + +At each collection, the collector marks all objects that are possibly +reachable from pointer variables. Since it cannot generally tell where pointer +variables are located, it scans the following _root segments_ for pointers: + + * The registers. Depending on the architecture, this may be done using + assembly code, or by calling a `setjmp`-like function which saves register + contents on the stack. + * The stack(s). In the case of a single-threaded application, on most + platforms this is done by scanning the memory between (an approximation of) + the current stack pointer and `GC_stackbottom`. (For Intel Itanium, the + register stack scanned separately.) The `GC_stackbottom` variable is set in + a highly platform-specific way depending on the appropriate configuration + information in `gcconfig.h`. Note that the currently active stack needs + to be scanned carefully, since callee-save registers of client code may + appear inside collector stack frames, which may change during the mark + process. This is addressed by scanning some sections of the stack _eagerly_, + effectively capturing a snapshot at one point in time. + * Static data region(s). In the simplest case, this is the region between + `DATASTART` and `DATAEND`, as defined in `gcconfig.h`. However, in most + cases, this will also involve static data regions associated with dynamic + libraries. These are identified by the mostly platform-specific code + in `dyn_load.c`. The marker maintains an explicit stack of memory regions + that are known to be accessible, but that have not yet been searched for + contained pointers. Each stack entry contains the starting address of the + block to be scanned, as well as a descriptor of the block. If no layout + information is available for the block, then the descriptor is simply + a length. (For other possibilities, see `gc_mark.h`.) + +At the beginning of the mark phase, all root segments (as described above) are +pushed on the stack by `GC_push_roots`. (Registers and eagerly scanned stack +sections are processed by pushing the referenced objects instead of the stack +section itself.) If `ALL_INTERIOR_POINTERS` is not defined, then stack roots +require special treatment. In this case, the normal marking code ignores +interior pointers, but `GC_push_all_stack` explicitly checks for interior +pointers and pushes descriptors for target objects. + +The marker is structured to allow incremental marking. Each call +to `GC_mark_some` performs a small amount of work towards marking the heap. +It maintains explicit state in the form of `GC_mark_state`, which identifies +a particular sub-phase. Some other pieces of state, most notably the mark +stack, identify how much work remains to be done in each sub-phase. The normal +progression of mark states for a stop-the-world collection is: + + 1. `MS_INVALID` indicating that there may be accessible unmarked objects. + In this case `GC_objects_are_marked` will simultaneously be false, so the + mark state is advanced to + 2. `MS_PUSH_UNCOLLECTABLE` indicating that it suffices to push uncollectible + objects, roots, and then mark everything reachable from them. `GC_scan_ptr` + 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 `GC_mark_some` calls `GC_push_roots` to push the roots. It, then, + advances the mark state to + 3. `MS_ROOTS_PUSHED` asserting that once the mark stack is empty, all + reachable objects are marked. Once in this state, we work only on emptying + the mark stack. Once this is completed, the state changes to + 4. `MS_NONE` indicating that reachable objects are marked. + +The core mark routine `GC_mark_from`, is called repeatedly by several of the +sub-phases when the mark stack starts to fill up. It is also called repeatedly +in `MS_ROOTS_PUSHED` state to empty the mark stack. The routine is designed +to only perform a limited amount of marking at each call, so that it can +also be used by the incremental collector. It is fairly carefully tuned, +since it usually consumes a large majority of the garbage collection time. + +The fact that it performs only a small amount of work per call also allows +it to be used as the core routine of the parallel marker. In that case it is +normally invoked on thread-private mark stacks instead of the global mark +stack. More details can be found [here](scale.md). + +The marker correctly handles mark stack overflows. Whenever the mark stack +overflows, the mark state is reset to `MS_INVALID`. Since there are already +marked objects in the heap, this eventually forces a complete scan of the +heap, searching for pointers, during which any unmarked objects referenced +by marked objects are again pushed on the mark stack. This process is repeated +until the mark phase completes without a stack overflow. Each time the stack +overflows, an attempt is made to grow the mark stack. All pieces of the +collector that push regions onto the mark stack have to be careful to ensure +forward progress, even in case of repeated mark stack overflows. Every mark +attempt results in additional marked objects. + +Each mark stack entry is processed by examining all candidate pointers in the +range described by the entry. If the region has no associated type information +then this typically requires that each 4-byte aligned quantity (8-byte aligned +if 64-bit pointers) be considered a candidate pointer. + +We determine whether a candidate pointer is actually the address of a heap +block. This is done in the following steps: + + * The candidate pointer is checked against rough heap bounds. These heap + bounds are maintained such that all actual heap objects fall between them. + In order to facilitate black-listing (see below) we also include address + regions that the heap is likely to expand into. Most non-pointers fail this + initial test. + * The candidate pointer is divided into two pieces; the most significant + bits identify a `HBLKSIZE`-sized page in the address space, and the least + significant bits specify an offset within that page. (A hardware page may + actually consist of multiple such pages. Normally, HBLKSIZE is usually the + page size divided by a small power of two. Alternatively, if the collector + is built with `-DLARGE_CONFIG`, such a page may consist of multiple hardware + pages.) + * The page address part of the candidate pointer is looked up in + a [table](tree.md). Each table entry contains either 0, indicating that + the page is not part of the garbage collected heap, a small integer _n_, + indicating that the page is part of large object, starting at least _n_ + pages back, or a pointer to a descriptor for the page. In the first case, + the candidate pointer is not a true pointer and can be safely ignored. + In the last two cases, we can obtain a descriptor for the page containing + the beginning of the object. + * The starting address of the referenced object is computed. The page + descriptor contains the size of the object(s) in that page, the object kind, + and the necessary mark bits for those objects. The size information can be + used to map the candidate pointer to the object starting address. + To accelerate this process, the page header also contains a pointer to + a precomputed map of page offsets to displacements from the beginning of an + object. The use of this map avoids a potentially slow integer remainder + operation in computing the object start address. + * The mark bit for the target object is checked and set. If the object was + previously unmarked, the object is pushed on the mark stack. The descriptor + is read from the page descriptor. (This is computed from information stored + in `GC_obj_kinds` when the page is first allocated.) + +At the end of the mark phase, mark bits for left-over free lists are cleared, +in case a free list was accidentally marked due to a stray pointer. + +## Sweep phase + +At the end of the mark phase, all blocks in the heap are examined. Unmarked +large objects are immediately returned to the large object free list. Each +small object page is checked to see if all mark bits are clear. If so, the +entire page is returned to the large object free list. Small object pages +containing some reachable object are queued for later sweeping, unless +we determine that the page contains very little free space, in which case +it is not examined further. + +This initial sweep pass touches only block headers, not the blocks themselves. +Thus it does not require significant paging, even if large sections of the +heap are not in physical memory. + +Nonempty small object pages are swept when an allocation attempt encounters +an empty free list for that object size and kind. Pages for the correct size +and kind are repeatedly swept until at least one empty block is found. +Sweeping such a page involves scanning the mark bit array in the page header, +and building a free list linked through the first words in the objects +themselves. This does involve touching the appropriate data page, but in most +cases it will be touched only just before it is used for allocation. Hence any +paging is essentially unavoidable. + +Except in the case of pointer-free objects, we maintain the invariant that any +object in a small object free list is cleared (except possibly for the link +field). Thus it becomes the burden of the small object sweep routine to clear +objects. This has the advantage that we can easily recover from accidentally +marking a free list, though that could also be handled by other means. The +collector currently spends a fair amount of time clearing objects, and this +approach should probably be revisited. In most configurations, we use +specialized sweep routines to handle common small object sizes. Since +we allocate one mark bit per word, it becomes easier to examine the relevant +mark bits if the object size divides the word length evenly. We also suitably +unroll the inner sweep loop in each case. (It is conceivable that +profile-based procedure cloning in the compiler could make this unnecessary +and counterproductive. I know of no existing compiler to which this applies.) + +The sweeping of small object pages could be avoided completely at the expense +of examining mark bits directly in the allocator. This would probably be more +expensive, since each allocation call would have to reload a large amount +of state (e.g. next object address to be swept, position in mark bit table) +before it could do its work. The current scheme keeps the allocator simple and +allows useful optimizations in the sweeper. + +## Finalization + +Both `GC_register_disappearing_link` and `GC_register_finalizer` add the +request to a corresponding hash table. The hash table is allocated out of +collected memory, but the reference to the finalizable object is hidden from +the collector. Currently finalization requests are processed non-incrementally +at the end of a mark cycle. + +The collector makes an initial pass over the table of finalizable objects, +pushing the contents of unmarked objects onto the mark stack. After pushing +each object, the marker is invoked to mark all objects reachable from it. The +object itself is not explicitly marked. This assures that objects on which +a finalizer depends are neither collected nor finalized. + +If in the process of marking from an object the object itself becomes marked, +we have uncovered a cycle involving the object. This usually results in +a warning from the collector. Such objects are not finalized, since it may be +unsafe to do so. See the more detailed discussion of +[finalization semantics](finalization.md). + +Any objects remaining unmarked at the end of this process are added to a queue +of objects whose finalizers can be run. Depending on collector configuration, +finalizers are dequeued and run either implicitly during allocation calls, +or explicitly in response to a user request. (Note that the former +is unfortunately both the default and not generally safe. If finalizers +perform synchronization, it may result in deadlocks. Nontrivial finalizers +generally need to perform synchronization, and thus require a different +collector configuration.) + +The collector provides a mechanism for replacing the procedure that is used +to mark through objects. This is used both to provide support for Java-style +unordered finalization, and to ignore certain kinds of cycles, e.g. those +arising from C++ implementations of virtual inheritance. + +## Generational Collection and Dirty Bits + +We basically use the concurrent and generational GC algorithm described in +["Mostly Parallel Garbage Collection"](http://www.hboehm.info/gc/papers/pldi91.ps.Z), +by Boehm, Demers, and Shenker. + +The most significant modification is that the collector always starts running +in the allocating thread. There is no separate garbage collector thread. (If +parallel GC is enabled, helper threads may also be woken up.) If an allocation +attempt either requests a large object, or encounters an empty small object +free list, and notices that there is a collection in progress, it immediately +performs a small amount of marking work as described above. + +This change was made both because we wanted to easily accommodate +single-threaded environments, and because a separate GC thread requires very +careful control over the scheduler to prevent the mutator from out-running the +collector, and hence provoking unneeded heap growth. + +In incremental mode, the heap is always expanded when we encounter +insufficient space for an allocation. Garbage collection is triggered whenever +we notice that more than `GC_heap_size / 2 * GC_free_space_divisor` bytes +of allocation have taken place. After `GC_full_freq` minor collections a major +collection is started. + +All collections initially run uninterrupted until a predetermined amount +of time (50 ms by default) has expired. If this allows the collection +to complete entirely, we can avoid correcting for data structure modifications +during the collection. If it does not complete, we return control to the +mutator, and perform small amounts of additional GC work during those later +allocations that cannot be satisfied from small object free lists. When +marking completes, the set of modified pages is retrieved, and we mark once +again from marked objects on those pages, this time with the mutator stopped. + +We keep track of modified pages using one of several distinct mechanisms: + + * (`MPROTECT_VDB`) By write-protecting physical pages and catching write + faults. This is implemented for many Unix-like systems and for Win32. It is + not possible in a few environments. + * (`GWW_VDB`) By using the Win32 `GetWriteWatch` function to read dirty + bits. + * (`PROC_VDB`) By retrieving dirty bit information from /proc. (Currently + only Sun's Solaris supports this. Though this is considerably cleaner, + performance may actually be better with `mprotect` and signals.) + * (`SOFT_VDB`) By retrieving Linux soft-dirty bit information from /proc. + * (`PCR_VDB`) By relying on an external dirty bit implementation, in this + case the one in Xerox PCR. + * Through explicit mutator cooperation. This enabled by + `GC_set_manual_vdb_allowed(1)` call, and requires the client code to call + `GC_ptr_store_and_dirty` or `GC_end_stubborn_change` (followed by a number + of `GC_reachable_here` calls), and is rarely used. + * (`DEFAULT_VDB`) By treating all pages as dirty. This is the default + if none of the other techniques is known to be usable. (Practical only for + testing.) + +## Black-listing + +The collector implements _black-listing_ of pages, as described in +["Space Efficient Conservative Collection", PLDI'93](http://dl.acm.org/citation.cfm?doid=155090.155109) +by Boehm, also available +[here](https://www.cs.rice.edu/~javaplt/311/Readings/pldi93.pdf). + +During the mark phase, the collector tracks _near misses_, i.e. attempts +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 misidentification, +they will be allocated only to small pointer-free objects. + +The collector understands two different kinds of black-listing. A page may be +black listed for interior pointer references (`GC_add_to_black_list_stack`), +if it was the target of a near miss from a location that requires interior +pointer recognition, e.g. the stack, or the heap if `GC_all_interior_pointers` +is set. In this case, we also avoid allocating large blocks that include this +page. + +If the near miss came from a source that did not require interior pointer +recognition, it is black-listed with `GC_add_to_black_list_normal`. A page +black-listed in this way may appear inside a large object, so long as it is +not the first page of a large object. + +The `GC_allochblk` routine respects black-listing when assigning 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 pointer-free +objects. + +## Thread support + +We support several different threading models. Unfortunately Pthreads, the +only reasonably well standardized thread model, supports too narrow +an interface for conservative garbage collection. There appears to be no +completely portable way to allow the collector to coexist with various +Pthreads implementations. Hence we currently support only the more common +Pthreads implementations. + +In particular, it is very difficult for the collector to stop all other +threads in the system and examine the register contents. This is currently +accomplished with very different mechanisms for some Pthreads implementations. +For Linux/HPUX/OSF1, Solaris and Irix it sends signals to individual Pthreads +and has them wait in the signal handler. + +The Linux and Irix implementations use only documented Pthreads calls, but +rely on extensions to their semantics. The Linux implementation +`pthread_stop_world.c` relies on only very mild extensions to the pthreads +semantics, and already supports a large number of other Unix-like pthreads +implementations. Our goal is to make this the only pthread support in the +collector. + +All implementations must intercept thread creation and a few other +thread-specific calls to allow enumeration of threads and location of thread +stacks. This is current accomplished with `#define`'s in `gc.h` (really +`gc_pthread_redirects.h`), or optionally by using `ld`'s function call +wrapping mechanism under Linux. + +The collector support several facilities to enhance the processor-scalability +and thread performance of the collector. These are discussed in more detail +[here](scale.md). We briefly outline the data approach to thread-local +allocation in the next section. + +## Thread-local allocation + +If thread-local allocation is enabled (which is true in the default +configuration for most supported platforms), the collector keeps separate +arrays of free lists for each thread. + +The free list arrays associated with each thread are only used to satisfy +requests for objects that are both very small, and belong to one of a small +number of well-known kinds. These include _normal_, pointer-free, _gcj_ and +_disclaim_ objects. + +Thread-local free list entries contain either a pointer to the first element +of a free list, or they contain a counter of the number of allocation +granules, corresponding to objects of this size, allocated so far. Initially +they contain the value one, i.e. a small counter value. + +Thread-local allocation goes directly through the global allocator if the +object is of a size or kind not covered by the local free lists. + +If there is an appropriate local free list, the allocator checks whether +it contains a sufficiently small counter value. If so, the counter is simply +incremented by a value, and the global allocator is used. In this way, +the initial few allocations of a given size bypass the local allocator. +A thread that only allocates a handful of objects of a given size will not +build up its own free list for that size. This avoids wasting space for +unpopular objects sizes or kinds. + +Once the counter passes a threshold, `GC_malloc_many` is called to allocate +roughly `HBLKSIZE` space and put it on the corresponding local free list. +Further allocations of that size and kind then use this free list, and no +longer need to acquire the allocation lock. The allocation procedure +is otherwise similar to the global free lists. The local free lists are also +linked using the first word in the object. In most cases this means they +require considerably less time. + +Local free lists are treated by most of the rest of the collector as though +they were in-use reachable data. This requires some care, since pointer-free +objects are not normally traced, and hence a special tracing procedure +is required to mark all objects on pointer-free and gcj local free lists. + +On thread exit, any remaining thread-local free list entries are transferred +back to the global free list. + +Note that if the collector is configured for thread-local allocation (the +default for most platforms), `GC_malloc` and friends only use thread-local +allocation. + +For some more details see [here](scale.md), and the technical report entitled +["Fast Multiprocessor Memory Allocation and Garbage Collection"](http://www.hpl.hp.com/techreports/2000/HPL-2000-165.html). diff --git a/bdwgc/doc/gcinterface.md b/bdwgc/doc/gcinterface.md new file mode 100644 index 000000000..8247ba226 --- /dev/null +++ b/bdwgc/doc/gcinterface.md @@ -0,0 +1,216 @@ +# C/C++ Interface + +On many platforms, a single-threaded garbage collector library can be built +to act as a plug-in `malloc` replacement. (Build it with +`-DREDIRECT_MALLOC=GC_malloc -DIGNORE_FREE`.) This is often the best way to +deal with third-party libraries which leak or prematurely free objects. +`-DREDIRECT_MALLOC=GC_malloc` is intended primarily as an easy way to adapt +old code, not for new development. + +New code should use the interface discussed below. + +Code must be linked against the GC library. On most UNIX platforms, depending +on how the collector is built, this will be `gc.a` or `libgc.{a,so}`. + +The following describes the standard C interface to the garbage collector. +It is not a complete definition of the interface. It describes only the most +commonly used functionality, approximately in decreasing order of frequency +of use. This somewhat duplicates the information in `gc.man` file. The full +interface is described in `gc.h` file. + +Clients should include `gc.h` (i.e., not `gc_config_macros.h`, +`gc_pthread_redirects.h`, `gc_version.h`). In the case of multi-threaded code, +`gc.h` should be included after the threads header file, and after defining +`GC_THREADS` macro. The header file `gc.h` must be included in files that use +either GC or threads primitives, since threads primitives will be redefined +to cooperate with the GC on many platforms. + +Thread users should also be aware that on many platforms objects reachable +only from thread-local variables may be prematurely reclaimed. Thus objects +pointed to by thread-local variables should also be pointed to by a globally +visible data area, e.g. thread's stack. (This behavior is viewed as a bug, but +as one that is exceedingly hard to fix without some `libc` hooks.) + +`void * GC_MALLOC(size_t _bytes_)` - Allocates and clears _bytes_ +of storage. Requires (amortized) time proportional to _bytes_. The resulting +object will be automatically deallocated when unreferenced. References from +objects allocated with the system malloc are usually not considered by the +collector. (See `GC_MALLOC_UNCOLLECTABLE`, however. Building the collector +with `-DREDIRECT_MALLOC=GC_malloc_uncollectable` is often a way around this.) +`GC_MALLOC` is a macro which invokes `GC_malloc` by default or, if `GC_DEBUG` +is defined before `gc.h` is included, a debugging version that checks +occasionally for overwrite errors, and the like. + +`void * GC_MALLOC_ATOMIC(size_t _bytes_)` - Allocates _bytes_ +of storage. Requires (amortized) time proportional to _bytes_. The resulting +object will be automatically deallocated when unreferenced. The client +promises that the resulting object will never contain any pointers. The memory +is not cleared. This is the preferred way to allocate strings, floating point +arrays, bitmaps, etc. More precise information about pointer locations can be +communicated to the collector using the interface in `gc_typed.h`. + +`void * GC_MALLOC_UNCOLLECTABLE(size_t _bytes_)` - Identical +to `GC_MALLOC`, except that the resulting object is not automatically +deallocated. Unlike the system-provided `malloc`, the collector does 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.) + +`void * GC_REALLOC(void * _old_object_, size_t _new_bytes_)` - Allocates +a new object of the indicated size and copy the old object's content into the +new object. The old object is reused in place if convenient. If the original +object was allocated with `GC_MALLOC_ATOMIC`, the new object is subject to the +same constraints. If it was allocated as an uncollectible object, then the new +object is uncollectible, and the old object (if different) is deallocated. + +`void GC_FREE(void * _object_)` - Explicitly deallocates an _object_. +Typically not useful for small collectible objects. + +`void * GC_MALLOC_IGNORE_OFF_PAGE(size_t _bytes_)` and +`void * GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(size_t _bytes_)` - Analogous +to `GC_MALLOC` and `GC_MALLOC_ATOMIC`, respectively, except that the client +guarantees that as long as the resulting object is of use, a pointer +is maintained to someplace inside the first heap block (hblk) of the object. +This pointer should be declared volatile to avoid interference from compiler +optimizations. (Other nonvolatile pointers to the object may exist as well.) +This is the preferred way to allocate objects that are likely to be +more than 100 KB in size. It greatly reduces the risk that such objects will +be accidentally retained when they are no longer needed. Thus space usage may +be significantly reduced. Another way is `GC_set_all_interior_pointers(0)` +called at program start (this, however, is generally not suitable for C++ code +because of multiple inheritance). + +`void GC_INIT()` - On some platforms, it is necessary to invoke this _from +the main executable_, _not from a dynamic library_, before the initial +invocation of a GC routine. It is recommended that this be done in portable +code, though we try to ensure that it expands to a no-op on as many platforms +as possible. + +`void GC_gcollect(void)` - Explicitly forces a garbage collection. + +`void GC_enable_incremental(void)` - Causes the garbage collector +to perform a small amount of work every few invocations of `GC_MALLOC` or the +like, instead of performing an entire collection at once. This is likely +to increase total running time. It will improve response on a platform that +has suitable support in the garbage collector (Linux and most Unix versions, +Win32 if the collector was suitably built). On many platforms this interacts +poorly with system calls that write to the garbage collected heap. + +`void GC_set_warn_proc(GC_warn_proc)` - Replaces the default procedure +used by the collector to print warnings. The collector may otherwise +write to `stderr`, most commonly because `GC_malloc` was used in a situation +in which `GC_malloc_ignore_off_page` would have been more appropriate. See +`gc.h` for details. + +`void GC_REGISTER_FINALIZER(...)` - Registers a function to be called when +an object becomes inaccessible. This is often useful as a backup method for +releasing system resources (e.g. closing files) when the object referencing +them becomes inaccessible. It is not an acceptable method to perform actions +that must be performed in a timely fashion. See `gc.h` for details of the +interface. See also [here](finalization.md) for a more detailed discussion +of the design. Note that an object may become inaccessible before client code +is done operating on objects referenced by its fields. Suitable +synchronization is usually required. See +[here](http://portal.acm.org/citation.cfm?doid=604131.604153) or +[here](http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html) for details. + +If you are concerned with multiprocessor performance and scalability, you +should consider enabling and using thread local allocation. + +If your platform supports it, you should also build the collector with +parallel marking support (`-DPARALLEL_MARK`); configure has it on by default. + +If the collector is used in an environment in which pointer location +information for heap objects is easily available, this can be passed on to the +collector using the interfaces in either `gc_typed.h` or `gc_gcj.h`. + +The collector distribution also includes a **string package** that takes +advantage of the collector. For details see `cord.h` file. + +## C++ Interface + +The C++ interface is implemented as a thin layer on the C interface. +Unfortunately, this thin layer appears to be very sensitive to variations +in C++ implementations, particularly since it tries to replace the global +`::new` operator, something that appears to not be well-standardized. Your +platform may need minor adjustments in this layer (`gc_badalc.cc`, +`gc_cpp.cc`, `gc_cpp.h`, and possibly `gc_allocator.h`). Such changes do not +require understanding of collector internals, though they may require a good +understanding of your platform. (Patches enhancing portability are welcome. +But it is easy to break one platform by fixing another.) + +Usage of the collector from C++ is also complicated by the fact that there are +many _standard_ ways to allocate memory in C++. The default `::new` operator, +default `malloc`, and default STL allocators allocate memory that is not +garbage collected, and is not normally _traced_ by the collector. This means +that any pointers in memory allocated by these default allocators will not be +seen by the collector. Garbage-collectible memory referenced only by pointers +stored in such default-allocated objects is likely to be reclaimed prematurely +by the collector. + +It is the programmers responsibility to ensure that garbage-collectible memory +is referenced by pointers stored in one of + + * Program variables + * Garbage-collected objects + * Uncollected but _traceable_ objects + +Traceable objects are not necessarily reclaimed by the collector, but are +scanned for pointers to collectible objects. They are usually allocated +by `GC_MALLOC_UNCOLLECTABLE`, as described above, and through some interfaces +described below. + +On most platforms, the collector may not trace correctly from in-flight +exception objects. Thus objects thrown as exceptions should only point +to otherwise reachable memory. This is another bug whose proper repair +requires platform hooks. + +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 `gc_allocator`. + +### STL allocators + +Recent versions of the collector include a hopefully standard-conforming +allocator implementation in `gc_allocator.h`. It defines `traceable_allocator` +and `gc_allocator` which may be used either directly to allocate memory or to +instantiate container templates. The former allocates uncollectible but traced +memory. The latter allocates garbage-collected memory. + +These should work with any fully standard-conforming C++ compiler. + +### Class inheritance based interface for new-based allocation + +Users may include `gc_cpp.h` and then cause members of classes to be allocated +in garbage collectible memory by having those classes inherit from class `gc`. +For details see `gc_cpp.h` file. + +Linking against `gccpp` in addition to the `gc` library overrides `::new` +(and friends) to allocate traceable but uncollectible memory, making +it safe to refer to collectible objects from the resulting memory. + +If the user includes `gc_cpp.h` but `::new` should not be overridden then +`gctba` (in addition to the `gc`) library should be linked with to provide +the definition of `GC_throw_bad_alloc` C++ function used by operator `new` of +class `gc`. Alternatively, the client may define `GC_NEW_ABORTS_ON_OOM` macro +before include of `gc_cpp.h` (this instructs `::new` to issue an abort instead +of throwing an exception), or may define `GC_INCLUDE_NEW` one before include +of `gc_cpp.h` (however, this might not compile or work as expected on some +platforms). + +## C interface + +It is also possible to use the C interface from `gc.h` directly. On platforms +which use `malloc` to implement `::new`, it should usually be possible to use +a version of the collector that has been compiled as a `malloc` replacement. +It is also possible to replace `::new` and other allocation functions +suitably, as is done by `gccpp`. + +Note that user-implemented small-block allocation often works poorly with +an underlying garbage-collected large block allocator, since the collector has +to view all objects accessible from the user's free list as reachable. This +is likely to cause problems if `GC_MALLOC` is used with something like the +original HP version of STL. This approach works well with the SGI versions +of the STL only if the `malloc_alloc` allocator is used. diff --git a/bdwgc/doc/leak.md b/bdwgc/doc/leak.md new file mode 100644 index 000000000..6b5ed2266 --- /dev/null +++ b/bdwgc/doc/leak.md @@ -0,0 +1,160 @@ +# Using the Garbage Collector as Leak Detector + +The garbage collector may be used as a leak detector. In this case, the +primary function of the collector is to report objects that were allocated +(typically with `GC_MALLOC`), not deallocated (normally with `GC_FREE`), but +are no longer accessible. Since the object is no longer accessible, there +is normally no way to deallocate the object at a later time; thus it can +safely be assumed that the object has been "leaked". + +This is substantially different from counting leak detectors, which simply +verify that all allocated objects are eventually deallocated. +A garbage-collector based leak detector can provide somewhat more precise +information when an object was leaked. More importantly, it does not report +objects that are never deallocated because they are part of "permanent" data +structures. Thus it does not require all objects to be deallocated at process +exit time, a potentially useless activity that often triggers large amounts +of paging. + +The garbage collector provides leak detection support. This includes the +following features: + + 1. Leak detection mode can be initiated at run-time by `GC_set_find_leak(1)` + call at program startup instead of building the collector with `FIND_LEAK` + macro defined. + 2. Leaked objects should be reported and then correctly garbage collected. + +To use the collector as a leak detector, do the following steps: + + 1. Activate the leak detection mode as described above. + 2. Change the program so that all allocation and deallocation goes through + the garbage collector. + 3. Arrange to call `GC_gcollect` (or `CHECK_LEAKS()`) at appropriate points + to check for leaks. (This happens implicitly but probably not with + a sufficient frequency for long running programs.) + +The second step can usually be accomplished with the +`-DREDIRECT_MALLOC=GC_malloc` option when the collector is built, or by +defining `malloc`, `calloc`, `realloc`, `free` (as well as `strdup`, +`strndup`, `wcsdup`, `memalign`, `posix_memalign`) to call the corresponding +garbage collector functions. But this, by itself, will not yield very +informative diagnostics, since the collector does not keep track of the +information about how objects were allocated. The error reports will include +only object addresses. + +For more precise error reports, as much of the program as possible should use +the all uppercase variants of these functions, after defining `GC_DEBUG`, and +then including `gc.h`. In this environment `GC_MALLOC` is a macro which causes +at least the file name and line number at the allocation point to be saved +as part of the object. Leak reports will then also include this information. + +Many collector features (e.g. finalization and disappearing links) are less +useful in this context, and are not fully supported. Their use will usually +generate additional bogus leak reports, since the collector itself drops some +associated objects. + +The same is generally true of thread support. However, the correct leak +reports should be generated with linuxthreads, at least. + +On a few platforms (currently Solaris/SPARC, Irix, and, with +`-DSAVE_CALL_CHAIN`, Linux/x86), `GC_MALLOC` also causes some more information +about its call stack to be saved in the object. Such information is reproduced +in the error reports in very non-symbolic form, but it can be very useful with +the aid of a debugger. + +## An Example + +The `leak_detector.h` file is included in the "include" subdirectory of the +distribution. + +Assume the collector has been built with `-DFIND_LEAK` or +`GC_set_find_leak(1)` exists as the first statement in `main`. + +The program to be tested for leaks could look like `tests/leak_test.c` file +of the distribution. + +On a Linux/x86 system this produces on the stderr stream: + + + Found 1 leaked objects: + 0x806dff0 (tests/leak_test.c:19, sz=4, NORMAL) + + +(On most unmentioned operating systems, the output is similar to this. If the +collector had been built on Linux/x86 with `-DSAVE_CALL_CHAIN`, the output +would be closer to the Solaris example. For this to work, the program should +not be compiled with `-fomit_frame_pointer`.) + +On Irix it reports: + + + Found 1 leaked objects: + 0x10040fe0 (tests/leak_test.c:19, sz=4, NORMAL) + Caller at allocation: + ##PC##= 0x10004910 + + +and on Solaris the error report is: + + + Found 1 leaked objects: + 0xef621fc8 (tests/leak_test.c:19, sz=4, NORMAL) + Call chain at allocation: + args: 4 (0x4), 200656 (0x30FD0) + ##PC##= 0x14ADC + args: 1 (0x1), -268436012 (0xEFFFFDD4) + ##PC##= 0x14A64 + + +In the latter two cases some additional information is given about how malloc +was called when the leaked object was allocated. For Solaris, the first line +specifies the arguments to `GC_debug_malloc` (the actual allocation routine), +The second one specifies the program counter inside `main`, the third one +specifies the arguments to `main`, and, finally, the program counter inside +the caller to `main` (i.e. in the C startup code). In the Irix case, only the +address inside the caller to `main` is given. + +In many cases, a debugger is needed to interpret the additional information. +On systems supporting the `adb` debugger, the `tools/callprocs.sh` script can +be used to replace program counter values with symbolic names. The collector +tries to generate symbolic names for call stacks if it knows how to do so on +the platform. This is true on Linux/x86, but not on most other platforms. + +## Simplified leak detection under Linux + +It should be possible to run the collector in the leak detection mode on +a program a.out under Linux/x86 as follows: + + 1. If possible, ensure that a.out is a single-threaded executable. On some + platforms this does not work at all for the multi-threaded programs. + 2. If possible, ensure that the `addr2line` program is installed + in `/usr/bin`. (It comes with most Linux distributions.) + 3. If possible, compile your program, which we'll call `a.out`, with full + debug information. This will improve the quality of the leak reports. + With this approach, it is no longer necessary to call `GC_` routines + explicitly, though that can also improve the quality of the leak reports. + 4. Build the collector and install it in directory _foo_ as follows (it may + be safe to omit the `--disable-threads` option on Linux, but the combination + of thread support and `malloc` replacement is not yet rock solid): + + - `configure --prefix=_foo_ --enable-gc-debug --enable-redirect-malloc --disable-threads` + - `make` + - `make install` + + 5. Set environment variables as follows (the last two are optional, just to + confirm the collector is running, and to facilitate debugging from another + console window if something goes wrong, respectively): + + - `LD_PRELOAD=_foo_/lib/libgc.so` + - `GC_FIND_LEAK` + - `GC_PRINT_STATS` + - `GC_LOOP_ON_ABORT` + + 6. Simply run `a.out` as you normally would. Note that if you run anything + else (e.g. your editor) with those environment variables set, it will also + be leak tested. This may or may not be useful and/or embarrassing. It can + generate mountains of leak reports if the application was not designed + to avoid leaks, e.g. because it's always short-lived. + +This has not yet been thoroughly tested on large applications, but it's known +to do the right thing on at least some small ones. diff --git a/bdwgc/doc/overview.md b/bdwgc/doc/overview.md new file mode 100644 index 000000000..465e2e007 --- /dev/null +++ b/bdwgc/doc/overview.md @@ -0,0 +1,311 @@ +[Interface Overview](gcinterface.md) | [Tutorial Slides](http://www.hboehm.info/gc/04tutorial.pdf) | [FAQ](http://www.hboehm.info/gc/faq.html) | [Example](simple_example.md) | [Download](https://github.com/ivmai/bdwgc/wiki/Download) | [License](http://www.hboehm.info/gc/license.txt) +---|---|---|---|---|--- + +# A garbage collector for C and C++ + + * Platforms + * Some collector details + * Further reading + * Information provided on the BDWGC site + * More background information + * Contacts and new release announcements + +[ This is an updated version of the page formerly at +`www.hpl.hp.com/personal/Hans_Boehm/gc/`, before that at +`http://reality.sgi.com/boehm/gc.html` and before that at +`ftp://ftp.parc.xerox.com/pub/gc/gc.html`. ] + +The +[Boehm](http://www.hboehm.info)-[Demers](http://www.cs.cornell.edu/annual_report/00-01/bios.htm#demers)-[Weiser](http://www.ubiq.com/hypertext/weiser/weiser.html) +conservative Garbage Collector (**BDWGC**) can be used as a garbage collecting +replacement for C `malloc` or C++ `new`. It allows you to allocate memory +basically as you normally would, without explicitly deallocating memory that +is no longer useful. The collector automatically recycles memory when +it determines that it can no longer be otherwise accessed. A simple example +of such a use is given [here](simple_example.md). + +The collector is also used by a number of programming language implementations +that either use C as intermediate code, want to facilitate easier +interoperation with C libraries, or just prefer the simple collector +interface. For a more detailed description of the interface, see +[here](gcinterface.md). + +Alternatively, the garbage collector may be used as a [leak detector](leak.md) +for C or C++ programs, though that is not its primary goal. + +Typically several versions are offered for +[downloading](https://github.com/ivmai/bdwgc/wiki/Download): preview, stable, +legacy. Usually you should use the one marked as the _latest stable_ release. +Preview versions may contain additional features, platform support, but are +likely to be less well tested. The list of changes for each version +is specified on the [releases](https://github.com/ivmai/bdwgc/releases) page. +The development version (snapshot) is available in the master branch of +[bdwgc git](https://github.com/ivmai/bdwgc) repository on GitHub. + +The arguments for and against conservative garbage collection in C and C++ are +briefly discussed [here](http://www.hboehm.info/gc/issues.html). The +beginnings of a frequently-asked-questions list are +[here](http://www.hboehm.info/gc/faq.html). + +The garbage collector code is copyrighted by +[Hans-J. Boehm](http://www.hboehm.info), Alan J. Demers, +[Xerox Corporation](http://www.xerox.com/), +[Silicon Graphics](http://www.sgi.com/), +[Hewlett-Packard Company](http://www.hp.com/), +[Ivan Maidanski](https://github.com/ivmai), and partially by some others. +It may be used and copied without payment of a fee under minimal restrictions. +See the README.md file in the distribution or the +[license](http://www.hboehm.info/gc/license.txt) for more details. +**IT IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED OR IMPLIED. +ANY USE IS AT YOUR OWN RISK.** + +Empirically, this collector works with most unmodified C programs, simply +by replacing `malloc` and `calloc` with `GC_malloc` calls, replacing `realloc` +with `GC_realloc` calls, and removing `free` calls. Exceptions are discussed +[here](http://www.hboehm.info/gc/issues.html). + +## Platforms + +The collector is not completely portable, but the distribution includes ports +to most standard PC and UNIX/Linux platforms. The collector should work +on Linux, Android, BSD variants, OS/2, Windows (Win32 and Win64), MacOS X, +iOS, HP/UX, Solaris, Tru64, Irix, Symbian and other operating systems. Some +platforms are more polished (better supported) than others. + +Irix pthreads, Linux threads, Windows threads, Solaris threads (pthreads +only), HP/UX 11 pthreads, Tru64 pthreads, and MacOS X threads are supported. + +## Some Collector Details + +The collector uses a [mark-sweep](http://www.hboehm.info/gc/complexity.html) +algorithm. It provides incremental and generational collection under operating +systems which provide the right kind of virtual memory support. (Currently +this includes SunOS[45], IRIX, OSF/1, Linux, and Windows, with varying +restrictions.) It allows [finalization](finalization.md) code to be invoked +when an object is collected. It can take advantage of type information +to locate pointers if such information is provided, but it is usually used +without such information. See the README and `gc.h` files in the distribution +for more details. + +For an overview of the implementation, see [here](gcdescr.md). + +The garbage collector distribution includes a C string (`cord.h`) package that +provides for fast concatenation and substring operations on long strings. +A simple curses- and Windows-based editor that represents the entire file as +a cord is included as a sample application. + +Performance of the non-incremental collector is typically competitive with +`malloc`/`free` implementations. Both space and time overhead are likely to be +only slightly higher for programs written for `malloc`/`free` (see Detlefs, +Dosser and Zorn's +[Memory Allocation Costs in Large C and C++ Programs](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.30.3073&rep=rep1&type=ps)). +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 multi-threaded environment and configured for +thread-local allocation, it may in some cases significantly outperform +`malloc`/`free` allocation in time. + +We also expect that in many cases any additional overhead will be more than +compensated for by e.g. decreased copying if programs are written and tuned +for garbage collection. + +## Further reading + +**The beginnings of a frequently asked questions list for this collector are +[here](http://www.hboehm.info/gc/faq.html).** + +**The following provide information on garbage collection in general:** + +Paul Wilson's +[garbage collection ftp archive](ftp://ftp.cs.utexas.edu/pub/garbage) +and [GC survey](ftp://ftp.cs.utexas.edu/pub/garbage/gcsurvey.ps). + +The Ravenbrook +[Memory Management Reference](http://www.memorymanagement.org/). + +David Chase's [GC FAQ](http://www.iecc.com/gclist/GC-faq.html). + +Richard Jones' +[Garbage Collection Page](https://www.cs.kent.ac.uk/people/staff/rej/gc.html) +and his [book](http://www.cs.kent.ac.uk/people/staff/rej/gcbook/gcbook.html). + +**The following papers describe the collector algorithms we use and the +underlying design decisions at a higher level:** + +(Some of the lower level details can be found [here](gcdescr.md).) + +The first one is not available electronically due to copyright considerations. +Most of the others are subject to ACM copyright. + +Boehm, H., Dynamic Memory Allocation and Garbage Collection, +_Computers in Physics 9_, 3, May/June 1995, pp. 297-303. This is directed +at an otherwise sophisticated audience unfamiliar with memory allocation +issues. The algorithmic details differ from those in the implementation. There +is a related letter to the editor and a minor correction in the next issue. + +Boehm, H., and [M. Weiser](http://www.ubiq.com/hypertext/weiser/weiser.html), +[Garbage Collection in an Uncooperative Environment](http://www.hboehm.info/spe_gc_paper/), +_Software Practice and Experience_, September 1988, pp. 807-820. + +Boehm, H., A. Demers, and S. Shenker, +[Mostly Parallel Garbage Collection](http://www.hboehm.info/gc/papers/pldi91.ps.Z), +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](http://www.hboehm.info/gc/papers/pldi93.ps.Z), +Proceedings of the ACM SIGPLAN '93 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_. +[Official version](http://portal.acm.org/citation.cfm?doid=362422.362438). +[Technical report](http://www.hpl.hp.com/techreports/2000/HPL-2000-99.html) +version. Describes the prefetch strategy incorporated into the collector for +some platforms. Explains why the sweep phase of a _mark-sweep_ collector +should not really be a distinct phase. + +M. Serrano, H. Boehm, Understanding Memory Allocation of Scheme Programs, +_Proceedings of the Fifth ACM SIGPLAN International Conference on Functional +Programming_, 2000, Montreal, Canada, pp. 245-256. +[Official version](http://dl.acm.org/citation.cfm?id=351264). Earlier +[Technical Report](http://www.hpl.hp.com/techreports/2000/HPL-2000-62.html) +version. Includes some discussion of the collector debugging facilities for +identifying causes of memory retention. + +Boehm, H., Fast Multiprocessor Memory Allocation and Garbage Collection, +[HP Labs Technical Report HPL 2000-165](http://www.hpl.hp.com/techreports/2000/HPL-2000-165.html). +Discusses the parallel collection algorithms, and presents some performance +results. + +Boehm, H., Bounding Space Usage of Conservative Garbage Collectors, +_Proceedings of the 2002 ACM SIGPLAN-SIGACT Symposium on Principles +of Programming Languages_, Jan. 2002, pp. 93-100. +[Official version](http://portal.acm.org/citation.cfm?doid=503272.503282). +[Technical report](http://www.hpl.hp.com/techreports/2001/HPL-2001-251.html) +version. Includes a discussion of a collector facility to much more reliably +test for the potential of unbounded heap growth. + +**The following papers discuss language and compiler restrictions necessary +to guaranteed safety of conservative garbage collection:** + +We thank John Levine and JCLT for allowing us to make the second paper +available electronically, and providing PostScript for the final version. + +Boehm, H., +[Simple Garbage-Collector-Safety](http://www.hboehm.info/gc/papers/pldi96.ps.gz), +Proceedings of the ACM SIGPLAN '96 Conference on Programming Language Design +and Implementation. + +Boehm, H., and D. Chase, +[A Proposal for Garbage-Collector-Safe C Compilation](http://www.hboehm.info/gc/papers/boecha.ps.gz), +_Journal of C Language Translation 4_, 2 (December 1992), pp. 126-141. + +**Other related information:** + +The Detlefs, Dosser and Zorn's +[Memory Allocation Costs in Large C and C++ Programs](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.30.3073&rep=rep1&type=ps). +This is a performance comparison of the Boehm-Demers-Weiser collector +to `malloc`/`free`, using programs written for `malloc`/`free`. + +Joel Bartlett's +[mostly copying conservative garbage collector for C++](ftp://gatekeeper.dec.com/pub/compaq/WRL/research-reports/WRL-TN-12.ps). + +John Ellis and David Detlef's +[Safe Efficient Garbage Collection for C++](http://dl.acm.org/citation.cfm?id=1267983) +proposal. + +Henry Baker's [paper collection](http://home.pipeline.com/%7Ehbaker1/). + +Slides for Hans Boehm's +[Allocation and GC Myths](http://www.hboehm.info/gc/myths.ps) talk. + +## Information provided on the BDWGC site + +[Current users](https://github.com/ivmai/bdwgc/wiki/Known-clients) (the list +may be rather outdated). + +[A simple illustration of how to build and use the collector](simple_example.md). + +[Description of alternate interfaces to the garbage collector](gcinterface.md). + +[Slides from an ISMM 2004 tutorial about the GC](http://www.hboehm.info/gc/04tutorial.pdf). + +[A FAQ (frequently asked questions) list](http://www.hboehm.info/gc/faq.html). + +[How to use the garbage collector as a leak detector](leak.md). + +[Some hints on debugging garbage collected applications](debugging.md). + +[An overview of the implementation of the garbage collector](gcdescr.md). + +[The data structure used for fast pointer lookups](tree.md). + +[Scalability of the collector to multiprocessors](scale.md). + +[Directory](http://www.hboehm.info/gc/gc_source/) containing the distribution +files of all garbage collector releases. It duplicates +[Download](https://github.com/ivmai/bdwgc/wiki/Download) page on GitHub. + +## More background information + +[An attempt to establish a bound on space usage of conservative garbage collectors](http://www.hboehm.info/gc/bounds.html). + +[Mark-sweep versus copying garbage collectors and their complexity](http://www.hboehm.info/gc/complexity.html). + +[Pros and cons of conservative garbage collectors, in comparison to other collectors](http://www.hboehm.info/gc/conservative.html). + +[Issues related to garbage collection vs. manual memory management in C/C++](http://www.hboehm.info/gc/issues.html). + +[An example of a case in which garbage collection results in a much faster implementation as a result of reduced synchronization](http://www.hboehm.info/gc/example.html). + +[Slide set discussing performance of nonmoving garbage collectors](http://www.hboehm.info/gc/nonmoving/). + +[Slide set discussing _Destructors, Finalizers, and Synchronization_, POPL 2003](http://www.hboehm.info/popl03/web/). + +[Paper corresponding to above slide set](http://portal.acm.org/citation.cfm?doid=604131.604153) +([Technical Report](http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html) +version). + +[A Java/Scheme/C/C++ garbage collection benchmark](http://www.hboehm.info/gc/gc_bench/). + +[Slides for talk on memory allocation myths](http://www.hboehm.info/gc/myths.ps). + +[Slides for OOPSLA 98 garbage collection talk](http://www.hboehm.info/gc/gctalk.ps). + +[Related papers](http://www.hboehm.info/gc/papers/). + +## Contacts and new release announcements + +GitHub and Stack Overflow are the major two places for communication. + +Technical questions (how to, how does it work, etc.) should be posted +to [Stack Overflow](https://stackoverflow.com/questions/tagged/boehm-gc) with +_boehm-gc_ tag. + +To contribute, please rebase your code to the latest +[master](https://github.com/ivmai/bdwgc/tree/master/) and submit +a [pull request](https://github.com/ivmai/bdwgc/pulls) to GitHub. + +To report a bug, or propose (request) a new feature, create +a [GitHub issue](https://github.com/ivmai/bdwgc/issues). Please make sure +it has not been reported yet by someone else. + +To receive notifications on every release, please subscribe to +[Releases RSS feed](https://github.com/ivmai/bdwgc/releases.atom). +Notifications on all issues and pull requests are available +by [watching](https://github.com/ivmai/bdwgc/watchers) the project. + +Mailing lists (bdwgc-announce@lists.opendylan.org, bdwgc@lists.opendylan.org, +and the former gc-announce@linux.hpl.hp.com and gc@linux.hpl.hp.com) are not +used at this moment. Their content is available +in +[bdwgc-announce](https://github.com/ivmai/bdwgc/files/1037650/bdwgc-announce-mailing-list-archive-2014_02.tar.gz) +and +[bdwgc](https://github.com/ivmai/bdwgc/files/1038163/bdwgc-mailing-list-archive-2017_04.tar.gz) +archive files, respectively. The gc list archive may also be read +at [Narkive](http://bdwgc.opendylan.narkive.com). + +Some prior discussion of the collector has taken place on the gcc java mailing +list, whose archives appear [here](http://gcc.gnu.org/ml/java/), and also +on [gclist@iecc.com](http://lists.tunes.org/mailman/listinfo/gclist). diff --git a/bdwgc/doc/porting.md b/bdwgc/doc/porting.md new file mode 100644 index 000000000..6025e53fc --- /dev/null +++ b/bdwgc/doc/porting.md @@ -0,0 +1,263 @@ +# Conservative Garbage Collector Porting Directions + +The collector is designed to be relatively easy to port, but is not portable +code per se. The collector inherently has to perform operations, such +as scanning the stack(s), that are not possible in portable C code. + +All of the following assumes that the collector is being ported to +a byte-addressable 32- or 64-bit machine. Currently all successful ports +to 64-bit machines involve LP64 and LLP64 targets (notably Win64). You +are hereby discouraged from attempting a port to non-byte-addressable, +or 8-bit, or 16-bit machines. + +The difficulty of porting the collector varies greatly depending on the needed +functionality. In the simplest case, only some small additions are needed for +the `include/private/gcconfig.h` file. This is described in the following +section. Later sections discuss some of the optional features, which typically +involve more porting effort. + +Note that the collector makes heavy use of `ifdef`s. Unlike some other +software projects, we have concluded repeatedly that this is preferable +to system dependent files, with code duplicated between the files. However, +to keep this manageable, we do strongly believe in indenting `ifdef`s +correctly (for historical reasons usually without the leading sharp sign). +(Separate source files are of course fine if they do not result in code +duplication.) + +## Adding Platforms to gcconfig.h + +If neither thread support, nor tracing of dynamic library data is required, +these are often the only changes you will need to make. + +The `gcconfig.h` file consists of three sections: + + 1. A section that defines GC-internal macros that identify the architecture + (e.g. `IA64` or `I386`) and operating system (e.g. `LINUX` or `MSWIN32`). + This is usually done by testing predefined macros. By defining our own + macros instead of using the predefined ones directly, we can impose a bit + more consistency, and somewhat isolate ourselves from compiler differences. + It is relatively straightforward to add a new entry here. But please try + to be consistent with the existing code. In particular, 64-bit variants + of 32-bit architectures general are _not_ treated as a new architecture. + Instead we explicitly test for 64-bit-ness in the few places in which + it matters. (The notable exception here is `I386` and `X86_64`. This + is partially historical, and partially justified by the fact that there are + arguably more substantial architecture and ABI differences here than for + RISC variants.) On GNU-based systems, `cpp -dM empty_source_file.c` seems + to generate a set of predefined macros. On some other systems, the "verbose" + compiler option may do so, or the manual page may list them. + + 2. A section that defines a small number of platform-specific macros, which + are then used directly by the collector. For simple ports, this is where + most of the effort is required. We describe the macros below. This section + contains a subsection for each architecture (enclosed in a suitable `ifdef`. + Each subsection usually contains some architecture-dependent defines, + followed by several sets of OS-dependent defines, again enclosed in + `ifdef`s. + + 3. A section that fills in defaults for some macros left undefined in the + preceding section, and defines some other macros that rarely need adjustment + for new platforms. You will typically not have to touch these. If you are + porting to an OS that was previously completely unsupported, it is likely + that you will need to add another clause to the definition of `GET_MEM`. + +The following macros must be defined correctly for each architecture and +operating system: + + * `MACH_TYPE` - Defined to a string that represents the machine + architecture. Usually just the macro name used to identify the architecture, + but enclosed in quotes. + * `OS_TYPE` - Defined to a string that represents the operating system name. + Usually just the macro name used to identify the operating system, but + enclosed in quotes. + * `CPP_WORDSZ` - The word size in bits as a constant suitable for + preprocessor tests, i.e. without casts or `sizeof` expressions. Currently + always defined as either 64 or 32. For platforms supporting both 32- and + 64-bit ABIs, this should be conditionally defined depending on the current + ABI. There is a default of 32. + * `ALIGNMENT` - Defined to be the largest _N_ such that all pointer + are guaranteed to be aligned on _N_-byte boundaries. Defining it to be _1_ + will always work, but perform poorly. For all modern 32-bit platforms, this + is 4. For all modern 64-bit platforms, this is 8. Whether or not x86 + qualifies as a modern architecture here is compiler- and OS-dependent. + * `DATASTART` - The beginning of the main data segment. The collector will + trace all memory between `DATASTART` and `DATAEND` for root pointers. + 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. `_data`) whose address is the beginning of the data segment. + Sometimes the value can be computed using the `GC_SysVGetDataStart` + function. Not used if either the next macro is defined, or if dynamic + loading is supported, and the dynamic loading support defines a function + `GC_register_main_static_data` which returns false. + * `SEARCH_FOR_DATA_START` - If this is defined `DATASTART` will be defined + to a dynamically computed value which is obtained by starting with the + address of `_end` and walking backwards until non-addressable memory + is found. This often works on Posix-like platforms. It makes it harder + to debug client programs, since startup involves generating and catching + a segmentation fault, which tends to confuse users. + * `DATAEND` - Set to the end of the main data segment. Defaults to `_end`, + where that is declared as an array. This works in some cases, since the + linker introduces a suitable symbol. + * `DATASTART2`, `DATAEND2` - Some platforms have two discontiguous main data + segments, e.g. for initialized and uninitialized data. If so, these two + macros should be defined to the limits of the second main data segment. + * `STACK_GROWS_UP` - Should be defined if the stack (or thread stacks) grow + towards higher addresses. (This appears to be true only on PA-RISC. If your + architecture has more than one stack per thread, and is not supported yet, + you will need to do more work. Grep for "IA64" in the source for an + example.) + * `STACKBOTTOM` - Defined to be the cold end of the stack, which is usually + (i.e. when the stacks grow down) the highest address in the stack. It must + bound the region of the stack that contains pointers into the GC heap. With + thread support, this must be the cold end of the main stack, which typically + cannot be found in the same way as the other thread stacks. If this is not + defined and none of the following three macros is defined, client code must + explicitly set `GC_stackbottom` to an appropriate value before calling + `GC_INIT` or any other `GC_` routine. + * `LINUX_STACKBOTTOM` - May be defined instead of `STACKBOTTOM`. If defined, + then the cold end of the stack will be determined, we usually read it from + `/proc`. + * `HEURISTIC1` - May be defined instead of `STACKBOTTOM`. `STACK_GRAN` + should generally also be redefined. The cold end of the stack is determined + by taking an address inside `GC_init`s frame, and rounding it up to the next + multiple of `STACK_GRAN`. This works well if the stack bottom is always + aligned to a large power of two. (`STACK_GRAN` is predefined to 0x1000000, + which is rarely optimal.) + * `HEURISTIC2` - May be defined instead of `STACKBOTTOM`. The cold end + of the stack is determined by taking an address inside `GC_init`s frame, + incrementing it repeatedly in small steps (decrement if `STACK_GROWS_UP`), + and reading the value at each location. We remember the value when the first + Segmentation violation or Bus error is signaled, round that to the nearest + plausible page boundary, and use that as the stack bottom. + * `DYNAMIC_LOADING` - Should be defined if `dyn_load.c` has been updated for + this platform and tracing of dynamic library roots is supported. + * `GWW_VDB`, `MPROTECT_VDB`, `PROC_VDB`, `SOFT_VDB` - May be defined if the + corresponding _virtual dirty bit_ implementation in `os_dep.c` is usable on + this platform. This allows incremental/generational garbage collection. + (`GWW_VDB` uses the Win32 `GetWriteWatch` function to read dirty bits, + `MPROTECT_VDB` identifies modified pages by write protecting the heap and + catching faults. `PROC_VDB` and `SOFT_VDB` use the /proc pseudo-files to + read dirty bits.) + * `PREFETCH`, `GC_PREFETCH_FOR_WRITE` - The collector uses `PREFETCH(x)` + to preload the cache with the data at _x_ address. This defaults to a no-op. + * `CLEAR_DOUBLE` - If `CLEAR_DOUBLE` is defined, then `CLEAR_DOUBLE(x)` + is used as a fast way to clear the two words at `GC_malloc`-aligned address + _x_. By default, word stores of 0 are used instead. + * `HEAP_START` - May be defined as the initial address hint for mmap-based + allocation. + +## Additional requirements for a basic port + +In some cases, you may have to add additional platform-specific code to other +files. A likely candidate is the implementation +of `GC_with_callee_saves_pushed` in `mach_dep.c`. This ensure that register +contents that the collector must trace 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. + +If your platform supports `getcontext` then defining the macro +`UNIX_LIKE` for your OS in `gcconfig.h` (if it is not defined there yet) +is likely to solve the problem. Otherwise, if you are using gcc, +`_builtin_unwind_init` will be used, and should work fine. If that is not +applicable either, the implementation will try to use `setjmp`. This will work +if your `setjmp` implementation saves all possibly pointer-valued registers +into the buffer, as opposed to trying to unwind the stack at `longjmp` time. +The `setjmp_test` test tries to determine this, but often does not get it +right. Registers tracing handled with an assembly code is generally to be +avoided. + +Most commonly `os_dep.c` will not require attention, but see below. + +## Thread support + +Supporting threads requires that the collector be able to find and suspend all +threads potentially accessing the garbage-collected heap, and locate any state +associated with each thread that must be traced. + +The functionality needed for thread support is generally implemented in one or +more files specific to the particular thread interface. For example, somewhat +portable pthread support is implemented in `pthread_support.c` and +`pthread_stop_world.c`. The essential functionality consists of: + + * `GC_stop_world` - Stops all threads which may access the garbage collected + heap, other than the caller; + * `GC_start_world` - Restart other threads; + * `GC_push_all_stacks` - Push the contents of all thread stacks (or, + at least, of pointer-containing regions in the thread stacks) onto the mark + stack. + +These very often require that the garbage collector maintain its own data +structures to track active threads. + +In addition, `LOCK` and `UNLOCK` must be implemented in `gc_locks.h`. + +The easiest case is probably a new pthreads platform on which threads can be +stopped with signals. In this case, the changes involve: + + 1. Introducing a suitable `GC_xxx_THREADS` macro, which should + be automatically defined by `gc_config_macros.h` in the right cases. + It should also result in a definition of `GC_PTHREADS`, as for the existing + cases. + 2. Ensuring that the `atomic_ops` package at least minimally + supports the platform. If incremental GC is needed, or if pthread locks + do not perform adequately as the allocation lock, you will probably need + to ensure that a sufficient `atomic_ops` port exists for the platform + to provided an atomic test and set operation. The latest GC code can use + GCC atomic intrinsics instead of `atomic_ops` package (see + `include/private/gc_atomic_ops.h`). + 3. Making any needed adjustments to `pthread_stop_world.c` and + `pthread_support.c`. Ideally none should be needed. In fact, not all of this + is as well standardized as one would like, and outright bugs requiring + workarounds are common. Non-preemptive threads packages will probably + require further work. Similarly thread-local allocation and parallel marking + requires further work in `pthread_support.c`, and may require better + `atomic_ops` support for the designed platform. + +## Dynamic library support + +So long as `DATASTART` and `DATAEND` are defined correctly, the collector will +trace memory reachable from file scope or `static` variables defined as part +of the main executable. This is sufficient if either the program is statically +linked, or if pointers to the garbage-collected heap are never stored +in non-stack variables defined in dynamic libraries. + +If dynamic library data sections must also be traced, then: + + * `DYNAMIC_LOADING` must be defined in the appropriate section of + `gcconfig.h`. + * An appropriate versions of the functions `GC_register_dynamic_libraries` + should be defined in `dyn_load.c`. This function should invoke + `GC_cond_add_roots(region_start, region_end, TRUE)` on each dynamic + library data section. + +Implementations that scan for writable data segments are error prone, +particularly in the presence of threads. They frequently result in race +conditions when threads exit and stacks disappear. They may also accidentally +trace large regions of graphics memory, or mapped files. On at least one +occasion they have been known to try to trace device memory that could not +safely be read in the manner the GC wanted to read it. + +It is usually safer to walk the dynamic linker data structure, especially +if the linker exports an interface to do so. But beware of poorly documented +locking behavior in this case. + +## Incremental GC support + +For incremental and generational collection to work, `os_dep.c` must contain +a suitable _virtual dirty bit_ implementation, which allows the collector +to track which heap pages (assumed to be a multiple of the collector's block +size) have been written during a certain time interval. The collector provides +several implementations, which might be adapted. The default (`DEFAULT_VDB`) +is a placeholder which treats all pages as having been written. This ensures +correctness, but renders incremental and generational collection essentially +useless. + +## Stack traces for debug support + +If stack traces in objects are needed for debug support, `GC_save_callers` and +`GC_print_callers` must be implemented. + +## Disclaimer + +This is an initial pass at porting guidelines. Some things have no doubt been +overlooked. diff --git a/bdwgc/doc/scale.md b/bdwgc/doc/scale.md new file mode 100644 index 000000000..77a83fd0d --- /dev/null +++ b/bdwgc/doc/scale.md @@ -0,0 +1,173 @@ +# Garbage collector scalability + +If Makefile.direct is used, in its default configuration the +Boehm-Demers-Weiser garbage collector is not thread-safe. Generally, it can be +made thread-safe by building the collector with `-DGC_THREADS` compilation +flag. This has primarily the following effects: + + 1. It causes the garbage collector to stop all other threads when it needs + to see a consistent memory state. It intercepts thread creation and + termination events to maintain a list of client threads to be stopped when + needed. + 2. It causes the collector to acquire a lock around essentially all + allocation and garbage collection activity. Since a single lock is used for + all allocation-related activity, only one thread can be allocating + or collecting at one point. This inherently limits performance + of multi-threaded applications on multiprocessors. + +On most platforms, the allocator/collector lock is implemented as a spin lock +with exponential back-off. Longer wait times are implemented by yielding +and/or sleeping. If a collection is in progress, the pure spinning stage +is skipped. This has the uncontested advantage that most uniprocessor lock +acquisitions are very cheap. It has the disadvantage that the application may +sleep for small periods of time even when there is work to be done. And +threads may be unnecessarily woken up for short periods. Nonetheless, this +scheme empirically outperforms native queue-based mutual exclusion +implementations in most cases, sometimes drastically so. + +## Options for enhanced scalability + +The collector uses two facilities to enhance collector scalability on +multiprocessors. They are intended to be used together. (The following refers +to Makefile.direct again.) + + * Building the collector with `-DPARALLEL_MARK` allows the collector to run + the mark phase in parallel in multiple threads, and thus on multiple + processors (or processor cores). The mark phase typically consumes the large + majority of the collection time. Thus, this largely parallelizes the garbage + collector itself, though not the allocation process. Currently the marking + is performed by the thread that triggered the collection, together with + _N_ - 1 dedicated threads, where _N_ is the number of processors (cores) + detected by the collector. The dedicated marker threads are created when the + client calls `GC_start_mark_threads()` or when the client starts the first + non-main thread after the GC initialization (or after fork operation in + a child process). Another effect of this flag is to switch to a more + concurrent implementation of `GC_malloc_many`, so that free lists can be + built and memory can be cleared by more than one thread concurrently. + * Building the collector with `-DTHREAD_LOCAL_ALLOC` adds support for + thread-local allocation. This causes `GC_malloc` (actually `GC_malloc_kind`) + and `GC_gcj_malloc` to be redefined to perform thread-local allocation. + +Memory returned from thread-local allocators is completely interchangeable +with that returned by the standard allocators. It may be used by other +threads. The only difference is that, if the thread allocates enough memory +of a certain kind, it will build a thread-local free list for objects of that +kind, and allocate from that. This greatly reduces locking. The thread-local +free lists are refilled using `GC_malloc_many`. + +An important side effect of this flag is to replace the default +spin-then-sleep lock to be replaced by a spin-then-queue based implementation. +This _reduces performance_ for the standard allocation functions, though +it usually improves performance when thread-local allocation is used heavily, +and, thus, the number of short-duration lock acquisitions is greatly reduced. + +## The Parallel Marking Algorithm + +We use an algorithm similar to +[that developed by Endo, Taura, and Yonezawa](http://www.yl.is.s.u-tokyo.ac.jp/gc/) +at the University of Tokyo. However, the data structures and implementation +are different, and represent a smaller change to the original collector +source, probably at the expense of extreme scalability. Some of the +refinements they suggest, e.g. splitting large objects, were also incorporated +into our approach. + +The global mark stack is transformed into a global work queue. Unlike the +usual case, it never shrinks during a mark phase. The mark threads remove +objects from the queue by copying them to a local mark stack and changing the +global descriptor to zero, indicating that there is no more work to be done +for this entry. This removal is done with no synchronization. Thus it is +possible for more than one worker to remove the same entry, resulting in some +work duplication. + +The global work queue grows only if a marker thread decides to return some +of its local mark stack to the global one. This is done if the global queue +appears to be running low, or if the local stack is in danger of overflowing. +It does require synchronization, but should be relatively rare. + +The sequential marking code is reused to process local mark stacks. Hence the +amount of additional code required for parallel marking is minimal. + +It should be possible to use incremental/generational collection in the +presence of the parallel collector by calling `GC_enable_incremental`, but +the current implementation does not allow interruption of the parallel marker, +so the latter is mostly avoided if the client sets the collection time limit. + +Gcj-style mark descriptors do not currently mix with the combination of local +allocation and incremental collection. They should work correctly with one or +the other, but not both. + +The number of marker threads is set on startup to the number of available +processor cores (or to the value of either `GC_MARKERS` or `GC_NPROCS` +environment variable, if provided). If only a single processor is detected, +parallel marking is disabled. + +Note that setting `GC_NPROCS` to 1 also causes some lock acquisitions inside +the collector to immediately yield the processor instead of busy waiting +first. In the case of a multiprocessor and a client with multiple +simultaneously runnable threads, this may have disastrous performance +consequences (e.g. a factor of 10 slowdown). + +## Performance + +We conducted some simple experiments with a version of +[our GC benchmark](http://www.hboehm.info/gc/gc_bench/) that was slightly +modified to run multiple concurrent client threads in the same address space. +Each client thread does the same work as the original benchmark, but they +share a heap. This benchmark involves very little work outside of memory +allocation. This was run with an ancient GC (released in 2000) on a dual +processor Pentium III/500 machine under Linux 2.2.12. + +Running with a thread-unsafe collector, the benchmark ran in 9 seconds. With +the simple thread-safe collector, built with `-DGC_THREADS`, the execution +time increased to 10.3 seconds, or 23.5 elapsed seconds with two clients. (The +times for the `malloc`/`free` version with glibc `malloc` are 10.51 (standard +library, pthreads not linked), 20.90 (one thread, pthreads linked), and 24.55 +seconds, respectively. The benchmark favors a garbage collector, since most +objects are small.) + +The following table gives execution times for the collector built with +parallel marking and thread-local allocation support +(`-DGC_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC`). We tested the client +using either one or two marker threads, and running one or two client threads. +Note that the client uses thread local allocation exclusively. With +`-DTHREAD_LOCAL_ALLOC` the collector switches to a locking strategy that +is better tuned to less frequent lock acquisition. The standard allocation +primitives thus perform slightly worse than without `-DTHREAD_LOCAL_ALLOC`, +and should be avoided in time-critical code. + +(The results using `pthread_mutex_lock` directly for allocation locking would +have been worse still, at least for older versions of linuxthreads. With +`-DTHREAD_LOCAL_ALLOC`, we first repeatedly try to acquire the lock with +`pthread_mutex_try_lock`, busy-waiting between attempts. After a fixed number +of attempts, we use `pthread_mutex_lock`.) + +These measurements do not use incremental collection, nor was prefetching +enabled in the marker. We used the C version of the benchmark. All +measurements are in elapsed seconds on an unloaded machine. + +Number of client threads| 1 marker thread (secs.)| 2 marker threads (secs.) +---|------|----- + 1| 10.45| 7.85 + 2| 19.95| 12.3 + +The execution time for the single threaded case is slightly worse than with +simple locking. However, even the single-threaded benchmark runs faster than +even the thread-unsafe version if a second processor is available. The +execution time for two clients with thread local allocation time is only 1.4 +times the sequential execution time for a single thread in a thread-unsafe +environment, even though it involves twice the client work. That represents +close to a factor of 2 improvement over the 2 client case with the old +collector. The old collector clearly still suffered from some contention +overhead, in spite of the fact that the locking scheme had been fairly well +tuned. + +Full linear speedup (i.e. the same execution time for 1 client on one +processor as 2 clients on 2 processors) is probably not achievable on this +kind of hardware even with such a small number of processors, since the memory +system is a major constraint for the garbage collector, the processors usually +share a single memory bus, and thus the aggregate memory bandwidth does not +increase in proportion to the number of processors (cores). + +These results are likely to be very sensitive to both hardware and OS issues. +Preliminary experiments with an older Pentium Pro machine running an older +kernel were far less encouraging. diff --git a/bdwgc/doc/simple_example.md b/bdwgc/doc/simple_example.md new file mode 100644 index 000000000..d24c9a085 --- /dev/null +++ b/bdwgc/doc/simple_example.md @@ -0,0 +1,182 @@ +# Using the Garbage Collector: A simple example + +The following consists of step-by-step instructions for building and using the +collector. We'll assume a Linux/gcc platform and a single-threaded +application. The green text contains information about other platforms +or scenarios. It can be skipped, especially on first reading. + +## Building the collector + +If you have not so yet, unpack the collector and enter the newly created +directory with: + + + tar xvfz gc-.tar.gz + cd gc- + + +You can configure, build, and install the collector in a private directory, +say /home/xyz/gc, with the following commands: + + + ./configure --prefix=/home/xyz/gc --disable-threads + make + make check + make install + + +Here the `make check` command is optional, but highly recommended. It runs +a basic correctness test which usually takes well under a minute. + +### Other platforms + +On non-Unix, non-Linux platforms, the collector is usually built by copying +the appropriate makefile (see the platform-specific README in doc/README.xxx +in the distribution) to the file "Makefile", and then typing `make` (or +`nmake` or ...). This builds the library in the source tree. You may want +to move it and the files in the include directory to a more convenient place. + +If you use a makefile that does not require running a configure script, you +should first look at the makefile, and adjust any options that are documented +there. + +If your platform provides a `make` utility, that is generally preferred +to platform- and compiler- dependent "project" files. (At least that is the +strong preference of the would-be maintainer of those project files.) + +### Threads + +If you do not need thread support, configure the collector with: + + + --disable-threads + + +Alternatively, if your target is a real old-fashioned uniprocessor (no +"hyperthreading", etc.), you may just want to turn off parallel marking with +`--disable-parallel-mark`. + +### C++ + +You will need to include the C++ support, which unfortunately tends to be +among the least portable parts of the collector, since it seems to rely +on some corner cases of the language. On Linux, it suffices to add +`--enable-cplusplus` to the configure options. + +## Writing the program + +You will need to include "gc.h" at the beginning of every file that allocates +memory through the garbage collector. Call `GC_MALLOC` wherever you would have +call `malloc`. This initializes memory to zero like `calloc`; there is no need +to explicitly clear the result. + +If you know that an object will not contain pointers to the garbage-collected +heap, and you don't need it to be initialized, call `GC_MALLOC_ATOMIC` +instead. + +A function `GC_FREE` is provided but need not be called. For very small +objects, your program will probably perform better if you do not call it, and +let the collector do its job. + +A `GC_REALLOC` function behaves like the C library `realloc`. It allocates +uninitialized pointer-free memory if the original object was allocated that +way. + +The following program `loop.c` is a trivial example: + + + #include "gc.h" + #include + #include + + int main(void) { + int i; + + GC_INIT(); + for (i = 0; i < 10000000; ++i) { + int **p = (int **) GC_MALLOC(sizeof(int *)); + int *q = (int *) GC_MALLOC_ATOMIC(sizeof(int)); + assert(*p == 0); + *p = (int *) GC_REALLOC(q, 2 * sizeof(int)); + if (i % 100000 == 0) + printf("Heap size = %lu bytes\n", + (unsigned long)GC_get_heap_size()); + } + return 0; + } + + +### Interaction with the system malloc + +It is usually best not to mix garbage-collected allocation with the system +`malloc`-`free`. If you do, you need to be careful not to store pointers +to the garbage-collected heap in memory allocated with the system `malloc`. + +### Other Platforms + +On some other platforms it is necessary to call `GC_INIT` from the main +program, which is presumed to be part of the main executable, not a dynamic +library. This can never hurt, and is thus generally good practice. + +### Threads + +For a multi-threaded program, some more rules apply: + + * Files that either allocate through the GC _or make thread-related calls_ + should first define the macro `GC_THREADS`, and then include `gc.h`. On some + platforms this will redefine some threads primitives, e.g. to let the + collector keep track of thread creation. + +### C++ + +In the case of C++, you need to be especially careful not to store pointers +to the garbage-collected heap in areas that are not traced by the collector. +The collector includes some _alternate interfaces_ to make that easier. + +### Debugging + +Additional debug checks can be performed by defining `GC_DEBUG` before +including `gc.h`. Additional options are available if the collector is also +built with `--enable-gc-debug` and all allocations are performed with +`GC_DEBUG` defined. + +### What if I can't rewrite/recompile my program? + +You may be able to build the collector with `--enable-redirect-malloc` and set +the `LD_PRELOAD` environment variable to point to the resulting library, thus +replacing the standard `malloc` with its garbage-collected counterpart. This +is rather platform dependent. See the _GC leak detection documentation_ for +some more details. + +## Compiling and linking + +The above application `loop.c` test program can be compiled and linked with: + + + cc -I/home/xyz/gc/include loop.c /home/xyz/gc/lib/libgc.a -o loop + + +The `-I` option directs the compiler to the right include directory. In this +case, we list the static library directly on the compile line; the dynamic +library could have been used instead, provided we arranged for the dynamic +loader to find it, e.g. by setting `LD_LIBRARY_PATH`. + +### Threads + +On pthread platforms, you will of course also have to link with `-lpthread`, +and compile with any thread-safety options required by your compiler. On some +platforms, you may also need to link with `-ldl` or `-lrt`. Looking +at `tools/threadlibs.c` should give you the appropriate list if a plain +`-lpthread` does not work. + +## Running the executable + +The executable can of course be run normally, e.g. by typing: + + + ./loop + + +The operation of the collector is affected by a number of environment +variables. For example, setting `GC_PRINT_STATS` produces some GC statistics +on stdout. See `README.environment` in the distribution for details. diff --git a/bdwgc/doc/tree.md b/bdwgc/doc/tree.md new file mode 100644 index 000000000..53551633f --- /dev/null +++ b/bdwgc/doc/tree.md @@ -0,0 +1,177 @@ +# Two-Level Tree Structure for Fast Pointer Lookup + +The Boehm-Demers-Weiser conservative Garbage Collector uses a 2-level tree +data structure to aid in fast pointer identification. This data structure +is described in a bit more detail here, since + + 1. Variations of the data structure are more generally useful. + 2. It appears to be hard to understand by reading the code. + 3. Some other collectors appear to use inferior data structures to solve the + same problem. + 4. It is central to fast collector operation. A candidate pointer + is divided into three sections, the _high_, _middle_, and _low_ bits. The + exact division between these three groups of bits is dependent on the + detailed collector configuration. + +The high and middle bits are used to look up an entry in the table described +here. The resulting table entry consists of either a block descriptor +(`struct hblkhdr *` or `hdr *`) identifying the layout of objects in the +block, or an indication that this address range corresponds to the middle of +a large block, together with a hint for locating the actual block descriptor. +Such a hint consist of a displacement that can be subtracted from the middle +bits of the candidate pointer without leaving the object. + +In either case, the block descriptor (`struct hblkhdr`) refers to a table +of object starting addresses (the `hb_map` field). The starting address table +is indexed by the low bits if the candidate pointer. The resulting entry +contains a displacement to the beginning of the object, or an indication that +this cannot be a valid object pointer. (If all interior pointer are +recognized, pointers into large objects are handled specially, +as appropriate.) + +## The Tree + +The rest of this discussion focuses on the two level data structure used +to map the high and middle bits to the block descriptor. + +The high bits are used as an index into the `GC_top_index` (really +`GC_arrays._top_index`) array. Each entry points to a `bottom_index` data +structure. This structure in turn consists mostly of an array `index` indexed +by the middle bits of the candidate pointer. The `index` array contains the +actual `hdr` pointers. + +Thus a pointer lookup consists primarily of a handful of memory references, +and can be quite fast: + + 1. The appropriate `bottom_index` pointer is looked up in `GC_top_index`, + based on the high bits of the candidate pointer. + 2. The appropriate `hdr` pointer is looked up in the `bottom_index` + structure, based on the middle bits. + 3. The block layout map pointer is retrieved from the `hdr` structure. (This + memory reference is necessary since we try to share block layout maps.) + 4. The displacement to the beginning of the object is retrieved from the + above map. + +In order to conserve space, not all `GC_top_index` entries in fact point +to distinct `bottom_index` structures. If no address with the corresponding +high bits is part of the heap, then the entry points to `GC_all_nils`, +a single `bottom_index` structure consisting only of `NULL` `hdr` pointers. + +`Bottom_index` structures contain slightly more information than just `hdr` +pointers. The `asc_link` field is used to link all `bottom_index` structures +in ascending order for fast traversal. This list is pointed to be +`GC_all_bottom_indices`. It is maintained with the aid of `key` field that +contains the high bits corresponding to the `bottom_index`. + +## 64-bit addresses + +In the case of 64-bit addresses, this picture is complicated slightly by the +fact that one of the index structures would have to be huge to cover the +entire address space with a two level tree. We deal with this by turning +`GC_top_index` into a chained hash table, instead of a simple array. This adds +a `hash_link` field to the `bottom_index` structure. + +The _hash function_ consists of dropping the high bits. This is cheap +to compute, and guarantees that there will be no collisions if the heap +is contiguous and not excessively large. + +## A picture + +The following is an _ASCII_ diagram of the data structure used by GC_base. This was +contributed originally by Dave Barrett. + + + 63 LOG_TOP_SZ[11] LOG_BOTTOM_SZ[10] LOG_HBLKSIZE[13] + +------------------+----------------+------------------+------------------+ + p:| | TL_HASH(hi) | | HBLKDISPL(p) | + +------------------+----------------+------------------+------------------+ + \-----------------------HBLKPTR(p)-------------------/ + \------------hi-------------------/ + \______ ________/ \________ _______/ \________ _______/ + V V V + | | | + GC_top_index[] | | | + --- +--------------+ | | | + ^ | | | | | + | | | | | | + TOP_SZ +--------------+<--+ | | + (items)+-<| [] | * | | + | | +--------------+ if 0 < bi< HBLKSIZE | | + | | | | then large object | | + | | | | starts at the bi'th | | + v | | | hblk before p. | i | + --- | +--------------+ | (word- | + v | aligned) | + bi= |GET_BI(p){->hash_link}->key==hi | | + v | | + | (bottom_index) \ scratch_alloc'd | | + | ( struct bi ) / by get_index() | | + --- +->+--------------+ | | + ^ | | | | + | | | | | + BOTTOM_SZ | | ha=GET_HDR_ADDR(p) | | + (items) +--------------+<----------------------+ +-------+ + | +--<| index[] | | + | | +--------------+ GC_obj_map: v + | | | | from / +-+-+-----+-+-+-+-+ --- + v | | | GC_add_map_entry <0| | | | | | | | ^ + --- | +--------------+ \ +-+-+-----+-+-+-+-+ | + | | asc_link | +-+-+-----+-+-+-+-+ MAXOBJGRANULES + | +--------------+ +-->| | | j | | | | | +1 + | | key | | +-+-+-----+-+-+-+-+ | + | +--------------+ | +-+-+-----+-+-+-+-+ | + | | hash_link | | | | | | | | | | v + | +--------------+ | +-+-+-----+-+-+-+-+ --- + | | |<----MAP_LEN---->| + | | =HBLKSIZE/GRANULE_BYTES + HDR(p)| GC_find_header(p) | (1024 on Alpha) + | \ from | (8/16 bits each) + | (hdr) (struct hblkhdr) / alloc_hdr() | + +--->+----------------------+ | + GET_HDR(p)| word hb_sz (words) | | + +----------------------+ | + | struct hblk *hb_next | | + +----------------------+ | + | word hb_descr | | + +----------------------+ | + | char * hb_map |>------------+ + +----------------------+ + | uchar hb_obj_kind | + +----------------------+ + | uchar hb_flags | + +----------------------+ + | hb_last_reclaimed | + --- +----------------------+ + ^ | | + MARK_BITS_SZ| hb_marks[] | * if hdr is free, hb_sz is the size of + (words) | | a heap chunk (struct hblk) of at least + v | | MININCR*HBLKSIZE bytes (below), + --- +----------------------+ otherwise, size of each object in chunk. + + +Dynamic data structures above are interleaved throughout the heap in blocks +of size `MININCR * HBLKSIZE` bytes as done by `gc_scratch_alloc` which cannot +be freed; free lists are used (e.g. `alloc_hdr`). `hblk`'s below are +collected. + + + (struct hblk) + --- +----------------------+ < HBLKSIZE --- + ^ +-----hb_body----------+ (and WORDSZ) ^ --- --- + | | | aligned | ^ ^ + | | | | hb_sz | + | | | | (words) | + | | Object 0 | | | | + | | | i |(word- v | + | + - - - - - - - - - - -+ --- (bytes)|aligned) --- | + | | | ^ | ^ | + | | | j (words) | | | + n * | Object 1 | v v hb_sz HBLKSIZE/BYTES_PER_WORD + HBLKSIZE | |--------------- | (words) + (bytes) | | v | + | + - - - - - - - - - - -+ --- | + | | | !ALL_INTERIOR_POINTERS ^ | + | | | sets j only for hb_sz | + | | Object N | valid object offsets. | | + v | | All objects WORDSZ- v v + --- +----------------------+ aligned. --- --- diff --git a/bdwgc/dyn_load.c b/bdwgc/dyn_load.c new file mode 100644 index 000000000..bee040c39 --- /dev/null +++ b/bdwgc/dyn_load.c @@ -0,0 +1,1574 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * 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. + */ + +#include "private/gc_priv.h" + +/* + * This is incredibly OS specific code for tracking down data sections in + * dynamic libraries. There appears to be no way of doing this quickly + * without groveling through undocumented data structures. We would argue + * that this is a bug in the design of the dlopen interface. THIS CODE + * MAY BREAK IN FUTURE OS RELEASES. If this matters to you, don't hesitate + * to let your vendor know ... + * + * None of this is safe with dlclose and incremental collection. + * But then not much of anything is safe in the presence of dlclose. + */ + +#if !defined(MACOS) && !defined(GC_NO_TYPES) && !defined(SN_TARGET_PSP2) \ + && !defined(_WIN32_WCE) && !defined(__CC_ARM) +# include +#endif + +/* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */ +#undef GC_MUST_RESTORE_REDEFINED_DLOPEN +#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) \ + && !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP) + /* To support threads in Solaris, gc.h interposes on dlopen by */ + /* defining "dlopen" to be "GC_dlopen", which is implemented below. */ + /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */ + /* real system dlopen() in their implementation. We first remove */ + /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */ +# undef dlopen +# define GC_MUST_RESTORE_REDEFINED_DLOPEN +#endif /* !GC_NO_DLOPEN */ + +/* A user-supplied routine (custom filter) that might be called to */ +/* determine whether a DSO really needs to be scanned by the GC. */ +/* 0 means no filter installed. May be unused on some platforms. */ +/* FIXME: Add filter support for more platforms. */ +STATIC GC_has_static_roots_func GC_has_static_roots = 0; + +#if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(CYGWIN32)) && !defined(PCR) + +#if !defined(DARWIN) && !defined(SCO_ELF) && !defined(SOLARISDL) \ + && !defined(AIX) && !defined(DGUX) && !defined(IRIX5) && !defined(HPUX) \ + && !defined(CYGWIN32) && !defined(MSWIN32) && !defined(MSWINCE) \ + && !(defined(ALPHA) && defined(OSF1)) \ + && !(defined(FREEBSD) && defined(__ELF__)) \ + && !(defined(LINUX) && defined(__ELF__)) \ + && !(defined(NETBSD) && defined(__ELF__)) \ + && !(defined(OPENBSD) && (defined(__ELF__) || defined(M68K))) \ + && !defined(HAIKU) && !defined(HURD) && !defined(NACL) \ + && !defined(CPPCHECK) +# error We only know how to find data segments of dynamic libraries for above. +# error Additional SVR4 variants might not be too hard to add. +#endif + +#include +#ifdef SOLARISDL +# include +# include +# include +#endif + +#if defined(NETBSD) +# include +# include +# include +# define ELFSIZE ARCH_ELFSIZE +#endif + +#if defined(OPENBSD) +# include +# if (OpenBSD >= 200519) && !defined(HAVE_DL_ITERATE_PHDR) +# define HAVE_DL_ITERATE_PHDR +# endif +#endif /* OPENBSD */ + +#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) || defined(NACL) \ + || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \ + || defined(NETBSD) || defined(OPENBSD))) +# include +# if !defined(OPENBSD) && !defined(HOST_ANDROID) + /* OpenBSD does not have elf.h file; link.h below is sufficient. */ + /* Exclude Android because linker.h below includes its own version. */ +# include +# endif +# ifdef HOST_ANDROID + /* 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 Bionic (as of Android 4.2) 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 +# include +# undef ELF_DATA +# undef EM_ALPHA +# endif +# include +# if !defined(GC_DONT_DEFINE_LINK_MAP) && !(__ANDROID_API__ >= 21) + /* link_map and r_debug are defined in link.h of NDK r10+. */ + /* bionic/linker/linker.h defines them too but the header */ + /* itself is a C++ one starting from Android 4.3. */ + struct link_map { + uintptr_t l_addr; + char* l_name; + uintptr_t l_ld; + struct link_map* l_next; + struct link_map* l_prev; + }; + struct r_debug { + int32_t r_version; + struct link_map* r_map; + void (*r_brk)(void); + int32_t r_state; + uintptr_t r_ldbase; + }; +# endif +# else + EXTERN_C_BEGIN /* Workaround missing extern "C" around _DYNAMIC */ + /* symbol in link.h of some Linux hosts. */ +# include + EXTERN_C_END +# endif +#endif + +/* Newer versions of GNU/Linux define this macro. We + * define it similarly for any ELF systems that don't. */ +# ifndef ElfW +# if defined(FREEBSD) +# if __ELF_WORD_SIZE == 32 +# define ElfW(type) Elf32_##type +# else +# define ElfW(type) Elf64_##type +# endif +# elif defined(NETBSD) || defined(OPENBSD) +# if ELFSIZE == 32 +# define ElfW(type) Elf32_##type +# elif ELFSIZE == 64 +# define ElfW(type) Elf64_##type +# else +# error Missing ELFSIZE define +# endif +# else +# if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32 +# define ElfW(type) Elf32_##type +# else +# define ElfW(type) Elf64_##type +# endif +# endif +# endif + +#if defined(SOLARISDL) && !defined(USE_PROC_FOR_LIBRARIES) + + EXTERN_C_BEGIN + extern ElfW(Dyn) _DYNAMIC; + EXTERN_C_END + + STATIC struct link_map * + GC_FirstDLOpenedLinkMap(void) + { + ElfW(Dyn) *dp; + static struct link_map * cachedResult = 0; + static ElfW(Dyn) *dynStructureAddr = 0; + /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */ + +# ifdef SUNOS53_SHARED_LIB + /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */ + /* up properly in dynamically linked .so's. This means we have */ + /* to use its value in the set of original object files loaded */ + /* at program startup. */ + if( dynStructureAddr == 0 ) { + void* startupSyms = dlopen(0, RTLD_LAZY); + dynStructureAddr = (ElfW(Dyn)*)(word)dlsym(startupSyms, "_DYNAMIC"); + } +# else + dynStructureAddr = &_DYNAMIC; +# endif + + if (0 == COVERT_DATAFLOW(dynStructureAddr)) { + /* _DYNAMIC symbol not resolved. */ + return(0); + } + if (cachedResult == 0) { + int tag; + for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) { + if (tag == DT_DEBUG) { + struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr; + if (rd != NULL) { + struct link_map *lm = rd->r_map; + if (lm != NULL) + cachedResult = lm->l_next; /* might be NULL */ + } + break; + } + } + } + return cachedResult; + } + +#endif /* SOLARISDL ... */ + +/* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */ +# ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN +# define dlopen GC_dlopen +# endif + +# if defined(SOLARISDL) + +/* Add dynamic library data sections to the root set. */ +# if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS) \ + && !defined(CPPCHECK) +# error Fix mutual exclusion with dlopen +# endif + +# ifndef USE_PROC_FOR_LIBRARIES +GC_INNER void GC_register_dynamic_libraries(void) +{ + struct link_map *lm; + + for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) { + ElfW(Ehdr) * e; + ElfW(Phdr) * p; + unsigned long offset; + char * start; + int i; + + e = (ElfW(Ehdr) *) lm->l_addr; + p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); + offset = ((unsigned long)(lm->l_addr)); + for( i = 0; i < (int)e->e_phnum; i++, p++ ) { + switch( p->p_type ) { + case PT_LOAD: + { + if( !(p->p_flags & PF_W) ) break; + start = ((char *)(p->p_vaddr)) + offset; + GC_add_roots_inner(start, start + p->p_memsz, TRUE); + } + break; + default: + break; + } + } + } +} + +# endif /* !USE_PROC_FOR_LIBRARIES */ +# endif /* SOLARISDL */ + +#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) || defined(NACL) \ + || (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \ + || defined(NETBSD) || defined(OPENBSD))) + +#ifdef USE_PROC_FOR_LIBRARIES + +#include + +#include +#include +#include + +#define MAPS_BUF_SIZE (32*1024) + +/* Sort an array of HeapSects by start address. */ +/* Unfortunately at least some versions of */ +/* Linux qsort end up calling malloc by way of sysconf, and hence can't */ +/* be used in the collector. Hence we roll our own. Should be */ +/* reasonably fast if the array is already mostly sorted, as we expect */ +/* it to be. */ +static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements) +{ + signed_word n = (signed_word)number_of_elements; + signed_word nsorted = 1; + + while (nsorted < n) { + signed_word i; + + while (nsorted < n && + (word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start) + ++nsorted; + if (nsorted == n) break; + GC_ASSERT((word)base[nsorted-1].hs_start > (word)base[nsorted].hs_start); + i = nsorted - 1; + 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((word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start); + ++nsorted; + } +} + +STATIC void GC_register_map_entries(const char *maps) +{ + const char *prot; + ptr_t start, end; + unsigned int maj_dev; + ptr_t least_ha, greatest_ha; + unsigned i; + + GC_ASSERT(I_HOLD_LOCK()); + sort_heap_sects(GC_our_memory, GC_n_memory); + least_ha = GC_our_memory[0].hs_start; + greatest_ha = GC_our_memory[GC_n_memory-1].hs_start + + GC_our_memory[GC_n_memory-1].hs_bytes; + + for (;;) { + maps = GC_parse_map_entry(maps, &start, &end, &prot, &maj_dev, 0); + if (NULL == maps) break; + + if (prot[1] == 'w') { + /* This is a writable mapping. Add it to */ + /* the root set unless it is already otherwise */ + /* accounted for. */ + if ((word)start <= (word)GC_stackbottom + && (word)end >= (word)GC_stackbottom) { + /* Stack mapping; discard */ + continue; + } +# ifdef THREADS + /* This may fail, since a thread may already be */ + /* unregistered, but its thread stack may still be there. */ + /* That can fail because the stack may disappear while */ + /* we're marking. Thus the marker is, and has to be */ + /* prepared to recover from segmentation faults. */ + + if (GC_segment_is_thread_stack(start, end)) continue; + + /* FIXME: NPTL squirrels */ + /* away pointers in pieces of the stack segment that we */ + /* don't scan. We work around this */ + /* by treating anything allocated by libpthread as */ + /* 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. */ + /* They may not be marked if the thread is still live. */ + /* This specific instance should be addressed by */ + /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite */ + /* seem to suffice. */ + /* We currently trace entire thread stacks, if they are */ + /* are currently cached but unused. This is */ + /* very suboptimal for performance reasons. */ +# endif + /* We no longer exclude the main data segment. */ + if ((word)end <= (word)least_ha + || (word)start >= (word)greatest_ha) { + /* The easy case; just trace entire segment */ + GC_add_roots_inner(start, end, TRUE); + continue; + } + /* Add sections that don't belong to us. */ + i = 0; + while ((word)(GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes) < (word)start) + ++i; + GC_ASSERT(i < GC_n_memory); + 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 + && (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(start, + GC_our_memory[i].hs_start, TRUE); + start = GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes; + ++i; + } + if ((word)start < (word)end) + GC_add_roots_inner(start, end, TRUE); + } else if (prot[0] == '-' && prot[1] == '-' && prot[2] == '-') { + /* Even roots added statically might disappear partially */ + /* (e.g. the roots added by INCLUDE_LINUX_THREAD_DESCR). */ + GC_remove_roots_subregion(start, end); + } + } +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + GC_register_map_entries(GC_get_maps()); +} + +/* We now take care of the main data segment ourselves: */ +GC_INNER GC_bool GC_register_main_static_data(void) +{ + return FALSE; +} + +# define HAVE_REGISTER_MAIN_STATIC_DATA + +#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 */ +/* versions. Thanks to Jakub Jelinek for most of the code. */ + +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \ + || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)) \ + || defined(HOST_ANDROID) /* Are others OK here, too? */ +# ifndef HAVE_DL_ITERATE_PHDR +# define HAVE_DL_ITERATE_PHDR +# endif +# ifdef HOST_ANDROID + /* Android headers might have no such definition for some targets. */ + EXTERN_C_BEGIN + extern int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, + size_t, void *), + void *data); + EXTERN_C_END +# endif +#endif /* __GLIBC__ >= 2 || HOST_ANDROID */ + +#if defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || (defined(FREEBSD) && __FreeBSD__ >= 7) + /* On the FreeBSD system, any target system at major version 7 shall */ + /* have dl_iterate_phdr; therefore, we need not make it weak as below. */ +# ifndef HAVE_DL_ITERATE_PHDR +# define HAVE_DL_ITERATE_PHDR +# endif +# define DL_ITERATE_PHDR_STRONG +#elif defined(HAVE_DL_ITERATE_PHDR) + /* 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. */ + EXTERN_C_BEGIN +# pragma weak dl_iterate_phdr + EXTERN_C_END +#endif + +#if defined(HAVE_DL_ITERATE_PHDR) + +# ifdef PT_GNU_RELRO +/* Instead of registering PT_LOAD sections directly, we keep them */ +/* in a temporary list, and filter them by excluding PT_GNU_RELRO */ +/* segments. Processing PT_GNU_RELRO sections with */ +/* GC_exclude_static_roots instead would be superficially cleaner. But */ +/* it runs into trouble if a client registers an overlapping segment, */ +/* which unfortunately seems quite possible. */ + +# define MAX_LOAD_SEGS MAX_ROOT_SETS + + static struct load_segment { + ptr_t start; + ptr_t end; + /* Room for a second segment if we remove a RELRO segment */ + /* from the middle. */ + ptr_t start2; + ptr_t end2; + } load_segs[MAX_LOAD_SEGS]; + + static int n_load_segs; + static GC_bool load_segs_overflow; +# endif /* PT_GNU_RELRO */ + +STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info, + size_t size, void * ptr) +{ + const ElfW(Phdr) * p; + ptr_t start, end; + int i; + + /* Make sure struct dl_phdr_info is at least as big as we need. */ + if (size < offsetof (struct dl_phdr_info, dlpi_phnum) + + sizeof (info->dlpi_phnum)) + return -1; + + p = info->dlpi_phdr; + for (i = 0; i < (int)info->dlpi_phnum; i++, p++) { + if (p->p_type == PT_LOAD) { + GC_has_static_roots_func callback = GC_has_static_roots; + if ((p->p_flags & PF_W) == 0) continue; + + start = (ptr_t)p->p_vaddr + info->dlpi_addr; + end = start + p->p_memsz; + if (callback != 0 && !callback(info->dlpi_name, start, p->p_memsz)) + continue; +# ifdef PT_GNU_RELRO +# if CPP_WORDSZ == 64 + /* TODO: GC_push_all eventually does the correct */ + /* rounding to the next multiple of ALIGNMENT, so, most */ + /* probably, we should remove the corresponding assertion */ + /* check in GC_add_roots_inner along with this code line. */ + /* start pointer value may require aligning. */ + start = (ptr_t)((word)start & ~(word)(sizeof(word) - 1)); +# endif + if (n_load_segs >= MAX_LOAD_SEGS) { + if (!load_segs_overflow) { + WARN("Too many PT_LOAD segments;" + " registering as roots directly...\n", 0); + load_segs_overflow = TRUE; + } + GC_add_roots_inner(start, end, TRUE); + } else { + load_segs[n_load_segs].start = start; + load_segs[n_load_segs].end = end; + load_segs[n_load_segs].start2 = 0; + load_segs[n_load_segs].end2 = 0; + ++n_load_segs; + } +# else + GC_add_roots_inner(start, end, TRUE); +# endif /* !PT_GNU_RELRO */ + } + } + +# ifdef PT_GNU_RELRO + p = info->dlpi_phdr; + for (i = 0; i < (int)info->dlpi_phnum; i++, p++) { + if (p->p_type == PT_GNU_RELRO) { + /* This entry is known to be constant and will eventually be */ + /* remapped as read-only. However, the address range covered */ + /* by this entry is typically a subset of a previously */ + /* encountered "LOAD" segment, so we need to exclude it. */ + int j; + + start = (ptr_t)p->p_vaddr + info->dlpi_addr; + end = start + p->p_memsz; + for (j = n_load_segs; --j >= 0; ) { + 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 one\n",0); + } else { + GC_ASSERT((word)end <= + (((word)load_segs[j].end + GC_page_size - 1) & + ~(GC_page_size - 1))); + /* Remove from the existing load segment */ + load_segs[j].end2 = load_segs[j].end; + load_segs[j].end = start; + load_segs[j].start2 = end; + /* Note that start2 may be greater than end2 because of */ + /* p->p_memsz value multiple of page size. */ + } + break; + } + if (0 == j && 0 == GC_has_static_roots) + WARN("Failed to find PT_GNU_RELRO segment" + " inside PT_LOAD region\n", 0); + /* No warning reported in case of the callback is present */ + /* because most likely the segment has been excluded. */ + } + } + } +# endif + + *(int *)ptr = 1; /* Signal that we were called */ + return 0; +} + +/* Do we need to separately register the main static data segment? */ +GC_INNER GC_bool GC_register_main_static_data(void) +{ +# ifdef DL_ITERATE_PHDR_STRONG + /* If dl_iterate_phdr is not a weak symbol then don't test against */ + /* zero (otherwise a compiler might issue a warning). */ + return FALSE; +# else + return 0 == COVERT_DATAFLOW(dl_iterate_phdr); +# endif +} + +/* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */ +STATIC GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void) +{ + int did_something; + if (GC_register_main_static_data()) + return FALSE; + +# ifdef PT_GNU_RELRO + { + static GC_bool excluded_segs = FALSE; + n_load_segs = 0; + load_segs_overflow = FALSE; + if (!EXPECT(excluded_segs, TRUE)) { + GC_exclude_static_roots_inner((ptr_t)load_segs, + (ptr_t)load_segs + sizeof(load_segs)); + excluded_segs = TRUE; + } + } +# endif + + did_something = 0; + dl_iterate_phdr(GC_register_dynlib_callback, &did_something); + if (did_something) { +# ifdef PT_GNU_RELRO + int i; + + for (i = 0; i < n_load_segs; ++i) { + 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 ((word)load_segs[i].end2 > (word)load_segs[i].start2) { + GC_add_roots_inner(load_segs[i].start2, load_segs[i].end2, TRUE); + } + } +# endif + } else { + ptr_t datastart, dataend; +# ifdef DATASTART_IS_FUNC + static ptr_t datastart_cached = (ptr_t)GC_WORD_MAX; + + /* Evaluate DATASTART only once. */ + if (datastart_cached == (ptr_t)GC_WORD_MAX) { + datastart_cached = DATASTART; + } + datastart = datastart_cached; +# else + datastart = DATASTART; +# endif +# ifdef DATAEND_IS_FUNC + { + static ptr_t dataend_cached = 0; + /* Evaluate DATAEND only once. */ + if (dataend_cached == 0) { + dataend_cached = DATAEND; + } + dataend = dataend_cached; + } +# else + dataend = DATAEND; +# endif + if (NULL == *(char * volatile *)&datastart + || (word)datastart > (word)dataend) + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)datastart, (void *)dataend); + + /* dl_iterate_phdr may forget the static data segment in */ + /* statically linked executables. */ + GC_add_roots_inner(datastart, dataend, TRUE); +# ifdef GC_HAVE_DATAREGION2 + if ((word)DATASTART2 - 1U >= (word)DATAEND2) { + /* Subtract one to check also for NULL */ + /* without a compiler warning. */ + ABORT_ARG2("Wrong DATASTART/END2 pair", + ": %p .. %p", (void *)DATASTART2, (void *)DATAEND2); + } + GC_add_roots_inner(DATASTART2, DATAEND2, TRUE); +# endif + } + return TRUE; +} + +# define HAVE_REGISTER_MAIN_STATIC_DATA + +#else /* !HAVE_DL_ITERATE_PHDR */ + +/* Dynamic loading code for Linux running ELF. Somewhat tested on + * Linux/x86, untested but hopefully should work on Linux/Alpha. + * This code was derived from the Solaris/ELF support. Thanks to + * whatever kind soul wrote that. - Patrick Bridges */ + +/* This doesn't necessarily work in all cases, e.g. with preloaded + * dynamic libraries. */ + +# if defined(NETBSD) || defined(OPENBSD) +# include + /* for compatibility with 1.4.x */ +# ifndef DT_DEBUG +# define DT_DEBUG 21 +# endif +# ifndef PT_LOAD +# define PT_LOAD 1 +# endif +# ifndef PF_W +# define PF_W 2 +# endif +# elif !defined(HOST_ANDROID) +# include +# endif + +# ifndef HOST_ANDROID +# include +# endif + +#endif /* !HAVE_DL_ITERATE_PHDR */ + +EXTERN_C_BEGIN +#ifdef __GNUC__ +# pragma weak _DYNAMIC +#endif +extern ElfW(Dyn) _DYNAMIC[]; +EXTERN_C_END + +STATIC struct link_map * +GC_FirstDLOpenedLinkMap(void) +{ + static struct link_map *cachedResult = 0; + + if (0 == COVERT_DATAFLOW(_DYNAMIC)) { + /* _DYNAMIC symbol not resolved. */ + return(0); + } + if( cachedResult == 0 ) { +# if defined(NETBSD) && defined(RTLD_DI_LINKMAP) +# if defined(CPPCHECK) +# define GC_RTLD_DI_LINKMAP 2 +# else +# define GC_RTLD_DI_LINKMAP RTLD_DI_LINKMAP +# endif + struct link_map *lm = NULL; + if (!dlinfo(RTLD_SELF, GC_RTLD_DI_LINKMAP, &lm) && lm != NULL) { + /* Now lm points link_map object of libgc. Since it */ + /* might not be the first dynamically linked object, */ + /* try to find it (object next to the main object). */ + while (lm->l_prev != NULL) { + lm = lm->l_prev; + } + cachedResult = lm->l_next; + } +# else + ElfW(Dyn) *dp; + int tag; + + for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) { + if (tag == DT_DEBUG) { + struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr; + /* d_ptr could be null if libs are linked statically. */ + if (rd != NULL) { + struct link_map *lm = rd->r_map; + if (lm != NULL) + cachedResult = lm->l_next; /* might be NULL */ + } + break; + } + } +# endif /* !NETBSD || !RTLD_DI_LINKMAP */ + } + return cachedResult; +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + struct link_map *lm; + +# ifdef HAVE_DL_ITERATE_PHDR + if (GC_register_dynamic_libraries_dl_iterate_phdr()) { + return; + } +# endif + for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) + { + ElfW(Ehdr) * e; + ElfW(Phdr) * p; + unsigned long offset; + char * start; + int i; + + e = (ElfW(Ehdr) *) lm->l_addr; +# ifdef HOST_ANDROID + if (e == NULL) + continue; +# endif + p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); + offset = ((unsigned long)(lm->l_addr)); + for( i = 0; i < (int)e->e_phnum; i++, p++ ) { + switch( p->p_type ) { + case PT_LOAD: + { + if( !(p->p_flags & PF_W) ) break; + start = ((char *)(p->p_vaddr)) + offset; + GC_add_roots_inner(start, start + p->p_memsz, TRUE); + } + break; + default: + break; + } + } + } +} + +#endif /* !USE_PROC_FOR_LIBRARIES */ + +#endif /* LINUX */ + +#if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX)) + +#include +#include +#include +#include +#include +#include /* Only for the following test. */ +#ifndef _sigargs +# define IRIX6 +#endif + +/* We use /proc to track down all parts of the address space that are */ +/* mapped by the process, and throw out regions we know we shouldn't */ +/* worry about. This may also work under other SVR4 variants. */ +GC_INNER void GC_register_dynamic_libraries(void) +{ + static int fd = -1; + char buf[30]; + static prmap_t * addr_map = 0; + static int current_sz = 0; /* Number of records currently in addr_map */ + int needed_sz = 0; /* Required size of addr_map */ + int i; + long flags; + ptr_t start; + ptr_t limit; + ptr_t heap_start = HEAP_START; + ptr_t heap_end = heap_start; + +# ifdef SOLARISDL +# define MA_PHYS 0 +# endif /* SOLARISDL */ + + if (fd < 0) { + (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"); + } + } + if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) { + ABORT_ARG2("/proc PIOCNMAP ioctl failed", + ": fd= %d, errno= %d", fd, errno); + } + if (needed_sz >= current_sz) { + GC_scratch_recycle_no_gww(addr_map, + (size_t)current_sz * sizeof(prmap_t)); + current_sz = needed_sz * 2 + 1; + /* Expansion, plus room for 0 record */ + addr_map = (prmap_t *)GC_scratch_alloc( + (size_t)current_sz * sizeof(prmap_t)); + if (addr_map == NULL) + ABORT("Insufficient memory for address map"); + } + if (ioctl(fd, PIOCMAP, addr_map) < 0) { + ABORT_ARG3("/proc PIOCMAP ioctl failed", + ": errcode= %d, needed_sz= %d, addr_map= %p", + errno, needed_sz, (void *)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 ((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; + if ((flags & (MA_BREAK | MA_STACK | MA_PHYS + | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant; + if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE)) + goto irrelevant; + /* The latter test is empirically useless in very old Irix */ + /* versions. Other than the */ + /* main data and stack segments, everything appears to be */ + /* mapped readable, writable, executable, and shared(!!). */ + /* This makes no sense to me. - HB */ + start = (ptr_t)(addr_map[i].pr_vaddr); + if (GC_roots_present(start)) goto irrelevant; + if ((word)start < (word)heap_end && (word)start >= (word)heap_start) + goto irrelevant; + + limit = start + addr_map[i].pr_size; + /* The following seemed to be necessary for very old versions */ + /* of Irix, but it has been reported to discard relevant */ + /* segments under Irix 6.5. */ +# ifndef IRIX6 + if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) { + /* Discard text segments, i.e. 0-offset mappings against */ + /* executable files which appear to have ELF headers. */ + caddr_t arg; + int obj; +# define MAP_IRR_SZ 10 + static ptr_t map_irr[MAP_IRR_SZ]; + /* Known irrelevant map entries */ + static int n_irr = 0; + struct stat buf; + int j; + + for (j = 0; j < n_irr; j++) { + if (map_irr[j] == start) goto irrelevant; + } + arg = (caddr_t)start; + obj = ioctl(fd, PIOCOPENM, &arg); + if (obj >= 0) { + fstat(obj, &buf); + close(obj); + if ((buf.st_mode & 0111) != 0) { + if (n_irr < MAP_IRR_SZ) { + map_irr[n_irr++] = start; + } + goto irrelevant; + } + } + } +# endif /* !IRIX6 */ + GC_add_roots_inner(start, limit, TRUE); + irrelevant: ; + } + /* Don't keep cached descriptor, for now. Some kernels don't like us */ + /* to keep a /proc file descriptor around during kill -9. */ + /* Otherwise, it should also require FD_CLOEXEC and proper handling */ + /* at fork (i.e. close because of the pid change). */ + if (close(fd) < 0) ABORT("Couldn't close /proc file"); + fd = -1; +} + +# endif /* USE_PROC_FOR_LIBRARIES || IRIX5 */ + +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + +# include + + /* We traverse the entire address space and register all segments */ + /* that could possibly have been written to. */ + STATIC void GC_cond_add_roots(char *base, char * limit) + { +# ifdef GC_WIN32_THREADS + char * curr_base = base; + char * next_stack_lo; + char * next_stack_hi; + + if (base == limit) return; + for(;;) { + GC_get_next_stack(curr_base, limit, &next_stack_lo, &next_stack_hi); + 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 ((word)curr_base < (word)limit) + GC_add_roots_inner(curr_base, limit, TRUE); +# else + char * stack_top + = (char *)((word)GC_approx_sp() & + ~(word)(GC_sysinfo.dwAllocationGranularity - 1)); + + if (base == limit) return; + if ((word)limit > (word)stack_top + && (word)base < (word)GC_stackbottom) { + /* Part of the stack; ignore it. */ + return; + } + GC_add_roots_inner(base, limit, TRUE); +# endif + } + +#ifdef DYNAMIC_LOADING + /* GC_register_main_static_data is not needed unless DYNAMIC_LOADING. */ + GC_INNER GC_bool GC_register_main_static_data(void) + { +# if defined(MSWINCE) || defined(CYGWIN32) + /* Do we need to separately register the main static data segment? */ + return FALSE; +# else + return GC_no_win32_dlls; +# endif + } +# define HAVE_REGISTER_MAIN_STATIC_DATA +#endif /* DYNAMIC_LOADING */ + +# ifdef DEBUG_VIRTUALQUERY + void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf) + { + GC_printf("BaseAddress= 0x%lx, AllocationBase= 0x%lx," + " RegionSize= 0x%lx(%lu)\n", + buf -> BaseAddress, buf -> AllocationBase, + buf -> RegionSize, buf -> RegionSize); + GC_printf("\tAllocationProtect= 0x%lx, State= 0x%lx, Protect= 0x%lx, " + "Type= 0x%lx\n", buf -> AllocationProtect, buf -> State, + buf -> Protect, buf -> Type); + } +# endif /* DEBUG_VIRTUALQUERY */ + +# if defined(MSWINCE) || defined(CYGWIN32) + /* FIXME: Should we really need to scan MEM_PRIVATE sections? */ + /* For now, we don't add MEM_PRIVATE sections to the data roots for */ + /* WinCE because otherwise SEGV fault sometimes happens to occur in */ + /* GC_mark_from() (and, even if we use WRAP_MARK_SOME, WinCE prints */ + /* a "Data Abort" message to the debugging console). */ + /* To workaround that, use -DGC_REGISTER_MEM_PRIVATE. */ +# define GC_wnt TRUE +# endif + + GC_INNER void GC_register_dynamic_libraries(void) + { + MEMORY_BASIC_INFORMATION buf; + DWORD protect; + LPVOID p; + char * base; + char * limit, * new_limit; + +# ifdef MSWIN32 + if (GC_no_win32_dlls) return; +# endif + p = GC_sysinfo.lpMinimumApplicationAddress; + base = limit = (char *)p; + while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) { + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + +# ifdef MSWINCE + if (result == 0) { + /* Page is free; advance to the next possible allocation base */ + new_limit = (char *) + (((DWORD) p + GC_sysinfo.dwAllocationGranularity) + & ~(GC_sysinfo.dwAllocationGranularity-1)); + } else +# endif + /* else */ { + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + new_limit = (char *)p + buf.RegionSize; + protect = buf.Protect; + if (buf.State == MEM_COMMIT + && (protect == PAGE_EXECUTE_READWRITE + || protect == PAGE_EXECUTE_WRITECOPY + || protect == PAGE_READWRITE + || protect == PAGE_WRITECOPY) + && (buf.Type == MEM_IMAGE +# ifdef GC_REGISTER_MEM_PRIVATE + || (protect == PAGE_READWRITE && buf.Type == MEM_PRIVATE) +# else + /* There is some evidence that we cannot always */ + /* ignore MEM_PRIVATE sections under Windows ME */ + /* and predecessors. Hence we now also check for */ + /* that case. */ + || (!GC_wnt && buf.Type == MEM_PRIVATE) +# endif + ) + && !GC_is_heap_base(buf.AllocationBase)) { +# ifdef DEBUG_VIRTUALQUERY + GC_dump_meminfo(&buf); +# endif + if ((char *)p != limit) { + GC_cond_add_roots(base, limit); + base = (char *)p; + } + limit = new_limit; + } + } + if ((word)p > (word)new_limit /* overflow */) break; + p = (LPVOID)new_limit; + } + GC_cond_add_roots(base, limit); + } + +#endif /* MSWIN32 || MSWINCE || CYGWIN32 */ + +#if defined(ALPHA) && defined(OSF1) + +#include + +EXTERN_C_BEGIN +extern char *sys_errlist[]; +extern int sys_nerr; +extern int errno; +EXTERN_C_END + +GC_INNER void GC_register_dynamic_libraries(void) +{ + ldr_module_t moduleid = LDR_NULL_MODULE; + ldr_process_t mypid = ldr_my_process(); /* obtain id of this process */ + + /* For each module */ + while (TRUE) { + ldr_module_info_t moduleinfo; + size_t modulereturnsize; + ldr_region_t region; + ldr_region_info_t regioninfo; + size_t regionreturnsize; + int status = ldr_next_module(mypid, &moduleid); + /* Get the next (first) module */ + + /* Any more modules? */ + 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. */ + if (status != 0) { + ABORT_ARG3("ldr_next_module failed", + ": status= %d, errcode= %d (%s)", status, errno, + errno < sys_nerr ? sys_errlist[errno] : ""); + } + + /* Get the module information */ + status = ldr_inq_module(mypid, moduleid, &moduleinfo, + sizeof(moduleinfo), &modulereturnsize); + if (status != 0 ) + ABORT("ldr_inq_module failed"); + + /* is module for the main program (i.e. nonshared portion)? */ + if (moduleinfo.lmi_flags & LDR_MAIN) + continue; /* skip the main module */ + +# ifdef DL_VERBOSE + GC_log_printf("---Module---\n"); + GC_log_printf("Module ID: %ld\n", moduleinfo.lmi_modid); + GC_log_printf("Count of regions: %d\n", moduleinfo.lmi_nregion); + GC_log_printf("Flags for module: %016lx\n", moduleinfo.lmi_flags); + GC_log_printf("Module pathname: \"%s\"\n", moduleinfo.lmi_name); +# endif + + /* For each region in this module */ + for (region = 0; region < moduleinfo.lmi_nregion; region++) { + /* Get the region information */ + status = ldr_inq_region(mypid, moduleid, region, ®ioninfo, + sizeof(regioninfo), ®ionreturnsize); + if (status != 0 ) + ABORT("ldr_inq_region failed"); + + /* only process writable (data) regions */ + if (! (regioninfo.lri_prot & LDR_W)) + continue; + +# ifdef DL_VERBOSE + GC_log_printf("--- Region ---\n"); + GC_log_printf("Region number: %ld\n", regioninfo.lri_region_no); + GC_log_printf("Protection flags: %016x\n", regioninfo.lri_prot); + GC_log_printf("Virtual address: %p\n", regioninfo.lri_vaddr); + GC_log_printf("Mapped address: %p\n", regioninfo.lri_mapaddr); + GC_log_printf("Region size: %ld\n", regioninfo.lri_size); + GC_log_printf("Region name: \"%s\"\n", regioninfo.lri_name); +# endif + + /* register region as a garbage collection root */ + GC_add_roots_inner((char *)regioninfo.lri_mapaddr, + (char *)regioninfo.lri_mapaddr + regioninfo.lri_size, + TRUE); + + } + } +} +#endif + +#if defined(HPUX) + +#include +#include + +EXTERN_C_BEGIN +extern char *sys_errlist[]; +extern int sys_nerr; +EXTERN_C_END + +GC_INNER void GC_register_dynamic_libraries(void) +{ + int index = 1; /* Ordinal position in shared library search list */ + + /* For each dynamic library loaded */ + while (TRUE) { + struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */ + int status = shl_get(index, &shl_desc); + /* Get info about next shared library */ + + /* 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 */ + /* as to whether errno should get set on a -1 return. */ + break; +# else + if (errno == EINVAL) { + break; /* Moved past end of shared library list --> finished */ + } else { + ABORT_ARG3("shl_get failed", + ": status= %d, errcode= %d (%s)", status, errno, + errno < sys_nerr ? sys_errlist[errno] : ""); + } +# endif + } + +# ifdef DL_VERBOSE + GC_log_printf("---Shared library---\n"); + GC_log_printf("filename= \"%s\"\n", shl_desc->filename); + GC_log_printf("index= %d\n", index); + GC_log_printf("handle= %08x\n", (unsigned long) shl_desc->handle); + GC_log_printf("text seg.start= %08x\n", shl_desc->tstart); + GC_log_printf("text seg.end= %08x\n", shl_desc->tend); + GC_log_printf("data seg.start= %08x\n", shl_desc->dstart); + GC_log_printf("data seg.end= %08x\n", shl_desc->dend); + GC_log_printf("ref.count= %lu\n", shl_desc->ref_count); +# endif + + /* register shared library's data segment as a garbage collection root */ + GC_add_roots_inner((char *) shl_desc->dstart, + (char *) shl_desc->dend, TRUE); + + index++; + } +} +#endif /* HPUX */ + +#ifdef AIX +# include +# include +# include + GC_INNER void GC_register_dynamic_libraries(void) + { + int ldibuflen = 8192; + + for (;;) { + int len; + struct ld_info *ldi; +# if defined(CPPCHECK) + char ldibuf[ldibuflen]; +# else + char *ldibuf = alloca(ldibuflen); +# endif + + len = loadquery(L_GETINFO, ldibuf, ldibuflen); + if (len < 0) { + if (errno != ENOMEM) { + ABORT("loadquery failed"); + } + ldibuflen *= 2; + continue; + } + + ldi = (struct ld_info *)ldibuf; + while (ldi) { + len = ldi->ldinfo_next; + GC_add_roots_inner( + ldi->ldinfo_dataorg, + (ptr_t)(unsigned long)ldi->ldinfo_dataorg + + ldi->ldinfo_datasize, + TRUE); + ldi = len ? (struct ld_info *)((char *)ldi + len) : 0; + } + break; + } + } +#endif /* AIX */ + +#ifdef DARWIN + +/* __private_extern__ hack required for pre-3.4 gcc versions. */ +#ifndef __private_extern__ +# define __private_extern__ extern +# include +# undef __private_extern__ +#else +# include +#endif +#include + +/*#define DARWIN_DEBUG*/ + +/* Writable sections generally available on Darwin. */ +STATIC const struct dyld_sections_s { + const char *seg; + const char *sect; +} GC_dyld_sections[] = { + { SEG_DATA, SECT_DATA }, + /* Used by FSF GCC, but not by OS X system tools, so far. */ + { SEG_DATA, "__static_data" }, + { SEG_DATA, SECT_BSS }, + { SEG_DATA, SECT_COMMON }, + /* FSF GCC - zero-sized object sections for targets */ + /*supporting section anchors. */ + { SEG_DATA, "__zobj_data" }, + { SEG_DATA, "__zobj_bss" } +}; + +/* Additional writable sections: */ +/* GCC on Darwin constructs aligned sections "on demand", where */ +/* the alignment size is embedded in the section name. */ +/* Furthermore, there are distinctions between sections */ +/* containing private vs. public symbols. It also constructs */ +/* sections specifically for zero-sized objects, when the */ +/* target supports section anchors. */ +STATIC const char * const GC_dyld_add_sect_fmts[] = { + "__bss%u", + "__pu_bss%u", + "__zo_bss%u", + "__zo_pu_bss%u" +}; + +/* Currently, mach-o will allow up to the max of 2^15 alignment */ +/* in an object file. */ +#ifndef L2_MAX_OFILE_ALIGNMENT +# define L2_MAX_OFILE_ALIGNMENT 15 +#endif + +STATIC const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr) +{ + unsigned long i, c; + c = _dyld_image_count(); + for (i = 0; i < c; i++) + if ((const struct GC_MACH_HEADER *)_dyld_get_image_header(i) == hdr) + return _dyld_get_image_name(i); + return NULL; +} + +/* This should never be called by a thread holding the lock. */ +STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr, + intptr_t slide) +{ + unsigned long start, end; + unsigned i, j; + const struct GC_MACH_SECTION *sec; + const char *name; + GC_has_static_roots_func callback = GC_has_static_roots; + DCL_LOCK_STATE; + + if (GC_no_dls) return; +# ifdef DARWIN_DEBUG + name = GC_dyld_name_for_hdr(hdr); +# else + name = callback != 0 ? GC_dyld_name_for_hdr(hdr) : NULL; +# endif + for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) { + sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg, + GC_dyld_sections[i].sect); + if (sec == NULL || sec->size < sizeof(word)) + continue; + start = slide + sec->addr; + end = start + sec->size; + LOCK(); + /* The user callback is called holding the lock. */ + if (callback == 0 || callback(name, (void*)start, (size_t)sec->size)) { +# ifdef DARWIN_DEBUG + GC_log_printf( + "Adding section __DATA,%s at %p-%p (%lu bytes) from image %s\n", + GC_dyld_sections[i].sect, (void*)start, (void*)end, + (unsigned long)sec->size, name); +# endif + GC_add_roots_inner((ptr_t)start, (ptr_t)end, FALSE); + } + UNLOCK(); + } + + /* Sections constructed on demand. */ + for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) { + const char *fmt = GC_dyld_add_sect_fmts[j]; + + /* Add our manufactured aligned BSS sections. */ + for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) { + char secnam[16]; + + (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; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_log_printf("Adding on-demand section __DATA,%s at" + " %p-%p (%lu bytes) from image %s\n", + secnam, (void*)start, (void*)end, + (unsigned long)sec->size, name); +# endif + GC_add_roots((char*)start, (char*)end); + } + } + +# if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING) + LOCK(); + GC_print_static_roots(); + UNLOCK(); +# endif +} + +/* This should never be called by a thread holding the lock. */ +STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr, + intptr_t slide) +{ + unsigned long start, end; + unsigned i, j; + const struct GC_MACH_SECTION *sec; +# if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING) + DCL_LOCK_STATE; +# endif + + for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) { + sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg, + GC_dyld_sections[i].sect); + if (sec == NULL || sec->size == 0) + continue; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_log_printf( + "Removing section __DATA,%s at %p-%p (%lu bytes) from image %s\n", + GC_dyld_sections[i].sect, (void*)start, (void*)end, + (unsigned long)sec->size, GC_dyld_name_for_hdr(hdr)); +# endif + GC_remove_roots((char*)start, (char*)end); + } + + /* Remove our on-demand sections. */ + for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) { + const char *fmt = GC_dyld_add_sect_fmts[j]; + + for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) { + char secnam[16]; + + (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; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_log_printf("Removing on-demand section __DATA,%s at" + " %p-%p (%lu bytes) from image %s\n", secnam, + (void*)start, (void*)end, (unsigned long)sec->size, + GC_dyld_name_for_hdr(hdr)); +# endif + GC_remove_roots((char*)start, (char*)end); + } + } + +# if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING) + LOCK(); + GC_print_static_roots(); + UNLOCK(); +# endif +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + /* Currently does nothing. The callbacks are setup by GC_init_dyld() + The dyld library takes it from there. */ +} + +/* The _dyld_* functions have an internal lock so no _dyld functions + can be called while the world is stopped without the risk of a deadlock. + Because of this we MUST setup callbacks BEFORE we ever stop the world. + This should be called BEFORE any thread in created and WITHOUT the + allocation lock held. */ + +GC_INNER void GC_init_dyld(void) +{ + static GC_bool initialized = FALSE; + + if (initialized) return; + +# ifdef DARWIN_DEBUG + GC_log_printf("Registering dyld callbacks...\n"); +# endif + + /* Apple's Documentation: + When you call _dyld_register_func_for_add_image, the dynamic linker + runtime calls the specified callback (func) once for each of the images + that is currently loaded into the program. When a new image is added to + the program, your callback is called again with the mach_header for the + new image, and the virtual memory slide amount of the new image. + + This WILL properly register already linked libraries and libraries + linked in the future. + */ + _dyld_register_func_for_add_image( + (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_add); + _dyld_register_func_for_remove_image( + (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_remove); + /* Structure mach_header64 has the same fields */ + /* as mach_header except for the reserved one */ + /* at the end, so these casts are OK. */ + + /* Set this early to avoid reentrancy issues. */ + initialized = TRUE; + +# ifdef NO_DYLD_BIND_FULLY_IMAGE + /* FIXME: What should we do in this case? */ +# else + if (GC_no_dls) return; /* skip main data segment registration */ + + /* When the environment variable is set, the dynamic linker binds */ + /* all undefined symbols the application needs at launch time. */ + /* This includes function symbols that are normally bound lazily at */ + /* the time of their first invocation. */ + if (GETENV("DYLD_BIND_AT_LAUNCH") == 0) { + /* The environment variable is unset, so we should bind manually. */ +# ifdef DARWIN_DEBUG + GC_log_printf("Forcing full bind of GC code...\n"); +# endif + /* FIXME: '_dyld_bind_fully_image_containing_address' is deprecated. */ + if (!_dyld_bind_fully_image_containing_address( + (unsigned long *)GC_malloc)) + ABORT("_dyld_bind_fully_image_containing_address failed"); + } +# endif +} + +#define HAVE_REGISTER_MAIN_STATIC_DATA +GC_INNER GC_bool GC_register_main_static_data(void) +{ + /* Already done through dyld callbacks */ + return FALSE; +} + +#endif /* DARWIN */ + +#if defined(HAIKU) +# include + + GC_INNER void GC_register_dynamic_libraries(void) + { + image_info info; + int32 cookie = 0; + + while (get_next_image_info(0, &cookie, &info) == B_OK) { + ptr_t data = (ptr_t)info.data; + GC_add_roots_inner(data, data + info.data_size, TRUE); + } + } +#endif /* HAIKU */ + +#elif defined(PCR) + +# include "il/PCR_IL.h" +# include "th/PCR_ThCtl.h" +# include "mm/PCR_MM.h" + + GC_INNER void GC_register_dynamic_libraries(void) + { + /* Add new static data areas of dynamically loaded modules. */ + PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile(); + PCR_IL_LoadedSegment * q; + + /* Skip uncommitted files */ + while (p != NIL && !(p -> lf_commitPoint)) { + /* The loading of this file has not yet been committed */ + /* Hence its description could be inconsistent. */ + /* Furthermore, it hasn't yet been run. Hence its data */ + /* segments can't possibly reference heap allocated */ + /* objects. */ + p = p -> lf_prev; + } + for (; p != NIL; p = p -> lf_prev) { + for (q = p -> lf_ls; q != NIL; q = q -> ls_next) { + if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK) + == PCR_IL_SegFlags_Traced_on) { + GC_add_roots_inner((ptr_t)q->ls_addr, + (ptr_t)q->ls_addr + q->ls_bytes, TRUE); + } + } + } + } +#endif /* PCR && !DYNAMIC_LOADING && !MSWIN32 */ + +#if !defined(HAVE_REGISTER_MAIN_STATIC_DATA) && defined(DYNAMIC_LOADING) + /* Do we need to separately register the main static data segment? */ + GC_INNER GC_bool GC_register_main_static_data(void) + { + return TRUE; + } +#endif /* HAVE_REGISTER_MAIN_STATIC_DATA */ + +/* Register a routine to filter dynamic library registration. */ +GC_API void GC_CALL GC_register_has_static_roots_callback( + GC_has_static_roots_func callback) +{ + GC_has_static_roots = callback; +} diff --git a/bdwgc/extra/AmigaOS.c b/bdwgc/extra/AmigaOS.c new file mode 100644 index 000000000..e81af6fec --- /dev/null +++ b/bdwgc/extra/AmigaOS.c @@ -0,0 +1,544 @@ + + +/****************************************************************** + + AmigaOS-specific routines for GC. + This file is normally included from os_dep.c + +******************************************************************/ + + +#if !defined(GC_AMIGA_DEF) && !defined(GC_AMIGA_SB) && !defined(GC_AMIGA_DS) && !defined(GC_AMIGA_AM) +# include "private/gc_priv.h" +# include +# include +# define GC_AMIGA_DEF +# define GC_AMIGA_SB +# define GC_AMIGA_DS +# define GC_AMIGA_AM +#endif + + +#ifdef GC_AMIGA_DEF + +# ifndef __GNUC__ +# include +# endif +# include +# include +# include +# include + +#endif + + + + +#ifdef GC_AMIGA_SB + +/****************************************************************** + Find the base of the stack. +******************************************************************/ + +ptr_t GC_get_main_stack_base(void) +{ + struct Process *proc = (struct Process*)SysBase->ThisTask; + + /* 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];*/ + + return (char *)proc->pr_ReturnAddr + sizeof(ULONG); + } else { + return (char *)proc->pr_Task.tc_SPUpper; + } +} + +#endif + + +#ifdef GC_AMIGA_DS +/****************************************************************** + Register data segments. +******************************************************************/ + + void GC_register_data_segments(void) + { + struct Process *proc; + struct CommandLineInterface *cli; + BPTR myseglist; + ULONG *data; + +# ifdef __GNUC__ + ULONG dataSegSize; + GC_bool found_segment = FALSE; + extern char __data_size[]; + + 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 */ + myseglist = proc->pr_SegList; + if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS) { + if (proc->pr_CLI != NULL) { + /* 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 */ + + 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]) { +# ifdef __GNUC__ + if (dataSegSize == data[-1]) { + found_segment = TRUE; + } +# endif + GC_add_roots_inner((char *)&data[1], + ((char *)&data[1]) + data[-1], FALSE); + } + } /* for */ +# ifdef __GNUC__ + if (!found_segment) { + ABORT("Can`t find correct Segments.\nSolution: Use an newer version of ixemul.library"); + } +# endif + } + +#endif + + + +#ifdef GC_AMIGA_AM + +#ifndef GC_AMIGA_FASTALLOC + +void *GC_amiga_allocwrapper(size_t size,void *(*AllocFunction)(size_t size2)){ + return (*AllocFunction)(size); +} + +void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2)) + =GC_amiga_allocwrapper; + +#else + + + + +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; + + +/****************************************************************** + 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 necessary. + + -Kjetil S. Matheussen, 2000. +******************************************************************/ + + + +/* List-header for all allocated memory. */ + +struct GC_Amiga_AllocedMemoryHeader{ + ULONG size; + struct GC_Amiga_AllocedMemoryHeader *next; +}; +struct GC_Amiga_AllocedMemoryHeader *GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(int)~(NULL); + + + +/* Type of memory. Once in the execution of a program, this might change to MEMF_ANY|MEMF_CLEAR */ + +ULONG GC_AMIGA_MEMF = MEMF_FAST | MEMF_CLEAR; + + +/* Prevents GC_amiga_get_mem from allocating memory if this one is TRUE. */ +#ifndef GC_AMIGA_ONLYFAST +BOOL GC_amiga_dontalloc=FALSE; +#endif + +#ifdef GC_AMIGA_PRINTSTATS +int succ=0,succ2=0; +int nsucc=0,nsucc2=0; +int nullretries=0; +int numcollects=0; +int chipa=0; +int allochip=0; +int allocfast=0; +int cur0=0; +int cur1=0; +int cur10=0; +int cur50=0; +int cur150=0; +int cur151=0; +int ncur0=0; +int ncur1=0; +int ncur10=0; +int ncur50=0; +int ncur150=0; +int ncur151=0; +#endif + +/* 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)); + +#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("Succeeded 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 succeeding 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){ + struct GC_Amiga_AllocedMemoryHeader *temp = gc_am->next; + FreeMem(gc_am,gc_am->size); + gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(temp)); + } +} + +#ifndef GC_AMIGA_ONLYFAST + +/* All memory with address lower than this one is chip-mem. */ + +char *chipmax; + + +/* + * Always set to the last size of memory tried to be allocated. + * Needed to ensure allocation when the size is bigger than 100000. + * + */ +size_t latestsize; + +#endif + + +#ifdef GC_AMIGA_FASTALLOC + +/* + * The actual function that is called with the GET_MEM macro. + * + */ + +void *GC_amiga_get_mem(size_t size){ + struct GC_Amiga_AllocedMemoryHeader *gc_am; + +#ifndef GC_AMIGA_ONLYFAST + if(GC_amiga_dontalloc==TRUE){ + 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->next=GC_AMIGAMEM; + gc_am->size=size + sizeof(struct GC_Amiga_AllocedMemoryHeader); + GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(gc_am)); + +#ifdef GC_AMIGA_PRINTSTATS + if((char *)gc_amchipmax || 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); + } + + return ret; +} +#endif + + +/* 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; + + GC_amiga_dontalloc=TRUE; /* Pretty tough thing to do, but it's indeed necessary. */ + latestsize=size; + + ret=(*AllocFunction)(size); + + if(((char *)ret) <= chipmax){ + if(ret==NULL){ + /* Give GC access to allocate memory. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=(*AllocFunction)(size); + } + if(ret==NULL) +#endif + { + 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; +#endif + } +#ifdef GC_AMIGA_RETRY + else{ + void *ret2; + /* 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. */ + if( + AllocFunction!=GC_malloc_uncollectable +#ifdef GC_ATOMIC_UNCOLLECTABLE + && AllocFunction!=GC_malloc_atomic_uncollectable +#endif + ){ + ret2=GC_amiga_rec_alloc(size,AllocFunction,0); + }else{ + ret2=(*AllocFunction)(size); +#ifdef GC_AMIGA_PRINTSTATS + if((char *)ret2chipmax){ + GC_free(ret); + ret=ret2; + }else{ + GC_free(ret2); + } + } +#endif + } + +# if defined(CPPCHECK) + if (GC_amiga_dontalloc) /* variable is actually used by AllocFunction */ +# endif + GC_amiga_dontalloc=FALSE; + + return ret; +} + + + +void (*GC_amiga_toany)(void)=NULL; + +void GC_amiga_set_toany(void (*func)(void)){ + GC_amiga_toany=func; +} + +#endif /* !GC_AMIGA_ONLYFAST */ + + +void *GC_amiga_allocwrapper_fast(size_t size,void *(*AllocFunction)(size_t size2)){ + void *ret; + + ret=(*AllocFunction)(size); + + if(ret==NULL){ + /* Enable chip-mem allocation. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=(*AllocFunction)(size); + } + if(ret==NULL) +#endif + { +#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); +#endif + } +#ifdef GC_AMIGA_PRINTSTATS + else{ + nullretries++; + } +#endif + } + + 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); +} + + +#endif /* GC_AMIGA_FASTALLOC */ + + + +/* + * The wrapped realloc function. + * + */ +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); +#else + void *ret; + latestsize=new_size_in_bytes; + ret=GC_realloc(old_object,new_size_in_bytes); + if(ret==NULL && new_size_in_bytes != 0 + && GC_AMIGA_MEMF==(MEMF_FAST | MEMF_CLEAR)){ + /* Out of fast-mem. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=GC_realloc(old_object,new_size_in_bytes); + } + if(ret==NULL) +#endif + { +#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); +#endif + } +#ifdef GC_AMIGA_PRINTSTATS + else{ + nullretries++; + } +#endif + } + if(ret==NULL && new_size_in_bytes != 0){ + WARN("Out of Memory! Returning NIL!\n", 0); + } +#ifdef GC_AMIGA_PRINTSTATS + if(((char *)ret) + + 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. + */ +/* Boehm, February 15, 1996 2:55 pm PST */ + +#include +#include +#include +#include +#include +#include + +#define GC_BUILD +#include "gc.h" +#include "private/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; +} *CodeZeroPtr, **CodeZeroHandle; + +void* GC_MacGetDataStart(void) +{ + 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); +# if !defined(CPPCHECK) + return 0; /* to avoid compiler complain about missing return */ +# endif +} + +#ifdef USE_TEMPORARY_MEMORY + +/* track the use of temporary memory so it can be freed all at once. */ + +typedef struct TemporaryMemoryBlock TemporaryMemoryBlock, **TemporaryMemoryHandle; + +struct TemporaryMemoryBlock { + TemporaryMemoryHandle nextBlock; + char data[]; +}; + +static TemporaryMemoryHandle theTemporaryMemory = NULL; + +void GC_MacFreeTemporaryMemory(void); + +Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory) +{ +# if !defined(SHARED_LIBRARY_BUILD) + static Boolean firstTime = true; +# endif + 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; + } +# endif + + return tempPtr; +} + +static void perform_final_collection(void) +{ + unsigned i; + word last_fo_entries = 0; + + /* adjust the stack bottom, because CFM calls us from another stack + location. */ + GC_stackbottom = (ptr_t)&i; + + /* try to collect and finalize everything in sight */ + for (i = 0; i < 2 || GC_fo_entries < last_fo_entries; i++) { + last_fo_entries = GC_fo_entries; + GC_gcollect(); + } +} + + +void GC_MacFreeTemporaryMemory(void) +{ +# if defined(SHARED_LIBRARY_BUILD) + /* if possible, collect all memory, and invoke all finalizers. */ + perform_final_collection(); +# endif + + if (theTemporaryMemory != NULL) { +# if !defined(SHARED_LIBRARY_BUILD) + long totalMemoryUsed = 0; +# endif + TemporaryMemoryHandle tempMemBlock = theTemporaryMemory; + while (tempMemBlock /* != NULL */) { + TemporaryMemoryHandle nextBlock = (**tempMemBlock).nextBlock; +# if !defined(SHARED_LIBRARY_BUILD) + totalMemoryUsed += GetHandleSize((Handle)tempMemBlock); +# endif + DisposeHandle((Handle)tempMemBlock); + tempMemBlock = nextBlock; + } + theTemporaryMemory = NULL; + +# if !defined(SHARED_LIBRARY_BUILD) + if (GC_print_stats) { + fprintf(stdout, "[total memory used: %ld bytes.]\n", + totalMemoryUsed); + fprintf(stdout, "[total collections: %lu]\n", + (unsigned long)GC_gc_no); + } +# endif + } +} + +#endif /* USE_TEMPORARY_MEMORY */ + +#if __option(far_data) + + void* GC_MacGetDataEnd(void) + { + 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); +# if !defined(CPPCHECK) + return 0; +# endif + } + +#endif /* __option(far_data) */ diff --git a/bdwgc/extra/Mac_files/MacOS_config.h b/bdwgc/extra/Mac_files/MacOS_config.h new file mode 100644 index 000000000..abbf9d8ef --- /dev/null +++ b/bdwgc/extra/Mac_files/MacOS_config.h @@ -0,0 +1,27 @@ +/* + MacOS_config.h + + Configuration flags for Macintosh development systems. + + + + 11/16/95 pcb Updated compilation flags to reflect latest 4.6 Makefile. + + by Patrick C. Beard. + */ +/* Boehm, November 17, 1995 12:10 pm PST */ + +#ifdef __MWERKS__ +/* for CodeWarrior Pro with Metrowerks Standard Library (MSL). */ +/* #define MSL_USE_PRECOMPILED_HEADERS 0 */ +#include +#endif /* __MWERKS__ */ + +/* these are defined again in gc_priv.h. */ +#undef TRUE +#undef FALSE + +#define ALL_INTERIOR_POINTERS /* follows interior pointers. */ +/* #define DONT_ADD_BYTE_AT_END */ /* no padding. */ +/* #define SMALL_CONFIG */ /* whether to use a smaller heap. */ +#define USE_TEMPORARY_MEMORY /* use Macintosh temporary memory. */ diff --git a/bdwgc/extra/Mac_files/dataend.c b/bdwgc/extra/Mac_files/dataend.c new file mode 100644 index 000000000..0bed46946 --- /dev/null +++ b/bdwgc/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/bdwgc/extra/Mac_files/datastart.c b/bdwgc/extra/Mac_files/datastart.c new file mode 100644 index 000000000..a1dc6f6de --- /dev/null +++ b/bdwgc/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/bdwgc/extra/gc.c b/bdwgc/extra/gc.c new file mode 100644 index 000000000..4f907a8ab --- /dev/null +++ b/bdwgc/extra/gc.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2009-2018 Ivan Maidanski + * + * 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. + */ + +/* This file could be used for the following purposes: */ +/* - get the complete GC as a single link object file (module); */ +/* - enable more compiler optimizations. */ + +/* Tip: to get the highest level of compiler optimizations, the typical */ +/* compiler options (GCC) to use are: */ +/* -O3 -fno-strict-aliasing -march=native -Wall -fprofile-generate/use */ + +/* Warning: GCC for Linux (for C++ clients only): Use -fexceptions both */ +/* for GC and the client otherwise GC_thread_exit_proc() is not */ +/* guaranteed to be invoked (see the comments in pthread_start.c). */ + +#ifndef __cplusplus + /* static is desirable here for more efficient linkage. */ + /* TODO: Enable this in case of the compilation as C++ code. */ +# define GC_INNER STATIC +# define GC_EXTERN GC_INNER + /* STATIC is defined in gcconfig.h. */ +#endif + +/* Small files go first... */ +#include "../backgraph.c" +#include "../blacklst.c" +#include "../checksums.c" +#include "../gcj_mlc.c" +#include "../headers.c" +#include "../new_hblk.c" +#include "../obj_map.c" +#include "../ptr_chck.c" + +#include "gc_inline.h" +#include "../allchblk.c" +#include "../alloc.c" +#include "../dbg_mlc.c" +#include "../finalize.c" +#include "../fnlz_mlc.c" +#include "../malloc.c" +#include "../mallocx.c" +#include "../mark.c" +#include "../mark_rts.c" +#include "../reclaim.c" +#include "../typd_mlc.c" + +#include "../misc.c" +#include "../os_dep.c" +#include "../thread_local_alloc.c" + +/* Most platform-specific files go here... */ +#include "../darwin_stop_world.c" +#include "../dyn_load.c" +#include "../gc_dlopen.c" +#if !defined(PLATFORM_MACH_DEP) +# include "../mach_dep.c" +#endif +#if !defined(PLATFORM_STOP_WORLD) +# include "../pthread_stop_world.c" +#endif +#include "../pthread_support.c" +#include "../specific.c" +#include "../win32_threads.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). */ +/* This is only useful if directly included from application */ +/* (instead of linking gc). */ +#ifndef GC_NO_THREAD_REDIRECTS +# define GC_PTHREAD_REDIRECTS_ONLY +# include "gc_pthread_redirects.h" +#endif + +/* The files from "extra" folder are not included. */ diff --git a/bdwgc/extra/msvc_dbg.c b/bdwgc/extra/msvc_dbg.c new file mode 100644 index 000000000..9337a8e81 --- /dev/null +++ b/bdwgc/extra/msvc_dbg.c @@ -0,0 +1,406 @@ +/* + Copyright (c) 2004 Andrei Polushin + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#if !defined(_M_ARM) && !defined(_M_ARM64) \ + && !defined(_M_X64) && defined(_MSC_VER) + +/* TODO: arm[64], x64 currently miss some machine-dependent code below. */ +/* See also GC_HAVE_BUILTIN_BACKTRACE in gc_config_macros.h. */ + +#define GC_BUILD +#include "private/msvc_dbg.h" +#include "gc.h" + +#include + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +#endif +#define NOSERVICE +#include + +#pragma pack(push, 8) +#include +#pragma pack(pop) + +#pragma comment(lib, "dbghelp.lib") +#pragma optimize("gy", off) + +/* Disable a warning that /GS can not protect parameters and local */ +/* variables from local buffer overrun because optimizations are off. */ +#pragma warning(disable:4748) + +typedef GC_word word; +#define GC_ULONG_PTR word + +#ifdef _WIN64 + typedef GC_ULONG_PTR ULONG_ADDR; +#else + typedef ULONG ULONG_ADDR; +#endif + +static HANDLE GetSymHandle(void) +{ + static HANDLE symHandle = NULL; + if (!symHandle) { + BOOL bRet = SymInitialize(symHandle = GetCurrentProcess(), NULL, FALSE); + if (bRet) { + DWORD dwOptions = SymGetOptions(); + dwOptions &= ~SYMOPT_UNDNAME; + dwOptions |= SYMOPT_LOAD_LINES; + SymSetOptions(dwOptions); + } + } + return symHandle; +} + +static void* CALLBACK FunctionTableAccess(HANDLE hProcess, + ULONG_ADDR dwAddrBase) +{ + return SymFunctionTableAccess(hProcess, dwAddrBase); +} + +static ULONG_ADDR CALLBACK GetModuleBase(HANDLE hProcess, ULONG_ADDR dwAddress) +{ + MEMORY_BASIC_INFORMATION memoryInfo; + ULONG_ADDR dwAddrBase = SymGetModuleBase(hProcess, dwAddress); + if (dwAddrBase != 0) { + return dwAddrBase; + } + if (VirtualQueryEx(hProcess, (void*)(GC_ULONG_PTR)dwAddress, &memoryInfo, + sizeof(memoryInfo))) { + char filePath[_MAX_PATH]; + char curDir[_MAX_PATH]; + char exePath[_MAX_PATH]; + DWORD size = GetModuleFileNameA((HINSTANCE)memoryInfo.AllocationBase, + filePath, sizeof(filePath)); + + /* Save and restore current directory around SymLoadModule, see KB */ + /* article Q189780. */ + GetCurrentDirectoryA(sizeof(curDir), curDir); + GetModuleFileNameA(NULL, exePath, sizeof(exePath)); +#if _MSC_VER > 1200 + strcat_s(exePath, sizeof(exePath), "\\.."); +#else /* VC6 or earlier */ + strcat(exePath, "\\.."); +#endif + SetCurrentDirectoryA(exePath); +#ifdef _DEBUG + GetCurrentDirectoryA(sizeof(exePath), exePath); +#endif + SymLoadModule(hProcess, NULL, size ? filePath : NULL, NULL, + (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase, 0); + SetCurrentDirectoryA(curDir); + } + return (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase; +} + +static ULONG_ADDR CheckAddress(void* address) +{ + ULONG_ADDR dwAddress = (ULONG_ADDR)(GC_ULONG_PTR)address; + GetModuleBase(GetSymHandle(), dwAddress); + return dwAddress; +} + +size_t GetStackFrames(size_t skip, void* frames[], size_t maxFrames) +{ + HANDLE hProcess = GetSymHandle(); + HANDLE hThread = GetCurrentThread(); + CONTEXT context; + context.ContextFlags = CONTEXT_FULL; + if (!GetThreadContext(hThread, &context)) { + return 0; + } + /* GetThreadContext might return invalid context for the current thread. */ +#if defined(_M_IX86) + __asm mov context.Ebp, ebp +#endif + return GetStackFramesFromContext(hProcess, hThread, &context, skip + 1, + frames, maxFrames); +} + +size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread, + CONTEXT* context, size_t skip, + void* frames[], size_t maxFrames) +{ + size_t frameIndex; + DWORD machineType; + STACKFRAME stackFrame = { 0 }; + stackFrame.AddrPC.Mode = AddrModeFlat; +#if defined(_M_IX86) + machineType = IMAGE_FILE_MACHINE_I386; + stackFrame.AddrPC.Offset = context->Eip; + stackFrame.AddrStack.Mode = AddrModeFlat; + stackFrame.AddrStack.Offset = context->Esp; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = context->Ebp; +#elif defined(_M_MRX000) + machineType = IMAGE_FILE_MACHINE_R4000; + stackFrame.AddrPC.Offset = context->Fir; +#elif defined(_M_ALPHA) + machineType = IMAGE_FILE_MACHINE_ALPHA; + stackFrame.AddrPC.Offset = (unsigned long)context->Fir; +#elif defined(_M_PPC) + machineType = IMAGE_FILE_MACHINE_POWERPC; + stackFrame.AddrPC.Offset = context->Iar; +#elif defined(_M_IA64) + machineType = IMAGE_FILE_MACHINE_IA64; + stackFrame.AddrPC.Offset = context->StIIP; +#elif defined(_M_ALPHA64) + machineType = IMAGE_FILE_MACHINE_ALPHA64; + stackFrame.AddrPC.Offset = context->Fir; +#elif !defined(CPPCHECK) +# error Unknown CPU +#endif + for (frameIndex = 0; frameIndex < maxFrames; ) { + BOOL bRet = StackWalk(machineType, hProcess, hThread, &stackFrame, + &context, NULL, FunctionTableAccess, GetModuleBase, NULL); + if (!bRet) { + break; + } + if (skip) { + skip--; + } else { + frames[frameIndex++] = (void*)(GC_ULONG_PTR)stackFrame.AddrPC.Offset; + } + } + return frameIndex; +} + +size_t GetModuleNameFromAddress(void* address, char* moduleName, size_t size) +{ + if (size) *moduleName = 0; + { + const char* sourceName; + IMAGEHLP_MODULE moduleInfo = { sizeof (moduleInfo) }; + if (!SymGetModuleInfo(GetSymHandle(), CheckAddress(address), + &moduleInfo)) { + return 0; + } + sourceName = strrchr(moduleInfo.ImageName, '\\'); + if (sourceName) { + sourceName++; + } else { + sourceName = moduleInfo.ImageName; + } + if (size) { + strncpy(moduleName, sourceName, size)[size - 1] = 0; + } + return strlen(sourceName); + } +} + +size_t GetModuleNameFromStack(size_t skip, char* moduleName, size_t size) +{ + void* address = NULL; + GetStackFrames(skip + 1, &address, 1); + if (address) { + return GetModuleNameFromAddress(address, moduleName, size); + } + return 0; +} + +union sym_namebuf_u { + IMAGEHLP_SYMBOL sym; + char symNameBuffer[sizeof(IMAGEHLP_SYMBOL) + MAX_SYM_NAME]; +}; + +size_t GetSymbolNameFromAddress(void* address, char* symbolName, size_t size, + size_t* offsetBytes) +{ + if (size) *symbolName = 0; + if (offsetBytes) *offsetBytes = 0; + __try { + ULONG_ADDR dwOffset = 0; + union sym_namebuf_u u; + + u.sym.SizeOfStruct = sizeof(u.sym); + u.sym.MaxNameLength = sizeof(u.symNameBuffer) - sizeof(u.sym); + + if (!SymGetSymFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset, + &u.sym)) { + return 0; + } else { + const char* sourceName = u.sym.Name; + char undName[1024]; + if (UnDecorateSymbolName(u.sym.Name, undName, sizeof(undName), + UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS)) { + sourceName = undName; + } else if (SymUnDName(&u.sym, undName, sizeof(undName))) { + sourceName = undName; + } + if (offsetBytes) { + *offsetBytes = dwOffset; + } + if (size) { + strncpy(symbolName, sourceName, size)[size - 1] = 0; + } + return strlen(sourceName); + } + } __except (EXCEPTION_EXECUTE_HANDLER) { + SetLastError(GetExceptionCode()); + } + return 0; +} + +size_t GetSymbolNameFromStack(size_t skip, char* symbolName, size_t size, + size_t* offsetBytes) +{ + void* address = NULL; + GetStackFrames(skip + 1, &address, 1); + if (address) { + return GetSymbolNameFromAddress(address, symbolName, size, offsetBytes); + } + return 0; +} + +size_t GetFileLineFromAddress(void* address, char* fileName, size_t size, + size_t* lineNumber, size_t* offsetBytes) +{ + if (size) *fileName = 0; + if (lineNumber) *lineNumber = 0; + if (offsetBytes) *offsetBytes = 0; + { + char* sourceName; + IMAGEHLP_LINE line = { sizeof (line) }; + GC_ULONG_PTR dwOffset = 0; + if (!SymGetLineFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset, + &line)) { + return 0; + } + if (lineNumber) { + *lineNumber = line.LineNumber; + } + if (offsetBytes) { + *offsetBytes = dwOffset; + } + sourceName = line.FileName; + /* TODO: resolve relative filenames, found in 'source directories' */ + /* registered with MSVC IDE. */ + if (size) { + strncpy(fileName, sourceName, size)[size - 1] = 0; + } + return strlen(sourceName); + } +} + +size_t GetFileLineFromStack(size_t skip, char* fileName, size_t size, + size_t* lineNumber, size_t* offsetBytes) +{ + void* address = NULL; + GetStackFrames(skip + 1, &address, 1); + if (address) { + return GetFileLineFromAddress(address, fileName, size, lineNumber, + offsetBytes); + } + return 0; +} + +#define GC_SNPRINTF _snprintf + +size_t GetDescriptionFromAddress(void* address, const char* format, + char* buffer, size_t size) +{ + char* const begin = buffer; + char* const end = buffer + size; + size_t line_number = 0; + + (void)format; + if (size) { + *buffer = 0; + } + buffer += GetFileLineFromAddress(address, buffer, size, &line_number, NULL); + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + + if (line_number) { + char str[20]; + + (void)GC_SNPRINTF(str, sizeof(str), "(%d) : ", (int)line_number); + str[sizeof(str) - 1] = '\0'; + if (size) { + strncpy(buffer, str, size)[size - 1] = 0; + } + buffer += strlen(str); + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + } + + if (size) { + strncpy(buffer, "at ", size)[size - 1] = 0; + } + buffer += sizeof("at ") - 1; + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + + buffer += GetSymbolNameFromAddress(address, buffer, size, NULL); + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + + if (size) { + strncpy(buffer, " in ", size)[size - 1] = 0; + } + buffer += sizeof(" in ") - 1; + size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer; + + buffer += GetModuleNameFromAddress(address, buffer, size); + return buffer - begin; +} + +size_t GetDescriptionFromStack(void* const frames[], size_t count, + const char* format, char* description[], + size_t size) +{ + const GC_ULONG_PTR begin = (GC_ULONG_PTR)description; + const GC_ULONG_PTR end = begin + size; + GC_ULONG_PTR buffer = begin + (count + 1) * sizeof(char*); + size_t i; + + for (i = 0; i < count; ++i) { + if (description) + description[i] = (char*)buffer; + buffer += 1 + GetDescriptionFromAddress(frames[i], format, (char*)buffer, + end < buffer ? 0 : end - buffer); + } + if (description) + description[count] = NULL; + return buffer - begin; +} + +/* Compatibility with execinfo.h: */ + +int backtrace(void* addresses[], int count) +{ + return GetStackFrames(1, addresses, count); +} + +char** backtrace_symbols(void*const* addresses, int count) +{ + size_t size = GetDescriptionFromStack(addresses, count, NULL, NULL, 0); + char** symbols = (char**)malloc(size); + if (symbols != NULL) + GetDescriptionFromStack(addresses, count, NULL, symbols, size); + return symbols; +} + +#else + + extern int GC_quiet; + /* ANSI C does not allow translation units to be empty. */ + +#endif diff --git a/bdwgc/extra/pcr_interface.c b/bdwgc/extra/pcr_interface.c new file mode 100644 index 000000000..7266c1b3e --- /dev/null +++ b/bdwgc/extra/pcr_interface.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ +# include "private/gc_priv.h" + +# ifdef PCR +/* + * We wrap all of the allocator functions to avoid questions of + * compatibility between the prototyped and nonprototyped versions of the f + */ +# include "config/PCR_StdTypes.h" +# include "mm/PCR_MM.h" +# include + +# define MY_MAGIC 17L +# define MY_DEBUGMAGIC 42L + +void * GC_AllocProc(size_t size, PCR_Bool ptrFree, PCR_Bool clear ) +{ + if (ptrFree) { + void * result = (void *)GC_malloc_atomic(size); + if (clear && result != 0) BZERO(result, size); + return(result); + } else { + return((void *)GC_malloc(size)); + } +} + +void * GC_DebugAllocProc(size_t size, PCR_Bool ptrFree, PCR_Bool clear ) +{ + if (ptrFree) { + void * result = (void *)GC_debug_malloc_atomic(size, __FILE__, + __LINE__); + if (clear && result != 0) BZERO(result, size); + return(result); + } else { + return((void *)GC_debug_malloc(size, __FILE__, __LINE__)); + } +} + +# define GC_ReallocProc GC_realloc +void * GC_DebugReallocProc(void * old_object, size_t new_size_in_bytes) +{ + return(GC_debug_realloc(old_object, new_size_in_bytes, __FILE__, __LINE__)); +} + +# define GC_FreeProc GC_free +# define GC_DebugFreeProc GC_debug_free + +typedef struct { + PCR_ERes (*ed_proc)(void *p, size_t size, PCR_Any data); + GC_bool ed_pointerfree; + PCR_ERes ed_fail_code; + PCR_Any ed_client_data; +} enumerate_data; + +void GC_enumerate_block(struct hblk *h, enumerate_data * ed) +{ + hdr * hhdr; + size_t sz; + ptr_t p; + ptr_t lim; + word descr; + +# if !defined(CPPCHECK) +# error This code was updated without testing. +# error and its precursor was clearly broken. +# endif + hhdr = HDR(h); + descr = hhdr -> hb_descr; + sz = (size_t)hhdr->hb_sz; + if (descr != 0 && ed -> ed_pointerfree + || descr == 0 && !(ed -> ed_pointerfree)) return; + lim = (ptr_t)(h+1) - sz; + p = (ptr_t)h; + do { + if (PCR_ERes_IsErr(ed -> ed_fail_code)) return; + ed -> ed_fail_code = + (*(ed -> ed_proc))(p, sz, ed -> ed_client_data); + p+= sz; + } while ((word)p <= (word)lim); +} + +struct PCR_MM_ProcsRep * GC_old_allocator = 0; + +PCR_ERes GC_EnumerateProc( + PCR_Bool ptrFree, + PCR_ERes (*proc)(void *p, size_t size, PCR_Any data), + PCR_Any data +) +{ + enumerate_data ed; + + ed.ed_proc = proc; + ed.ed_pointerfree = ptrFree; + ed.ed_fail_code = PCR_ERes_okay; + ed.ed_client_data = data; + GC_apply_to_all_blocks(GC_enumerate_block, &ed); + 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)); + } +} + +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 */ +}; + +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 */ +}; + +GC_bool GC_use_debug = 0; + +void GC_pcr_install() +{ + PCR_MM_Install((GC_use_debug? &GC_DebugRep : &GC_Rep), &GC_old_allocator); +} + +PCR_ERes +PCR_GC_Setup(void) +{ + return PCR_ERes_okay; +} + +extern GC_bool GC_quiet; + +PCR_ERes +PCR_GC_Run(void) +{ + + if( !PCR_Base_TestPCRArg("-nogc") ) { + GC_quiet = ( PCR_Base_TestPCRArg("-gctrace") ? 0 : 1 ); + GC_use_debug = (GC_bool)PCR_Base_TestPCRArg("-debug_alloc"); + GC_init(); + if( !PCR_Base_TestPCRArg("-nogc_incremental") ) { + /* + * awful hack to test whether VD is implemented ... + */ + if( PCR_VD_Start( 0, NIL, 0) != PCR_ERes_FromErr(ENOSYS) ) { + GC_enable_incremental(); + } + } + } + return PCR_ERes_okay; +} + +void GC_push_thread_structures(void) +{ + /* PCR doesn't work unless static roots are pushed. Can't get here. */ + ABORT("In GC_push_thread_structures()"); +} + +# endif diff --git a/bdwgc/extra/real_malloc.c b/bdwgc/extra/real_malloc.c new file mode 100644 index 000000000..145e73f3b --- /dev/null +++ b/bdwgc/extra/real_malloc.c @@ -0,0 +1,39 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef PCR +/* + * This definition should go in its own file that includes no other + * header files. Otherwise, we risk not getting the underlying system + * malloc. + */ +# define PCR_NO_RENAME +# include + +void * real_malloc(size_t size) +{ + return(malloc(size)); +} + +# else + +extern int GC_quiet; + /* ANSI C doesn't allow translation units to be empty. */ + /* So we guarantee this one is nonempty. */ + +#endif /* PCR */ diff --git a/bdwgc/extra/symbian.cpp b/bdwgc/extra/symbian.cpp new file mode 100644 index 000000000..2ec7afbbd --- /dev/null +++ b/bdwgc/extra/symbian.cpp @@ -0,0 +1,52 @@ + +#include +#include +#include +#include +#include +#include + +extern "C" { + +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 path8; + path8.Copy( path ); + _LIT8( KZero8, "zero" ); + path8.Append( KZero8 ); + + size_t size = path8.Length() + 1; + char* copyChar = (char*) malloc( size ); + if (copyChar) + memcpy( copyChar, path8.PtrZ(), size ); + + return copyChar; // ownership passed +} + +} /* extern "C" */ diff --git a/bdwgc/extra/symbian/global_end.cpp b/bdwgc/extra/symbian/global_end.cpp new file mode 100644 index 000000000..14c7710d4 --- /dev/null +++ b/bdwgc/extra/symbian/global_end.cpp @@ -0,0 +1,10 @@ +// Symbian-specific file. + +// INCLUDE FILES +#include "private/gcconfig.h" + +extern "C" { + +int winscw_data_end; + +} /* extern "C" */ diff --git a/bdwgc/extra/symbian/global_start.cpp b/bdwgc/extra/symbian/global_start.cpp new file mode 100644 index 000000000..1b030bc4d --- /dev/null +++ b/bdwgc/extra/symbian/global_start.cpp @@ -0,0 +1,10 @@ +// Symbian-specific file. + +// INCLUDE FILES +#include "private/gcconfig.h" + +extern "C" { + +int winscw_data_start; + +} /* extern "C" */ diff --git a/bdwgc/extra/symbian/init_global_static_roots.cpp b/bdwgc/extra/symbian/init_global_static_roots.cpp new file mode 100644 index 000000000..683d4adab --- /dev/null +++ b/bdwgc/extra/symbian/init_global_static_roots.cpp @@ -0,0 +1,28 @@ +// Symbian-specific file. + +// INCLUDE FILES +#include + +#include "private/gcconfig.h" +#include "gc.h" + +extern "C" { + +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); +} + +} /* extern "C" */ diff --git a/bdwgc/finalize.c b/bdwgc/finalize.c new file mode 100644 index 000000000..446cc340a --- /dev/null +++ b/bdwgc/finalize.c @@ -0,0 +1,1386 @@ +/* + * Copyright 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) 2007 Free Software Foundation, Inc + * Copyright (c) 2008-2020 Ivan Maidanski + * + * 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. + */ + +#include "private/gc_pmark.h" + +#ifndef GC_NO_FINALIZATION +# include "javaxfc.h" /* to get GC_finalize_all() as extern "C" */ + +/* Type of mark procedure used for marking from finalizable object. */ +/* This procedure normally does not mark the object, only its */ +/* 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, (word)1 << (log_size), log_size) + +struct hash_chain_entry { + word hidden_key; + struct hash_chain_entry * next; +}; + +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) \ + (void)((x)->prolog.next = (struct hash_chain_entry *)(y)) + word dl_hidden_obj; /* Pointer to object base */ +}; + +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)) + 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 */ +}; + +#ifdef AO_HAVE_store + /* Update finalize_now atomically as GC_should_invoke_finalizers does */ + /* not acquire the allocation lock. */ +# define SET_FINALIZE_NOW(fo) \ + AO_store((volatile AO_t *)&GC_fnlz_roots.finalize_now, (AO_t)(fo)) +#else +# define SET_FINALIZE_NOW(fo) (void)(GC_fnlz_roots.finalize_now = (fo)) +#endif /* !THREADS */ + +GC_API void GC_CALL GC_push_finalizer_structures(void) +{ + GC_ASSERT((word)(&GC_dl_hashtbl.head) % sizeof(word) == 0); + GC_ASSERT((word)(&GC_fnlz_roots) % sizeof(word) == 0); +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_ASSERT((word)(&GC_ll_hashtbl.head) % sizeof(word) == 0); + GC_PUSH_ALL_SYM(GC_ll_hashtbl.head); +# endif + GC_PUSH_ALL_SYM(GC_dl_hashtbl.head); + GC_PUSH_ALL_SYM(GC_fnlz_roots); + /* GC_toggleref_arr is pushed specially by GC_mark_togglerefs. */ +} + +/* Threshold of log_size to initiate full collection before growing */ +/* a hash table. */ +#ifndef GC_ON_GROW_LOG_SIZE_MIN +# define GC_ON_GROW_LOG_SIZE_MIN CPP_LOG_HBLKSIZE +#endif + +/* Double the size of a hash table. *log_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. */ +STATIC void GC_grow_table(struct hash_chain_entry ***table, + unsigned *log_size_ptr, word *entries_ptr) +{ + word i; + struct hash_chain_entry *p; + unsigned log_old_size = *log_size_ptr; + unsigned log_new_size = log_old_size + 1; + word old_size = *table == NULL ? 0 : (word)1 << log_old_size; + word new_size = (word)1 << log_new_size; + /* FIXME: Power of 2 size often gets rounded up to one more page. */ + struct hash_chain_entry **new_table; + + GC_ASSERT(I_HOLD_LOCK()); + /* Avoid growing the table in case of at least 25% of entries can */ + /* be deleted by enforcing a collection. Ignored for small tables. */ + /* In incremental mode we skip this optimization, as we want to */ + /* avoid triggering a full GC whenever possible. */ + if (log_old_size >= GC_ON_GROW_LOG_SIZE_MIN && !GC_incremental) { + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + (void)GC_try_to_collect_inner(GC_never_stop_func); + RESTORE_CANCEL(cancel_state); + /* GC_finalize might decrease entries value. */ + if (*entries_ptr < ((word)1 << log_old_size) - (*entries_ptr >> 2)) + return; + } + + new_table = (struct hash_chain_entry **) + GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + (size_t)new_size * sizeof(struct hash_chain_entry *), + NORMAL); + if (new_table == 0) { + if (*table == 0) { + ABORT("Insufficient space for initial table allocation"); + } else { + return; + } + } + for (i = 0; i < old_size; i++) { + p = (*table)[i]; + while (p != 0) { + ptr_t real_key = (ptr_t)GC_REVEAL_POINTER(p->hidden_key); + struct hash_chain_entry *next = p -> next; + size_t new_hash = HASH3(real_key, new_size, log_new_size); + + p -> next = new_table[new_hash]; + GC_dirty(p); + new_table[new_hash] = p; + p = next; + } + } + *log_size_ptr = log_new_size; + *table = new_table; + GC_dirty(new_table); /* entire object */ +} + +GC_API int GC_CALL GC_register_disappearing_link(void * * link) +{ + ptr_t base; + + 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)); +} + +STATIC int GC_register_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, void **link, + const void *obj, const char *tbl_log_name) +{ + struct disappearing_link *curr_dl; + size_t index; + struct disappearing_link * new_dl; + DCL_LOCK_STATE; + + if (EXPECT(GC_find_leak, FALSE)) return GC_UNIMPLEMENTED; + LOCK(); + GC_ASSERT(obj != NULL && GC_base_C(obj) == obj); + if (EXPECT(NULL == dl_hashtbl -> head, FALSE) + || EXPECT(dl_hashtbl -> entries + > ((word)1 << dl_hashtbl -> log_size), FALSE)) { + GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head, + &dl_hashtbl -> log_size, &dl_hashtbl -> entries); + GC_COND_LOG_PRINTF("Grew %s table to %u entries\n", tbl_log_name, + 1U << dl_hashtbl -> log_size); + } + 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(); + return GC_DUPLICATE; + } + } + new_dl = (struct disappearing_link *) + GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL); + if (0 == new_dl) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + new_dl = (struct disappearing_link *) + (*oom_fn)(sizeof(struct disappearing_link)); + if (0 == new_dl) { + return GC_NO_MEMORY; + } + /* It's not likely we'll make it here, but ... */ + LOCK(); + /* Recalculate index since the table may grow. */ + index = HASH2(link, dl_hashtbl -> log_size); + /* Check again that our disappearing link not in the table. */ + 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(); +# ifndef DBG_HDRS_ALL + /* Free unused new_dl returned by GC_oom_fn() */ + GC_free((void *)new_dl); +# endif + return GC_DUPLICATE; + } + } + } + new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); + new_dl -> dl_hidden_link = GC_HIDE_POINTER(link); + dl_set_next(new_dl, dl_hashtbl -> head[index]); + GC_dirty(new_dl); + dl_hashtbl -> head[index] = new_dl; + dl_hashtbl -> entries++; + GC_dirty(dl_hashtbl->head + index); + UNLOCK(); + return GC_SUCCESS; +} + +GC_API int GC_CALL GC_general_register_disappearing_link(void * * link, + const void * obj) +{ + if (((word)link & (ALIGNMENT-1)) != 0 || !NONNULL_ARG_NOT_NULL(link)) + ABORT("Bad arg to GC_general_register_disappearing_link"); + return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj, + "dl"); +} + +#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 + +/* Unregisters given link and returns the link entry to free. */ +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; + + GC_ASSERT(I_HOLD_LOCK()); + if (EXPECT(NULL == dl_hashtbl -> head, FALSE)) return NULL; + + 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)) { + /* Remove found entry from the table. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[index] = dl_next(curr_dl); + GC_dirty(dl_hashtbl->head + index); + } else { + dl_set_next(prev_dl, dl_next(curr_dl)); + GC_dirty(prev_dl); + } + dl_hashtbl -> entries--; + break; + } + prev_dl = 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(); + if (NULL == curr_dl) return 0; + FREE_DL_ENTRY(curr_dl); + return 1; +} + +/* Toggle-ref support. */ +#ifndef GC_TOGGLE_REFS_NOT_NEEDED + typedef union toggle_ref_u GCToggleRef; + + STATIC GC_toggleref_func GC_toggleref_callback = 0; + + GC_INNER void GC_process_togglerefs(void) + { + size_t i; + size_t new_size = 0; + GC_bool needs_barrier = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < GC_toggleref_array_size; ++i) { + GCToggleRef r = GC_toggleref_arr[i]; + void *obj = r.strong_ref; + + if (((word)obj & 1) != 0) { + obj = GC_REVEAL_POINTER(r.weak_ref); + } + if (NULL == obj) { + continue; + } + switch (GC_toggleref_callback(obj)) { + case GC_TOGGLE_REF_DROP: + break; + case GC_TOGGLE_REF_STRONG: + GC_toggleref_arr[new_size++].strong_ref = obj; + needs_barrier = TRUE; + break; + case GC_TOGGLE_REF_WEAK: + GC_toggleref_arr[new_size++].weak_ref = GC_HIDE_POINTER(obj); + break; + default: + ABORT("Bad toggle-ref status returned by callback"); + } + } + + if (new_size < GC_toggleref_array_size) { + BZERO(&GC_toggleref_arr[new_size], + (GC_toggleref_array_size - new_size) * sizeof(GCToggleRef)); + GC_toggleref_array_size = new_size; + } + if (needs_barrier) + GC_dirty(GC_toggleref_arr); /* entire object */ + } + + STATIC void GC_normal_finalize_mark_proc(ptr_t); + + static void push_and_mark_object(void *p) + { + GC_normal_finalize_mark_proc((ptr_t)p); + while (!GC_mark_stack_empty()) { + MARK_FROM_MARK_STACK(); + } + GC_set_mark_bit(p); + if (GC_mark_state != MS_NONE) { + while (!GC_mark_some(0)) { + /* Empty. */ + } + } + } + + STATIC void GC_mark_togglerefs(void) + { + size_t i; + if (NULL == GC_toggleref_arr) + return; + + GC_set_mark_bit(GC_toggleref_arr); + for (i = 0; i < GC_toggleref_array_size; ++i) { + void *obj = GC_toggleref_arr[i].strong_ref; + if (obj != NULL && ((word)obj & 1) == 0) { + push_and_mark_object(obj); + } + } + } + + STATIC void GC_clear_togglerefs(void) + { + size_t i; + for (i = 0; i < GC_toggleref_array_size; ++i) { + if ((GC_toggleref_arr[i].weak_ref & 1) != 0) { + if (!GC_is_marked(GC_REVEAL_POINTER(GC_toggleref_arr[i].weak_ref))) { + GC_toggleref_arr[i].weak_ref = 0; + } else { + /* No need to copy, BDWGC is a non-moving collector. */ + } + } + } + } + + GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func fn) + { + DCL_LOCK_STATE; + + LOCK(); + GC_toggleref_callback = fn; + UNLOCK(); + } + + GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void) + { + GC_toggleref_func fn; + DCL_LOCK_STATE; + + LOCK(); + fn = GC_toggleref_callback; + UNLOCK(); + return fn; + } + + static GC_bool ensure_toggleref_capacity(size_t capacity_inc) + { + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == GC_toggleref_arr) { + GC_toggleref_array_capacity = 32; /* initial capacity */ + GC_toggleref_arr = (GCToggleRef *)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + GC_toggleref_array_capacity * sizeof(GCToggleRef), + NORMAL); + if (NULL == GC_toggleref_arr) + return FALSE; + } + if (GC_toggleref_array_size + capacity_inc + >= GC_toggleref_array_capacity) { + GCToggleRef *new_array; + while (GC_toggleref_array_capacity + < GC_toggleref_array_size + capacity_inc) { + GC_toggleref_array_capacity *= 2; + if ((GC_toggleref_array_capacity + & ((size_t)1 << (sizeof(size_t) * 8 - 1))) != 0) + return FALSE; /* overflow */ + } + + new_array = (GCToggleRef *)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + GC_toggleref_array_capacity * sizeof(GCToggleRef), + NORMAL); + if (NULL == new_array) + return FALSE; + if (EXPECT(GC_toggleref_array_size > 0, TRUE)) + BCOPY(GC_toggleref_arr, new_array, + GC_toggleref_array_size * sizeof(GCToggleRef)); + GC_INTERNAL_FREE(GC_toggleref_arr); + GC_toggleref_arr = new_array; + } + return TRUE; + } + + GC_API int GC_CALL GC_toggleref_add(void *obj, int is_strong_ref) + { + int res = GC_SUCCESS; + DCL_LOCK_STATE; + + GC_ASSERT(NONNULL_ARG_NOT_NULL(obj)); + LOCK(); + if (GC_toggleref_callback != 0) { + if (!ensure_toggleref_capacity(1)) { + res = GC_NO_MEMORY; + } else { + GC_toggleref_arr[GC_toggleref_array_size].strong_ref = + is_strong_ref ? obj : (void *)GC_HIDE_POINTER(obj); + if (is_strong_ref) + GC_dirty(GC_toggleref_arr + GC_toggleref_array_size); + GC_toggleref_array_size++; + } + } + UNLOCK(); + return res; + } +#endif /* !GC_TOGGLE_REFS_NOT_NEEDED */ + +/* Finalizer callback support. */ +STATIC GC_await_finalize_proc GC_object_finalized_proc = 0; + +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc fn) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_object_finalized_proc = fn; + UNLOCK(); +} + +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void) +{ + GC_await_finalize_proc fn; + DCL_LOCK_STATE; + + LOCK(); + fn = GC_object_finalized_proc; + UNLOCK(); + return fn; +} + +#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 || !NONNULL_ARG_NOT_NULL(link)) + ABORT("Bad arg to GC_register_long_link"); + return GC_register_disappearing_link_inner(&GC_ll_hashtbl, link, obj, + "long dl"); + } + + 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 + /* 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, *new_dl; + struct disappearing_link *prev_dl = NULL; + size_t curr_index, new_index; + word curr_hidden_link, new_hidden_link; + + GC_ASSERT(I_HOLD_LOCK()); + if (EXPECT(NULL == dl_hashtbl -> head, FALSE)) return GC_NOT_FOUND; + + /* Find current link. */ + curr_index = HASH2(link, dl_hashtbl -> log_size); + curr_hidden_link = GC_HIDE_POINTER(link); + 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 (EXPECT(NULL == curr_dl, FALSE)) { + return GC_NOT_FOUND; + } else if (link == new_link) { + return GC_SUCCESS; /* Nothing to do. */ + } + + /* link found; now check new_link not present. */ + new_index = HASH2(new_link, dl_hashtbl -> log_size); + new_hidden_link = GC_HIDE_POINTER(new_link); + 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. */ + return GC_DUPLICATE; + } + } + + /* Remove from old, add to new, update link. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[curr_index] = dl_next(curr_dl); + } else { + dl_set_next(prev_dl, dl_next(curr_dl)); + GC_dirty(prev_dl); + } + curr_dl -> dl_hidden_link = new_hidden_link; + dl_set_next(curr_dl, dl_hashtbl -> head[new_index]); + dl_hashtbl -> head[new_index] = curr_dl; + GC_dirty(curr_dl); + GC_dirty(dl_hashtbl->head); /* entire object */ + 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 + || !NONNULL_ARG_NOT_NULL(new_link)) + 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 + || !NONNULL_ARG_NOT_NULL(new_link)) + ABORT("Bad new_link arg to GC_move_long_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 */ +/* overflow is handled by the caller, and is not a disaster. */ +#if defined(_MSC_VER) && defined(I386) + GC_ATTR_NOINLINE + /* Otherwise some optimizer bug is tickled in VC for x86 (v19, at least). */ +#endif +STATIC void GC_normal_finalize_mark_proc(ptr_t p) +{ + GC_mark_stack_top = GC_push_obj(p, HDR(p), GC_mark_stack_top, + GC_mark_stack + GC_mark_stack_size); +} + +/* This only pays very partial attention to the mark descriptor. */ +/* It does the right thing for normal and atomic objects, and treats */ +/* most others as normal. */ +STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p) +{ + hdr * hhdr = HDR(p); + word descr = hhdr -> hb_descr; + ptr_t q; + ptr_t scan_limit; + ptr_t target_limit = p + hhdr -> hb_sz - 1; + + if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) { + scan_limit = p + descr - sizeof(word); + } else { + scan_limit = target_limit + 1 - sizeof(word); + } + for (q = p; (word)q <= (word)scan_limit; q += ALIGNMENT) { + word r = *(word *)q; + + if (r < (word)p || r > (word)target_limit) { + GC_PUSH_ONE_HEAP(r, q, GC_mark_stack_top); + } + } +} + +STATIC void GC_null_finalize_mark_proc(ptr_t p GC_ATTR_UNUSED) {} + +/* Possible finalization_marker procedures. Note that mark stack */ +/* overflow is handled by the caller, and is not a disaster. */ + +/* GC_unreachable_finalize_mark_proc is an alias for normal marking, */ +/* but it is explicitly tested for, and triggers different */ +/* behavior. Objects registered in this way are not finalized */ +/* if they are reachable by other finalizable objects, even if those */ +/* other objects specify no ordering. */ +STATIC void GC_unreachable_finalize_mark_proc(ptr_t p) +{ + /* A dummy comparison to ensure the compiler not to optimize two */ + /* identical functions into a single one (thus, to ensure a unique */ + /* address of each). Alternatively, GC_noop1(p) could be used. */ + if (EXPECT(NULL == p, FALSE)) return; + + GC_normal_finalize_mark_proc(p); +} + +static GC_bool need_unreachable_finalization = FALSE; + /* Avoid the work if this is not used. */ + /* TODO: turn need_unreachable_finalization into a counter */ + +/* Register a finalization function. See gc.h for details. */ +/* The last parameter is a procedure that determines */ +/* marking for finalization ordering. Any objects marked */ +/* by that procedure will be guaranteed to not have been */ +/* finalized when this finalizer is invoked. */ +STATIC void GC_register_finalizer_inner(void * obj, + GC_finalization_proc fn, void *cd, + GC_finalization_proc *ofn, void **ocd, + finalization_mark_proc mp) +{ + struct finalizable_object * curr_fo; + size_t index; + struct finalizable_object *new_fo = 0; + hdr *hhdr = NULL; /* initialized to prevent warning. */ + DCL_LOCK_STATE; + + if (EXPECT(GC_find_leak, FALSE)) { + /* No-op. *ocd and *ofn remain unchanged. */ + return; + } + LOCK(); + if (mp == GC_unreachable_finalize_mark_proc) + need_unreachable_finalization = TRUE; + if (EXPECT(NULL == GC_fnlz_roots.fo_head, FALSE) + || EXPECT(GC_fo_entries > ((word)1 << GC_log_fo_table_size), FALSE)) { + GC_grow_table((struct hash_chain_entry ***)&GC_fnlz_roots.fo_head, + &GC_log_fo_table_size, &GC_fo_entries); + GC_COND_LOG_PRINTF("Grew fo table to %u entries\n", + 1U << GC_log_fo_table_size); + } + /* in the THREADS case we hold allocation lock. */ + for (;;) { + struct finalizable_object *prev_fo = NULL; + GC_oom_func oom_fn; + + index = HASH2(obj, GC_log_fo_table_size); + curr_fo = GC_fnlz_roots.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(obj)) { + /* Interruption by a signal in the middle of this */ + /* should be safe. The client may see only *ocd */ + /* updated, but we'll declare that to be his problem. */ + if (ocd) *ocd = (void *) (curr_fo -> fo_client_data); + if (ofn) *ofn = curr_fo -> fo_fn; + /* Delete the structure for obj. */ + if (prev_fo == 0) { + GC_fnlz_roots.fo_head[index] = fo_next(curr_fo); + } else { + fo_set_next(prev_fo, fo_next(curr_fo)); + GC_dirty(prev_fo); + } + if (fn == 0) { + GC_fo_entries--; + /* May not happen if we get a signal. But a high */ + /* estimate will only make the table larger than */ + /* necessary. */ +# if !defined(THREADS) && !defined(DBG_HDRS_ALL) + GC_free((void *)curr_fo); +# endif + } else { + curr_fo -> fo_fn = fn; + curr_fo -> fo_client_data = (ptr_t)cd; + curr_fo -> fo_mark_proc = mp; + GC_dirty(curr_fo); + /* Reinsert it. We deleted it first to maintain */ + /* consistency in the event of a signal. */ + if (prev_fo == 0) { + GC_fnlz_roots.fo_head[index] = curr_fo; + } else { + fo_set_next(prev_fo, curr_fo); + GC_dirty(prev_fo); + } + } + if (NULL == prev_fo) + GC_dirty(GC_fnlz_roots.fo_head + index); + UNLOCK(); +# ifndef DBG_HDRS_ALL + /* Free unused new_fo returned by GC_oom_fn() */ + GC_free((void *)new_fo); +# endif + return; + } + prev_fo = curr_fo; + curr_fo = fo_next(curr_fo); + } + if (EXPECT(new_fo != 0, FALSE)) { + /* new_fo is returned by GC_oom_fn(). */ + GC_ASSERT(fn != 0); +# ifdef LINT2 + if (NULL == hhdr) ABORT("Bad hhdr in GC_register_finalizer_inner"); +# endif + break; + } + if (fn == 0) { + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + UNLOCK(); + return; + } + GET_HDR(obj, hhdr); + if (EXPECT(0 == hhdr, FALSE)) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + UNLOCK(); + return; + } + new_fo = (struct finalizable_object *) + GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL); + if (EXPECT(new_fo != 0, TRUE)) + break; + oom_fn = GC_oom_fn; + UNLOCK(); + new_fo = (struct finalizable_object *) + (*oom_fn)(sizeof(struct finalizable_object)); + if (0 == new_fo) { + /* No enough memory. *ocd and *ofn remain unchanged. */ + return; + } + /* It's not likely we'll make it here, but ... */ + LOCK(); + /* Recalculate index since the table may grow and */ + /* check again that our finalizer is not in the table. */ + } + GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object)); + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + new_fo -> fo_hidden_base = GC_HIDE_POINTER(obj); + new_fo -> fo_fn = fn; + 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, GC_fnlz_roots.fo_head[index]); + GC_dirty(new_fo); + GC_fo_entries++; + GC_fnlz_roots.fo_head[index] = new_fo; + GC_dirty(GC_fnlz_roots.fo_head + index); + UNLOCK(); +} + +GC_API void GC_CALL GC_register_finalizer(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_normal_finalize_mark_proc); +} + +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_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_ignore_self_finalize_mark_proc); +} + +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_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_null_finalize_mark_proc); +} + +GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_ASSERT(GC_java_finalization); + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_unreachable_finalize_mark_proc); +} + +#ifndef NO_DEBUGGING + STATIC void GC_dump_finalization_links( + const struct dl_hashtbl_s *dl_hashtbl) + { + size_t dl_size = (size_t)1 << dl_hashtbl -> log_size; + size_t i; + + if (NULL == dl_hashtbl -> head) return; /* empty table */ + + for (i = 0; i < dl_size; i++) { + struct disappearing_link *curr_dl; + + for (curr_dl = dl_hashtbl -> head[i]; curr_dl != 0; + curr_dl = dl_next(curr_dl)) { + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_dl->dl_hidden_obj); + ptr_t real_link = (ptr_t)GC_REVEAL_POINTER(curr_dl->dl_hidden_link); + + GC_printf("Object: %p, link: %p\n", + (void *)real_ptr, (void *)real_link); + } + } + } + + GC_API void GC_CALL GC_dump_finalization(void) + { + struct finalizable_object * curr_fo; + size_t i; + size_t fo_size = GC_fnlz_roots.fo_head == NULL ? 0 : + (size_t)1 << GC_log_fo_table_size; + + 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 = GC_fnlz_roots.fo_head[i]; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); + + GC_printf("Finalizable object: %p\n", (void *)real_ptr); + } + } + } +#endif /* !NO_DEBUGGING */ + +#ifndef SMALL_CONFIG + STATIC word GC_old_dl_entries = 0; /* for stats printing */ +# 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 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. */ + /* Returns NULL if GC_invoke_finalizers() should not be called by the */ + /* collector (to minimize the risk of a deep finalizers recursion), */ + /* otherwise returns a pointer to GC_finalizer_nested. */ + STATIC unsigned char *GC_check_finalizer_nested(void) + { + 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() */ + /* depending on the nesting (recursion) level. */ + if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL; + GC_finalizer_skipped = 0; + } + *(char *)&GC_finalizer_nested = (char)(nesting_level + 1); + return (unsigned char *)&GC_finalizer_nested; + } +#endif /* THREADS */ + +GC_INLINE void GC_make_disappearing_links_disappear( + struct dl_hashtbl_s* dl_hashtbl, + GC_bool is_remove_dangling) +{ + size_t i; + size_t dl_size = (size_t)1 << dl_hashtbl -> log_size; + GC_bool needs_barrier = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == dl_hashtbl -> head) return; /* empty table */ + + for (i = 0; i < dl_size; i++) { + struct disappearing_link *curr_dl, *next_dl; + struct disappearing_link *prev_dl = NULL; + + for (curr_dl = dl_hashtbl->head[i]; curr_dl != NULL; curr_dl = next_dl) { + next_dl = dl_next(curr_dl); + if (is_remove_dangling) { + ptr_t real_link = (ptr_t)GC_base(GC_REVEAL_POINTER( + curr_dl->dl_hidden_link)); + + if (NULL == real_link || EXPECT(GC_is_marked(real_link), TRUE)) { + prev_dl = curr_dl; + continue; + } + } else { + if (EXPECT(GC_is_marked((ptr_t)GC_REVEAL_POINTER( + curr_dl->dl_hidden_obj)), TRUE)) { + prev_dl = curr_dl; + continue; + } + *(ptr_t *)GC_REVEAL_POINTER(curr_dl->dl_hidden_link) = NULL; + } + + /* Delete curr_dl entry from dl_hashtbl. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[i] = next_dl; + needs_barrier = TRUE; + } else { + dl_set_next(prev_dl, next_dl); + GC_dirty(prev_dl); + } + GC_clear_mark_bit(curr_dl); + dl_hashtbl -> entries--; + } + } + if (needs_barrier) + GC_dirty(dl_hashtbl -> head); /* entire object */ +} + +/* 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 finalizable_object * curr_fo, * prev_fo, * next_fo; + ptr_t real_ptr; + size_t i; + size_t fo_size = GC_fnlz_roots.fo_head == NULL ? 0 : + (size_t)1 << GC_log_fo_table_size; + GC_bool needs_barrier = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); +# ifndef SMALL_CONFIG + /* 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 + +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_mark_togglerefs(); +# endif + GC_make_disappearing_links_disappear(&GC_dl_hashtbl, FALSE); + + /* 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 = GC_fnlz_roots.fo_head[i]; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); + real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); + if (!GC_is_marked(real_ptr)) { + GC_MARKED_FOR_FINALIZATION(real_ptr); + GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc); + if (GC_is_marked(real_ptr)) { + WARN("Finalization cycle involving %p\n", real_ptr); + } + } + } + } + /* Enqueue for finalization all objects that are still */ + /* unreachable. */ + GC_bytes_finalized = 0; + for (i = 0; i < fo_size; i++) { + curr_fo = GC_fnlz_roots.fo_head[i]; + prev_fo = 0; + while (curr_fo != 0) { + real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); + if (!GC_is_marked(real_ptr)) { + if (!GC_java_finalization) { + GC_set_mark_bit(real_ptr); + } + /* Delete from hash table */ + next_fo = fo_next(curr_fo); + if (NULL == prev_fo) { + GC_fnlz_roots.fo_head[i] = next_fo; + if (GC_object_finalized_proc) { + GC_dirty(GC_fnlz_roots.fo_head + i); + } else { + needs_barrier = TRUE; + } + } else { + fo_set_next(prev_fo, next_fo); + GC_dirty(prev_fo); + } + GC_fo_entries--; + if (GC_object_finalized_proc) + GC_object_finalized_proc(real_ptr); + + /* Add to list of objects awaiting finalization. */ + fo_set_next(curr_fo, GC_fnlz_roots.finalize_now); + GC_dirty(curr_fo); + SET_FINALIZE_NOW(curr_fo); + /* unhide object pointer so any future collections will */ + /* see it. */ + curr_fo -> fo_hidden_base = + (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + GC_bytes_finalized += + curr_fo -> fo_object_size + + sizeof(struct finalizable_object); + GC_ASSERT(GC_is_marked(GC_base(curr_fo))); + curr_fo = next_fo; + } else { + prev_fo = curr_fo; + curr_fo = fo_next(curr_fo); + } + } + } + + if (GC_java_finalization) { + /* make sure we mark everything reachable from objects finalized + using the no_order mark_proc */ + for (curr_fo = GC_fnlz_roots.finalize_now; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + real_ptr = (ptr_t)curr_fo -> fo_hidden_base; + if (!GC_is_marked(real_ptr)) { + if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) { + GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); + } + if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) { + GC_set_mark_bit(real_ptr); + } + } + } + + /* now revive finalize-when-unreachable objects reachable from + other finalizable objects */ + if (need_unreachable_finalization) { + curr_fo = GC_fnlz_roots.finalize_now; + GC_ASSERT(NULL == curr_fo || GC_fnlz_roots.fo_head != NULL); + prev_fo = NULL; + while (curr_fo != NULL) { + next_fo = fo_next(curr_fo); + if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) { + real_ptr = (ptr_t)curr_fo -> fo_hidden_base; + if (!GC_is_marked(real_ptr)) { + GC_set_mark_bit(real_ptr); + } else { + if (NULL == prev_fo) { + SET_FINALIZE_NOW(next_fo); + } else { + fo_set_next(prev_fo, next_fo); + GC_dirty(prev_fo); + } + curr_fo -> fo_hidden_base = + GC_HIDE_POINTER(curr_fo -> fo_hidden_base); + GC_bytes_finalized -= + curr_fo->fo_object_size + sizeof(struct finalizable_object); + + i = HASH2(real_ptr, GC_log_fo_table_size); + fo_set_next(curr_fo, GC_fnlz_roots.fo_head[i]); + GC_dirty(curr_fo); + GC_fo_entries++; + GC_fnlz_roots.fo_head[i] = curr_fo; + curr_fo = prev_fo; + needs_barrier = TRUE; + } + } + prev_fo = curr_fo; + curr_fo = next_fo; + } + } + } + if (needs_barrier) + GC_dirty(GC_fnlz_roots.fo_head); /* entire object */ + + /* Remove dangling disappearing links. */ + GC_make_disappearing_links_disappear(&GC_dl_hashtbl, TRUE); + +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_clear_togglerefs(); +# endif +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_make_disappearing_links_disappear(&GC_ll_hashtbl, FALSE); + GC_make_disappearing_links_disappear(&GC_ll_hashtbl, TRUE); +# endif + + if (GC_fail_count) { + /* Don't prevent running finalizers if there has been an allocation */ + /* failure recently. */ +# ifdef THREADS + GC_reset_finalizer_nested(); +# else + GC_finalizer_nested = 0; +# endif + } +} + +#ifndef JAVA_FINALIZATION_NOT_NEEDED + + /* Enqueue all remaining finalizers to be run. */ + STATIC void GC_enqueue_all_finalizers(void) + { + struct finalizable_object * next_fo; + size_t i; + size_t fo_size = GC_fnlz_roots.fo_head == NULL ? 0 : + (size_t)1 << GC_log_fo_table_size; + + GC_ASSERT(I_HOLD_LOCK()); + GC_bytes_finalized = 0; + for (i = 0; i < fo_size; i++) { + struct finalizable_object * curr_fo = GC_fnlz_roots.fo_head[i]; + + GC_fnlz_roots.fo_head[i] = NULL; + while (curr_fo != NULL) { + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); + + GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); + GC_set_mark_bit(real_ptr); + + next_fo = fo_next(curr_fo); + + /* Add to list of objects awaiting finalization. */ + fo_set_next(curr_fo, GC_fnlz_roots.finalize_now); + GC_dirty(curr_fo); + SET_FINALIZE_NOW(curr_fo); + + /* unhide object pointer so any future collections will */ + /* see it. */ + curr_fo -> fo_hidden_base = + (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + GC_bytes_finalized += + curr_fo -> fo_object_size + sizeof(struct finalizable_object); + curr_fo = next_fo; + } + } + GC_fo_entries = 0; /* all entries deleted from the hash table */ + } + + /* Invoke all remaining finalizers that haven't yet been run. + * This is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * Unfortunately, the Java standard implies we have to keep running + * finalizers until there are no more left, a potential infinite loop. + * YUCK. + * Note that this is even more dangerous than the usual Java + * finalizers, in that objects reachable from static variables + * may have been finalized when these finalizers are run. + * Finalizers run at this point must be prepared to deal with a + * mostly broken world. + * This routine is externally callable, so is called without + * the allocation lock. + */ + GC_API void GC_CALL GC_finalize_all(void) + { + DCL_LOCK_STATE; + + LOCK(); + while (GC_fo_entries > 0) { + GC_enqueue_all_finalizers(); + UNLOCK(); + GC_invoke_finalizers(); + /* Running the finalizers in this thread is arguably not a good */ + /* idea when we should be notifying another thread to run them. */ + /* But otherwise we don't have a great way to wait for them to */ + /* run. */ + LOCK(); + } + UNLOCK(); + } + +#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 */ +/* getting into that safe state is expensive.) */ +GC_API int GC_CALL GC_should_invoke_finalizers(void) +{ +# ifdef AO_HAVE_load + return AO_load((volatile AO_t *)&GC_fnlz_roots.finalize_now) != 0; +# else + return GC_fnlz_roots.finalize_now != NULL; +# endif /* !THREADS */ +} + +/* Invoke finalizers for all objects that are ready to be finalized. */ +/* Should be called without allocation lock. */ +GC_API int GC_CALL GC_invoke_finalizers(void) +{ + int count = 0; + word bytes_freed_before = 0; /* initialized to prevent warning. */ + DCL_LOCK_STATE; + + while (GC_should_invoke_finalizers()) { + struct finalizable_object * curr_fo; + +# ifdef THREADS + LOCK(); +# endif + if (count == 0) { + bytes_freed_before = GC_bytes_freed; + /* Don't do this outside, since we need the lock. */ + } + curr_fo = GC_fnlz_roots.finalize_now; +# ifdef THREADS + if (curr_fo != NULL) + SET_FINALIZE_NOW(fo_next(curr_fo)); + UNLOCK(); + if (curr_fo == 0) break; +# else + GC_fnlz_roots.finalize_now = fo_next(curr_fo); +# endif + fo_set_next(curr_fo, 0); + (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base), + curr_fo -> fo_client_data); + curr_fo -> fo_client_data = 0; + ++count; + /* Explicit freeing of curr_fo is probably a bad idea. */ + /* It throws off accounting if nearly all objects are */ + /* finalizable. Otherwise it should not matter. */ + } + /* bytes_freed_before is initialized whenever count != 0 */ + if (count != 0 +# if defined(THREADS) && !defined(THREAD_SANITIZER) + /* A quick check whether some memory was freed. */ + /* The race with GC_free() is safe to be ignored */ + /* because we only need to know if the current */ + /* thread has deallocated something. */ + && bytes_freed_before != GC_bytes_freed +# endif + ) { + LOCK(); + GC_finalizer_bytes_freed += (GC_bytes_freed - bytes_freed_before); + UNLOCK(); + } + return count; +} + +static word last_finalizer_notification = 0; + +GC_INNER void GC_notify_or_invoke_finalizers(void) +{ + GC_finalizer_notifier_proc notifier_fn = 0; +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + static word last_back_trace_gc_no = 1; /* Skip first one. */ +# endif + DCL_LOCK_STATE; + +# if defined(THREADS) && !defined(KEEP_BACK_PTRS) \ + && !defined(MAKE_BACK_GRAPH) + /* Quick check (while unlocked) for an empty finalization queue. */ + if (!GC_should_invoke_finalizers()) + return; +# endif + LOCK(); + + /* This is a convenient place to generate backtraces if appropriate, */ + /* since that code is not callable with the allocation lock. */ +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + if (GC_gc_no > last_back_trace_gc_no) { +# ifdef KEEP_BACK_PTRS + long i; + /* Stops when GC_gc_no wraps; that's OK. */ + last_back_trace_gc_no = GC_WORD_MAX; /* disable others. */ + for (i = 0; i < GC_backtraces; ++i) { + /* FIXME: This tolerates concurrent heap mutation, */ + /* which may cause occasional mysterious results. */ + /* We need to release the GC lock, since GC_print_callers */ + /* acquires it. It probably shouldn't. */ + void *current = GC_generate_random_valid_address(); + + UNLOCK(); + GC_printf("\n****Chosen address %p in object\n", current); + GC_print_backtrace(current); + LOCK(); + } + last_back_trace_gc_no = GC_gc_no; +# endif +# ifdef MAKE_BACK_GRAPH + if (GC_print_back_height) { + GC_print_back_graph_stats(); + } +# endif + } +# endif + if (NULL == GC_fnlz_roots.finalize_now) { + UNLOCK(); + return; + } + + if (!GC_finalize_on_demand) { + unsigned char *pnested = GC_check_finalizer_nested(); + UNLOCK(); + /* Skip GC_invoke_finalizers() if nested */ + if (pnested != NULL) { + (void) GC_invoke_finalizers(); + *pnested = 0; /* Reset since no more finalizers. */ +# ifndef THREADS + GC_ASSERT(NULL == GC_fnlz_roots.finalize_now); +# endif /* Otherwise GC can run concurrently and add more */ + } + return; + } + + /* These variables require synchronization to avoid data races. */ + if (last_finalizer_notification != GC_gc_no) { + notifier_fn = GC_finalizer_notifier; + last_finalizer_notification = GC_gc_no; + } + UNLOCK(); + if (notifier_fn != 0) + (*notifier_fn)(); /* Invoke the notifier */ +} + +#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; + unsigned long ready = 0; + + 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_fnlz_roots.finalize_now; fo != NULL; 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/bdwgc/fnlz_mlc.c b/bdwgc/fnlz_mlc.c new file mode 100644 index 000000000..cf64a9db4 --- /dev/null +++ b/bdwgc/fnlz_mlc.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011 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. + * + * 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. + * + */ + +#include "private/gc_priv.h" + +#ifdef ENABLE_DISCLAIM + +#include "gc_disclaim.h" +#include "gc_inline.h" /* for GC_malloc_kind */ +#include "private/dbg_mlc.h" /* for oh type */ + +#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + /* The first bit is already used for a debug purpose. */ +# define FINALIZER_CLOSURE_FLAG 0x2 +#else +# define FINALIZER_CLOSURE_FLAG 0x1 +#endif + +STATIC int GC_CALLBACK GC_finalized_disclaim(void *obj) +{ +# ifdef AO_HAVE_load + word fc_word = (word)AO_load((volatile AO_t *)obj); +# else + word fc_word = *(word *)obj; +# endif + + if ((fc_word & FINALIZER_CLOSURE_FLAG) != 0) { + /* The disclaim function may be passed fragments from the */ + /* free-list, on which it should not run finalization. */ + /* To recognize this case, we use the fact that the first word */ + /* on such fragments is always multiple of 4 (a link to the next */ + /* fragment, or NULL). If it is desirable to have a finalizer */ + /* which does not use the first word for storing finalization */ + /* info, GC_disclaim_and_reclaim() must be extended to clear */ + /* fragments so that the assumption holds for the selected word. */ + const struct GC_finalizer_closure *fc + = (struct GC_finalizer_closure *)(fc_word + & ~(word)FINALIZER_CLOSURE_FLAG); + GC_ASSERT(!GC_find_leak); + (*fc->proc)((word *)obj + 1, fc->cd); + } + return 0; +} + +GC_API void GC_CALL GC_init_finalized_malloc(void) +{ + DCL_LOCK_STATE; + + GC_init(); /* In case it's not already done. */ + LOCK(); + if (GC_finalized_kind != 0) { + UNLOCK(); + return; + } + + /* The finalizer closure is placed in the first word in order to */ + /* use the lower bits to distinguish live objects from objects on */ + /* the free list. The downside of this is that we need one-word */ + /* offset interior pointers, and that GC_base does not return the */ + /* start of the user region. */ + GC_register_displacement_inner(sizeof(word)); + + /* And, the pointer to the finalizer closure object itself is */ + /* displaced due to baking in this indicator. */ + GC_register_displacement_inner(FINALIZER_CLOSURE_FLAG); + GC_register_displacement_inner(sizeof(oh) + FINALIZER_CLOSURE_FLAG); + + GC_finalized_kind = GC_new_kind_inner(GC_new_free_list_inner(), + GC_DS_LENGTH, TRUE, TRUE); + GC_ASSERT(GC_finalized_kind != 0); + GC_register_disclaim_proc(GC_finalized_kind, GC_finalized_disclaim, TRUE); + UNLOCK(); +} + +GC_API void GC_CALL GC_register_disclaim_proc(int kind, GC_disclaim_proc proc, + int mark_unconditionally) +{ + GC_ASSERT((unsigned)kind < MAXOBJKINDS); + if (!EXPECT(GC_find_leak, FALSE)) { + GC_obj_kinds[kind].ok_disclaim_proc = proc; + GC_obj_kinds[kind].ok_mark_unconditionally = + (GC_bool)mark_unconditionally; + } +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_finalized_malloc(size_t lb, + const struct GC_finalizer_closure *fclos) +{ + void *op; + + GC_ASSERT(GC_finalized_kind != 0); + GC_ASSERT(NONNULL_ARG_NOT_NULL(fclos)); + GC_ASSERT(((word)fclos & FINALIZER_CLOSURE_FLAG) == 0); + op = GC_malloc_kind(SIZET_SAT_ADD(lb, sizeof(word)), GC_finalized_kind); + if (EXPECT(NULL == op, FALSE)) + return NULL; +# ifdef AO_HAVE_store + AO_store((volatile AO_t *)op, (AO_t)fclos | FINALIZER_CLOSURE_FLAG); +# else + *(word *)op = (word)fclos | FINALIZER_CLOSURE_FLAG; +# endif + GC_dirty(op); + REACHABLE_AFTER_DIRTY(fclos); + return (word *)op + 1; +} + +#endif /* ENABLE_DISCLAIM */ diff --git a/bdwgc/gc_badalc.cc b/bdwgc/gc_badalc.cc new file mode 100644 index 000000000..6015dc56d --- /dev/null +++ b/bdwgc/gc_badalc.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Ivan Maidanski + * + * 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. + * + */ + +// This file provides the implementation of GC_throw_bad_alloc() which +// is invoked by GC operator "new" in case of an out-of-memory event. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_BUILD +# define GC_BUILD +#endif + +#define GC_DONT_INCL_WINDOWS_H +#include "gc.h" + +#include // for bad_alloc, precedes include of gc_cpp.h + +#if defined(GC_NEW_ABORTS_ON_OOM) || defined(_LIBCPP_NO_EXCEPTIONS) +# define GC_ALLOCATOR_THROW_OR_ABORT() GC_abort_on_oom() +#else +# define GC_ALLOCATOR_THROW_OR_ABORT() throw std::bad_alloc() +#endif + +GC_API void GC_CALL GC_throw_bad_alloc() { + GC_ALLOCATOR_THROW_OR_ABORT(); +} diff --git a/bdwgc/gc_badalc.cpp b/bdwgc/gc_badalc.cpp new file mode 100644 index 000000000..7d4dc41ad --- /dev/null +++ b/bdwgc/gc_badalc.cpp @@ -0,0 +1,2 @@ +// Visual C++ seems to prefer a .cpp extension to .cc one. +#include "gc_badalc.cc" diff --git a/bdwgc/gc_cpp.cc b/bdwgc/gc_cpp.cc new file mode 100644 index 000000000..341805a64 --- /dev/null +++ b/bdwgc/gc_cpp.cc @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/************************************************************************* +This implementation module for gc_cpp.h provides an implementation of +the global operators "new" and "delete" that calls the Boehm +allocator. All objects allocated by this implementation will be +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 +built-in "new" and "delete". +**************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_BUILD +# define GC_BUILD +#endif + +#define GC_DONT_INCL_WINDOWS_H +#include "gc.h" + +#ifndef GC_INCLUDE_NEW +# define GC_INCLUDE_NEW +#endif +#include "gc_cpp.h" + +#if !(defined(_MSC_VER) || defined(__DMC__)) || defined(GC_NO_INLINE_STD_NEW) + +#if defined(GC_NEW_ABORTS_ON_OOM) || defined(_LIBCPP_NO_EXCEPTIONS) +# define GC_ALLOCATOR_THROW_OR_ABORT() GC_abort_on_oom() +#else +// Use bad_alloc() directly instead of GC_throw_bad_alloc() call. +# define GC_ALLOCATOR_THROW_OR_ABORT() throw std::bad_alloc() +#endif + + void* operator new(size_t size) GC_DECL_NEW_THROW { + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; + } + +# ifdef _MSC_VER + // 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) + { +# ifdef GC_DEBUG + void* obj = GC_debug_malloc_uncollectable(size, szFileName, nLine); +# else + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + (void)szFileName; (void)nLine; +# endif + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; + } +# endif // _MSC_VER + + void operator delete(void* obj) GC_NOEXCEPT { + GC_FREE(obj); + } + +# if defined(GC_OPERATOR_NEW_ARRAY) && !defined(CPPCHECK) + void* operator new[](size_t size) GC_DECL_NEW_THROW { + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; + } + +# ifdef _MSC_VER + // This new operator is used by VC++ 7+ in Debug builds. + void* operator new[](size_t size, int nBlockUse, + const char* szFileName, int nLine) + { + return operator new(size, nBlockUse, szFileName, nLine); + } +# endif // _MSC_VER + + void operator delete[](void* obj) GC_NOEXCEPT { + GC_FREE(obj); + } +# endif // GC_OPERATOR_NEW_ARRAY + +# if __cplusplus >= 201402L || _MSVC_LANG >= 201402L // C++14 + void operator delete(void* obj, size_t size) GC_NOEXCEPT { + (void)size; // size is ignored + GC_FREE(obj); + } + +# if defined(GC_OPERATOR_NEW_ARRAY) && !defined(CPPCHECK) + void operator delete[](void* obj, size_t size) GC_NOEXCEPT { + (void)size; + GC_FREE(obj); + } +# endif +# endif // C++14 + +#endif // !_MSC_VER && !__DMC__ || GC_NO_INLINE_STD_NEW diff --git a/bdwgc/gc_cpp.cpp b/bdwgc/gc_cpp.cpp new file mode 100644 index 000000000..f6bd95e59 --- /dev/null +++ b/bdwgc/gc_cpp.cpp @@ -0,0 +1,2 @@ +// Visual C++ seems to prefer a .cpp extension to .cc +#include "gc_cpp.cc" diff --git a/bdwgc/gc_dlopen.c b/bdwgc/gc_dlopen.c new file mode 100644 index 000000000..13991bae6 --- /dev/null +++ b/bdwgc/gc_dlopen.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 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. + * + * 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. + */ + +#include "private/gc_priv.h" + +/* This used to be in dyn_load.c. It was extracted into a separate */ +/* file to avoid having to link against libdl.{a,so} if the client */ +/* doesn't call dlopen. Of course this fails if the collector is in */ +/* a dynamic library. -HB */ +#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) + +#undef GC_MUST_RESTORE_REDEFINED_DLOPEN +#if defined(dlopen) && !defined(GC_USE_LD_WRAP) + /* To support various threads pkgs, gc.h interposes on dlopen by */ + /* defining "dlopen" to be "GC_dlopen", which is implemented below. */ + /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */ + /* real system dlopen() in their implementation. We first remove */ + /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */ +# undef dlopen +# define GC_MUST_RESTORE_REDEFINED_DLOPEN +#endif + +/* Make sure we're not in the middle of a collection, and make sure we */ +/* don't start any. This is invoked prior to a dlopen call to avoid */ +/* synchronization issues. We can't just acquire the allocation lock, */ +/* since startup code in dlopen may try to allocate. This solution */ +/* risks heap growth (or, even, heap overflow) in the presence of many */ +/* dlopen calls in either a multi-threaded environment, or if the */ +/* library initialization code allocates substantial amounts of GC'ed */ +/* memory. */ +#ifndef USE_PROC_FOR_LIBRARIES + static void disable_gc_for_dlopen(void) + { + DCL_LOCK_STATE; + LOCK(); + while (GC_incremental && GC_collection_in_progress()) { + ENTER_GC(); + GC_collect_a_little_inner(1000); + EXIT_GC(); + } + ++GC_dont_gc; + UNLOCK(); + } +#endif + +/* Redefine dlopen to guarantee mutual exclusion with */ +/* GC_register_dynamic_libraries. Should probably happen for */ +/* other operating systems, too. */ + +/* This is similar to WRAP/REAL_FUNC() in pthread_support.c. */ +#ifdef GC_USE_LD_WRAP +# define WRAP_DLFUNC(f) __wrap_##f +# define REAL_DLFUNC(f) __real_##f + void * REAL_DLFUNC(dlopen)(const char *, int); +#else +# define WRAP_DLFUNC(f) GC_##f +# define REAL_DLFUNC(f) f +#endif + +GC_API void * WRAP_DLFUNC(dlopen)(const char *path, int mode) +{ + void * result; + +# ifndef USE_PROC_FOR_LIBRARIES + /* Disable collections. This solution risks heap growth (or, */ + /* even, heap overflow) but there seems no better solutions. */ + disable_gc_for_dlopen(); +# endif + result = REAL_DLFUNC(dlopen)(path, mode); +# ifndef USE_PROC_FOR_LIBRARIES + GC_enable(); /* undoes disable_gc_for_dlopen */ +# endif + return(result); +} + +#ifdef GC_USE_LD_WRAP + /* Define GC_ function as an alias for the plain one, which will be */ + /* intercepted. This allows files which include gc.h, and hence */ + /* generate references to the GC_ symbol, to see the right symbol. */ + GC_API void *GC_dlopen(const char *path, int mode) + { + return dlopen(path, mode); + } +#endif /* GC_USE_LD_WRAP */ + +#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN +# define dlopen GC_dlopen +#endif + +#endif /* GC_PTHREADS && !GC_NO_DLOPEN */ diff --git a/bdwgc/gcj_mlc.c b/bdwgc/gcj_mlc.c new file mode 100644 index 000000000..478206fbe --- /dev/null +++ b/bdwgc/gcj_mlc.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * + * 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. + * + */ + +#include "private/gc_pmark.h" /* includes gc_priv.h */ + +#ifdef GC_GCJ_SUPPORT + +/* + * This is an allocator interface tuned for gcj (the GNU static + * java compiler). + * + * Each allocated object has a pointer in its first word to a vtable, + * which for our purposes is simply a structure describing the type of + * the object. + * This descriptor structure contains a GC marking descriptor at offset + * MARK_DESCR_OFFSET. + * + * It is hoped that this interface may also be useful for other systems, + * possibly with some tuning of the constants. But the immediate goal + * is to get better gcj performance. + * + * We assume: counting on explicit initialization of this interface is OK. + */ + +#include "gc_gcj.h" +#include "private/dbg_mlc.h" + +int GC_gcj_kind = 0; /* Object kind for objects with descriptors */ + /* in "vtable". */ +int GC_gcj_debug_kind = 0; + /* The kind of objects that is always marked */ + /* with a mark proc call. */ + +STATIC struct GC_ms_entry * GC_gcj_fake_mark_proc(word * addr GC_ATTR_UNUSED, + struct GC_ms_entry *mark_stack_ptr, + struct GC_ms_entry * mark_stack_limit GC_ATTR_UNUSED, + word env GC_ATTR_UNUSED) +{ + ABORT_RET("No client gcj mark proc is specified"); + return mark_stack_ptr; +} + +/* Caller does not hold allocation lock. */ +GC_API void GC_CALL GC_init_gcj_malloc(int mp_index, + void * /* really GC_mark_proc */mp) +{ +# ifndef GC_IGNORE_GCJ_INFO + GC_bool ignore_gcj_info; +# endif + DCL_LOCK_STATE; + + if (mp == 0) /* In case GC_DS_PROC is unused. */ + mp = (void *)(word)GC_gcj_fake_mark_proc; + + GC_init(); /* In case it's not already done. */ + LOCK(); + if (GC_gcjobjfreelist != NULL) { + /* Already initialized. */ + UNLOCK(); + return; + } +# ifdef GC_IGNORE_GCJ_INFO + /* This is useful for debugging on platforms with missing getenv(). */ +# define ignore_gcj_info TRUE +# else + ignore_gcj_info = (0 != GETENV("GC_IGNORE_GCJ_INFO")); +# endif + 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; + if ((unsigned)mp_index >= GC_n_mark_procs) + ABORT("GC_init_gcj_malloc: bad index"); + /* Set up object kind gcj-style indirect descriptor. */ + GC_gcjobjfreelist = (ptr_t *)GC_new_free_list_inner(); + if (ignore_gcj_info) { + /* Use a simple length-based descriptor, thus forcing a fully */ + /* conservative scan. */ + GC_gcj_kind = GC_new_kind_inner((void **)GC_gcjobjfreelist, + /* 0 | */ GC_DS_LENGTH, + TRUE, TRUE); + GC_gcj_debug_kind = GC_gcj_kind; + } else { + GC_gcj_kind = GC_new_kind_inner( + (void **)GC_gcjobjfreelist, + (((word)(-(signed_word)MARK_DESCR_OFFSET + - GC_INDIR_PER_OBJ_BIAS)) + | GC_DS_PER_OBJECT), + FALSE, TRUE); + /* Set up object kind for objects that require mark proc call. */ + GC_gcj_debug_kind = GC_new_kind_inner(GC_new_free_list_inner(), + GC_MAKE_PROC(mp_index, + 1 /* allocated with debug info */), + FALSE, TRUE); + } + UNLOCK(); +# undef ignore_gcj_info +} + +#define GENERAL_MALLOC_INNER(lb,k) \ + GC_clear_stack(GC_generic_malloc_inner(lb, k)) + +#define GENERAL_MALLOC_INNER_IOP(lb,k) \ + GC_clear_stack(GC_generic_malloc_inner_ignore_off_page(lb, k)) + +/* We need a mechanism to release the lock and invoke finalizers. */ +/* We don't really have an opportunity to do this on a rarely executed */ +/* path on which the lock is not held. Thus we check at a */ +/* rarely executed point at which it is safe to release the lock. */ +/* We do this even where we could just call GC_INVOKE_FINALIZERS, */ +/* since it's probably cheaper and certainly more uniform. */ +/* TODO: Consider doing the same elsewhere? */ +static void maybe_finalize(void) +{ + static word last_finalized_no = 0; + DCL_LOCK_STATE; + + if (GC_gc_no == last_finalized_no || + !EXPECT(GC_is_initialized, TRUE)) return; + UNLOCK(); + GC_INVOKE_FINALIZERS(); + LOCK(); + last_finalized_no = GC_gc_no; +} + +/* 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.*/ +#ifdef THREAD_LOCAL_ALLOC + GC_INNER void * GC_core_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr) +#else + GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr) +#endif +{ + ptr_t op; + DCL_LOCK_STATE; + + GC_DBG_COLLECT_AT_MALLOC(lb); + if(SMALL_OBJ(lb)) { + word lg; + + LOCK(); + lg = GC_size_map[lb]; + op = GC_gcjobjfreelist[lg]; + if(EXPECT(0 == op, FALSE)) { + maybe_finalize(); + op = (ptr_t)GENERAL_MALLOC_INNER((word)lb, GC_gcj_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return((*oom_fn)(lb)); + } + } else { + GC_gcjobjfreelist[lg] = (ptr_t)obj_link(op); + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + } + GC_ASSERT(((void **)op)[1] == 0); + } else { + LOCK(); + maybe_finalize(); + op = (ptr_t)GENERAL_MALLOC_INNER((word)lb, GC_gcj_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return((*oom_fn)(lb)); + } + } + *(void **)op = ptr_to_struct_containing_descr; + UNLOCK(); + GC_dirty(op); + REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); + return (void *)op; +} + +/* Similar to GC_gcj_malloc, but add debug info. This is allocated */ +/* with GC_gcj_debug_kind. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr, GC_EXTRA_PARAMS) +{ + void * result; + DCL_LOCK_STATE; + + /* We're careful to avoid extra calls, which could */ + /* confuse the backtrace. */ + LOCK(); + maybe_finalize(); + result = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), + GC_gcj_debug_kind); + if (result == 0) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + 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; + if (!GC_debugging_started) { + GC_start_debugging_inner(); + } + ADD_CALL_CHAIN(result, ra); + result = GC_store_debug_info_inner(result, (word)lb, s, i); + UNLOCK(); + GC_dirty(result); + REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); + return result; +} + +/* There is no THREAD_LOCAL_ALLOC for GC_gcj_malloc_ignore_off_page(). */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc_ignore_off_page(size_t lb, + void * ptr_to_struct_containing_descr) +{ + ptr_t op; + DCL_LOCK_STATE; + + GC_DBG_COLLECT_AT_MALLOC(lb); + if(SMALL_OBJ(lb)) { + word lg; + + LOCK(); + lg = GC_size_map[lb]; + op = GC_gcjobjfreelist[lg]; + if (EXPECT(0 == op, FALSE)) { + maybe_finalize(); + op = (ptr_t)GENERAL_MALLOC_INNER_IOP(lb, GC_gcj_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return((*oom_fn)(lb)); + } + } else { + GC_gcjobjfreelist[lg] = (ptr_t)obj_link(op); + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + } + } else { + LOCK(); + maybe_finalize(); + op = (ptr_t)GENERAL_MALLOC_INNER_IOP(lb, GC_gcj_kind); + if (0 == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return((*oom_fn)(lb)); + } + } + *(void **)op = ptr_to_struct_containing_descr; + UNLOCK(); + GC_dirty(op); + REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); + return (void *)op; +} + +#endif /* GC_GCJ_SUPPORT */ diff --git a/bdwgc/headers.c b/bdwgc/headers.c new file mode 100644 index 000000000..38ef23827 --- /dev/null +++ b/bdwgc/headers.c @@ -0,0 +1,422 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * 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. + */ + +#include "private/gc_priv.h" + +/* + * This implements: + * 1. allocation of heap block headers + * 2. A map from addresses to heap block addresses to heap block headers + * + * Access speed is crucial. We implement an index structure based on a 2 + * level tree. + */ + +/* Non-macro version of header location routine */ +GC_INNER hdr * GC_find_header(ptr_t h) +{ +# ifdef HASH_TL + hdr * result; + GET_HDR(h, result); + return(result); +# else + return(HDR_INNER(h)); +# endif +} + +/* Handle a header cache miss. Returns a pointer to the */ +/* header corresponding to p, if p can possibly be a valid */ +/* object pointer, and 0 otherwise. */ +/* GUARANTEED to return 0 for a pointer past the first page */ +/* of an object unless both GC_all_interior_pointers is set */ +/* and p is in fact a valid object pointer. */ +/* Never returns a pointer to a free hblk. */ +GC_INNER hdr * +#ifdef PRINT_BLACK_LIST + GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, ptr_t source) +#else + GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce) +#endif +{ + hdr *hhdr; + HC_MISS(); + GET_HDR(p, hhdr); + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + if (GC_all_interior_pointers) { + if (hhdr != 0) { + ptr_t current = p; + + current = (ptr_t)HBLKPTR(current); + do { + current = current - HBLKSIZE*(word)hhdr; + hhdr = HDR(current); + } while(IS_FORWARDING_ADDR_OR_NIL(hhdr)); + /* current points to near the start of the large object */ + if (hhdr -> hb_flags & IGNORE_OFF_PAGE) + return 0; + if (HBLK_IS_FREE(hhdr) + || p - current >= (ptrdiff_t)(hhdr->hb_sz)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + /* Pointer past the end of the block */ + return 0; + } + } else { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + /* And return zero: */ + } + GC_ASSERT(hhdr == 0 || !HBLK_IS_FREE(hhdr)); + return hhdr; + /* Pointers past the first page are probably too rare */ + /* to add them to the cache. We don't. */ + /* And correctness relies on the fact that we don't. */ + } else { + if (hhdr == 0) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + } + return 0; + } + } else { + if (HBLK_IS_FREE(hhdr)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + return 0; + } else { + hce -> block_addr = (word)(p) >> LOG_HBLKSIZE; + hce -> hce_hdr = hhdr; + return hhdr; + } + } +} + +/* Routines to dynamically allocate collector data structures that will */ +/* never be freed. */ + +GC_INNER ptr_t GC_scratch_alloc(size_t bytes) +{ + ptr_t result = GC_scratch_free_ptr; + size_t bytes_to_get; + + bytes = ROUNDUP_GRANULE_SIZE(bytes); + for (;;) { + GC_ASSERT((word)GC_scratch_end_ptr >= (word)result); + if (bytes <= (word)GC_scratch_end_ptr - (word)result) { + /* Unallocated space of scratch buffer has enough size. */ + GC_scratch_free_ptr = result + bytes; + return result; + } + + GC_ASSERT(GC_page_size != 0); + if (bytes >= MINHINCR * HBLKSIZE) { + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes); + result = (ptr_t)GET_MEM(bytes_to_get); + if (result != NULL) { + GC_add_to_our_memory(result, bytes_to_get); + /* No update of scratch free area pointer; */ + /* get memory directly. */ +# ifdef USE_SCRATCH_LAST_END_PTR + /* Update end point of last obtained area (needed only */ + /* by GC_register_dynamic_libraries for some targets). */ + GC_scratch_last_end_ptr = result + bytes; +# endif + } + return result; + } + + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(MINHINCR * HBLKSIZE); + /* round up for safety */ + result = (ptr_t)GET_MEM(bytes_to_get); + if (EXPECT(NULL == result, FALSE)) { + WARN("Out of memory - trying to allocate requested amount" + " (%" WARN_PRIuPTR " bytes)...\n", bytes); + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes); + result = (ptr_t)GET_MEM(bytes_to_get); + if (result != NULL) { + GC_add_to_our_memory(result, bytes_to_get); +# ifdef USE_SCRATCH_LAST_END_PTR + GC_scratch_last_end_ptr = result + bytes; +# endif + } + return result; + } + + GC_add_to_our_memory(result, bytes_to_get); + /* TODO: some amount of unallocated space may remain unused forever */ + /* Update scratch area pointers and retry. */ + GC_scratch_free_ptr = result; + GC_scratch_end_ptr = GC_scratch_free_ptr + bytes_to_get; +# ifdef USE_SCRATCH_LAST_END_PTR + GC_scratch_last_end_ptr = GC_scratch_end_ptr; +# endif + } +} + +/* Return an uninitialized header */ +static hdr * alloc_hdr(void) +{ + hdr * result; + + if (NULL == GC_hdr_free_list) { + result = (hdr *)GC_scratch_alloc(sizeof(hdr)); + } else { + result = GC_hdr_free_list; + GC_hdr_free_list = (hdr *) result -> hb_next; + } + return(result); +} + +GC_INLINE void free_hdr(hdr * hhdr) +{ + hhdr -> hb_next = (struct hblk *) GC_hdr_free_list; + GC_hdr_free_list = hhdr; +} + +#ifdef COUNT_HDR_CACHE_HITS + /* Used for debugging/profiling (the symbols are externally visible). */ + word GC_hdr_cache_hits = 0; + word GC_hdr_cache_misses = 0; +#endif + +GC_INNER void GC_init_headers(void) +{ + unsigned i; + + GC_ASSERT(NULL == GC_all_nils); + GC_all_nils = (bottom_index *)GC_scratch_alloc(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; + } +} + +/* Make sure that there is a bottom level index block for address addr. */ +/* Return FALSE on failure. */ +static GC_bool get_index(word addr) +{ + word hi = (word)(addr) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + bottom_index * r; + bottom_index * p; + bottom_index ** prev; + bottom_index *pi; /* old_p */ + word i; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef HASH_TL + i = TL_HASH(hi); + + pi = p = GC_top_index[i]; + while(p != GC_all_nils) { + if (p -> key == hi) return(TRUE); + p = p -> hash_link; + } +# else + if (GC_top_index[hi] != GC_all_nils) + return TRUE; + i = hi; +# endif + r = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index)); + if (EXPECT(NULL == r, FALSE)) + return FALSE; + BZERO(r, sizeof(bottom_index)); + r -> key = hi; +# ifdef HASH_TL + r -> hash_link = pi; +# endif + + /* Add it to the list of bottom indices */ + prev = &GC_all_bottom_indices; /* pointer to p */ + pi = 0; /* bottom_index preceding p */ + while ((p = *prev) != 0 && p -> key < hi) { + pi = p; + prev = &(p -> asc_link); + } + r -> desc_link = pi; + if (0 == p) { + GC_all_bottom_indices_end = r; + } else { + p -> desc_link = r; + } + r -> asc_link = p; + *prev = r; + + GC_top_index[i] = r; + return(TRUE); +} + +/* Install a header for block h. */ +/* The header is uninitialized. */ +/* Returns the header or 0 on failure. */ +GC_INNER struct hblkhdr * GC_install_header(struct hblk *h) +{ + hdr * result; + + if (!get_index((word) h)) return(0); + result = alloc_hdr(); + if (result) { + SET_HDR(h, result); +# ifdef USE_MUNMAP + result -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + } + return(result); +} + +/* Set up forwarding counts for block h of size sz */ +GC_INNER GC_bool GC_install_counts(struct hblk *h, size_t sz/* bytes */) +{ + struct hblk * hbp; + + for (hbp = h; (word)hbp < (word)h + sz; hbp += BOTTOM_SZ) { + if (!get_index((word)hbp)) + return FALSE; + if ((word)hbp > GC_WORD_MAX - (word)BOTTOM_SZ * HBLKSIZE) + break; /* overflow of hbp+=BOTTOM_SZ is expected */ + } + if (!get_index((word)h + sz - 1)) + return FALSE; + for (hbp = h + 1; (word)hbp < (word)h + sz; hbp += 1) { + word i = HBLK_PTR_DIFF(hbp, h); + + SET_HDR(hbp, (hdr *)(i > MAX_JUMP? MAX_JUMP : i)); + } + return TRUE; +} + +/* Remove the header for block h */ +GC_INNER void GC_remove_header(struct hblk *h) +{ + hdr **ha; + GET_HDR_ADDR(h, ha); + free_hdr(*ha); + *ha = 0; +} + +/* Remove forwarding counts for h */ +GC_INNER void GC_remove_counts(struct hblk *h, size_t sz/* bytes */) +{ + struct hblk * hbp; + + if (sz <= HBLKSIZE) return; + if (HDR(h+1) == 0) { +# ifdef GC_ASSERTIONS + for (hbp = h+2; (word)hbp < (word)h + sz; hbp++) + GC_ASSERT(HDR(hbp) == 0); +# endif + return; + } + + for (hbp = h+1; (word)hbp < (word)h + sz; hbp += 1) { + SET_HDR(hbp, 0); + } +} + +/* Apply fn to all allocated blocks. It is the caller responsibility */ +/* to avoid data race during the function execution (e.g. by holding */ +/* the allocation lock). */ +void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data), + word client_data) +{ + signed_word j; + bottom_index * index_p; + + for (index_p = GC_all_bottom_indices; index_p != 0; + index_p = index_p -> asc_link) { + for (j = BOTTOM_SZ-1; j >= 0;) { + if (!IS_FORWARDING_ADDR_OR_NIL(index_p->index[j])) { + if (!HBLK_IS_FREE(index_p->index[j])) { + (*fn)(((struct hblk *) + (((index_p->key << LOG_BOTTOM_SZ) + (word)j) + << LOG_HBLKSIZE)), + client_data); + } + j--; + } else if (index_p->index[j] == 0) { + j--; + } else { + j -= (signed_word)(index_p->index[j]); + } + } + } +} + +GC_INNER struct hblk * GC_next_block(struct hblk *h, GC_bool allow_free) +{ + REGISTER bottom_index * bi; + REGISTER word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1); + + GC_ASSERT(I_HOLD_LOCK()); + GET_BI(h, bi); + if (bi == GC_all_nils) { + REGISTER word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + + bi = GC_all_bottom_indices; + while (bi != 0 && bi -> key < hi) bi = bi -> asc_link; + j = 0; + } + + while (bi != 0) { + while (j < BOTTOM_SZ) { + hdr * hhdr = bi -> index[j]; + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + j++; + } else { + if (allow_free || !HBLK_IS_FREE(hhdr)) { + return ((struct hblk *) + (((bi -> key << LOG_BOTTOM_SZ) + j) + << LOG_HBLKSIZE)); + } else { + j += divHBLKSZ(hhdr -> hb_sz); + } + } + } + j = 0; + bi = bi -> asc_link; + } + return(0); +} + +GC_INNER struct hblk * GC_prev_block(struct hblk *h) +{ + bottom_index * bi; + signed_word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1); + + GC_ASSERT(I_HOLD_LOCK()); + GET_BI(h, bi); + if (bi == GC_all_nils) { + word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + bi = GC_all_bottom_indices_end; + while (bi != 0 && bi -> key > hi) bi = bi -> desc_link; + j = BOTTOM_SZ - 1; + } + while(bi != 0) { + while (j >= 0) { + hdr * hhdr = bi -> index[j]; + if (0 == hhdr) { + --j; + } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + j -= (signed_word)hhdr; + } else { + return((struct hblk *) + (((bi -> key << LOG_BOTTOM_SZ) + j) + << LOG_HBLKSIZE)); + } + } + j = BOTTOM_SZ - 1; + bi = bi -> desc_link; + } + return(0); +} diff --git a/bdwgc/ia64_save_regs_in_stack.s b/bdwgc/ia64_save_regs_in_stack.s new file mode 100644 index 000000000..2b81edfaa --- /dev/null +++ b/bdwgc/ia64_save_regs_in_stack.s @@ -0,0 +1,11 @@ + .text + .align 16 + .global GC_save_regs_in_stack + .proc GC_save_regs_in_stack +GC_save_regs_in_stack: + .body + flushrs + ;; + mov r8=ar.bsp + br.ret.sptk.few rp + .endp GC_save_regs_in_stack diff --git a/bdwgc/include/cord.h b/bdwgc/include/cord.h new file mode 100644 index 000000000..e8a72b0db --- /dev/null +++ b/bdwgc/include/cord.h @@ -0,0 +1,378 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/* + * Cords are immutable character strings. A number of operations + * on long cords are much more efficient than their strings.h counterpart. + * In particular, concatenation takes constant time independent of the length + * of the arguments. (Cords are represented as trees, with internal + * nodes representing concatenation and leaves consisting of either C + * strings or a functional description of the string.) + * + * The following are reasonable applications of cords. They would perform + * unacceptably if C strings were used: + * - A compiler that produces assembly language output by repeatedly + * concatenating instructions onto a cord representing the output file. + * - A text editor that converts the input file to a cord, and then + * performs editing operations by producing a new cord representing + * the file after each character change (and keeping the old ones in an + * edit history). + * + * For optimal performance, cords should be built by + * concatenating short sections. + * This interface is designed for maximum compatibility with C strings. + * ASCII NUL characters may be embedded in cords using CORD_from_fn. + * This is handled correctly, but CORD_to_char_star will produce a string + * with embedded NULs when given such a cord. + * + * This interface is fairly big, largely for performance reasons. + * The most basic constants and functions: + * + * CORD - the type of a cord; + * CORD_EMPTY - empty cord; + * CORD_len(cord) - length of a cord; + * CORD_cat(cord1,cord2) - concatenation of two cords; + * CORD_substr(cord, start, len) - substring (or subcord); + * CORD_pos i; CORD_FOR(i, cord) { ... CORD_pos_fetch(i) ... } - + * examine each character in a cord (CORD_pos_fetch(i) is the char); + * CORD_fetch(int i) - Retrieve i'th character (slowly); + * CORD_cmp(cord1, cord2) - compare two cords; + * CORD_from_file(FILE * f) - turn a read-only file into a cord; + * CORD_to_char_star(cord) - convert to C string + * (non-NULL C constant strings are cords); + * CORD_printf (etc.) - cord version of printf (use %r for cords). + */ + +#ifndef CORD_H +#define CORD_H + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#if defined(GC_DLL) && !defined(CORD_NOT_DLL) + /* Same as for GC_API in gc_config_macros.h. */ +# ifdef CORD_BUILD +# if defined(__MINGW32__) || defined(__CEGCC__) +# define CORD_API __declspec(dllexport) +# elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \ + || defined(__CYGWIN__) || defined(__WATCOMC__) +# define CORD_API extern __declspec(dllexport) +# elif defined(__GNUC__) && !defined(GC_NO_VISIBILITY) \ + && (__GNUC__ >= 4 || defined(GC_VISIBILITY_HIDDEN_SET)) + /* Only matters if used in conjunction with -fvisibility=hidden option. */ +# define CORD_API extern __attribute__((__visibility__("default"))) +# endif +# else +# if defined(__MINGW32__) || defined(__CEGCC__) || defined(_MSC_VER) \ + || defined(__DMC__) || defined(__BORLANDC__) || defined(__CYGWIN__) +# define CORD_API __declspec(dllimport) +# elif defined(__WATCOMC__) +# define CORD_API extern __declspec(dllimport) +# endif +# endif /* !CORD_BUILD */ +#endif /* GC_DLL */ + +#ifndef CORD_API +# define CORD_API extern +#endif + +/* Cords have type const char *. This is cheating quite a bit, and not */ +/* 100% portable. But it means that nonempty character string */ +/* constants may be used as cords directly, provided the string is */ +/* never modified in place. The empty cord is represented by, and */ +/* can be written as, 0. */ + +typedef const char * CORD; + +/* An empty cord is always represented as nil. */ +#define CORD_EMPTY 0 + +/* Is a nonempty cord represented as a C string? */ +#define CORD_IS_STRING(s) (*(s) != '\0') + +/* Concatenate two cords. If the arguments are C strings, they may */ +/* not be subsequently altered. */ +CORD_API CORD CORD_cat(CORD x, CORD y); + +/* Concatenate a cord and a C string with known length. Except for the */ +/* empty string case, this is a special case of CORD_cat. Since the */ +/* length is known, it can be faster. */ +/* The string y is shared with the resulting CORD. Hence it should */ +/* not be altered by the caller. */ +CORD_API CORD CORD_cat_char_star(CORD x, const char * y, size_t leny); + +/* Compute the length of a cord. */ +CORD_API size_t CORD_len(CORD x); + +/* Cords may be represented by functions defining the ith character. */ +typedef char (* CORD_fn)(size_t i, void * client_data); + +/* Turn a functional description into a cord. */ +CORD_API CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len); + +/* Return the substring (subcord really) of x with length at most n, */ +/* starting at position i. (The initial character has position 0.) */ +CORD_API CORD CORD_substr(CORD x, size_t i, size_t n); + +/* Return the argument, but rebalanced to allow more efficient */ +/* character retrieval, substring operations, and comparisons. */ +/* This is useful only for cords that were built using repeated */ +/* concatenation. Guarantees log time access to the result, unless */ +/* x was obtained through a large number of repeated substring ops */ +/* or the embedded functional descriptions take longer to evaluate. */ +/* May reallocate significant parts of the cord. The argument is not */ +/* modified; only the result is balanced. */ +CORD_API CORD CORD_balance(CORD x); + +/* The following traverse a cord by applying a function to each */ +/* character. This is occasionally appropriate, especially where */ +/* speed is crucial. But, since C doesn't have nested functions, */ +/* clients of this sort of traversal are clumsy to write. Consider */ +/* the functions that operate on cord positions instead. */ + +/* Function to iteratively apply to individual characters in cord. */ +typedef int (* CORD_iter_fn)(char c, void * client_data); + +/* Function to apply to substrings of a cord. Each substring is a */ +/* a C character string, not a general cord. */ +typedef int (* CORD_batched_iter_fn)(const char * s, void * client_data); +#define CORD_NO_FN ((CORD_batched_iter_fn)0) + +/* Apply f1 to each character in the cord, in ascending order, */ +/* starting at position i. If f2 is not CORD_NO_FN, then */ +/* multiple calls to f1 may be replaced by */ +/* a single call to f2. The parameter f2 is provided only to allow */ +/* some optimization by the client. This terminates when the right */ +/* end of this string is reached, or when f1 or f2 return != 0. In the */ +/* latter case CORD_iter returns != 0. Otherwise it returns 0. */ +/* The specified value of i must be < CORD_len(x). */ +CORD_API int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, + CORD_batched_iter_fn f2, void * client_data); + +/* A simpler version that starts at 0, and without f2. */ +CORD_API int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data); +#define CORD_iter(x, f1, cd) CORD_iter5(x, 0, f1, CORD_NO_FN, cd) + +/* Similar to CORD_iter5, but end-to-beginning. No provisions for */ +/* CORD_batched_iter_fn. */ +CORD_API int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data); + +/* A simpler version that starts at the end. */ +CORD_API int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +/* Functions that operate on cord positions. The easy way to traverse */ +/* cords. A cord position is logically a pair consisting of a cord */ +/* and an index into that cord. But it is much faster to retrieve a */ +/* character based on a position than on an index. Unfortunately, */ +/* positions are big (order of a few 100 bytes), so allocate them with */ +/* caution. */ +/* Things in cord_pos.h should be treated as opaque, except as */ +/* described below. Also, note that CORD_pos_fetch, CORD_next and */ +/* CORD_prev have both macro and function definitions. The former */ +/* may evaluate their argument more than once. */ +#include "cord_pos.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* + Visible definitions from above: + + typedef CORD_pos[1]; + + * Extract the cord from a position: + CORD CORD_pos_to_cord(CORD_pos p); + + * Extract the current index from a position: + size_t CORD_pos_to_index(CORD_pos p); + + * Fetch the character located at the given position: + char CORD_pos_fetch(CORD_pos p); + + * Initialize the position to refer to the given cord and index. + * Note that this is the most expensive function on positions: + void CORD_set_pos(CORD_pos p, CORD x, size_t i); + + * Advance the position to the next character. + * P must be initialized and valid. + * Invalidates p if past end: + void CORD_next(CORD_pos p); + + * Move the position to the preceding character. + * P must be initialized and valid. + * Invalidates p if past beginning: + void CORD_prev(CORD_pos p); + + * Is the position valid, i.e. inside the cord? + int CORD_pos_valid(CORD_pos p); +*/ +#define CORD_FOR(pos, cord) \ + for (CORD_set_pos(pos, cord, 0); CORD_pos_valid(pos); CORD_next(pos)) + + +/* An out of memory handler to call. May be supplied by client. */ +/* Must not return. */ +extern void (* CORD_oom_fn)(void); + +/* Dump the representation of x to stdout in an implementation defined */ +/* manner. Intended for debugging only. */ +CORD_API void CORD_dump(CORD x); + +/* The following could easily be implemented by the client. They are */ +/* provided in cordxtra.c for convenience. */ + +/* Concatenate a character to the end of a cord. */ +CORD_API CORD CORD_cat_char(CORD x, char c); + +/* Concatenate n cords. */ +CORD_API CORD CORD_catn(int n, /* CORD */ ...); + +/* Return the character in CORD_substr(x, i, 1). */ +CORD_API char CORD_fetch(CORD x, size_t i); + +/* Return < 0, 0, or > 0, depending on whether x < y, x = y, x > y. */ +CORD_API int CORD_cmp(CORD x, CORD y); + +/* A generalization that takes both starting positions for the */ +/* comparison, and a limit on the number of characters to be compared. */ +CORD_API int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, + size_t len); + +/* Find the first occurrence of s in x at position start or later. */ +/* Return the position of the first character of s in x, or */ +/* CORD_NOT_FOUND if there is none. */ +CORD_API size_t CORD_str(CORD x, size_t start, CORD s); + +/* Return a cord consisting of i copies of (possibly NUL) c. Dangerous */ +/* in conjunction with CORD_to_char_star. */ +/* The resulting representation takes constant space, independent of i. */ +CORD_API CORD CORD_chars(char c, size_t i); +#define CORD_nul(i) CORD_chars('\0', (i)) + +/* Turn a file into cord. The file must be seekable. Its contents */ +/* must remain constant. The file may be accessed as an immediate */ +/* result of this call and/or as a result of subsequent accesses to */ +/* the cord. Short files are likely to be immediately read, but */ +/* long files are likely to be read on demand, possibly relying on */ +/* stdio for buffering. */ +/* We must have exclusive access to the descriptor f, i.e. we may */ +/* read it at any time, and expect the file pointer to be */ +/* where we left it. Normally this should be invoked as */ +/* CORD_from_file(fopen(...)). */ +/* CORD_from_file arranges to close the file descriptor when it is no */ +/* longer needed (e.g. when the result becomes inaccessible). */ +/* The file f must be such that ftell reflects the actual character */ +/* position in the file, i.e. the number of characters that can be */ +/* or were read with fread. On UNIX systems this is always true. */ +/* On Windows systems, f must be opened in binary mode. */ +CORD_API CORD CORD_from_file(FILE * f); + +/* Equivalent to the above, except that the entire file will be read */ +/* and the file pointer will be closed immediately. */ +/* The binary mode restriction from above does not apply. */ +CORD_API CORD CORD_from_file_eager(FILE * f); + +/* Equivalent to the above, except that the file will be read on */ +/* demand. The binary mode restriction applies. */ +CORD_API CORD CORD_from_file_lazy(FILE * f); + +/* Turn a cord into a C string. The result shares no structure with */ +/* x, and is thus modifiable. */ +CORD_API char * CORD_to_char_star(CORD x); + +/* Turn a C string into a CORD. The C string is copied, and so may */ +/* subsequently be modified. */ +CORD_API CORD CORD_from_char_star(const char *s); + +/* Identical to the above, but the result may share structure with */ +/* the argument and is thus not modifiable. */ +CORD_API const char * CORD_to_const_char_star(CORD x); + +/* Write a cord to a file, starting at the current position. */ +/* No trailing NULs are newlines are added. */ +/* Returns EOF if a write error occurs, 1 otherwise. */ +CORD_API int CORD_put(CORD x, FILE * f); + +/* "Not found" result for the following two functions. */ +#define CORD_NOT_FOUND ((size_t)(-1)) + +/* A vague analog of strchr. Returns the position (an integer, not */ +/* a pointer) of the first occurrence of (char) c inside x at position */ +/* i or later. The value i must be < CORD_len(x). */ +CORD_API size_t CORD_chr(CORD x, size_t i, int c); + +/* A vague analog of strrchr. Returns index of the last occurrence */ +/* of (char) c inside x at position i or earlier. The value i */ +/* must be < CORD_len(x). */ +CORD_API size_t CORD_rchr(CORD x, size_t i, int c); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +/* The following are also not primitive, but are implemented in */ +/* cordprnt.c. They provide functionality similar to the ANSI C */ +/* functions with corresponding names, but with the following */ +/* additions and changes: */ +/* 1. A %r conversion specification specifies a CORD argument. Field */ +/* width, precision, etc. have the same semantics as for %s. */ +/* (Note that %c, %C, and %S were already taken.) */ +/* 2. The format string is represented as a CORD. */ +/* 3. CORD_sprintf and CORD_vsprintf assign the result through the 1st */ +/* argument. Unlike their ANSI C versions, there is no need to */ +/* guess the correct buffer size. */ +/* 4. Most of the conversions are implement through the native */ +/* vsprintf. Hence they are usually no faster, and */ +/* 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. */ +/* The format string is copied. */ +/* All functions return the number of characters generated or -1 on */ +/* error. This complies with the ANSI standard, but is inconsistent */ +/* with some older implementations of sprintf. */ + +/* The implementation of these is probably less portable than the rest */ +/* of this package. */ + +#ifndef CORD_NO_IO + +#include + +# ifdef __cplusplus + extern "C" { +# endif + +CORD_API int CORD_sprintf(CORD * out, CORD format, ...); +CORD_API int CORD_vsprintf(CORD * out, CORD format, va_list args); +CORD_API int CORD_fprintf(FILE * f, CORD format, ...); +CORD_API int CORD_vfprintf(FILE * f, CORD format, va_list args); +CORD_API int CORD_printf(CORD format, ...); +CORD_API int CORD_vprintf(CORD format, va_list args); + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +#endif /* CORD_NO_IO */ + +#endif /* CORD_H */ diff --git a/bdwgc/include/cord_pos.h b/bdwgc/include/cord_pos.h new file mode 100644 index 000000000..3459f2423 --- /dev/null +++ b/bdwgc/include/cord_pos.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/* This should never be included directly; included only from cord.h. */ +#if !defined(CORD_POSITION_H) && defined(CORD_H) +#define CORD_POSITION_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* The representation of CORD_position. This is private to the */ +/* implementation, but the size is known to clients. Also */ +/* the implementation of some exported macros relies on it. */ +/* Don't use anything defined here and not in cord.h. */ + +# define MAX_DEPTH 48 + /* The maximum depth of a balanced cord + 1. */ + /* We don't let cords get deeper than MAX_DEPTH. */ + +struct CORD_pe { + CORD pe_cord; + size_t pe_start_pos; +}; + +/* A structure describing an entry on the path from the root */ +/* to current position. */ +typedef struct CORD_Pos { + size_t cur_pos; + int path_len; +# define CORD_POS_INVALID (0x55555555) + /* path_len == INVALID <==> position invalid */ + const char *cur_leaf; /* Current leaf, if it is a string. */ + /* If the current leaf is a function, */ + /* then this may point to function_buf */ + /* containing the next few characters. */ + /* Always points to a valid string */ + /* containing the current character */ + /* unless cur_end is 0. */ + size_t cur_start; /* Start position of cur_leaf. */ + size_t cur_end; /* Ending position of cur_leaf; */ + /* 0 if cur_leaf is invalid. */ + struct CORD_pe path[MAX_DEPTH + 1]; + /* path[path_len] is the leaf corresponding to cur_pos */ + /* path[0].pe_cord is the cord we point to. */ +# define FUNCTION_BUF_SZ 8 + char function_buf[FUNCTION_BUF_SZ]; /* Space for next few chars */ + /* from function node. */ +} CORD_pos[1]; + +/* Extract the cord from a position: */ +CORD_API CORD CORD_pos_to_cord(CORD_pos p); + +/* Extract the current index from a position: */ +CORD_API size_t CORD_pos_to_index(CORD_pos p); + +/* Fetch the character located at the given position: */ +CORD_API char CORD_pos_fetch(CORD_pos p); + +/* Initialize the position to refer to the give cord and index. */ +/* Note that this is the most expensive function on positions: */ +CORD_API void CORD_set_pos(CORD_pos p, CORD x, size_t i); + +/* Advance the position to the next character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past end: */ +CORD_API void CORD_next(CORD_pos p); + +/* Move the position to the preceding character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past beginning: */ +CORD_API void CORD_prev(CORD_pos p); + +/* Is the position valid, i.e. inside the cord? */ +CORD_API int CORD_pos_valid(CORD_pos p); + +CORD_API char CORD__pos_fetch(CORD_pos); +CORD_API void CORD__next(CORD_pos); +CORD_API void CORD__prev(CORD_pos); + +#define CORD_pos_fetch(p) \ + (((p)[0].cur_end != 0)? \ + (p)[0].cur_leaf[(p)[0].cur_pos - (p)[0].cur_start] \ + : CORD__pos_fetch(p)) + +#define CORD_next(p) \ + (((p)[0].cur_pos + 1 < (p)[0].cur_end)? \ + (p)[0].cur_pos++ \ + : (CORD__next(p), 0)) + +#define CORD_prev(p) \ + (((p)[0].cur_end != 0 && (p)[0].cur_pos > (p)[0].cur_start)? \ + (p)[0].cur_pos-- \ + : (CORD__prev(p), 0)) + +#define CORD_pos_to_index(p) ((p)[0].cur_pos) + +#define CORD_pos_to_cord(p) ((p)[0].path[0].pe_cord) + +#define CORD_pos_valid(p) ((p)[0].path_len != CORD_POS_INVALID) + +/* Some grubby stuff for performance-critical friends: */ +#define CORD_pos_chars_left(p) ((long)((p)[0].cur_end)-(long)((p)[0].cur_pos)) + /* Number of characters in cache. <= 0 ==> none */ + +#define CORD_pos_advance(p,n) ((p)[0].cur_pos += (n) - 1, CORD_next(p)) + /* Advance position by n characters; */ + /* 0 < n < CORD_pos_chars_left(p). */ + +#define CORD_pos_cur_char_addr(p) \ + (p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start) + /* Address of the current character in cache. */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/bdwgc/include/ec.h b/bdwgc/include/ec.h new file mode 100644 index 000000000..54df5f885 --- /dev/null +++ b/bdwgc/include/ec.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#ifndef EC_H +#define EC_H + +# ifndef CORD_H +# include "cord.h" +# endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Extensible cords are strings that may be destructively appended to. */ +/* They allow fast construction of cords from characters that are */ +/* being read from a stream. */ +/* + * A client might look like: + * + * { + * CORD_ec x; + * CORD result; + * char c; + * FILE *f; + * + * ... + * CORD_ec_init(x); + * while (...) { + * c = getc(f); + * ... + * CORD_ec_append(x, c); + * } + * result = CORD_balance(CORD_ec_to_cord(x)); + * + * If a C string is desired as the final result, the call to CORD_balance + * may be replaced by a call to CORD_to_char_star. + */ + +# ifndef CORD_BUFSZ +# define CORD_BUFSZ 128 +# endif + +/* This structure represents the concatenation of ec_cord with */ +/* ec_buf[0 .. ec_bufptr-ec_buf-1]. */ +typedef struct CORD_ec_struct { + CORD ec_cord; + char * ec_bufptr; + char ec_buf[CORD_BUFSZ+1]; +} CORD_ec[1]; + +/* Flush the buffer part of the extended cord into ec_cord. */ +/* Note that this is almost the only real function, and it is */ +/* implemented in 6 lines in cordxtra.c */ +void CORD_ec_flush_buf(CORD_ec x); + +/* Convert an extensible cord to a cord. */ +# 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, (void)((x)[0].ec_bufptr = (x)[0].ec_buf)) + +/* Append a character to an extensible cord. */ +#define CORD_ec_append(x, c) \ + ((void)((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. */ +void CORD_ec_append_cord(CORD_ec x, CORD s); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* EC_H */ diff --git a/bdwgc/include/extra/gc.h b/bdwgc/include/extra/gc.h new file mode 100644 index 000000000..55ae4c6c1 --- /dev/null +++ b/bdwgc/include/extra/gc.h @@ -0,0 +1,2 @@ +/* This file is installed for backward compatibility. */ +#include diff --git a/bdwgc/include/extra/gc_cpp.h b/bdwgc/include/extra/gc_cpp.h new file mode 100644 index 000000000..36669f9c8 --- /dev/null +++ b/bdwgc/include/extra/gc_cpp.h @@ -0,0 +1,2 @@ +/* This file is installed for backward compatibility. */ +#include diff --git a/bdwgc/include/gc.h b/bdwgc/include/gc.h new file mode 100644 index 000000000..a1506f2f9 --- /dev/null +++ b/bdwgc/include/gc.h @@ -0,0 +1,2195 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright 1999 by Hewlett-Packard Company. All rights reserved. + * Copyright (C) 2007 Free Software Foundation, Inc + * Copyright (c) 2000-2011 by Hewlett-Packard Development Company. + * Copyright (c) 2009-2020 Ivan Maidanski + * + * 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. + */ + +/* + * Note that this defines a large number of tuning hooks, which can + * safely be ignored in nearly all cases. For normal use it suffices + * to call only GC_MALLOC and perhaps GC_REALLOC. + * For better performance, also look at GC_MALLOC_ATOMIC, and + * GC_enable_incremental. If you need an action to be performed + * immediately before an object is collected, look at GC_register_finalizer. + * Everything else is best ignored unless you encounter performance + * problems. + */ + +#ifndef GC_H +#define GC_H + +/* Help debug mixed up preprocessor symbols. */ +#if (defined(WIN64) && !defined(_WIN64)) && defined(_MSC_VER) +#pragma message("Warning: Expecting _WIN64 for x64 targets! Notice the leading underscore!") +#endif + +#include "gc_version.h" + /* Define version numbers here to allow test on build machine */ + /* for cross-builds. Note that this defines the header */ + /* version number, which may or may not match that of the */ + /* dynamic library. GC_get_version() can be used to obtain */ + /* the latter. */ + +#include "gc_config_macros.h" + +#ifdef __cplusplus + 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 */ +/* even semi-portably. The following is probably no better/worse */ +/* than almost anything else. */ +/* The ANSI standard suggests that size_t and ptrdiff_t might be */ +/* better choices. But those had incorrect definitions on some older */ +/* systems. Notably "typedef int size_t" is WRONG. */ +#ifdef _WIN64 +# if defined(__int64) && !defined(CPPCHECK) + typedef unsigned __int64 GC_word; + typedef __int64 GC_signed_word; +# else + typedef unsigned long long GC_word; + typedef long long GC_signed_word; +# endif +#else + typedef unsigned long GC_word; + typedef long GC_signed_word; +#endif + +/* Get the GC library version. The returned value is a constant in the */ +/* form: ((version_major<<16) | (version_minor<<8) | version_micro). */ +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_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 */ + /* it requires GC_call_with_alloc_lock() to */ + /* avoid data races on multiprocessors. */ + +#ifdef GC_THREADS + /* GC is parallelized for performance on multiprocessors. Set to */ + /* a non-zero value when client calls GC_start_mark_threads() */ + /* directly or starts the first non-main thread, provided the */ + /* collector is built with PARALLEL_MARK defined, and either */ + /* GC_MARKERS (or GC_NPROCS) environment variable is set to a value */ + /* bigger than 1, or multiple cores (processors) are available, or */ + /* the client calls GC_set_markers_count() before GC initialization. */ + /* After setting, GC_parallel value is equal to the number of marker */ + /* threads minus one (i.e. the number of existing parallel marker */ + /* threads excluding the initiating one). */ + GC_API GC_ATTR_DEPRECATED int GC_parallel; + + /* Return value of GC_parallel. Does not acquire the GC lock. */ + GC_API int GC_CALL GC_get_parallel(void); + + /* Set the number of marker threads (including the initiating one) */ + /* to the desired value at start-up. Zero value means the collector */ + /* is to decide. If the correct non-zero value is passed, then later */ + /* GC_parallel will be set to the value minus one. Has no effect if */ + /* called after GC initialization. Does not itself cause creation of */ + /* the marker threads. Does not use any synchronization. */ + GC_API void GC_CALL GC_set_markers_count(unsigned); +#endif + + +/* Public R/W variables */ +/* 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_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 */ + /* returns NULL. */ + /* If it returns, it must return 0 or a valid */ + /* pointer to a previously allocated heap */ + /* 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_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_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. */ +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc); +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). */ + +typedef enum { + GC_EVENT_START /* COLLECTION */, + GC_EVENT_MARK_START, + GC_EVENT_MARK_END, + GC_EVENT_RECLAIM_START, + GC_EVENT_RECLAIM_END, + GC_EVENT_END /* COLLECTION */, + GC_EVENT_PRE_STOP_WORLD /* STOPWORLD_BEGIN */, + GC_EVENT_POST_STOP_WORLD /* STOPWORLD_END */, + GC_EVENT_PRE_START_WORLD /* STARTWORLD_BEGIN */, + GC_EVENT_POST_START_WORLD /* STARTWORLD_END */, + GC_EVENT_THREAD_SUSPENDED, + GC_EVENT_THREAD_UNSUSPENDED +} GC_EventType; + +typedef void (GC_CALLBACK * GC_on_collection_event_proc)(GC_EventType); + /* Invoked to indicate progress through the */ + /* collection process. Not used for thread */ + /* suspend/resume notifications. Called with */ + /* the GC lock held (or, even, the world */ + /* stopped). May be 0 (means no notifier). */ +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc); +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void); + /* Both the supplied setter and the getter */ + /* acquire the GC lock (to avoid data races). */ + +#if defined(GC_THREADS) || (defined(GC_BUILD) && defined(NN_PLATFORM_CTR)) + typedef void (GC_CALLBACK * GC_on_thread_event_proc)(GC_EventType, + void * /* thread_id */); + /* Invoked when a thread is suspended or */ + /* resumed during collection. Called with the */ + /* GC lock held (and the world stopped */ + /* partially). May be 0 (means no notifier). */ + GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc); + GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void); + /* Both the supplied setter and the getter */ + /* acquire the GC lock (to avoid data races). */ +#endif + +GC_API GC_ATTR_DEPRECATED int GC_find_leak; + /* Set to true to turn on the leak-finding mode */ + /* (do not actually garbage collect, but simply */ + /* report inaccessible memory that was not */ + /* deallocated with GC_FREE). Initial value */ + /* is determined by FIND_LEAK macro. */ + /* The value should not typically be modified */ + /* after GC initialization (and, thus, it does */ + /* not use or need synchronization). */ +GC_API void GC_CALL GC_set_find_leak(int); +GC_API int GC_CALL GC_get_find_leak(void); + +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 */ + /* case of calling it after the GC is */ + /* initialized, the setter acquires the GC lock */ + /* (to avoid data races). The initial value */ + /* depends on whether the GC is built with */ + /* ALL_INTERIOR_POINTERS macro defined or not. */ + /* Unless DONT_ADD_BYTE_AT_END is defined, this */ + /* also affects whether sizes are increased by */ + /* at least a byte to allow "off the end" */ + /* pointer recognition. Must be only 0 or 1. */ +GC_API void GC_CALL GC_set_all_interior_pointers(int); +GC_API int GC_CALL GC_get_all_interior_pointers(void); + +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 */ + /* the FINALIZE_ON_DEMAND macro is defined */ + /* when the collector is built. */ + /* The setter and getter are unsynchronized. */ +GC_API void GC_CALL GC_set_finalize_on_demand(int); +GC_API int GC_CALL GC_get_finalize_on_demand(void); + +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- */ + /* ordered finalization. Default value is */ + /* determined by JAVA_FINALIZATION macro. */ + /* Enables GC_register_finalizer_unreachable to */ + /* work correctly. */ + /* The setter and getter are unsynchronized. */ +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_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 */ + /* GC_finalize_on_demand is set. */ + /* Typically this will notify a finalization */ + /* thread, which will call GC_invoke_finalizers */ + /* in response. May be 0 (means no notifier). */ + /* Both the supplied setter and the getter */ + /* acquire the GC lock (to avoid data races). */ +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 +# ifndef GC_DONT_GC + GC_ATTR_DEPRECATED +# endif + int GC_dont_gc; /* != 0 ==> Do not collect. This overrides */ + /* explicit GC_gcollect() calls as well. */ + /* 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. To check whether */ + /* GC is disabled, GC_is_disabled() is */ + /* preferred for new code. */ + +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 GC_ATTR_DEPRECATED int GC_use_entire_heap; + /* Causes the non-incremental collector to use the */ + /* entire heap before collecting. This sometimes */ + /* results in more large block fragmentation, since */ + /* very large blocks will tend to get broken up */ + /* during each GC cycle. It is likely to result in a */ + /* larger working set, but lower collection */ + /* frequencies, and hence fewer instructions executed */ + /* in the collector. */ + +GC_API GC_ATTR_DEPRECATED int GC_full_freq; + /* Number of partial collections between */ + /* full collections. Matters only if */ + /* GC_is_incremental_mode(). */ + /* Full collections are also triggered if */ + /* the collector detects a substantial */ + /* increase in the number of in-use heap */ + /* blocks. Values in the tens are now */ + /* perfectly reasonable, unlike for */ + /* earlier GC versions. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ +GC_API void GC_CALL GC_set_full_freq(int); +GC_API int GC_CALL GC_get_full_freq(void); + +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 */ + /* GC_malloc_uncollectable and GC_free. */ + /* Wizards only. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ +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 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. */ + /* (In some environments like Microsoft Windows */ + /* and Apple's Darwin, this may also prevent */ + /* registration of the main data segment as part */ + /* of the root set.) */ + /* The setter and getter are unsynchronized. */ +GC_API void GC_CALL GC_set_no_dls(int); +GC_API int GC_CALL GC_get_no_dls(void); + +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 */ + /* of traced bytes, plus the number of untraced */ + /* bytes (bytes in "atomic" objects), plus */ + /* a rough estimate of the root set size. */ + /* N approximates GC tracing work per GC. */ + /* The initial value is GC_FREE_SPACE_DIVISOR. */ + /* Increasing its value will use less space */ + /* but more collection time. Decreasing it */ + /* will appreciably decrease collection time */ + /* at the expense of space. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ + /* In GC v7.1 (and before), the setter returned */ + /* the old value. */ +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_ATTR_DEPRECATED GC_word GC_max_retries; + /* The maximum number of GCs attempted before */ + /* reporting out of memory after heap */ + /* expansion fails. Initially 0. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ +GC_API void GC_CALL GC_set_max_retries(GC_word); +GC_API GC_word GC_CALL GC_get_max_retries(void); + + +GC_API GC_ATTR_DEPRECATED char *GC_stackbottom; + /* The cold end (bottom) of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. */ + /* For multi-threaded code, this is the */ + /* cold end of the stack for the */ + /* primordial thread. Portable clients */ + /* should use GC_get_stack_base(), */ + /* GC_call_with_gc_active() and */ + /* GC_register_my_thread() instead. */ + +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 */ + /* before the first collection. */ + /* Interferes with blacklisting. */ + /* Wizards only. The setter and getter */ + /* are unsynchronized (and no external */ + /* locking is needed since the value is */ + /* accessed at GC initialization only). */ +GC_API void GC_CALL GC_set_dont_precollect(int); +GC_API int GC_CALL GC_get_dont_precollect(void); + +GC_API GC_ATTR_DEPRECATED unsigned long GC_time_limit; + /* If incremental collection is enabled, */ + /* we try to terminate collections */ + /* after this many milliseconds (plus */ + /* the amount of nanoseconds as given in */ + /* the latest GC_set_time_limit_tv call, */ + /* if any). Not a hard time bound. */ + /* Setting this variable to */ + /* GC_TIME_UNLIMITED will essentially */ + /* disable incremental collection while */ + /* leaving generational collection */ + /* enabled. */ +#define GC_TIME_UNLIMITED 999999 + /* Setting GC_time_limit to this value */ + /* will disable the "pause time exceeded"*/ + /* tests. */ + /* The setter and getter are unsynchronized, so */ + /* GC_call_with_alloc_lock() is required to */ + /* avoid data races (if the value is modified */ + /* after the GC is put to multi-threaded mode). */ + /* The setter does not update the value of the */ + /* nanosecond part of the time limit (it is */ + /* zero unless ever set by GC_set_time_limit_tv */ + /* call). */ +GC_API void GC_CALL GC_set_time_limit(unsigned long); +GC_API unsigned long GC_CALL GC_get_time_limit(void); + +/* A portable type definition of time with a nanosecond precision. */ +struct GC_timeval_s { + unsigned long tv_ms; /* time in milliseconds */ + unsigned long tv_nsec;/* nanoseconds fraction (<1000000) */ +}; + +/* Public procedures */ + +/* Set/get the time limit of the incremental collections. This is */ +/* similar to GC_set_time_limit and GC_get_time_limit but the time is */ +/* provided with the nanosecond precision. The value of tv_nsec part */ +/* should be less than a million. If the value of tv_ms part is */ +/* GC_TIME_UNLIMITED then tv_nsec is ignored. Initially, the value of */ +/* tv_nsec part of the time limit is zero. The functions do not use */ +/* any synchronization. Defined only if the library has been compiled */ +/* without NO_CLOCK. */ +GC_API void GC_CALL GC_set_time_limit_tv(struct GC_timeval_s); +GC_API struct GC_timeval_s GC_CALL GC_get_time_limit_tv(void); + +/* Set/get the minimum value of the ratio of allocated bytes since GC */ +/* to the amount of finalizers created since that GC (value > */ +/* GC_bytes_allocd / (GC_fo_entries - last_fo_entries)) which triggers */ +/* the collection instead heap expansion. The value has no effect in */ +/* the GC incremental mode. The default value is 10000 unless */ +/* GC_ALLOCD_BYTES_PER_FINALIZER macro with a custom value is defined */ +/* to build libgc. The default value might be not the right choice for */ +/* clients where e.g. most objects have a finalizer. Zero value */ +/* effectively disables taking amount of finalizers in the decision */ +/* whether to collect or not. The functions do not use any */ +/* synchronization. */ +GC_API void GC_CALL GC_set_allocd_bytes_per_finalizer(GC_word); +GC_API GC_word GC_CALL GC_get_allocd_bytes_per_finalizer(void); + +/* Tell the collector to start various performance measurements. */ +/* Only the total time taken by full collections is calculated, as */ +/* of now. And, currently, there is no way to stop the measurements. */ +/* The function does not use any synchronization. Defined only if the */ +/* library has been compiled without NO_CLOCK. */ +GC_API void GC_CALL GC_start_performance_measurement(void); + +/* Get the total time of all full collections since the start of the */ +/* performance measurements. The measurement unit is one millisecond. */ +/* Note that the returned value wraps around on overflow. */ +/* The function does not use any synchronization. Defined only if the */ +/* library has been compiled without NO_CLOCK. */ +GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void); + +/* Set whether the GC will allocate executable memory pages or not. */ +/* A non-zero argument instructs the collector to allocate memory with */ +/* the executable flag on. Must be called before the collector is */ +/* initialized. May have no effect on some platforms. The default */ +/* value is controlled by NO_EXECUTE_PERMISSION macro (if present then */ +/* the flag is off). Portable clients should have */ +/* GC_set_pages_executable(1) call (before GC_INIT) provided they are */ +/* going to execute code on any of the GC-allocated memory objects. */ +GC_API void GC_CALL GC_set_pages_executable(int); + +/* Returns non-zero value if the GC is set to the allocate-executable */ +/* mode. The mode could be changed by GC_set_pages_executable (before */ +/* GC_INIT) unless the former has no effect on the platform. Does not */ +/* use or need synchronization (i.e. acquiring the allocator lock). */ +GC_API int GC_CALL GC_get_pages_executable(void); + +/* The setter and getter of the minimum value returned by the internal */ +/* min_bytes_allocd(). The value should not be zero; the default value */ +/* is one. Not synchronized. */ +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t); +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void); + +/* Set/get the size in pages of units operated by GC_collect_a_little. */ +/* The value should not be zero. Not synchronized. */ +GC_API void GC_CALL GC_set_rate(int); +GC_API int GC_CALL GC_get_rate(void); + +/* Set/get the maximum number of prior attempts at the world-stop */ +/* marking. Not synchronized. */ +GC_API void GC_CALL GC_set_max_prior_attempts(int); +GC_API int GC_CALL GC_get_max_prior_attempts(void); + +/* Control whether to disable algorithm deciding if a collection should */ +/* be started when we allocated enough to amortize GC. Both the setter */ +/* and the getter acquire the GC lock (to avoid data races). */ +GC_API void GC_CALL GC_set_disable_automatic_collection(int); +GC_API int GC_CALL GC_get_disable_automatic_collection(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. Issues 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); + +/* Returns non-zero (TRUE) if and only if the collector is initialized */ +/* (or, at least, the initialization is in progress). */ +GC_API int GC_CALL GC_is_init_called(void); + +/* Perform the collector shutdown. (E.g. dispose critical sections on */ +/* Win32 target.) A duplicate invocation is a no-op. GC_INIT should */ +/* not be called after the shutdown. See also GC_win32_free_heap(). */ +GC_API void GC_CALL GC_deinit(void); + +/* General purpose allocation routines, with roughly malloc calling */ +/* conv. The atomic versions promise that no relevant pointers are */ +/* contained in the object. The non-atomic versions guarantee that the */ +/* new object is cleared. GC_malloc_uncollectable 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. */ +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_DEPRECATED void * GC_CALL GC_malloc_stubborn(size_t); + +/* GC_memalign() is not well tested. */ +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 */) GC_ATTR_NONNULL(1); + +/* Explicitly deallocate an object. Dangerous if used incorrectly. */ +/* Requires a pointer to the base of an object. */ +/* An object should not be enabled for finalization (and it should not */ +/* contain registered disappearing links of any kind) when it is */ +/* explicitly deallocated. */ +/* GC_free(0) is a no-op, as required by ANSI C for free. */ +GC_API void GC_CALL GC_free(void *); + +/* The "stubborn" objects allocation is not supported anymore. Exists */ +/* only for the backward compatibility. */ +#define GC_MALLOC_STUBBORN(sz) GC_MALLOC(sz) +#define GC_NEW_STUBBORN(t) GC_NEW(t) +#define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p) +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_change_stubborn(const void *); + +/* Inform the collector that the object has been changed. */ +/* Only non-NULL pointer stores into the object are considered to be */ +/* changes. Matters only if the incremental collection is enabled in */ +/* the manual VDB mode (otherwise the function does nothing). */ +/* Should be followed typically by GC_reachable_here called for each */ +/* of the stored pointers. */ +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 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. */ +/* Return 0 if displaced_pointer doesn't point to within a valid */ +/* object. */ +/* Note that a deallocated object in the garbage collected heap */ +/* may be considered valid, even if it has been deallocated with */ +/* GC_free. */ +GC_API void * GC_CALL GC_base(void * /* displaced_pointer */); + +/* 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 * /* 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 */ +/* or with the standard C library, your code is broken. In my */ +/* opinion, it shouldn't have been invented, but now we're stuck. -HB */ +/* The resulting object has the same kind as the original. */ +/* It is an error to have changes enabled for the original object. */ +/* It does not change the content of the object from its beginning to */ +/* the minimum of old size and new_size_in_bytes; the content above in */ +/* case of object size growth is initialized to zero (not guaranteed */ +/* for atomic object type). The function follows ANSI conventions for */ +/* NULL old_object (i.e., equivalent to GC_malloc regardless of new */ +/* size). If new size is zero (and old_object is non-NULL) then the */ +/* call is equivalent to GC_free (and NULL is returned). If old_object */ +/* is non-NULL, it must have been returned by an earlier call to */ +/* GC_malloc* or GC_realloc. In case of the allocation failure, the */ +/* memory pointed by old_object is untouched (and not freed). */ +/* If the returned pointer is not the same as old_object and both of */ +/* them are non-NULL then old_object is freed. Returns either NULL (in */ +/* case of the allocation failure or zero new size) or pointer to the */ +/* allocated memory. */ +GC_API void * GC_CALL GC_realloc(void * /* old_object */, + size_t /* new_size_in_bytes */) + /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2); + +/* Explicitly increase the heap size. */ +/* Returns 0 on failure, 1 on success. */ +GC_API int GC_CALL GC_expand_hp(size_t /* number_of_bytes */); + +/* Limit the heap size to n bytes. Useful when you're debugging, */ +/* especially on systems that don't handle running out of memory well. */ +/* n == 0 ==> unbounded. This is the default. This setter function is */ +/* unsynchronized (so it might require GC_call_with_alloc_lock to avoid */ +/* data races). */ +GC_API void GC_CALL GC_set_max_heap_size(GC_word /* n */); + +/* Inform the collector that a certain section of statically allocated */ +/* memory contains no pointers to garbage collected memory. Thus it */ +/* 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. */ +/* 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 */); + +/* Clear the number of entries in the exclusion table. Wizards only. */ +GC_API void GC_CALL GC_clear_exclusion_table(void); + +/* Clear the set of root segments. Wizards only. */ +GC_API void GC_CALL GC_clear_roots(void); + +/* Add a root segment. Wizards only. */ +/* May merge adjacent or overlapping segments if appropriate. */ +/* 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 */); + +/* Remove root segments located fully in the region. Wizards only. */ +GC_API void GC_CALL GC_remove_roots(void * /* low_address */, + void * /* high_address_plus_1 */); + +/* Add a displacement to the set of those considered valid by the */ +/* collector. GC_register_displacement(n) means that if p was returned */ +/* by GC_malloc, then (char *)p + n will be considered to be a valid */ +/* pointer to p. N must be small and less than the size of p. */ +/* (All pointers to the interior of objects from the stack are */ +/* considered valid in any case. This applies to heap objects and */ +/* static data.) */ +/* Preferably, this should be called before any other GC procedures. */ +/* Calling it later adds to the probability of excess memory */ +/* retention. */ +/* This is a no-op if the collector has recognition of */ +/* arbitrary interior pointers enabled, which is now the default. */ +GC_API void GC_CALL GC_register_displacement(size_t /* n */); + +/* The following version should be used if any debugging allocation is */ +/* being done. */ +GC_API void GC_CALL GC_debug_register_displacement(size_t /* n */); + +/* Explicitly trigger a full, world-stop collection. */ +GC_API void GC_CALL GC_gcollect(void); + +/* Same as above but ignores the default stop_func setting and tries to */ +/* unmap as much memory as possible (regardless of the corresponding */ +/* switch setting). The recommended usage: on receiving a system */ +/* low-memory event; before retrying a system call failed because of */ +/* the system is running out of resources. */ +GC_API void GC_CALL GC_gcollect_and_unmap(void); + +/* Trigger a full world-stopped collection. Abort the collection if */ +/* and when stop_func returns a nonzero value. Stop_func will be */ +/* called frequently, and should be reasonably fast. (stop_func is */ +/* called with the allocation lock held and the world might be stopped; */ +/* it's not allowed for stop_func to manipulate pointers to the garbage */ +/* collected heap or call most of GC functions.) This works even */ +/* if virtual dirty bits, and hence incremental collection is not */ +/* available for this architecture. Collections can be aborted faster */ +/* than normal pause times for incremental collection. However, */ +/* aborted collections do no useful work; the next collection needs */ +/* to start from the beginning. stop_func must not be 0. */ +/* 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_ATTR_NONNULL(1); + +/* Set and get the default stop_func. The default stop_func is used by */ +/* GC_gcollect() and by implicitly triggered 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_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 */ +/* data structures. Excludes the unmapped memory (returned to the OS). */ +/* Includes empty blocks and fragmentation loss. Includes some pages */ +/* 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 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 */ +/* four ones below were made thread-safe in GC v7.2alpha1 and */ +/* reverted back in v7.2alpha7 for the reason described.) */ +GC_API size_t GC_CALL GC_get_heap_size(void); + +/* Return a lower bound on the number of free bytes in the heap */ +/* (excluding the unmapped memory space). This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_free_bytes(void); + +/* Return the size (in bytes) of the unmapped memory (which is returned */ +/* to the OS but could be remapped back by the collector later unless */ +/* the OS runs out of system/virtual memory). This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_unmapped_bytes(void); + +/* Return the number of bytes allocated since the last collection. */ +/* This is an unsynchronized getter (see GC_get_heap_size comment */ +/* regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_bytes_since_gc(void); + +/* Return the number of explicitly deallocated bytes of memory since */ +/* the recent collection. This is an unsynchronized getter. */ +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void); + +/* Return the total number of bytes allocated in this process. */ +/* Never decreases, except due to wrapping. This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_total_bytes(void); + +/* Return the total number of bytes obtained from OS. Includes the */ +/* unmapped memory. Never decreases. It is an unsynchronized getter. */ +GC_API size_t GC_CALL GC_get_obtained_from_os_bytes(void); + +/* Return the heap usage information. This is a thread-safe (atomic) */ +/* alternative for the five above getters. (This function acquires */ +/* 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. */ +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. */ + GC_word expl_freed_bytes_since_gc; + /* Number of bytes freed explicitly since the recent GC. */ + /* Same as returned by GC_get_expl_freed_bytes_since_gc(). */ + GC_word obtained_from_os_bytes; + /* Total amount of memory obtained from OS, in bytes. */ +}; + +/* 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 + +/* Get the element value (converted to bytes) at a given index of */ +/* size_map table which provides requested-to-actual allocation size */ +/* mapping. Assumes the collector is initialized. Returns -1 if the */ +/* index is out of size_map table bounds. Does not use synchronization, */ +/* thus clients should call it using GC_call_with_alloc_lock typically */ +/* to avoid data races on multiprocessors. */ +GC_API size_t GC_CALL GC_get_size_map_at(int i); + +/* Count total memory use in bytes by all allocated blocks. Acquires */ +/* the lock. */ +GC_API size_t GC_CALL GC_get_memory_use(void); + +/* Disable garbage collection. Even GC_gcollect calls will be */ +/* ineffective. */ +GC_API void GC_CALL GC_disable(void); + +/* 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); + +/* Select whether to use the manual VDB mode for the incremental */ +/* collection. Has no effect if called after enabling the incremental */ +/* collection. The default value is off unless the collector is */ +/* compiled with MANUAL_VDB defined. The manual VDB mode should be */ +/* used only if the client has the appropriate GC_END_STUBBORN_CHANGE */ +/* and GC_reachable_here (or, alternatively, GC_PTR_STORE_AND_DIRTY) */ +/* calls (to ensure proper write barriers). Both the setter and getter */ +/* are not synchronized, and are defined only if the library has been */ +/* compiled without SMALL_CONFIG. */ +GC_API void GC_CALL GC_set_manual_vdb_allowed(int); +GC_API int GC_CALL GC_get_manual_vdb_allowed(void); + +/* Enable incremental/generational collection. Not advisable unless */ +/* 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_time_limit is set to 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 early as possible. */ +/* On some platforms, calling it later may have adverse effects. */ +/* Safe to call before GC_INIT(). Includes a GC_init() call. */ +GC_API void GC_CALL GC_enable_incremental(void); + +/* Return non-zero (TRUE) if and only if the incremental mode is on. */ +/* Does not acquire the lock. */ +GC_API int GC_CALL GC_is_incremental_mode(void); + +#define GC_PROTECTS_POINTER_HEAP 1 /* May protect non-atomic objects. */ +#define GC_PROTECTS_PTRFREE_HEAP 2 +#define GC_PROTECTS_STATIC_DATA 4 /* Currently never. */ +#define GC_PROTECTS_STACK 8 /* Probably impractical. */ + +#define GC_PROTECTS_NONE 0 + +/* Does incremental mode write-protect pages? Returns zero or */ +/* more of the above GC_PROTECTS_*, or'ed together. */ +/* The collector is assumed to be initialized before this call. */ +/* The result is not affected by GC_set_manual_vdb_allowed(). */ +/* Call of GC_enable_incremental() may change the result to */ +/* GC_PROTECTS_NONE if some implementation is chosen at runtime */ +/* not needing to write-protect the pages. */ +GC_API int GC_CALL GC_incremental_protection_needs(void); + +/* Force start of incremental collection. Acquires the GC lock. */ +/* No-op unless GC incremental mode is on. */ +GC_API void GC_CALL GC_start_incremental_collection(void); + +/* Perform some garbage collection work, if appropriate. */ +/* Return 0 if there is no more work to be done (including the */ +/* case when garbage collection is not appropriate). */ +/* Typically performs an amount of work corresponding roughly */ +/* to marking from one page. May do more work if further */ +/* progress requires it, e.g. if incremental collection is */ +/* disabled. It is reasonable to call this in a wait loop */ +/* until it returns 0. */ +GC_API int GC_CALL GC_collect_a_little(void); + +/* Allocate an object of size lb bytes. The client guarantees that as */ +/* long as the object is live, it will be referenced by a pointer that */ +/* points to somewhere within the first GC heap block (hblk) of the */ +/* object. (This should normally be declared volatile to prevent the */ +/* compiler from invalidating this assertion.) This routine is only */ +/* useful if a large array is being allocated. It reduces the chance */ +/* of accidentally retaining such an array as a result of scanning an */ +/* integer that happens to be an address inside the array. (Actually, */ +/* it reduces the chance of the allocator not finding space for such */ +/* an array, since it will try hard to avoid introducing such a false */ +/* reference.) On a SunOS 4.X or Windows system this is recommended */ +/* for arrays likely to be larger than 100 KB 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 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__ +# define GC_EXTRA_PARAMS GC_word ra, const char * s, int i +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS const char * s, int i +#endif + +/* The following is only defined if the library has been suitably */ +/* compiled: */ +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 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_DEPRECATED 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 GC_ATTR_DEPRECATED void GC_CALL GC_debug_change_stubborn(const void *); +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. */ +/* Thus they can serve as drop-in malloc/realloc replacements. This */ +/* can be useful for two reasons: */ +/* 1) It allows the collector to be built with DBG_HDRS_ALL defined */ +/* even if some allocation calls come from 3rd party libraries */ +/* that can't be recompiled. */ +/* 2) On some platforms, the file and line information is redundant, */ +/* since it can be reconstructed from a stack trace. On such */ +/* 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 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) +# define GC_REALLOC(old, sz) GC_debug_realloc_replacement(old, sz) +#elif defined(GC_DEBUG) +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) +#else +# define GC_MALLOC(sz) GC_malloc(sz) +# define GC_REALLOC(old, sz) GC_realloc(old, sz) +#endif /* !GC_DEBUG_REPLACEMENT && !GC_DEBUG */ + +#ifdef GC_DEBUG +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) +# define GC_STRDUP(s) GC_debug_strdup(s, GC_EXTRAS) +# define GC_STRNDUP(s, sz) GC_debug_strndup(s, sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) \ + GC_debug_malloc_atomic_uncollectable(sz, GC_EXTRAS) +# define GC_MALLOC_UNCOLLECTABLE(sz) \ + GC_debug_malloc_uncollectable(sz, GC_EXTRAS) +# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ + GC_debug_malloc_ignore_off_page(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \ + GC_debug_malloc_atomic_ignore_off_page(sz, GC_EXTRAS) +# define GC_FREE(p) GC_debug_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_debug_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_debug_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \ + GC_debug_register_finalizer_no_order(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_UNREACHABLE(p, f, d, of, od) \ + GC_debug_register_finalizer_unreachable(p, f, d, of, od) +# define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p) +# define GC_PTR_STORE_AND_DIRTY(p, q) GC_debug_ptr_store_and_dirty(p, q) +# 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) +# define GC_STRDUP(s) GC_strdup(s) +# define GC_STRNDUP(s, sz) GC_strndup(s, sz) +# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) GC_malloc_atomic_uncollectable(sz) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) +# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ + GC_malloc_ignore_off_page(sz) +# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \ + GC_malloc_atomic_ignore_off_page(sz) +# define GC_FREE(p) GC_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \ + GC_register_finalizer_no_order(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_UNREACHABLE(p, f, d, of, od) \ + GC_register_finalizer_unreachable(p, f, d, of, od) +# define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) +# define GC_PTR_STORE_AND_DIRTY(p, q) GC_ptr_store_and_dirty(p, q) +# 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 */ + +/* The following are included because they are often convenient, and */ +/* reduce the chance for a misspecified size argument. But calls may */ +/* expand to something syntactically incorrect if t is a complicated */ +/* type expression. Note that, unlike C++ new operator, these ones */ +/* may return NULL (if out of memory). */ +#define GC_NEW(t) ((t*)GC_MALLOC(sizeof(t))) +#define GC_NEW_ATOMIC(t) ((t*)GC_MALLOC_ATOMIC(sizeof(t))) +#define GC_NEW_UNCOLLECTABLE(t) ((t*)GC_MALLOC_UNCOLLECTABLE(sizeof(t))) + +#ifdef GC_REQUIRE_WCSDUP + /* This might be unavailable on some targets (or not needed). */ + /* wchar_t should be defined in stddef.h */ + 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 +# define GC_WCSDUP(s) GC_wcsdup(s) +# endif +#endif /* GC_REQUIRE_WCSDUP */ + +/* Finalization. Some of these primitives are grossly unsafe. */ +/* The idea is to make them both cheap, and sufficient to build */ +/* a safer layer, closer to Modula-3, Java, or PCedar finalization. */ +/* The interface represents my conclusions from a long discussion */ +/* with Alan Demers, Dan Greene, Carl Hauser, Barry Hayes, */ +/* Christian Jacobi, and Russ Atkinson. It's not perfect, and */ +/* probably nobody else agrees with it. Hans-J. Boehm 3/13/92 */ +typedef void (GC_CALLBACK * GC_finalization_proc)(void * /* obj */, + void * /* client_data */); + +GC_API void GC_CALL GC_register_finalizer(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + 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_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 */ + /* made to disappear), then only a will be */ + /* finalized. (If this does not create any new */ + /* pointers to b, then b will be finalized after the */ + /* next collection.) Any finalizable object that */ + /* is reachable from itself by following one or more */ + /* pointers will not be finalized (or collected). */ + /* Thus cycles involving finalizable objects should */ + /* be avoided, or broken by disappearing links. */ + /* All but the last finalizer registered for an object */ + /* is ignored. */ + /* No-op in the leak-finding mode. */ + /* Finalization may be removed by passing 0 as fn. */ + /* Finalizers are implicitly unregistered when they are */ + /* enqueued for finalization (i.e. become ready to be */ + /* finalized). */ + /* The old finalizer and client data are stored in */ + /* *ofn and *ocd. (ofn and/or ocd may be NULL. */ + /* The allocation lock is held while *ofn and *ocd are */ + /* updated. In case of error (no memory to register */ + /* new finalizer), *ofn and *ocd remain unchanged.) */ + /* Fn is never invoked on an accessible object, */ + /* provided hidden pointers are converted to real */ + /* pointers only if the allocation lock is held, and */ + /* such conversions are not performed by finalization */ + /* routines. */ + /* If GC_register_finalizer is aborted as a result of */ + /* a signal, the object may be left with no */ + /* finalization, even if neither the old nor new */ + /* finalizer were NULL. */ + /* Obj should be the starting address of an object */ + /* 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 collectible object referenced */ + /* by cd will be considered accessible until the */ + /* finalizer is invoked. */ + +/* Another versions of the above follow. It ignores */ +/* self-cycles, i.e. pointers from a finalizable object to */ +/* itself. There is a stylistic argument that this is wrong, */ +/* but it's unavoidable for C++, since the compiler may */ +/* silently introduce these. It's also benign in that specific */ +/* case. And it helps if finalizable objects are split to */ +/* avoid cycles. */ +/* Note that cd will still be viewed as accessible, even if it */ +/* 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_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_ATTR_NONNULL(1); + +/* Another version of the above. It ignores all cycles. */ +/* It should probably only be used by Java implementations. */ +/* Note that cd will still be viewed as accessible, even if it */ +/* 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_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_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 */ +/* longer reachable, not even from other finalizable objects. */ +/* It behaves like "normal" finalization, except that the */ +/* finalizer is not run while the object is reachable from */ +/* other objects specifying unordered finalization. */ +/* Effectively it allows an object referenced, possibly */ +/* indirectly, from an unordered finalizable object to override */ +/* the unordered finalization request. */ +/* This can be used in combination with finalizer_no_order so */ +/* as to release resources that must not be released while an */ +/* object can still be brought back to life by other */ +/* finalizers. */ +/* Only works if GC_java_finalization is set. Probably only */ +/* of interest when implementing a language that requires */ +/* 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_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_ATTR_NONNULL(1); + +#define GC_NO_MEMORY 2 /* Failure due to lack of memory. */ + +/* The following routine may be used to break cycles between */ +/* finalizable objects, thus causing cyclic finalizable */ +/* objects to be finalized in the correct order. Standard */ +/* use involves calling GC_register_disappearing_link(&p), */ +/* 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_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 */ + /* finalization code is invoked, and BEFORE any */ + /* decisions about finalization order are made. */ + /* This is useful in telling the finalizer that */ + /* some pointers are not essential for proper */ + /* finalization. This may avoid finalization cycles. */ + /* Note that obj may be resurrected by another */ + /* finalizer, and thus the clearing of *link may */ + /* be visible to non-finalization code. */ + /* There's an argument that an arbitrary action should */ + /* be allowed here, instead of just clearing a pointer. */ + /* But this causes problems if that action alters, or */ + /* examines connectivity. Returns GC_DUPLICATE if link */ + /* was already registered, GC_SUCCESS if registration */ + /* succeeded, GC_NO_MEMORY if it failed for lack of */ + /* memory, and GC_oom_fn did not handle the problem. */ + /* Only exists for backward compatibility. See below: */ + +GC_API int GC_CALL GC_general_register_disappearing_link(void ** /* link */, + 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 */ + /* safely. Typically link will point to a location */ + /* holding a disguised pointer to obj. (A pointer */ + /* inside an "atomic" object is effectively disguised.) */ + /* In this way, weak pointers are broken before any */ + /* object reachable from them gets finalized. */ + /* Each link may be registered only with one obj value, */ + /* i.e. all objects but the last one (link registered */ + /* with) are ignored. This was added after a long */ + /* email discussion with John Ellis. */ + /* link must be non-NULL (and be properly aligned). */ + /* obj must be a pointer to the first word of an object */ + /* allocated by GC_malloc or friends. A link */ + /* disappears when it is unregistered manually, or when */ + /* (*link) is cleared, or when the object containing */ + /* this link is garbage collected. It is unsafe to */ + /* explicitly deallocate the object containing link. */ + /* Explicit deallocation of obj may or may not cause */ + /* link to eventually be cleared. */ + /* No-op in the leak-finding mode. */ + /* This function can be used to implement certain types */ + /* of weak pointers. Note, however, this generally */ + /* requires that the allocation lock is held (see */ + /* GC_call_with_alloc_lock() below) when the disguised */ + /* pointer is accessed. Otherwise a strong pointer */ + /* could be recreated between the time the collector */ + /* decides to reclaim the object and the link is */ + /* cleared. Returns GC_SUCCESS if registration */ + /* succeeded (a new link is registered), GC_DUPLICATE */ + /* if link was already registered (with some object), */ + /* GC_NO_MEMORY if registration failed for lack of */ + /* memory (and GC_oom_fn did not handle the problem), */ + /* GC_UNIMPLEMENTED if GC_find_leak is true. */ + +GC_API int GC_CALL GC_move_disappearing_link(void ** /* 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 */ + /* target object of the weak reference. Does not */ + /* change (*new_link) content. May be called with */ + /* new_link equal to link (to check whether link has */ + /* been registered). Returns GC_SUCCESS on success, */ + /* GC_DUPLICATE if there is already another */ + /* disappearing link at the new location (never */ + /* returned if new_link is equal to link), GC_NOT_FOUND */ + /* if no link is registered at the original location. */ + +GC_API int GC_CALL GC_unregister_disappearing_link(void ** /* link */); + /* Undoes a registration by either of the above two */ + /* 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. */ + +/* Support of toggle-ref style of external memory management */ +/* without hooking up to the host retain/release machinery. */ +/* The idea of toggle-ref is that an external reference to */ +/* an object is kept and it can be either a strong or weak */ +/* reference; a weak reference is used when the external peer */ +/* has no interest in the object, and a strong otherwise. */ +typedef enum { + GC_TOGGLE_REF_DROP, + GC_TOGGLE_REF_STRONG, + GC_TOGGLE_REF_WEAK +} GC_ToggleRefStatus; + +/* The callback is to decide (return) the new state of a given */ +/* object. Invoked by the collector for all objects registered */ +/* for toggle-ref processing. Invoked with the allocation lock */ +/* held (but the "world" is running). */ +typedef GC_ToggleRefStatus (GC_CALLBACK *GC_toggleref_func)(void * /* obj */); + +/* Set (register) a callback that decides the state of a given */ +/* object (by, probably, inspecting its native state). */ +/* The argument may be 0 (means no callback). Both the setter */ +/* and the getter acquire the allocation lock (to avoid data */ +/* races). */ +GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func); +GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void); + +/* Register a given object for toggle-ref processing. It will */ +/* be stored internally and the toggle-ref callback will be */ +/* invoked on the object until the callback returns */ +/* GC_TOGGLE_REF_DROP or the object is collected. If is_strong */ +/* is true then the object is registered with a strong ref, */ +/* a weak one otherwise. Returns GC_SUCCESS if registration */ +/* succeeded (or no callback registered yet), GC_NO_MEMORY if */ +/* it failed for lack of memory. */ +GC_API int GC_CALL GC_toggleref_add(void * /* obj */, int /* is_strong */) + GC_ATTR_NONNULL(1); + +/* Finalizer callback support. Invoked by the collector (with */ +/* the allocation lock held) for each unreachable object */ +/* enqueued for finalization. */ +typedef void (GC_CALLBACK * GC_await_finalize_proc)(void * /* obj */); +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc); +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void); + /* Zero means no callback. The setter */ + /* and getter acquire the lock too. */ + +/* Returns !=0 if GC_invoke_finalizers has something to do. */ +/* Does not use any synchronization. */ +GC_API int GC_CALL GC_should_invoke_finalizers(void); + +GC_API int GC_CALL GC_invoke_finalizers(void); + /* Run finalizers for all objects that are ready to */ + /* be finalized. Return the number of finalizers */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* GC_finalize_on_demand is nonzero, it must be called */ + /* explicitly. */ + +/* Explicitly tell the collector that an object is reachable */ +/* at a particular program point. This prevents the argument */ +/* pointer from being optimized away, even it is otherwise no */ +/* longer needed. It should have no visible effect in the */ +/* absence of finalizers or disappearing links. But it may be */ +/* needed to prevent finalizers from running while the */ +/* associated external resource is still in use. */ +/* The function is sometimes called keep_alive in other */ +/* settings. */ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +# if defined(__e2k__) +# define GC_reachable_here(ptr) \ + __asm__ __volatile__ (" " : : "r"(ptr) : "memory") +# else +# define GC_reachable_here(ptr) \ + __asm__ __volatile__ (" " : : "X"(ptr) : "memory") +# endif +#else + GC_API void GC_CALL GC_noop1(GC_word); +# ifdef LINT2 +# define GC_reachable_here(ptr) GC_noop1(~(GC_word)(ptr)^(~(GC_word)0)) + /* The expression matches the one of COVERT_DATAFLOW(). */ +# else +# define GC_reachable_here(ptr) GC_noop1((GC_word)(ptr)) +# endif +#endif + +/* GC_set_warn_proc can be used to redirect or filter warning messages. */ +/* 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). In GC v7.1 (and before), the setter */ +/* returned the old warn_proc value. */ +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_ATTR_NONNULL(1); +/* GC_get_warn_proc returns the current warn_proc. */ +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void); + +/* GC_ignore_warn_proc may be used as an argument for GC_set_warn_proc */ +/* to suppress all warnings (unless statistics printing is turned on). */ +GC_API void GC_CALLBACK GC_ignore_warn_proc(char *, GC_word); + +/* Change file descriptor of GC log. Unavailable on some targets. */ +GC_API void GC_CALL GC_set_log_fd(int); + +/* 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); + +/* A portable way to abort the application because of not enough memory.*/ +GC_API void GC_CALL GC_abort_on_oom(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 */ +/* disappear. Otherwise objects can be accessed after they */ +/* have been collected. */ +/* Should not be used in the leak-finding mode. */ +/* Note that putting pointers in atomic objects or in */ +/* non-pointer slots of "typed" objects is equivalent to */ +/* disguising them in this way, and may have other advantages. */ +typedef GC_word GC_hidden_pointer; +#define GC_HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +/* Converting a hidden pointer to a real pointer requires verifying */ +/* that the object still exists. This involves acquiring the */ +/* allocator lock to avoid a race with the collector. */ +#define GC_REVEAL_POINTER(p) ((void *)GC_HIDE_POINTER(p)) + +#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) +# define REVEAL_POINTER(p) GC_REVEAL_POINTER(p) +#endif + +/* The routines to acquire/release the allocator lock. */ +/* The lock is not reentrant. GC_alloc_unlock() should not be called */ +/* unless the lock is acquired by the current thread. */ +#ifdef GC_THREADS + GC_API void GC_CALL GC_alloc_lock(void); + GC_API void GC_CALL GC_alloc_unlock(void); +#else + /* No need for real locking if the client is single-threaded. */ +# define GC_alloc_lock() (void)0 +# define GC_alloc_unlock() (void)0 +#endif /* !GC_THREADS */ + +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 */) GC_ATTR_NONNULL(1); + +/* These routines are intended to explicitly notify the collector */ +/* of new threads. Often this is unnecessary because thread creation */ +/* is implicitly intercepted by the collector, using header-file */ +/* defines, or linker-based interception. In the long run the intent */ +/* is to always make redundant registration safe. In the short run, */ +/* this is being implemented a platform at a time. */ +/* The interface is complicated by the fact that we probably will not */ +/* ever be able to automatically determine the stack bottom for thread */ +/* stacks on all platforms. */ + +/* Structure representing the bottom (cold end) of a thread stack. */ +/* On most platforms this contains just a single address. */ +struct GC_stack_base { + void * mem_base; /* the bottom of the general-purpose stack */ +# if defined(__e2k__) \ + || defined(__ia64) || defined(__ia64__) || defined(_M_IA64) + void * reg_base; /* the bottom of the register stack */ +# endif +}; + +typedef void * (GC_CALLBACK * GC_stack_base_func)( + struct GC_stack_base * /* sb */, void * /* arg */); + +/* Call a function with a stack base structure corresponding to */ +/* somewhere in the GC_call_with_stack_base frame. This often can */ +/* be used to provide a sufficiently accurate stack bottom. And we */ +/* implement it everywhere. */ +GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */, + void * /* arg */) GC_ATTR_NONNULL(1); + +#define GC_SUCCESS 0 +#define GC_DUPLICATE 1 /* Was already registered. */ +#define GC_NO_THREADS 2 /* No thread support in GC. */ + /* GC_NO_THREADS is not returned by any GC function anymore. */ +#define GC_UNIMPLEMENTED 3 /* Not yet implemented on this platform. */ +#define GC_NOT_FOUND 4 /* Requested link not found (returned */ + /* by GC_move_disappearing_link). */ + +/* Start the parallel marker threads, if available. Useful, e.g., */ +/* after POSIX fork in a child process (provided not followed by exec) */ +/* or in single-threaded clients (provided it is OK for the client to */ +/* perform marking in parallel). Acquires the GC lock to avoid a race. */ +GC_API void GC_CALL GC_start_mark_threads(void); + +#if defined(GC_DARWIN_THREADS) || defined(GC_WIN32_THREADS) + /* Use implicit thread registration and processing (via Win32 DllMain */ + /* or Darwin task_threads). Deprecated. Must be called before */ + /* GC_INIT() and other GC routines. Should be avoided if */ + /* GC_pthread_create, GC_beginthreadex (or GC_CreateThread) could be */ + /* called instead. Disables parallelized GC on Win32. */ + GC_API void GC_CALL GC_use_threads_discovery(void); +#endif + +#ifdef GC_THREADS + /* 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 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); + + /* Explicitly enable GC_register_my_thread() invocation. */ + /* Done implicitly if a GC thread-creation function is called (or */ + /* implicit thread registration is activated, or the collector is */ + /* compiled with GC_ALWAYS_MULTITHREADED defined). Otherwise, it */ + /* must be called from the main (or any previously registered) thread */ + /* between the collector initialization and the first explicit */ + /* registering of a thread (it should be called as late as possible). */ + /* Includes a GC_start_mark_threads() call. */ + GC_API void GC_CALL GC_allow_register_threads(void); + + /* Register the current thread, with the indicated stack bottom, as */ + /* a new thread whose stack(s) should be traced by the GC. If it */ + /* is not implicitly called by the GC, this must be called before a */ + /* thread can allocate garbage collected memory, or assign pointers */ + /* to the garbage collected heap. Once registered, a thread will be */ + /* stopped during garbage collections. */ + /* This call must be previously enabled (see above). */ + /* This should never be called from the main thread, where it is */ + /* always done implicitly. This is normally done implicitly if GC_ */ + /* functions are called to create the thread, e.g. by including gc.h */ + /* (which redefines some system functions) before calling the system */ + /* thread creation function. Nonetheless, thread cleanup routines */ + /* (e.g., pthread key destructor) typically require manual thread */ + /* registering (and unregistering) if pointers to GC-allocated */ + /* objects are manipulated inside. */ + /* It is also always done implicitly on some platforms if */ + /* GC_use_threads_discovery() is called at start-up. Except for the */ + /* latter case, the explicit call is normally required for threads */ + /* created by third-party libraries. */ + /* A manually registered thread requires manual unregistering. */ + /* Returns GC_SUCCESS on success, GC_DUPLICATE if already registered. */ + GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *) + GC_ATTR_NONNULL(1); + + /* Return non-zero (TRUE) if and only if the calling thread is */ + /* registered with the garbage collector. */ + /* If the thread is finished (e.g. running in a destructor and not */ + /* registered manually again), it is considered as not registered. */ + GC_API int GC_CALL GC_thread_is_registered(void); + + /* Notify the collector about the stack and the alt-stack of the */ + /* current thread. stack_start/size is used to determine the stack */ + /* boundaries when a thread is suspended while it is on an alt-stack. */ + GC_API void GC_CALL GC_register_altstack(void * /* stack_start */, + GC_word /* stack_size */, + void * /* altstack_base */, + GC_word /* altstack_size */); + + /* Unregister the current thread. Only an explicitly registered */ + /* thread (i.e. for which GC_register_my_thread() returns GC_SUCCESS) */ + /* is allowed (and required) to call this function. (As a special */ + /* exception, it is also allowed to once unregister the main thread.) */ + /* The thread may no longer allocate garbage collected memory or */ + /* manipulate pointers to the garbage collected heap after making */ + /* this call. Specifically, if it wants to return or otherwise */ + /* communicate a pointer to the garbage-collected heap to another */ + /* thread, it must do this before calling GC_unregister_my_thread, */ + /* most probably by saving it in a global data structure. Must not */ + /* be called inside a GC callback function (except for */ + /* GC_call_with_stack_base() one). */ + GC_API int GC_CALL GC_unregister_my_thread(void); + + /* Stop/start the world explicitly. Not recommended for general use. */ + GC_API void GC_CALL GC_stop_world_external(void); + GC_API void GC_CALL GC_start_world_external(void); + + /* Provide a verifier/modifier of the stack pointer when pushing the */ + /* thread stacks. This might be useful for a crude integration */ + /* with certain coroutine implementations. (*sp_ptr) is the captured */ + /* stack pointer of the suspended thread with pthread_id (the latter */ + /* is actually of pthread_t type). The functionality is unsupported */ + /* on some targets (the getter always returns 0 in such a case). */ + /* Both the setter and the getter acquire the GC lock. The client */ + /* function (if provided) is called with the GC lock acquired, and */ + /* might be with the world stopped. */ + typedef void (GC_CALLBACK * GC_sp_corrector_proc)(void ** /* sp_ptr */, + void * /* pthread_id */); + GC_API void GC_CALL GC_set_sp_corrector(GC_sp_corrector_proc); + GC_API GC_sp_corrector_proc GC_CALL GC_get_sp_corrector(void); +#endif /* GC_THREADS */ + +/* Wrapper for functions that are likely to block (or, at least, do not */ +/* allocate garbage collected memory and/or manipulate pointers to the */ +/* garbage collected heap) for an appreciable length of time. While fn */ +/* is running, the collector is said to be in the "inactive" state for */ +/* the current thread (this means that the thread is not suspended and */ +/* the thread's stack frames "belonging" to the functions in the */ +/* "inactive" state are not scanned during garbage collections). It is */ +/* assumed that the collector is already initialized and the current */ +/* thread is registered. It is allowed for fn to call */ +/* GC_call_with_gc_active() (even recursively), thus temporarily */ +/* toggling the collector's state back to "active". The latter */ +/* technique might be used to make stack scanning more precise (i.e. */ +/* scan only stack frames of functions that allocate garbage collected */ +/* memory and/or manipulate pointers to the garbage collected heap). */ +GC_API void * GC_CALL GC_do_blocking(GC_fn_type /* fn */, + 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 */ +/* GC function and/or manipulate pointers to the garbage collected */ +/* heap). GC_call_with_gc_active() has the functionality opposite to */ +/* GC_do_blocking() one. It is assumed that the collector is already */ +/* initialized and the current thread is registered. fn may toggle */ +/* the collector thread's state temporarily to "inactive" one by using */ +/* GC_do_blocking. GC_call_with_gc_active() often can be used to */ +/* provide a sufficiently accurate stack bottom. */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type /* fn */, + void * /* client_data */) GC_ATTR_NONNULL(1); + +/* Attempt to fill in the GC_stack_base structure with the stack bottom */ +/* for this thread. This appears to be required to implement anything */ +/* like the JNI AttachCurrentThread in an environment in which new */ +/* threads are not automatically registered with the collector. */ +/* It is also unfortunately hard to implement well on many platforms. */ +/* Returns GC_SUCCESS or GC_UNIMPLEMENTED. This function acquires the */ +/* GC lock on some platforms. */ +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *) + GC_ATTR_NONNULL(1); + +/* Fill in the GC_stack_base structure with the cold end (bottom) of */ +/* the stack of the current thread (or coroutine). */ +/* Unlike GC_get_stack_base, it retrieves the value stored in the */ +/* collector (which is initially set by the collector upon the thread */ +/* is started or registered manually but it could be later updated by */ +/* client using GC_set_stackbottom). Returns the GC-internal non-NULL */ +/* handle of the thread which could be passed to GC_set_stackbottom */ +/* later. It is assumed that the collector is already initialized and */ +/* the thread is registered. Acquires the GC lock to avoid data races. */ +GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *) + GC_ATTR_NONNULL(1); + +/* Set the cool end of the user (coroutine) stack of the specified */ +/* thread. The GC thread handle is either the one returned by */ +/* GC_get_my_stackbottom or NULL (the latter designates the current */ +/* thread). The caller should hold the GC lock (e.g. using */ +/* GC_call_with_alloc_lock). Also, the function could be used for */ +/* setting GC_stackbottom value (the bottom of the primordial thread) */ +/* before the collector is initialized (the GC lock is not needed to be */ +/* acquired in this case). */ +GC_API void GC_CALL GC_set_stackbottom(void * /* gc_thread_handle */, + const struct GC_stack_base *) + GC_ATTR_NONNULL(2); + +/* The following routines are primarily intended for use with a */ +/* preprocessor which inserts calls to check C pointer arithmetic. */ +/* They indicate failure by invoking the corresponding _print_proc. */ + +/* Check that p and q point to the same object. */ +/* Fail conspicuously if they don't. */ +/* Returns the first argument. */ +/* Succeeds if neither p nor q points to the heap. */ +/* May succeed if both p and q point to between heap objects. */ +GC_API void * GC_CALL GC_same_obj(void * /* p */, void * /* q */); + +/* Checked pointer pre- and post- increment operations. Note that */ +/* 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_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. */ +/* If it isn't fail conspicuously. */ +/* Returns the argument in all cases. May erroneously succeed */ +/* 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 multi-threaded worlds. */ +GC_API void * GC_CALL GC_is_visible(void * /* p */); + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. */ +/* Fail conspicuously if this property does not hold. */ +/* Uninteresting with GC_all_interior_pointers. */ +/* Always returns its argument. */ +GC_API void * GC_CALL GC_is_valid_displacement(void * /* p */); + +/* Explicitly dump the GC state. This is most often called from the */ +/* debugger, or by setting the GC_DUMP_REGULARLY environment variable, */ +/* but it may be useful to call it from client code during debugging. */ +/* The current collection number is printed in the header of the dump. */ +/* Acquires the GC lock to avoid data races. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump(void); + +/* The same as GC_dump but allows to specify the name of dump and does */ +/* not acquire the lock. If name is non-NULL, it is printed to help */ +/* identifying individual dumps. Otherwise the current collection */ +/* number is used as the name. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_named(const char * /* name */); + +/* Dump information about each block of every GC memory section. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_regions(void); + +/* Dump information about every registered disappearing link and */ +/* finalizable object. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_finalization(void); + +/* Safer, but slow, pointer addition. Probably useful mainly with */ +/* a preprocessor. Useful only for heap pointers. */ +/* Only the macros without trailing digits are meant to be used */ +/* by clients. These are designed to model the available C pointer */ +/* arithmetic expressions. */ +/* Even then, these are probably more useful as */ +/* documentation than as part of the API. */ +/* Note that GC_PTR_ADD evaluates the first argument more than once. */ +#if defined(GC_DEBUG) && defined(__GNUC__) +# define GC_PTR_ADD3(x, n, type_of_result) \ + ((type_of_result)GC_same_obj((x)+(n), (x))) +# define GC_PRE_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_pre_incr((void **)(&(x)), (n)*sizeof(*x))) +# define GC_POST_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_post_incr((void **)(&(x)), (n)*sizeof(*x))) +# define GC_PTR_ADD(x, n) GC_PTR_ADD3(x, n, __typeof__(x)) +# define GC_PRE_INCR(x, n) GC_PRE_INCR3(x, n, __typeof__(x)) +# define GC_POST_INCR(x) GC_POST_INCR3(x, 1, __typeof__(x)) +# define GC_POST_DECR(x) GC_POST_INCR3(x, -1, __typeof__(x)) +#else /* !GC_DEBUG || !__GNUC__ */ + /* We can't do this right without typeof, which ANSI decided was not */ + /* sufficiently useful. Without it we resort to the non-debug version. */ + /* TODO: This should eventually support C++0x decltype. */ +# define GC_PTR_ADD(x, n) ((x)+(n)) +# define GC_PRE_INCR(x, n) ((x) += (n)) +# define GC_POST_INCR(x) ((x)++) +# define GC_POST_DECR(x) ((x)--) +#endif /* !GC_DEBUG || !__GNUC__ */ + +/* Safer assignment of a pointer to a non-stack location. */ +#ifdef GC_DEBUG +# define GC_PTR_STORE(p, q) \ + (*(void **)GC_is_visible((void *)(p)) = \ + GC_is_valid_displacement((void *)(q))) +#else +# define GC_PTR_STORE(p, q) (*(void **)(p) = (void *)(q)) +#endif + +/* GC_PTR_STORE_AND_DIRTY(p,q) is equivalent to GC_PTR_STORE(p,q) */ +/* followed by GC_END_STUBBORN_CHANGE(p) and GC_reachable_here(q) */ +/* (assuming p and q do not have side effects). */ +GC_API void GC_CALL GC_ptr_store_and_dirty(void * /* p */, + const void * /* q */); +GC_API void GC_CALL GC_debug_ptr_store_and_dirty(void * /* p */, + const void * /* q */); + +/* Functions called to report pointer checking errors */ +GC_API void (GC_CALLBACK * GC_same_obj_print_proc)(void * /* p */, + void * /* q */); +GC_API void (GC_CALLBACK * GC_is_valid_displacement_print_proc)(void *); +GC_API void (GC_CALLBACK * GC_is_visible_print_proc)(void *); + +#ifdef GC_PTHREADS + /* For pthread support, we generally need to intercept a number of */ + /* thread library calls. We do that here by macro defining them. */ +# ifdef __cplusplus + } /* extern "C" */ +# endif +# include "gc_pthread_redirects.h" +# ifdef __cplusplus + extern "C" { +# endif +#endif + +/* 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 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. */ + +/* A filter function to control the scanning of dynamic libraries. */ +/* If implemented, called by GC before registering a dynamic library */ +/* (discovered by GC) section as a static data root (called only as */ +/* a last reason not to register). The filename of the library, the */ +/* address and the length of the memory region (section) are passed. */ +/* This routine should return nonzero if that region should be scanned. */ +/* Always called with the allocation lock held. Depending on the */ +/* platform, might be called with the "world" stopped. */ +typedef int (GC_CALLBACK * GC_has_static_roots_func)( + const char * /* dlpi_name */, + void * /* section_start */, + size_t /* section_size */); + +/* Register a new callback (a user-supplied filter) to control the */ +/* scanning of dynamic libraries. Replaces any previously registered */ +/* callback. May be 0 (means no filtering). May be unused on some */ +/* platforms (if the filtering is unimplemented or inappropriate). */ +GC_API void GC_CALL GC_register_has_static_roots_callback( + GC_has_static_roots_func); + +#if !defined(CPPCHECK) && !defined(GC_WINDOWS_H_INCLUDED) && defined(WINAPI) + /* windows.h is included before gc.h */ +# define GC_WINDOWS_H_INCLUDED +#endif + +#if defined(GC_WIN32_THREADS) \ + && (!defined(GC_PTHREADS) || defined(GC_BUILD) \ + || defined(GC_WINDOWS_H_INCLUDED)) + /* Note: for Cygwin and pthreads-win32, this is skipped */ + /* unless windows.h is included before gc.h. */ + +# if (!defined(GC_NO_THREAD_DECLS) || defined(GC_BUILD)) \ + && !defined(GC_DONT_INCL_WINDOWS_H) + +# ifdef __cplusplus + } /* Including windows.h in an extern "C" context no longer works. */ +# endif + +# if !defined(_WIN32_WCE) && !defined(__CEGCC__) +# include /* For _beginthreadex, _endthreadex */ +# endif + +# if defined(GC_BUILD) || !defined(GC_DONT_INCLUDE_WINDOWS_H) +# include +# define GC_WINDOWS_H_INCLUDED +# endif + +# ifdef __cplusplus + extern "C" { +# endif + +# ifdef GC_UNDERSCORE_STDCALL + /* Explicitly prefix exported/imported WINAPI (__stdcall) symbols */ + /* with '_' (underscore). Might be useful if MinGW/x86 is used. */ +# define GC_CreateThread _GC_CreateThread +# define GC_ExitThread _GC_ExitThread +# endif + +# ifndef DECLSPEC_NORETURN + /* Typically defined in winnt.h. */ +# ifdef GC_WINDOWS_H_INCLUDED +# define DECLSPEC_NORETURN /* empty */ +# else +# define DECLSPEC_NORETURN __declspec(noreturn) +# endif +# endif + +# 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 + +# ifdef _WIN64 +# define GC_WIN32_SIZE_T GC_uintptr_t +# elif defined(GC_WINDOWS_H_INCLUDED) +# define GC_WIN32_SIZE_T DWORD +# else +# define GC_WIN32_SIZE_T unsigned long +# endif + +# ifdef GC_INSIDE_DLL + /* Export GC DllMain to be invoked from client DllMain. */ +# ifdef GC_UNDERSCORE_STDCALL +# define GC_DllMain _GC_DllMain +# endif +# ifdef GC_WINDOWS_H_INCLUDED + GC_API BOOL WINAPI GC_DllMain(HINSTANCE /* inst */, + ULONG /* reason */, + LPVOID /* reserved */); +# else + GC_API int __stdcall GC_DllMain(void *, unsigned long, void *); +# endif +# endif /* GC_INSIDE_DLL */ + + /* 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 */ + /* that they will be recorded in the thread table. For backward */ + /* compatibility, it is possible to build the GC with GC_DLL */ + /* defined, and to call GC_use_threads_discovery. This implicitly */ + /* registers all created threads, but appears to be less robust. */ + /* Currently the collector expects all threads to fall through and */ + /* terminate normally, or call GC_endthreadex() or GC_ExitThread, */ + /* so that the thread is properly unregistered. */ +# ifdef GC_WINDOWS_H_INCLUDED + GC_API HANDLE WINAPI GC_CreateThread( + LPSECURITY_ATTRIBUTES /* lpThreadAttributes */, + GC_WIN32_SIZE_T /* dwStackSize */, + LPTHREAD_START_ROUTINE /* lpStartAddress */, + LPVOID /* lpParameter */, DWORD /* dwCreationFlags */, + LPDWORD /* lpThreadId */); + + GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread( + DWORD /* dwExitCode */); +# else + struct _SECURITY_ATTRIBUTES; + GC_API void *__stdcall GC_CreateThread(struct _SECURITY_ATTRIBUTES *, + GC_WIN32_SIZE_T, + unsigned long (__stdcall *)(void *), + void *, unsigned long, unsigned long *); + GC_API DECLSPEC_NORETURN void __stdcall GC_ExitThread(unsigned long); +# endif + +# if !defined(_WIN32_WCE) && !defined(__CEGCC__) + GC_API GC_uintptr_t GC_CALL GC_beginthreadex( + void * /* security */, unsigned /* stack_size */, + unsigned (__stdcall *)(void *), + void * /* arglist */, unsigned /* initflag */, + unsigned * /* thrdaddr */); + + /* Note: _endthreadex() is not currently marked as no-return in */ + /* VC++ and MinGW headers, so we don't mark it neither. */ + GC_API void GC_CALL GC_endthreadex(unsigned /* retval */); +# endif /* !_WIN32_WCE */ + +# endif /* !GC_NO_THREAD_DECLS */ + +# ifdef GC_WINMAIN_REDIRECT + /* win32_threads.c implements the real WinMain(), which will start */ + /* a new thread to call GC_WinMain() after initializing the garbage */ + /* collector. */ +# define WinMain GC_WinMain +# endif + + /* For compatibility only. */ +# define GC_use_DllMain GC_use_threads_discovery + +# ifndef GC_NO_THREAD_REDIRECTS +# define CreateThread GC_CreateThread +# define ExitThread GC_ExitThread +# undef _beginthreadex +# define _beginthreadex GC_beginthreadex +# undef _endthreadex +# define _endthreadex GC_endthreadex +/* #define _beginthread { > "Please use _beginthreadex instead of _beginthread" < } */ +# endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_WIN32_THREADS */ + +/* Public setter and getter for switching "unmap as much as possible" */ +/* mode on(1) and off(0). Has no effect unless unmapping is turned on. */ +/* Has no effect on implicitly-initiated garbage collections. Initial */ +/* value is controlled by GC_FORCE_UNMAP_ON_GCOLLECT. The setter and */ +/* getter are unsynchronized. */ +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int); +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void); + +/* Fully portable code should call GC_INIT() from the main program */ +/* before making any other GC_ calls. On most platforms this is a */ +/* no-op and the collector self-initializes. But a number of */ +/* platforms make that too hard. */ +/* A GC_INIT call is required if the collector is built with */ +/* THREAD_LOCAL_ALLOC defined and the initial allocation call is not */ +/* to GC_malloc() or GC_malloc_atomic(). */ + +#if defined(__CYGWIN32__) || defined(__CYGWIN__) + /* Similarly gnu-win32 DLLs need explicit initialization from the */ + /* main program, as does AIX. */ +# ifdef __x86_64__ + /* Cygwin/x64 does not add leading underscore to symbols anymore. */ + extern int __data_start__[], __data_end__[]; + extern int __bss_start__[], __bss_end__[]; +# define GC_DATASTART ((GC_word)__data_start__ < (GC_word)__bss_start__ \ + ? (void *)__data_start__ : (void *)__bss_start__) +# define GC_DATAEND ((GC_word)__data_end__ > (GC_word)__bss_end__ \ + ? (void *)__data_end__ : (void *)__bss_end__) +# else + extern int _data_start__[], _data_end__[], _bss_start__[], _bss_end__[]; +# define GC_DATASTART ((GC_word)_data_start__ < (GC_word)_bss_start__ \ + ? (void *)_data_start__ : (void *)_bss_start__) +# define GC_DATAEND ((GC_word)_data_end__ > (GC_word)_bss_end__ \ + ? (void *)_data_end__ : (void *)_bss_end__) +# endif /* !__x86_64__ */ +# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND); \ + GC_gcollect() /* For blacklisting. */ + /* Required at least if GC is in a DLL. And doesn't hurt. */ +#elif defined(_AIX) + extern int _data[], _end[]; +# define GC_DATASTART ((void *)_data) +# define GC_DATAEND ((void *)_end) +# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND) +#elif (defined(HOST_ANDROID) || defined(__ANDROID__)) \ + && defined(IGNORE_DYNAMIC_LOADING) + /* This is ugly but seems the only way to register data roots of the */ + /* client shared library if the GC dynamic loading support is off. */ +# pragma weak __dso_handle + extern int __dso_handle[]; + GC_API void * GC_CALL GC_find_limit(void * /* start */, int /* up */); +# define GC_INIT_CONF_ROOTS (void)(__dso_handle != 0 \ + ? (GC_add_roots(__dso_handle, \ + GC_find_limit(__dso_handle, \ + 1 /*up*/)), 0) : 0) +#else +# define GC_INIT_CONF_ROOTS /* empty */ +#endif + +#ifdef GC_DONT_EXPAND + /* Set GC_dont_expand to TRUE at start-up */ +# define GC_INIT_CONF_DONT_EXPAND GC_set_dont_expand(1) +#else +# define GC_INIT_CONF_DONT_EXPAND /* empty */ +#endif + +#ifdef GC_FORCE_UNMAP_ON_GCOLLECT + /* Turn on "unmap as much as possible on explicit GC" mode at start-up */ +# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT \ + GC_set_force_unmap_on_gcollect(1) +#else +# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT /* empty */ +#endif + +#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) && !defined(CPPCHECK) + /* 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 +# define GC_INIT_CONF_MAX_RETRIES /* empty */ +#endif + +#if defined(GC_ALLOCD_BYTES_PER_FINALIZER) && !defined(CPPCHECK) + /* Set GC_allocd_bytes_per_finalizer to the desired value at start-up. */ +# define GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER \ + GC_set_allocd_bytes_per_finalizer(GC_ALLOCD_BYTES_PER_FINALIZER) +#else +# define GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER /* empty */ +#endif + +#if defined(GC_FREE_SPACE_DIVISOR) && !defined(CPPCHECK) + /* Set GC_free_space_divisor to the desired value at start-up */ +# define GC_INIT_CONF_FREE_SPACE_DIVISOR \ + GC_set_free_space_divisor(GC_FREE_SPACE_DIVISOR) +#else +# define GC_INIT_CONF_FREE_SPACE_DIVISOR /* empty */ +#endif + +#if defined(GC_FULL_FREQ) && !defined(CPPCHECK) + /* Set GC_full_freq to the desired value at start-up */ +# define GC_INIT_CONF_FULL_FREQ GC_set_full_freq(GC_FULL_FREQ) +#else +# define GC_INIT_CONF_FULL_FREQ /* empty */ +#endif + +#if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + /* Set GC_time_limit (in ms) to the desired value at start-up. */ +# define GC_INIT_CONF_TIME_LIMIT GC_set_time_limit(GC_TIME_LIMIT) +#else +# define GC_INIT_CONF_TIME_LIMIT /* empty */ +#endif + +#if defined(GC_MARKERS) && defined(GC_THREADS) && !defined(CPPCHECK) + /* Set the number of marker threads (including the initiating */ + /* one) to the desired value at start-up. */ +# define GC_INIT_CONF_MARKERS GC_set_markers_count(GC_MARKERS) +#else +# define GC_INIT_CONF_MARKERS /* empty */ +#endif + +#if defined(GC_SIG_SUSPEND) && defined(GC_THREADS) && !defined(CPPCHECK) +# 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) && !defined(CPPCHECK) +# 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 + +#if defined(GC_MAXIMUM_HEAP_SIZE) && !defined(CPPCHECK) + /* Limit the heap size to the desired value (useful for debugging). */ + /* The limit could be overridden either at the program start-up by */ + /* the similar environment variable or anytime later by the */ + /* corresponding API function call. */ +# define GC_INIT_CONF_MAXIMUM_HEAP_SIZE \ + GC_set_max_heap_size(GC_MAXIMUM_HEAP_SIZE) +#else +# define GC_INIT_CONF_MAXIMUM_HEAP_SIZE /* empty */ +#endif + +#ifdef GC_IGNORE_WARN + /* Turn off all warnings at start-up (after GC initialization) */ +# define GC_INIT_CONF_IGNORE_WARN GC_set_warn_proc(GC_ignore_warn_proc) +#else +# define GC_INIT_CONF_IGNORE_WARN /* empty */ +#endif + +#if defined(GC_INITIAL_HEAP_SIZE) && !defined(CPPCHECK) + /* Set heap size to the desired value at start-up */ +# define GC_INIT_CONF_INITIAL_HEAP_SIZE \ + { size_t heap_size = GC_get_heap_size(); \ + if (heap_size < (GC_INITIAL_HEAP_SIZE)) \ + (void)GC_expand_hp((GC_INITIAL_HEAP_SIZE) - heap_size); } +#else +# define GC_INIT_CONF_INITIAL_HEAP_SIZE /* empty */ +#endif + +/* Portable clients should call this at the program start-up. More */ +/* over, some platforms require this call to be done strictly from the */ +/* primordial thread. Multiple invocations are harmless. */ +#define GC_INIT() { GC_INIT_CONF_DONT_EXPAND; /* pre-init */ \ + GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT; \ + GC_INIT_CONF_MAX_RETRIES; \ + GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER; \ + GC_INIT_CONF_FREE_SPACE_DIVISOR; \ + GC_INIT_CONF_FULL_FREQ; \ + GC_INIT_CONF_TIME_LIMIT; \ + GC_INIT_CONF_MARKERS; \ + 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 */ \ + GC_INIT_CONF_IGNORE_WARN; \ + GC_INIT_CONF_INITIAL_HEAP_SIZE; } + +/* win32s may not free all resources on process exit. */ +/* This explicitly deallocates the heap. Defined only for Windows. */ +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); +# define GC_realloc(a,b) GC_amiga_realloc(a,b) + void GC_amiga_set_toany(void (*)(void)); + extern int GC_amiga_free_space_divisor_inc; + extern void *(*GC_amiga_allocwrapper_do)(size_t, void *(GC_CALL *)(size_t)); +# define GC_malloc(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc) +# define GC_malloc_atomic(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic) +# define GC_malloc_uncollectable(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_uncollectable) +# define GC_malloc_atomic_uncollectable(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_uncollectable) +# define GC_malloc_ignore_off_page(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_ignore_off_page) +# define GC_malloc_atomic_ignore_off_page(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page) +#endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_H */ diff --git a/bdwgc/include/gc_allocator.h b/bdwgc/include/gc_allocator.h new file mode 100644 index 000000000..597c7f131 --- /dev/null +++ b/bdwgc/include/gc_allocator.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 1996-1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Copyright (c) 2002 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +/* + * This implements standard-conforming allocators that interact with + * the garbage collector. Gc_allocator allocates garbage-collectible + * objects of type T. Traceable_allocator allocates objects that + * are not themselves garbage collected, but are scanned by the + * collector for pointers to collectible objects. Traceable_alloc + * should be used for explicitly managed STL containers that may + * 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. + * + * Ignore-off-page allocator: George T. Talbot + */ + +#ifndef GC_ALLOCATOR_H +#define GC_ALLOCATOR_H + +#include "gc.h" +#include // for placement new and bad_alloc + +#if !defined(GC_NO_MEMBER_TEMPLATES) && defined(_MSC_VER) && _MSC_VER <= 1200 + // MSVC++ 6.0 do not support member templates. +# define GC_NO_MEMBER_TEMPLATES +#endif + +#if defined(GC_NEW_ABORTS_ON_OOM) || defined(_LIBCPP_NO_EXCEPTIONS) +# define GC_ALLOCATOR_THROW_OR_ABORT() GC_abort_on_oom() +#else +# define GC_ALLOCATOR_THROW_OR_ABORT() throw std::bad_alloc() +#endif + +/* First some helpers to allow us to dispatch on whether or not a type + * is known to be pointer-free. + * These are private, except that the client may invoke the + * GC_DECLARE_PTRFREE macro. + */ + +struct GC_true_type {}; +struct GC_false_type {}; + +template +struct GC_type_traits { + GC_false_type GC_is_ptr_free; +}; + +# define GC_DECLARE_PTRFREE(T) \ +template<> struct GC_type_traits { GC_true_type GC_is_ptr_free; } + +GC_DECLARE_PTRFREE(char); +GC_DECLARE_PTRFREE(signed char); +GC_DECLARE_PTRFREE(unsigned char); +GC_DECLARE_PTRFREE(signed short); +GC_DECLARE_PTRFREE(unsigned short); +GC_DECLARE_PTRFREE(signed int); +GC_DECLARE_PTRFREE(unsigned int); +GC_DECLARE_PTRFREE(signed long); +GC_DECLARE_PTRFREE(unsigned long); +GC_DECLARE_PTRFREE(float); +GC_DECLARE_PTRFREE(double); +GC_DECLARE_PTRFREE(long double); +/* The client may want to add others. */ + +// In the following GC_Tp is GC_true_type if we are allocating a +// pointer-free object. +template +inline void * GC_selective_alloc(size_t n, GC_Tp, bool ignore_off_page) { + void *obj = ignore_off_page ? GC_MALLOC_IGNORE_OFF_PAGE(n) : GC_MALLOC(n); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; +} + +#if !defined(__WATCOMC__) + /* Note: template-id not supported in this context by Watcom compiler. */ + template <> + inline void * GC_selective_alloc(size_t n, GC_true_type, + bool ignore_off_page) { + void * obj = ignore_off_page ? GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(n) + : GC_MALLOC_ATOMIC(n); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; + } +#endif + +// Now the public gc_allocator class. +template +class gc_allocator { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef gc_allocator other; + }; + + gc_allocator() GC_NOEXCEPT {} + gc_allocator(const gc_allocator&) GC_NOEXCEPT {} +# ifndef GC_NO_MEMBER_TEMPLATES + template GC_ATTR_EXPLICIT + gc_allocator(const gc_allocator&) GC_NOEXCEPT {} +# endif + ~gc_allocator() GC_NOEXCEPT {} + + pointer address(reference GC_x) const { return &GC_x; } + const_pointer address(const_reference GC_x) const { return &GC_x; } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_type_traits traits; + return static_cast(GC_selective_alloc(GC_n * sizeof(GC_Tp), + traits.GC_is_ptr_free, + false)); + } + + void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT + { GC_FREE(__p); } + + size_type max_size() const GC_NOEXCEPT + { return size_t(-1) / sizeof(GC_Tp); } + + void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } + void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class gc_allocator { + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef gc_allocator other; + }; +}; + + +template +inline bool operator==(const gc_allocator&, + const gc_allocator&) GC_NOEXCEPT +{ + return true; +} + +template +inline bool operator!=(const gc_allocator&, + const gc_allocator&) GC_NOEXCEPT +{ + return false; +} + + +// Now the public gc_allocator_ignore_off_page class. +template +class gc_allocator_ignore_off_page { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef gc_allocator_ignore_off_page other; + }; + + gc_allocator_ignore_off_page() GC_NOEXCEPT {} + gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page&) + GC_NOEXCEPT {} +# ifndef GC_NO_MEMBER_TEMPLATES + template GC_ATTR_EXPLICIT + gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page&) + GC_NOEXCEPT {} +# endif + ~gc_allocator_ignore_off_page() GC_NOEXCEPT {} + + pointer address(reference GC_x) const { return &GC_x; } + const_pointer address(const_reference GC_x) const { return &GC_x; } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_type_traits traits; + return static_cast(GC_selective_alloc(GC_n * sizeof(GC_Tp), + traits.GC_is_ptr_free, + true)); + } + + void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT + { GC_FREE(__p); } + + size_type max_size() const GC_NOEXCEPT + { return size_t(-1) / sizeof(GC_Tp); } + + void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } + void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class gc_allocator_ignore_off_page { + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef gc_allocator_ignore_off_page other; + }; +}; + +template +inline bool operator==(const gc_allocator_ignore_off_page&, + const gc_allocator_ignore_off_page&) GC_NOEXCEPT +{ + return true; +} + +template +inline bool operator!=(const gc_allocator_ignore_off_page&, + const gc_allocator_ignore_off_page&) GC_NOEXCEPT +{ + return false; +} + +// And the public traceable_allocator class. + +/* Note that we currently don't specialize the pointer-free case, since a + * pointer-free traceable container doesn't make that much sense, + * though it could become an issue due to abstraction boundaries. + */ + +template +class traceable_allocator { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef traceable_allocator other; + }; + + traceable_allocator() GC_NOEXCEPT {} + traceable_allocator(const traceable_allocator&) GC_NOEXCEPT {} +# ifndef GC_NO_MEMBER_TEMPLATES + template GC_ATTR_EXPLICIT + traceable_allocator(const traceable_allocator&) GC_NOEXCEPT {} +# endif + ~traceable_allocator() GC_NOEXCEPT {} + + pointer address(reference GC_x) const { return &GC_x; } + const_pointer address(const_reference GC_x) const { return &GC_x; } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_Tp* allocate(size_type GC_n, const void* = 0) { + void * obj = GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp)); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return static_cast(obj); + } + + void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT + { GC_FREE(__p); } + + size_type max_size() const GC_NOEXCEPT + { return size_t(-1) / sizeof(GC_Tp); } + + void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } + void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class traceable_allocator { + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef traceable_allocator other; + }; +}; + + +template +inline bool operator==(const traceable_allocator&, + const traceable_allocator&) GC_NOEXCEPT +{ + return true; +} + +template +inline bool operator!=(const traceable_allocator&, + const traceable_allocator&) GC_NOEXCEPT +{ + return false; +} + +#endif /* GC_ALLOCATOR_H */ diff --git a/bdwgc/include/gc_backptr.h b/bdwgc/include/gc_backptr.h new file mode 100644 index 000000000..5fd919592 --- /dev/null +++ b/bdwgc/include/gc_backptr.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +/* + * This is a simple API to implement pointer back tracing, i.e. + * to answer questions such as "who is pointing to this" or + * "why is this object being retained by the collector" + * + * Most of these calls yield useful information on only after + * a garbage collection. Usually the client will first force + * a full collection and then gather information, preferably + * before much intervening allocation. + * + * The implementation of the interface is only about 99.9999% + * correct. It is intended to be good enough for profiling, + * but is not intended to be used with production code. + * + * Results are likely to be much more useful if all allocation is + * accomplished through the debugging allocators. + * + * The implementation idea is due to A. Demers. + */ + +#ifndef GC_BACKPTR_H +#define GC_BACKPTR_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Store information about the object referencing dest in *base_p */ +/* and *offset_p. */ +/* If multiple objects or roots point to dest, the one reported */ +/* will be the last on used by the garbage collector to trace the */ +/* object. */ +/* source is root ==> *base_p = address, *offset_p = 0 */ +/* source is heap object ==> *base_p != 0, *offset_p = offset */ +/* Returns 1 on success, 0 if source couldn't be determined. */ +/* Dest can be any address within a heap object. */ +typedef enum { + GC_UNREFERENCED, /* No reference info available. */ + GC_NO_SPACE, /* Dest not allocated with debug alloc. */ + GC_REFD_FROM_ROOT, /* Referenced directly by root *base_p. */ + GC_REFD_FROM_REG, /* Referenced from a register, i.e. */ + /* a root without an address. */ + GC_REFD_FROM_HEAP, /* Referenced from another heap obj. */ + GC_FINALIZER_REFD /* Finalizable and hence accessible. */ +} GC_ref_kind; + +GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void * /* dest */, + void ** /* base_p */, size_t * /* offset_p */) + GC_ATTR_NONNULL(1); + +/* Generate a random heap address. */ +/* The resulting address is in the heap, but */ +/* not necessarily inside a valid object. */ +GC_API void * GC_CALL GC_generate_random_heap_address(void); + +/* Generate a random address inside a valid marked heap object. */ +GC_API void * GC_CALL GC_generate_random_valid_address(void); + +/* Force a garbage collection and generate a backtrace from a */ +/* random heap address. */ +/* This uses the GC logging mechanism (GC_printf) to produce */ +/* output. It can often be called from a debugger. The */ +/* source in dbg_mlc.c also serves as a sample client. */ +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_ATTR_NONNULL(1); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_BACKPTR_H */ diff --git a/bdwgc/include/gc_config_macros.h b/bdwgc/include/gc_config_macros.h new file mode 100644 index 000000000..f26f92ce4 --- /dev/null +++ b/bdwgc/include/gc_config_macros.h @@ -0,0 +1,458 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2008-2020 Ivan Maidanski + * + * 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. + */ + +/* This should never be included directly; it is included only from gc.h. */ +/* We separate it only to make gc.h more suitable as documentation. */ +#if defined(GC_H) + +/* Convenient internal macro to test version of GCC. */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +# define GC_GNUC_PREREQ(major, minor) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((major) << 16) + (minor)) +#else +# define GC_GNUC_PREREQ(major, minor) 0 /* FALSE */ +#endif + +/* Some tests for old macros. These violate our namespace rules and */ +/* will disappear shortly. Use the GC_ names. */ +#if defined(SOLARIS_THREADS) || defined(_SOLARIS_THREADS) \ + || defined(_SOLARIS_PTHREADS) || defined(GC_SOLARIS_PTHREADS) + /* We no longer support old style Solaris threads. */ + /* GC_SOLARIS_THREADS now means pthreads. */ +# ifndef GC_SOLARIS_THREADS +# define GC_SOLARIS_THREADS +# endif +#endif +#if defined(IRIX_THREADS) +# define GC_IRIX_THREADS +#endif +#if defined(DGUX_THREADS) && !defined(GC_DGUX386_THREADS) +# define GC_DGUX386_THREADS +#endif +#if defined(AIX_THREADS) +# define GC_AIX_THREADS +#endif +#if defined(HPUX_THREADS) +# define GC_HPUX_THREADS +#endif +#if defined(OSF1_THREADS) +# define GC_OSF1_THREADS +#endif +#if defined(LINUX_THREADS) +# define GC_LINUX_THREADS +#endif +#if defined(WIN32_THREADS) +# define GC_WIN32_THREADS +#endif +#if defined(RTEMS_THREADS) +# define GC_RTEMS_PTHREADS +#endif +#if defined(USE_LD_WRAP) +# define GC_USE_LD_WRAP +#endif + +#if defined(GC_WIN32_PTHREADS) && !defined(GC_WIN32_THREADS) + /* Using pthreads-win32 library (or other Win32 implementation). */ +# define GC_WIN32_THREADS +#endif + +#if defined(GC_AIX_THREADS) || defined(GC_DARWIN_THREADS) \ + || defined(GC_DGUX386_THREADS) || defined(GC_FREEBSD_THREADS) \ + || defined(GC_HPUX_THREADS) \ + || defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) \ + || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) \ + || defined(GC_OSF1_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(GC_WIN32_THREADS) || defined(GC_RTEMS_PTHREADS) +# ifndef GC_THREADS +# define GC_THREADS +# endif +#elif defined(GC_THREADS) +# if defined(__linux__) +# define GC_LINUX_THREADS +# elif defined(__OpenBSD__) +# define GC_OPENBSD_THREADS +# elif defined(_PA_RISC1_1) || defined(_PA_RISC2_0) || defined(hppa) \ + || defined(__HPPA) || (defined(__ia64) && defined(_HPUX_SOURCE)) +# define GC_HPUX_THREADS +# elif defined(__HAIKU__) +# define GC_HAIKU_THREADS +# elif (defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || defined(__FreeBSD__)) && !defined(GC_NO_FREEBSD) +# define GC_FREEBSD_THREADS +# elif defined(__NetBSD__) +# define GC_NETBSD_THREADS +# elif defined(__alpha) || defined(__alpha__) /* && !Linux && !xBSD */ +# define GC_OSF1_THREADS +# elif (defined(mips) || defined(__mips) || defined(_mips)) \ + && !(defined(nec_ews) || defined(_nec_ews) \ + || defined(ultrix) || defined(__ultrix)) +# define GC_IRIX_THREADS +# elif defined(__sparc) /* && !Linux */ \ + || ((defined(sun) || defined(__sun)) \ + && (defined(i386) || defined(__i386__) \ + || defined(__amd64) || defined(__amd64__))) +# define GC_SOLARIS_THREADS +# elif defined(__APPLE__) && defined(__MACH__) +# define GC_DARWIN_THREADS +# endif +# if defined(DGUX) && (defined(i386) || defined(__i386__)) +# define GC_DGUX386_THREADS +# endif +# if defined(_AIX) +# define GC_AIX_THREADS +# endif +# if (defined(_WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) \ + || defined(__CYGWIN32__) || defined(__CYGWIN__) || defined(__CEGCC__) \ + || defined(_WIN32_WCE) || defined(__MINGW32__)) \ + && !defined(GC_WIN32_THREADS) + /* Either posix or native Win32 threads. */ +# define GC_WIN32_THREADS +# endif +# if defined(__rtems__) && (defined(i386) || defined(__i386__)) +# define GC_RTEMS_PTHREADS +# endif +#endif /* GC_THREADS */ + +#undef GC_PTHREADS +#if (!defined(GC_WIN32_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(__CYGWIN32__) || defined(__CYGWIN__)) && defined(GC_THREADS) \ + && !defined(NN_PLATFORM_CTR) && !defined(NN_BUILD_TARGET_PLATFORM_NX) + /* Posix threads. */ +# define GC_PTHREADS +#endif + +#if !defined(_PTHREADS) && defined(GC_NETBSD_THREADS) +# define _PTHREADS +#endif + +#if defined(GC_DGUX386_THREADS) && !defined(_POSIX4A_DRAFT10_SOURCE) +# define _POSIX4A_DRAFT10_SOURCE 1 +#endif + +#if !defined(_REENTRANT) && defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + /* Better late than never. This fails if system headers that depend */ + /* on this were previously included. */ +# define _REENTRANT 1 +#endif + +#define __GC +#if !defined(_WIN32_WCE) || defined(__GNUC__) +# include +# if defined(__MINGW32__) && !defined(_WIN32_WCE) +# include + /* We mention uintptr_t. */ + /* Perhaps this should be included in pure msft environments */ + /* as well? */ +# endif +#else /* _WIN32_WCE */ + /* Yet more kludges for WinCE. */ +# include /* size_t is defined here */ +# ifndef _PTRDIFF_T_DEFINED + /* ptrdiff_t is not defined */ +# define _PTRDIFF_T_DEFINED + typedef long ptrdiff_t; +# endif +#endif /* _WIN32_WCE */ + +#if !defined(GC_NOT_DLL) && !defined(GC_DLL) \ + && ((defined(_DLL) && !defined(__GNUC__)) \ + || (defined(DLL_EXPORT) && defined(GC_BUILD))) +# define GC_DLL +#endif + +#if defined(GC_DLL) && !defined(GC_API) + +# if defined(__CEGCC__) +# if defined(GC_BUILD) +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif + +# elif defined(__MINGW32__) +# if defined(__cplusplus) && defined(GC_BUILD) +# define GC_API extern __declspec(dllexport) +# elif defined(GC_BUILD) || defined(__MINGW32_DELAY_LOAD__) +# define GC_API __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif + +# elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \ + || defined(__CYGWIN__) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif + +# elif defined(__WATCOMC__) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# 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) && !defined(GC_NO_VISIBILITY) \ + && (GC_GNUC_PREREQ(4, 0) || defined(GC_VISIBILITY_HIDDEN_SET)) +# define GC_API extern __attribute__((__visibility__("default"))) +# endif +# endif +#endif /* GC_DLL */ + +#ifndef GC_API +# define GC_API extern +#endif + +#ifndef GC_CALL +# define GC_CALL +#endif + +#ifndef GC_CALLBACK +# define GC_CALLBACK GC_CALL +#endif + +#ifndef GC_ATTR_MALLOC + /* 'malloc' attribute should be used for all malloc-like functions */ + /* (to tell the compiler that a function may be treated as if any */ + /* 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. */ +# ifdef GC_OOM_FUNC_RETURNS_ALIAS +# define GC_ATTR_MALLOC /* empty */ +# elif GC_GNUC_PREREQ(3, 1) +# define GC_ATTR_MALLOC __attribute__((__malloc__)) +# elif defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(__EDG__) +# define GC_ATTR_MALLOC \ + __declspec(allocator) __declspec(noalias) __declspec(restrict) +# elif defined(_MSC_VER) && _MSC_VER >= 1400 +# define GC_ATTR_MALLOC __declspec(noalias) __declspec(restrict) +# else +# define GC_ATTR_MALLOC +# endif +#endif + +#ifndef GC_ATTR_ALLOC_SIZE + /* 'alloc_size' attribute improves __builtin_object_size correctness. */ +# undef GC_ATTR_CALLOC_SIZE +# ifdef __clang__ +# if __has_attribute(__alloc_size__) +# define GC_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum))) +# define GC_ATTR_CALLOC_SIZE(n, s) __attribute__((__alloc_size__(n, s))) +# else +# define GC_ATTR_ALLOC_SIZE(argnum) /* empty */ +# endif +# elif GC_GNUC_PREREQ(4, 3) && !defined(__ICC) +# define GC_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum))) +# define GC_ATTR_CALLOC_SIZE(n, s) __attribute__((__alloc_size__(n, s))) +# else +# define GC_ATTR_ALLOC_SIZE(argnum) /* empty */ +# endif +#endif + +#ifndef GC_ATTR_CALLOC_SIZE +# define GC_ATTR_CALLOC_SIZE(n, s) /* empty */ +#endif + +#ifndef GC_ATTR_NONNULL +# if GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_NONNULL(argnum) __attribute__((__nonnull__(argnum))) +# else +# define GC_ATTR_NONNULL(argnum) /* empty */ +# endif +#endif + +#ifndef GC_ATTR_CONST +# if GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_CONST __attribute__((__const__)) +# else +# define GC_ATTR_CONST /* empty */ +# endif +#endif + +#ifndef GC_ATTR_DEPRECATED +# ifdef GC_BUILD +# undef GC_ATTR_DEPRECATED +# define GC_ATTR_DEPRECATED /* empty */ +# elif GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_DEPRECATED __attribute__((__deprecated__)) +# elif defined(_MSC_VER) && _MSC_VER >= 1200 +# 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 +#endif + +#if defined(__linux__) || defined(__GLIBC__) +# if !defined(__native_client__) +# include +# endif +# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2) \ + && !defined(__ia64__) \ + && !defined(GC_MISSING_EXECINFO_H) \ + && !defined(GC_HAVE_BUILTIN_BACKTRACE) +# define GC_HAVE_BUILTIN_BACKTRACE +# endif +# if defined(__i386__) || defined(__amd64__) || defined(__x86_64__) +# define GC_CAN_SAVE_CALL_STACKS +# endif +#endif /* GLIBC */ + +#if defined(_MSC_VER) && _MSC_VER >= 1200 /* version 12.0+ (MSVC 6.0+) */ \ + && !defined(_M_ARM) && !defined(_M_ARM64) \ + && !defined(_AMD64_) && !defined(_M_X64) && !defined(_WIN32_WCE) \ + && !defined(GC_HAVE_NO_BUILTIN_BACKTRACE) \ + && !defined(GC_HAVE_BUILTIN_BACKTRACE) +# define GC_HAVE_BUILTIN_BACKTRACE +#endif + +#if defined(GC_HAVE_BUILTIN_BACKTRACE) && !defined(GC_CAN_SAVE_CALL_STACKS) +# define GC_CAN_SAVE_CALL_STACKS +#endif + +#if defined(__sparc__) +# define GC_CAN_SAVE_CALL_STACKS +#endif + +/* If we're on a platform on which we can't save call stacks, but */ +/* gcc is normally used, we go ahead and define GC_ADD_CALLER. */ +/* We make this decision independent of whether gcc is actually being */ +/* used, in order to keep the interface consistent, and allow mixing */ +/* of compilers. */ +/* This may also be desirable if it is possible but expensive to */ +/* retrieve the call chain. */ +#if (defined(__linux__) || defined(__DragonFly__) || defined(__FreeBSD__) \ + || defined(__FreeBSD_kernel__) || defined(__HAIKU__) \ + || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(HOST_ANDROID) || defined(__ANDROID__)) \ + && !defined(GC_CAN_SAVE_CALL_STACKS) +# define GC_ADD_CALLER +# if GC_GNUC_PREREQ(2, 95) + /* 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 GC_GNUC_PREREQ(4, 0) && (defined(__i386__) || defined(__amd64__) \ + || defined(__x86_64__) /* and probably others... */) \ + && !defined(GC_NO_RETURN_ADDR_PARENT) +# define GC_HAVE_RETURN_ADDR_PARENT +# define GC_RETURN_ADDR_PARENT \ + (GC_word)__builtin_extract_return_addr(__builtin_return_address(1)) + /* Note: a compiler might complain that calling */ + /* __builtin_return_address with a nonzero argument is unsafe. */ +# endif +# else + /* Just pass 0 for gcc compatibility. */ +# define GC_RETURN_ADDR 0 +# endif +#endif /* !GC_CAN_SAVE_CALL_STACKS */ + +#ifdef GC_PTHREADS + +# if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(__native_client__) || defined(GC_RTEMS_PTHREADS)) \ + && !defined(GC_NO_DLOPEN) + /* Either there is no dlopen() or we do not need to intercept it. */ +# define GC_NO_DLOPEN +# endif + +# if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(GC_OPENBSD_THREADS) || defined(__native_client__)) \ + && !defined(GC_NO_PTHREAD_SIGMASK) + /* Either there is no pthread_sigmask() or no need to intercept it. */ +# define GC_NO_PTHREAD_SIGMASK +# endif + +# if defined(__native_client__) + /* At present, NaCl pthread_create() prototype does not have */ + /* "const" for its "attr" argument; also, NaCl pthread_exit() one */ + /* does not have "noreturn" attribute. */ +# ifndef GC_PTHREAD_CREATE_CONST +# define GC_PTHREAD_CREATE_CONST /* empty */ +# endif +# ifndef GC_HAVE_PTHREAD_EXIT +# define GC_HAVE_PTHREAD_EXIT +# define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */ +# endif +# endif + +# if !defined(GC_HAVE_PTHREAD_EXIT) \ + && !defined(HOST_ANDROID) && !defined(__ANDROID__) \ + && (defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS)) +# define GC_HAVE_PTHREAD_EXIT + /* Intercept pthread_exit on Linux and Solaris. */ +# if GC_GNUC_PREREQ(2, 7) +# define GC_PTHREAD_EXIT_ATTRIBUTE __attribute__((__noreturn__)) +# elif defined(__NORETURN) /* used in Solaris */ +# define GC_PTHREAD_EXIT_ATTRIBUTE __NORETURN +# else +# define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */ +# endif +# endif + +# if (!defined(GC_HAVE_PTHREAD_EXIT) || defined(__native_client__)) \ + && !defined(GC_NO_PTHREAD_CANCEL) + /* Either there is no pthread_cancel() or no need to intercept it. */ +# define GC_NO_PTHREAD_CANCEL +# endif + +#endif /* GC_PTHREADS */ + +#ifdef __cplusplus + +#ifndef GC_ATTR_EXPLICIT +# if __cplusplus >= 201103L && !defined(__clang__) || _MSVC_LANG >= 201103L \ + || defined(CPPCHECK) +# define GC_ATTR_EXPLICIT explicit +# else +# define GC_ATTR_EXPLICIT /* empty */ +# endif +#endif + +#ifndef GC_NOEXCEPT +# if defined(__DMC__) || (defined(__BORLANDC__) \ + && (defined(_RWSTD_NO_EXCEPTIONS) || defined(_RWSTD_NO_EX_SPEC))) \ + || (defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) \ + || (defined(__WATCOMC__) && !defined(_CPPUNWIND)) +# define GC_NOEXCEPT /* empty */ +# ifndef GC_NEW_ABORTS_ON_OOM +# define GC_NEW_ABORTS_ON_OOM +# endif +# elif __cplusplus >= 201103L || _MSVC_LANG >= 201103L +# define GC_NOEXCEPT noexcept +# else +# define GC_NOEXCEPT throw() +# endif +#endif + +#endif /* __cplusplus */ + +#endif diff --git a/bdwgc/include/gc_cpp.h b/bdwgc/include/gc_cpp.h new file mode 100644 index 000000000..edb691848 --- /dev/null +++ b/bdwgc/include/gc_cpp.h @@ -0,0 +1,603 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#ifndef GC_CPP_H +#define GC_CPP_H + +/**************************************************************************** +C++ Interface to the Boehm Collector + + John R. Ellis and Jesse Hull + +This interface provides access to the Boehm collector. It provides +basic facilities similar to those described in "Safe, Efficient +Garbage Collection for C++", by John R. Ellis and David L. Detlefs +(ftp://ftp.parc.xerox.com/pub/ellis/gc). + +All heap-allocated objects are either "collectible" or +"uncollectible". Programs must explicitly delete uncollectible +objects, whereas the garbage collector will automatically delete +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 uncollectible. + +Objects derived from class "gc" are collectible. For example: + + class A: public gc {...}; + A* a = new A; // a is collectible. + +Collectible instances of non-class types can be allocated using the GC +(or UseGC) placement: + + typedef int A[ 10 ]; + A* a = new (GC) A; + +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 uncollectible. + +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 uncollectible and collectible objects can be explicitly deleted +with "delete", which invokes an object's destructors and frees its +storage immediately. + +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 +object's destructors. Explicit clean-up functions may be specified as +an additional placement argument: + + A* a = ::new (GC, MyCleanup) A; + +An object is considered "accessible" by the collector if it can be +reached by a path of pointers from static variables, automatic +variables of active functions, or from some object with clean-up +enabled; pointers from an object to itself are ignored. + +Thus, if objects A and B both have clean-up functions, and A points at +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 +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 +objects. In practice, it finds almost all of them. + + +Cautions: + +1. Be sure the collector has been augmented with "make c++" or +"--enable-cplusplus". + +2. If your compiler supports the new "operator new[]" syntax, then +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 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 ]; // collectible or uncollectible? + A* a2 = new (GC) A[ 10 ]; // collectible. + +3. The destructors of collectible arrays of objects derived from +"gc_cleanup" will not be invoked properly. For example: + + class A: public gc_cleanup {...}; + A* a = new (GC) A[ 10 ]; // destructors not invoked correctly + +Typically, only the destructor for the first element of the array will +be invoked when the array is garbage-collected. To get all the +destructors of any array executed, you must supply an explicit +clean-up function: + + A* a = new (GC, MyCleanUp) A[ 10 ]; + +(Implementing clean-up of arrays correctly, portably, and in a way +that preserves the correct exception semantics requires a language +extension, e.g. the "gc" keyword.) + +4. Compiler bugs (now hopefully history): + +* Solaris 2's CC (SC3.0) doesn't implement t->~T() correctly, so the +destructors of classes derived from gc_cleanup won't be invoked. +You'll have to explicitly register a clean-up function with +new-placement syntax. + +* Evidently cfront 3.0 does not allow destructors to be explicitly +invoked using the ANSI-conforming syntax t->~T(). If you're using +cfront 3.0, you'll have to comment out the class gc_cleanup, which +uses explicit invocation. + +5. GC name conflicts: + +Many other systems seem to use the identifier "GC" as an abbreviation +for "Graphics Context". Thus, GC placement has been replaced +by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined. + +****************************************************************************/ + +#include "gc.h" + +#ifdef GC_INCLUDE_NEW +# include // for std, bad_alloc +#endif + +#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 +# define GC_cdecl _cdecl +#endif + +#if !defined(GC_NO_OPERATOR_NEW_ARRAY) \ + && !defined(_ENABLE_ARRAYNEW) /* Digimars */ \ + && (defined(__BORLANDC__) && (__BORLANDC__ < 0x450) \ + || (defined(__GNUC__) && !GC_GNUC_PREREQ(2, 6)) \ + || (defined(_MSC_VER) && _MSC_VER <= 1020) \ + || (defined(__WATCOMC__) && __WATCOMC__ < 1050)) +# define GC_NO_OPERATOR_NEW_ARRAY +#endif + +#if !defined(GC_NO_OPERATOR_NEW_ARRAY) && !defined(GC_OPERATOR_NEW_ARRAY) +# define GC_OPERATOR_NEW_ARRAY +#endif + +#if (!defined(__BORLANDC__) || __BORLANDC__ > 0x0620) \ + && ! defined (__sgi) && ! defined(__WATCOMC__) \ + && (!defined(_MSC_VER) || _MSC_VER > 1020) +# define GC_PLACEMENT_DELETE +#endif + +#if !defined(GC_NEW_DELETE_THROW_NOT_NEEDED) \ + && !defined(GC_NEW_DELETE_NEED_THROW) && GC_GNUC_PREREQ(4, 2) \ + && (__cplusplus < 201103L || defined(__clang__)) +# define GC_NEW_DELETE_NEED_THROW +#endif + +#ifndef GC_NEW_DELETE_NEED_THROW +# define GC_DECL_NEW_THROW /* empty */ +#elif __cplusplus >= 201703L || _MSVC_LANG >= 201703L + // The "dynamic exception" syntax had been deprecated in C++11 + // and was removed in C++17. +# define GC_DECL_NEW_THROW noexcept(false) +#elif defined(GC_INCLUDE_NEW) +# define GC_DECL_NEW_THROW throw(std::bad_alloc) +#else +# define GC_DECL_NEW_THROW /* empty (as bad_alloc might be undeclared) */ +#endif + +#if defined(GC_NEW_ABORTS_ON_OOM) || defined(_LIBCPP_NO_EXCEPTIONS) +# define GC_OP_NEW_OOM_CHECK(obj) \ + do { if (!(obj)) GC_abort_on_oom(); } while (0) +#elif defined(GC_INCLUDE_NEW) +# define GC_OP_NEW_OOM_CHECK(obj) if (obj) {} else throw std::bad_alloc() +#else + // "new" header is not included, so bad_alloc cannot be thrown directly. + GC_API void GC_CALL GC_throw_bad_alloc(); +# define GC_OP_NEW_OOM_CHECK(obj) if (obj) {} else GC_throw_bad_alloc() +#endif // !GC_NEW_ABORTS_ON_OOM && !GC_INCLUDE_NEW + +#ifdef GC_NAMESPACE +namespace boehmgc +{ +#endif + +enum GCPlacement +{ + UseGC, +# ifndef GC_NAME_CONFLICT + GC = UseGC, +# endif + NoGC, + PointerFreeGC +# ifdef GC_ATOMIC_UNCOLLECTABLE + , PointerFreeNoGC +# endif +}; + +/** + * Instances of classes derived from "gc" will be allocated in the collected + * heap by default, unless an explicit NoGC placement is specified. + */ +class gc +{ +public: + inline void* operator new(size_t size); + inline void* operator new(size_t size, GCPlacement gcp); + inline void* operator new(size_t size, void* p) GC_NOEXCEPT; + // Must be redefined here, since the other overloadings hide + // the global definition. + inline void operator delete(void* obj) GC_NOEXCEPT; + +# ifdef GC_PLACEMENT_DELETE + inline void operator delete(void*, GCPlacement) GC_NOEXCEPT; + // Called if construction fails. + inline void operator delete(void*, void*) GC_NOEXCEPT; +# endif // GC_PLACEMENT_DELETE + +# ifdef GC_OPERATOR_NEW_ARRAY + inline void* operator new[](size_t size); + inline void* operator new[](size_t size, GCPlacement gcp); + inline void* operator new[](size_t size, void* p) GC_NOEXCEPT; + inline void operator delete[](void* obj) GC_NOEXCEPT; +# ifdef GC_PLACEMENT_DELETE + inline void operator delete[](void*, GCPlacement) GC_NOEXCEPT; + inline void operator delete[](void*, void*) GC_NOEXCEPT; +# endif +# endif // GC_OPERATOR_NEW_ARRAY +}; + +/** + * Instances of classes derived from "gc_cleanup" will be allocated + * in the collected heap by default. When the collector discovers + * an inaccessible object derived from "gc_cleanup" or containing + * a member derived from "gc_cleanup", its destructors will be invoked. + */ +class gc_cleanup: virtual public gc +{ +public: + inline gc_cleanup(); + inline virtual ~gc_cleanup(); + +private: + inline static void GC_cdecl cleanup(void* obj, void* clientData); +}; + +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) + // TODO: "non-member operator new or delete may not be declared inline" + // warning is disabled for now. +# pragma warning(disable:4595) +#endif + +inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, + GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */ = 0, + void* /* clientData */ = 0); + // Allocates a collectible or uncollectible object, according to the + // value of "gcp". + // + // 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 + // delete an object allocated with a non-null "cleanup". + // + // It is an error to specify a non-null "cleanup" with NoGC or for + // classes derived from "gc_cleanup" or containing members derived + // from "gc_cleanup". + +#ifdef GC_PLACEMENT_DELETE + inline void operator delete(void*, GC_NS_QUALIFY(GCPlacement), + GC_NS_QUALIFY(GCCleanUpFunc), + void*) GC_NOEXCEPT; +#endif + +#ifndef GC_NO_INLINE_STD_NEW + +#if defined(_MSC_VER) || defined(__DMC__) \ + || ((defined(__BORLANDC__) || defined(__CYGWIN__) \ + || defined(__CYGWIN32__) || defined(__MINGW32__) \ + || defined(__WATCOMC__)) \ + && !defined(GC_BUILD) && !defined(GC_NOT_DLL)) + // Inlining done to avoid mix up of new and delete operators by VC++ 9 (due + // to arbitrary ordering during linking). + +# ifdef GC_OPERATOR_NEW_ARRAY + inline void* operator new[](size_t size) GC_DECL_NEW_THROW + { + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; + } + + inline void operator delete[](void* obj) GC_NOEXCEPT + { + GC_FREE(obj); + } +# endif + + inline void* operator new(size_t size) GC_DECL_NEW_THROW + { + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; + } + + inline void operator delete(void* obj) GC_NOEXCEPT + { + GC_FREE(obj); + } + +# if __cplusplus >= 201402L || _MSVC_LANG >= 201402L // C++14 + inline void operator delete(void* obj, size_t size) GC_NOEXCEPT { + (void)size; // size is ignored + GC_FREE(obj); + } + +# if defined(GC_OPERATOR_NEW_ARRAY) + inline void operator delete[](void* obj, size_t size) GC_NOEXCEPT { + (void)size; + GC_FREE(obj); + } +# endif +# endif // C++14 +#endif + +#ifdef _MSC_VER + + // This new operator is used by VC++ in case of Debug builds: +# ifdef GC_DEBUG + inline void* operator new(size_t size, int /* nBlockUse */, + const char* szFileName, int nLine) + { + void* obj = GC_debug_malloc_uncollectable(size, szFileName, nLine); + GC_OP_NEW_OOM_CHECK(obj); + return obj; + } +# else + inline void* operator new(size_t size, int /* nBlockUse */, + const char* /* szFileName */, int /* nLine */) + { + void* obj = GC_malloc_uncollectable(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; + } +# endif /* !GC_DEBUG */ + +# ifdef GC_OPERATOR_NEW_ARRAY + // This new operator is used by VC++ 7+ in Debug builds: + inline void* operator new[](size_t size, int nBlockUse, + const char* szFileName, int nLine) + { + return operator new(size, nBlockUse, szFileName, nLine); + } +# endif + +#endif // _MSC_VER + +#elif defined(_MSC_VER) + // The following ensures that the system default operator new[] does not + // get undefined, which is what seems to happen on VC++ 6 for some reason + // if we define a multi-argument operator new[]. + // There seems to be no way to redirect new in this environment without + // including this everywhere. +# ifdef GC_OPERATOR_NEW_ARRAY + void *operator new[](size_t size) GC_DECL_NEW_THROW; + void operator delete[](void* obj) GC_NOEXCEPT; + + void* operator new[](size_t size, int /* nBlockUse */, + const char * szFileName, int nLine); +# endif // GC_OPERATOR_NEW_ARRAY + + void* operator new(size_t size) GC_DECL_NEW_THROW; + void operator delete(void* obj) GC_NOEXCEPT; + + void* operator new(size_t size, int /* nBlockUse */, + const char * szFileName, int nLine); +#endif // GC_NO_INLINE_STD_NEW && _MSC_VER + +#ifdef GC_OPERATOR_NEW_ARRAY + // The operator new for arrays, identical to the above. + inline void* operator new[](size_t size, GC_NS_QUALIFY(GCPlacement) gcp, + GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */ = 0, + void* /* clientData */ = 0); +#endif // GC_OPERATOR_NEW_ARRAY + +/* Inline implementation */ + +#ifdef GC_NAMESPACE +namespace boehmgc +{ +#endif + +inline void* gc::operator new(size_t size) +{ + void* obj = GC_MALLOC(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; +} + +inline void* gc::operator new(size_t size, GCPlacement gcp) +{ + void* obj; + switch (gcp) { + case UseGC: + obj = GC_MALLOC(size); + break; + case PointerFreeGC: + obj = GC_MALLOC_ATOMIC(size); + break; +# ifdef GC_ATOMIC_UNCOLLECTABLE + case PointerFreeNoGC: + obj = GC_MALLOC_ATOMIC_UNCOLLECTABLE(size); + break; +# endif + case NoGC: + default: + obj = GC_MALLOC_UNCOLLECTABLE(size); + } + GC_OP_NEW_OOM_CHECK(obj); + return obj; +} + +inline void* gc::operator new(size_t /* size */, void* p) GC_NOEXCEPT +{ + return p; +} + +inline void gc::operator delete(void* obj) GC_NOEXCEPT +{ + GC_FREE(obj); +} + +#ifdef GC_PLACEMENT_DELETE + inline void gc::operator delete(void*, void*) GC_NOEXCEPT {} + + inline void gc::operator delete(void* p, GCPlacement /* gcp */) GC_NOEXCEPT + { + GC_FREE(p); + } +#endif // GC_PLACEMENT_DELETE + +#ifdef GC_OPERATOR_NEW_ARRAY + inline void* gc::operator new[](size_t size) + { + return gc::operator new(size); + } + + inline void* gc::operator new[](size_t size, GCPlacement gcp) + { + return gc::operator new(size, gcp); + } + + inline void* gc::operator new[](size_t /* size */, void* p) GC_NOEXCEPT + { + return p; + } + + inline void gc::operator delete[](void* obj) GC_NOEXCEPT + { + gc::operator delete(obj); + } + +# ifdef GC_PLACEMENT_DELETE + inline void gc::operator delete[](void*, void*) GC_NOEXCEPT {} + + inline void gc::operator delete[](void* p, + GCPlacement /* gcp */) GC_NOEXCEPT + { + gc::operator delete(p); + } +# endif +#endif // GC_OPERATOR_NEW_ARRAY + +inline gc_cleanup::~gc_cleanup() +{ +# ifndef GC_NO_FINALIZATION + void* base = GC_base(this); + if (0 == base) return; // Non-heap object. + GC_register_finalizer_ignore_self(base, 0, 0, 0, 0); +# endif +} + +inline void GC_CALLBACK gc_cleanup::cleanup(void* obj, void* displ) +{ + ((gc_cleanup*) ((char*) obj + (ptrdiff_t) displ))->~gc_cleanup(); +} + +inline gc_cleanup::gc_cleanup() +{ +# ifndef GC_NO_FINALIZATION + GC_finalization_proc oldProc = 0; + void* oldData = NULL; // to avoid "might be uninitialized" compiler warning + void* this_ptr = (void*)this; + void* base = GC_base(this_ptr); + if (base != 0) { + // Don't call the debug version, since this is a real base address. + GC_register_finalizer_ignore_self(base, (GC_finalization_proc) cleanup, + (void*)((char*)this_ptr-(char*)base), + &oldProc, &oldData); + if (oldProc != 0) { + GC_register_finalizer_ignore_self(base, oldProc, oldData, 0, 0); + } + } +# elif defined(CPPCHECK) + (void)cleanup; +# endif +} + +#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; + switch (gcp) { + case GC_NS_QUALIFY(UseGC): + obj = GC_MALLOC(size); +# ifndef GC_NO_FINALIZATION + if (cleanup != 0 && obj != 0) { + GC_REGISTER_FINALIZER_IGNORE_SELF(obj, cleanup, clientData, 0, 0); + } +# else + (void)cleanup; + (void)clientData; +# endif + break; + case GC_NS_QUALIFY(PointerFreeGC): + obj = GC_MALLOC_ATOMIC(size); + break; +# ifdef GC_ATOMIC_UNCOLLECTABLE + case GC_NS_QUALIFY(PointerFreeNoGC): + obj = GC_MALLOC_ATOMIC_UNCOLLECTABLE(size); + break; +# endif + case GC_NS_QUALIFY(NoGC): + default: + obj = GC_MALLOC_UNCOLLECTABLE(size); + } + GC_OP_NEW_OOM_CHECK(obj); + return obj; +} + +#ifdef GC_PLACEMENT_DELETE + inline void operator delete(void* p, GC_NS_QUALIFY(GCPlacement) /* gcp */, + GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */, + void* /* clientData */) GC_NOEXCEPT + { + GC_FREE(p); + } +#endif // GC_PLACEMENT_DELETE + +#ifdef GC_OPERATOR_NEW_ARRAY + 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); + } +#endif // GC_OPERATOR_NEW_ARRAY + +#endif /* GC_CPP_H */ diff --git a/bdwgc/include/gc_disclaim.h b/bdwgc/include/gc_disclaim.h new file mode 100644 index 000000000..785182b5e --- /dev/null +++ b/bdwgc/include/gc_disclaim.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2007-2011 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. + * + * 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. + * + */ + +#ifndef GC_DISCLAIM_H +#define GC_DISCLAIM_H + +#include "gc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* This API is defined only if the library has been suitably compiled */ +/* (i.e. with ENABLE_DISCLAIM defined). */ + +/* Prepare the object kind used by GC_finalized_malloc. Call it from */ +/* your initialization code or, at least, at some point before using */ +/* finalized allocations. The function is thread-safe. */ +GC_API void GC_CALL GC_init_finalized_malloc(void); + +/* Type of a disclaim call-back. Called with the allocation lock held. */ +typedef int (GC_CALLBACK * GC_disclaim_proc)(void * /*obj*/); + +/* Register "proc" to be called on each object of "kind" ready to be */ +/* reclaimed. If "proc" returns non-zero, the collector will not */ +/* reclaim the object on this GC cycle ("proc" should not try to */ +/* resurrect the object otherwise). Objects reachable from "proc" */ +/* (including the referred closure object) will be protected from */ +/* collection if "mark_from_all" is non-zero, but at the expense that */ +/* long chains of objects will take many cycles to reclaim. */ +/* Calls to GC_free() will free its argument without inquiring "proc". */ +/* No-op in the leak-finding mode. */ +GC_API void GC_CALL GC_register_disclaim_proc(int /*kind*/, + GC_disclaim_proc /*proc*/, + int /*mark_from_all*/); + +/* The finalizer closure used by GC_finalized_malloc. */ +struct GC_finalizer_closure { + GC_finalization_proc proc; + void *cd; +}; + +/* Allocate "size" bytes which is finalized by "fc". This uses a */ +/* 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. */ +/* The collector will reclaim the object during this GC cycle (thus, */ +/* "proc" should not try to resurrect the object). The other objects */ +/* reachable from "proc" (including the closure object in case it is */ +/* a heap-allocated one) will be protected from collection. */ +/* Note that GC_size (applied to such allocated object) returns a value */ +/* slightly bigger than the specified allocation size, and that GC_base */ +/* result points to a word prior to the start of the allocated object. */ +/* The disclaim procedure is not invoked in the leak-finding mode. */ +/* There is no debugging version of this allocation API. */ +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*/) GC_ATTR_NONNULL(2); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/bdwgc/include/gc_gcj.h b/bdwgc/include/gc_gcj.h new file mode 100644 index 000000000..4dbf0220f --- /dev/null +++ b/bdwgc/include/gc_gcj.h @@ -0,0 +1,111 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright 1999 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. + * + * 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. + */ + +/* This file assumes the collector has been compiled with GC_GCJ_SUPPORT. */ + +/* + * We allocate objects whose first word contains a pointer to a struct + * describing the object type. This struct contains a garbage collector mark + * descriptor at offset MARK_DESCR_OFFSET. Alternatively, the objects + * may be marked by the mark procedure passed to GC_init_gcj_malloc. + */ + +#ifndef GC_GCJ_H +#define GC_GCJ_H + + /* Gcj keeps GC descriptor as second word of vtable. This */ + /* probably needs to be adjusted for other clients. */ + /* We currently assume that this offset is such that: */ + /* - all objects of this kind are large enough to have */ + /* a value at that offset, and */ + /* - it is not zero. */ + /* These assumptions allow objects on the free list to be */ + /* marked normally. */ + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* The following allocators signal an out of memory condition with */ +/* return GC_oom_fn(bytes); */ + +/* The following function must be called before the gcj allocators */ +/* can be invoked. */ +/* mp_index and mp are the index and mark_proc (see gc_mark.h) */ +/* respectively for the allocated objects. Mark_proc will be */ +/* used to build the descriptor for objects allocated through the */ +/* debugging interface. The mark_proc will be invoked on all such */ +/* objects with an "environment" value of 1. The client may choose */ +/* to use the same mark_proc for some of its generated mark descriptors.*/ +/* In that case, it should use a different "environment" value to */ +/* detect the presence or absence of the debug header. */ +/* Mp is really of type mark_proc, as defined in gc_mark.h. We don't */ +/* want to include that here for namespace pollution reasons. */ +/* Passing in mp_index here instead of having GC_init_gcj_malloc() */ +/* internally call GC_new_proc() is quite ugly, but in typical usage */ +/* scenarios a compiler also has to know about mp_index, so */ +/* generating it dynamically is not acceptable. Mp_index will */ +/* typically be an integer < RESERVED_MARK_PROCS, so that it doesn't */ +/* collide with GC_new_proc allocated indices. If the application */ +/* needs no other reserved indices, zero */ +/* (GC_GCJ_RESERVED_MARK_PROC_INDEX in gc_mark.h) is an obvious choice. */ +GC_API void GC_CALL GC_init_gcj_malloc(int /* mp_index */, + void * /* really mark_proc */ /* mp */); + +/* 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 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 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 (i.e. within the first heap block) of the allocated object */ +/* is always maintained. */ +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. */ +GC_API int GC_gcj_kind; + +GC_API int GC_gcj_debug_kind; + +#ifdef GC_DEBUG +# define GC_GCJ_MALLOC(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS) +# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS) +#else +# define GC_GCJ_MALLOC(s,d) GC_gcj_malloc(s,d) +# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_gcj_malloc_ignore_off_page(s,d) +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_GCJ_H */ diff --git a/bdwgc/include/gc_inline.h b/bdwgc/include/gc_inline.h new file mode 100644 index 000000000..a2aa5e6c3 --- /dev/null +++ b/bdwgc/include/gc_inline.h @@ -0,0 +1,209 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * + * 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. + */ + +#ifndef GC_INLINE_H +#define GC_INLINE_H + +/* WARNING: */ +/* Note that for these routines, it is the clients responsibility to */ +/* add the extra byte at the end to deal with one-past-the-end pointers.*/ +/* In the standard collector configuration, the collector assumes that */ +/* such a byte has been added, and hence does not trace the last word */ +/* in the resulting object. */ +/* This is not an issue if the collector is compiled with */ +/* DONT_ADD_BYTE_AT_END, or if GC_all_interior_pointers is not set. */ +/* This interface is most useful for compilers that generate C. */ +/* It is also used internally for thread-local allocation. */ +/* Manual use is hereby discouraged. */ +/* There is no debugging version of this allocation API. */ + +#include "gc.h" +#include "gc_tiny_fl.h" + +#if GC_GNUC_PREREQ(3, 0) +# define GC_EXPECT(expr, outcome) __builtin_expect(expr,outcome) + /* Equivalent to (expr), but predict that usually (expr)==outcome. */ +#else +# define GC_EXPECT(expr, outcome) (expr) +#endif + +#ifndef GC_ASSERT +# ifdef NDEBUG +# define GC_ASSERT(expr) /* empty */ +# else +# include +# define GC_ASSERT(expr) assert(expr) +# endif +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef GC_PREFETCH_FOR_WRITE +# if GC_GNUC_PREREQ(3, 0) && !defined(GC_NO_PREFETCH_FOR_WRITE) +# define GC_PREFETCH_FOR_WRITE(x) __builtin_prefetch((x), 1) +# else +# define GC_PREFETCH_FOR_WRITE(x) (void)0 +# endif +#endif + +/* Object kinds; must match PTRFREE, NORMAL in gc_priv.h. */ +#define GC_I_PTRFREE 0 +#define GC_I_NORMAL 1 + +/* Store a pointer to a list of newly allocated objects of kind k and */ +/* size lb in *result. The caller must make sure that *result is */ +/* traced even if objects are ptrfree. */ +GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */, + void ** /* result */); + +/* Generalized version of GC_malloc and GC_malloc_atomic. */ +/* Uses appropriately the thread-local (if available) or the global */ +/* free-list of the specified kind. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_kind(size_t /* lb */, int /* k */); + +#ifdef GC_THREADS + /* Same as above but uses only the global free-list. */ + GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_kind_global(size_t /* lb */, int /* k */); +#else +# define GC_malloc_kind_global GC_malloc_kind +#endif + +/* An internal macro to update the free list pointer atomically (if */ +/* the AO primitives are available) to avoid race with the marker. */ +#if defined(GC_THREADS) && defined(AO_HAVE_store) +# define GC_FAST_M_AO_STORE(my_fl, next) \ + AO_store((volatile AO_t *)(my_fl), (AO_t)(next)) +#else +# define GC_FAST_M_AO_STORE(my_fl, next) (void)(*(my_fl) = (next)) +#endif + +/* The ultimately general inline allocation macro. Allocate an object */ +/* of size granules, putting the resulting pointer in result. Tiny_fl */ +/* is a "tiny" free list array, which will be used first, if the size */ +/* is appropriate. If granules argument is too large, we allocate with */ +/* default_expr instead. If we need to refill the free list, we use */ +/* GC_generic_malloc_many with the indicated kind. */ +/* Tiny_fl should be an array of GC_TINY_FREELISTS void * pointers. */ +/* If num_direct is nonzero, and the individual free list pointers */ +/* are initialized to (void *)1, then we allocate num_direct granules */ +/* directly using generic_malloc before putting multiple objects into */ +/* the tiny_fl entry. If num_direct is zero, then the free lists may */ +/* also be initialized to (void *)0. */ +/* Note that we use the zeroth free list to hold objects 1 granule in */ +/* size that are used to satisfy size 0 allocation requests. */ +/* We rely on much of this hopefully getting optimized away in the */ +/* num_direct = 0 case. */ +/* Particularly, if granules argument is constant, this should generate */ +/* a small amount of code. */ +# define GC_FAST_MALLOC_GRANS(result,granules,tiny_fl,num_direct, \ + kind,default_expr,init) \ + 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; \ + \ + for (;;) { \ + if (GC_EXPECT((GC_word)my_entry \ + > (num_direct) + GC_TINY_FREELISTS + 1, 1)) { \ + next = *(void **)(my_entry); \ + result = (void *)my_entry; \ + GC_FAST_M_AO_STORE(my_fl, next); \ + init; \ + GC_PREFETCH_FOR_WRITE(next); \ + if ((kind) != GC_I_PTRFREE) { \ + GC_end_stubborn_change(my_fl); \ + GC_reachable_here(next); \ + } \ + GC_ASSERT(GC_size(result) >= (granules)*GC_GRANULE_BYTES); \ + GC_ASSERT((kind) == GC_I_PTRFREE \ + || ((GC_word *)result)[1] == 0); \ + break; \ + } \ + /* Entry contains counter or NULL */ \ + if ((GC_signed_word)my_entry - (GC_signed_word)(num_direct) <= 0 \ + /* (GC_word)my_entry <= (num_direct) */ \ + && my_entry != 0 /* NULL */) { \ + /* Small counter value, not NULL */ \ + GC_FAST_M_AO_STORE(my_fl, (char *)my_entry \ + + (granules) + 1); \ + result = (default_expr); \ + break; \ + } else { \ + /* Large counter or NULL */ \ + GC_generic_malloc_many(((granules) == 0? GC_GRANULE_BYTES : \ + GC_RAW_BYTES_FROM_INDEX(granules)), \ + kind, my_fl); \ + my_entry = *my_fl; \ + if (my_entry == 0) { \ + result = (*GC_get_oom_fn())((granules)*GC_GRANULE_BYTES); \ + break; \ + } \ + } \ + } \ + } \ + } while (0) + +# define GC_WORDS_TO_WHOLE_GRANULES(n) \ + GC_WORDS_TO_GRANULES((n) + GC_GRANULE_WORDS - 1) + +/* Allocate n words (NOT BYTES). X is made to point to the result. */ +/* This should really only be used if GC_all_interior_pointers is */ +/* not set, or DONT_ADD_BYTE_AT_END is set. See above. */ +/* Does not acquire lock. The caller is responsible for supplying */ +/* a cleared tiny_fl free list array. For single-threaded */ +/* applications, this may be a global array. */ +# define GC_MALLOC_WORDS_KIND(result,n,tiny_fl,kind,init) \ + do { \ + size_t granules = GC_WORDS_TO_WHOLE_GRANULES(n); \ + GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, 0, kind, \ + GC_malloc_kind(granules*GC_GRANULE_BYTES, kind), \ + init); \ + } while (0) + +# define GC_MALLOC_WORDS(result,n,tiny_fl) \ + GC_MALLOC_WORDS_KIND(result, n, tiny_fl, GC_I_NORMAL, \ + *(void **)(result) = 0) + +# define GC_MALLOC_ATOMIC_WORDS(result,n,tiny_fl) \ + GC_MALLOC_WORDS_KIND(result, n, tiny_fl, GC_I_PTRFREE, (void)0) + +/* And once more for two word initialized objects: */ +# define GC_CONS(result, first, second, tiny_fl) \ + do { \ + void *l = (void *)(first); \ + void *r = (void *)(second); \ + GC_MALLOC_WORDS_KIND(result, 2, tiny_fl, GC_I_NORMAL, (void)0); \ + if ((result) != 0 /* NULL */) { \ + *(void **)(result) = l; \ + GC_ptr_store_and_dirty((void **)(result) + 1, r); \ + GC_reachable_here(l); \ + } \ + } while (0) + +GC_API void GC_CALL GC_print_free_list(int /* kind */, + size_t /* sz_in_granules */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* !GC_INLINE_H */ diff --git a/bdwgc/include/gc_mark.h b/bdwgc/include/gc_mark.h new file mode 100644 index 000000000..90121d890 --- /dev/null +++ b/bdwgc/include/gc_mark.h @@ -0,0 +1,333 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2001 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. + * + * 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. + * + */ + +/* + * This contains interfaces to the GC marker that are likely to be useful to + * clients that provide detailed heap layout information to the collector. + * This interface should not be used by normal C or C++ clients. + * It will be useful to runtimes for other languages. + * + * This is an experts-only interface! There are many ways to break the + * collector in subtle ways by using this functionality. + */ +#ifndef GC_MARK_H +#define GC_MARK_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#define GC_PROC_BYTES 100 + +#if defined(GC_BUILD) || defined(NOT_GCBUILD) + struct GC_ms_entry; +#else + struct GC_ms_entry { void *opaque; }; +#endif + +/* A client supplied mark procedure. Returns new mark stack pointer. */ +/* Primary effect should be to push new entries on the mark stack. */ +/* Mark stack pointer values are passed and returned explicitly. */ +/* Global variables describing mark stack are not necessarily valid. */ +/* (This usually saves a few cycles by keeping things in registers.) */ +/* Assumed to scan about GC_PROC_BYTES on average. If it needs to do */ +/* much more work than that, it should do it in smaller pieces by */ +/* pushing itself back on the mark stack. */ +/* Note that it should always do some work (defined as marking some */ +/* objects) before pushing more than one entry on the mark stack. */ +/* This is required to ensure termination in the event of mark stack */ +/* overflows. */ +/* This procedure is always called with at least one empty entry on the */ +/* mark stack. */ +/* Currently we require that mark procedures look for pointers in a */ +/* subset of the places the conservative marker would. It must be safe */ +/* to invoke the normal mark procedure instead. */ +/* WARNING: Such a mark procedure may be invoked on an unused object */ +/* residing on a free list. Such objects are cleared, except for a */ +/* free list link field in the first word. Thus mark procedures may */ +/* not count on the presence of a type descriptor, and must handle this */ +/* case correctly somehow. Also, a mark procedure should be prepared */ +/* to be executed concurrently from the marker threads (the later ones */ +/* are created only if the client has called GC_start_mark_threads() */ +/* or started a user thread previously). */ +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 */, + GC_word /* env */); + +#define GC_LOG_MAX_MARK_PROCS 6 +#define GC_MAX_MARK_PROCS (1 << GC_LOG_MAX_MARK_PROCS) + +/* In a few cases it's necessary to assign statically known indices to */ +/* certain mark procs. Thus we reserve a few for well known clients. */ +/* (This is necessary if mark descriptors are compiler generated.) */ +#define GC_RESERVED_MARK_PROCS 8 +#define GC_GCJ_RESERVED_MARK_PROC_INDEX 0 + +/* Object descriptors on mark stack or in objects. Low order two */ +/* bits are tags distinguishing among the following 4 possibilities */ +/* for the high order 30 bits. */ +#define GC_DS_TAG_BITS 2 +#define GC_DS_TAGS ((1 << GC_DS_TAG_BITS) - 1) +#define GC_DS_LENGTH 0 /* The entire word is a length in bytes that */ + /* must be a multiple of 4. */ +#define GC_DS_BITMAP 1 /* 30 (62) bits are a bitmap describing pointer */ + /* fields. The msb is 1 if the first word */ + /* is a pointer. */ + /* (This unconventional ordering sometimes */ + /* makes the marker slightly faster.) */ + /* Zeroes indicate definite nonpointers. Ones */ + /* indicate possible pointers. */ + /* Only usable if pointers are word aligned. */ +#define GC_DS_PROC 2 + /* The objects referenced by this object can be */ + /* pushed on the mark stack by invoking */ + /* PROC(descr). ENV(descr) is passed as the */ + /* last argument. */ +#define GC_MAKE_PROC(proc_index, env) \ + (((((env) << GC_LOG_MAX_MARK_PROCS) \ + | (proc_index)) << GC_DS_TAG_BITS) | GC_DS_PROC) +#define GC_DS_PER_OBJECT 3 /* The real descriptor is at the */ + /* byte displacement from the beginning of the */ + /* object given by descr & ~GC_DS_TAGS. */ + /* If the descriptor is negative, the real */ + /* descriptor is at (*) - */ + /* (descr&~GC_DS_TAGS) - GC_INDIR_PER_OBJ_BIAS */ + /* The latter alternative can be used if each */ + /* object contains a type descriptor in the */ + /* first word. */ + /* 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 */ + /* held. */ +#define GC_INDIR_PER_OBJ_BIAS 0x10 + +GC_API void * GC_least_plausible_heap_addr; +GC_API void * GC_greatest_plausible_heap_addr; + /* Bounds on the heap. Guaranteed valid */ + /* Likely to include future heap expansion. */ + /* Hence usually includes not-yet-mapped */ + /* memory, or might overlap with other data */ + /* roots. */ + +/* Handle nested references in a custom mark procedure. */ +/* Check if obj is a valid object. If so, ensure that it is marked. */ +/* If it was not previously marked, push its contents onto the mark */ +/* stack for future scanning. The object will then be scanned using */ +/* its mark descriptor. */ +/* Returns the new mark stack pointer. */ +/* Handles mark stack overflows correctly. */ +/* Since this marks first, it makes progress even if there are mark */ +/* stack overflows. */ +/* Src is the address of the pointer to obj, which is used only */ +/* for back pointer-based heap debugging. */ +/* It is strongly recommended that most objects be handled without mark */ +/* procedures, e.g. with bitmap descriptors, and that mark procedures */ +/* be reserved for exceptional cases. That will ensure that */ +/* performance of this call is not extremely performance critical. */ +/* (Otherwise we would need to inline GC_mark_and_push completely, */ +/* which would tie the client code to a fixed collector version.) */ +/* Note that mark procedures should explicitly call FIXUP_POINTER() */ +/* if required. */ +GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void * /* obj */, + struct GC_ms_entry * /* mark_stack_ptr */, + struct GC_ms_entry * /* mark_stack_limit */, + void ** /* src */); + +#define GC_MARK_AND_PUSH(obj, msp, lim, src) \ + ((GC_word)(obj) >= (GC_word)GC_least_plausible_heap_addr && \ + (GC_word)(obj) <= (GC_word)GC_greatest_plausible_heap_addr ? \ + GC_mark_and_push(obj, msp, lim, src) : (msp)) + +/* The size of the header added to objects allocated through the */ +/* GC_debug routines. Defined as a function so that client mark */ +/* procedures do not need to be recompiled for the collector library */ +/* version changes. */ +GC_API GC_ATTR_CONST size_t GC_CALL GC_get_debug_header_size(void); +#define GC_USR_PTR_FROM_BASE(p) \ + ((void *)((char *)(p) + GC_get_debug_header_size())) + +/* The same but defined as a variable. Exists only for the backward */ +/* compatibility. Some compilers do not accept "const" together with */ +/* deprecated or dllimport attributes, so the symbol is exported as */ +/* a non-constant one. */ +GC_API GC_ATTR_DEPRECATED +# ifdef GC_BUILD + const +# endif + size_t GC_debug_header_size; + +/* And some routines to support creation of new "kinds", e.g. with */ +/* custom mark procedures, by language runtimes. */ +/* The _inner versions assume the caller holds the allocation lock. */ + +/* Return a new free list array. */ +GC_API void ** GC_CALL GC_new_free_list(void); +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_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_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. By default, there are only */ +/* a few kinds: composite (pointerful), 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 GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL GC_generic_malloc( + size_t /* lb */, + int /* knd */); + +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_malloc_ignore_off_page( + size_t /* lb */, int /* knd */); + /* As above, but pointers to past the */ + /* first hblk of the resulting object */ + /* are ignored. */ + +/* Generalized version of GC_malloc_[atomic_]uncollectable. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_malloc_uncollectable( + size_t /* lb */, int /* knd */); + +/* Same as above but primary for allocating an object of the same kind */ +/* as an existing one (kind obtained by GC_get_kind_and_size). */ +/* Not suitable for GCJ and typed-malloc kinds. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_or_special_malloc( + size_t /* size */, int /* knd */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_generic_or_special_malloc( + size_t /* size */, int /* knd */, + GC_EXTRA_PARAMS); + +#ifdef GC_DEBUG +# define GC_GENERIC_OR_SPECIAL_MALLOC(sz, knd) \ + GC_debug_generic_or_special_malloc(sz, knd, GC_EXTRAS) +#else +# define GC_GENERIC_OR_SPECIAL_MALLOC(sz, knd) \ + GC_generic_or_special_malloc(sz, knd) +#endif /* !GC_DEBUG */ + +/* Similar to GC_size but returns object kind. Size is returned too */ +/* if psize is not NULL. */ +GC_API int GC_CALL GC_get_kind_and_size(const void *, size_t * /* psize */) + GC_ATTR_NONNULL(1); + +typedef void (GC_CALLBACK * GC_describe_type_fn)(void * /* p */, + char * /* out_buf */); + /* A procedure which */ + /* produces a human-readable */ + /* description of the "type" of object */ + /* p into the buffer out_buf of length */ + /* GC_TYPE_DESCR_LEN. This is used by */ + /* the debug support when printing */ + /* objects. */ + /* These functions should be as robust */ + /* as possible, though we do avoid */ + /* invoking them on objects on the */ + /* global free list. */ +#define GC_TYPE_DESCR_LEN 40 + +GC_API void GC_CALL GC_register_describe_type_fn(int /* kind */, + GC_describe_type_fn); + /* Register a describe_type function */ + /* 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 */ +/* correctly. Unless you really understand the collector internals, */ +/* the callback should not, directly or indirectly, make any GC_ or */ +/* potentially blocking calls. In particular, it is not safe to */ +/* allocate memory using the garbage collector from within the callback */ +/* function. Both the setter and getter acquire the GC lock. */ +typedef void (GC_CALLBACK * GC_start_callback_proc)(void); +GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc); +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_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_push_all_eager also ensures that stack */ +/* is scanned immediately, not just scheduled for scanning. */ +GC_API void GC_CALL GC_push_all(void * /* bottom */, void * /* top */); +GC_API void GC_CALL GC_push_all_eager(void * /* bottom */, void * /* top */); +GC_API void GC_CALL GC_push_conditional(void * /* bottom */, void * /* top */, + int /* bool all */); +GC_API void GC_CALL GC_push_finalizer_structures(void); + +/* 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); + +/* Walk the GC heap visiting all reachable objects. Assume the caller */ +/* holds the allocation lock. Object base pointer, object size and */ +/* client custom data are passed to the callback (holding the lock). */ +typedef void (GC_CALLBACK *GC_reachable_object_proc)(void * /* obj */, + size_t /* bytes */, + void * /* client_data */); +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( + GC_reachable_object_proc, + void * /* client_data */) GC_ATTR_NONNULL(1); + +GC_API int GC_CALL GC_is_tmp_root(void *); + +GC_API void GC_CALL GC_print_trace(GC_word /* gc_no */); +GC_API void GC_CALL GC_print_trace_inner(GC_word /* gc_no */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_MARK_H */ diff --git a/bdwgc/include/gc_pthread_redirects.h b/bdwgc/include/gc_pthread_redirects.h new file mode 100644 index 000000000..c7e72fe64 --- /dev/null +++ b/bdwgc/include/gc_pthread_redirects.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +/* Our pthread support normally needs to intercept a number of thread */ +/* calls. We arrange to do that here, if appropriate. */ + +#ifndef GC_PTHREAD_REDIRECTS_H +#define GC_PTHREAD_REDIRECTS_H + +/* Included from gc.h only. Included only if GC_PTHREADS. */ +#if defined(GC_H) && defined(GC_PTHREADS) + +/* We need to intercept calls to many of the threads primitives, so */ +/* 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 */ +/* 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. */ + +#ifndef GC_PTHREAD_REDIRECTS_ONLY + +# include +# ifndef GC_NO_DLOPEN +# include +# endif +# ifndef GC_NO_PTHREAD_SIGMASK +# include /* needed anyway for proper redirection */ +# endif + +# ifdef __cplusplus + extern "C" { +# endif + +# ifndef GC_SUSPEND_THREAD_ID +# define GC_SUSPEND_THREAD_ID pthread_t +# endif + +# ifndef GC_NO_DLOPEN + GC_API void *GC_dlopen(const char * /* path */, int /* mode */); +# endif /* !GC_NO_DLOPEN */ + +# ifndef GC_NO_PTHREAD_SIGMASK +# if defined(GC_PTHREAD_SIGMASK_NEEDED) \ + || defined(_BSD_SOURCE) || defined(_GNU_SOURCE) \ + || (_POSIX_C_SOURCE >= 199506L) || (_XOPEN_SOURCE >= 500) + GC_API int GC_pthread_sigmask(int /* how */, const sigset_t *, + sigset_t * /* oset */); +# else +# define GC_NO_PTHREAD_SIGMASK +# endif +# endif /* !GC_NO_PTHREAD_SIGMASK */ + +# ifndef GC_PTHREAD_CREATE_CONST + /* This is used for pthread_create() only. */ +# define GC_PTHREAD_CREATE_CONST const +# endif + + GC_API int GC_pthread_create(pthread_t *, + GC_PTHREAD_CREATE_CONST pthread_attr_t *, + void *(*)(void *), void * /* arg */); + GC_API int GC_pthread_join(pthread_t, void ** /* retval */); + GC_API int GC_pthread_detach(pthread_t); + +# ifndef GC_NO_PTHREAD_CANCEL + GC_API int GC_pthread_cancel(pthread_t); +# endif + +# if defined(GC_HAVE_PTHREAD_EXIT) && !defined(GC_PTHREAD_EXIT_DECLARED) +# define GC_PTHREAD_EXIT_DECLARED + GC_API void GC_pthread_exit(void *) GC_PTHREAD_EXIT_ATTRIBUTE; +# endif + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +#endif /* !GC_PTHREAD_REDIRECTS_ONLY */ + +#if !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP) + /* Unless the compiler supports #pragma extern_prefix, the Tru64 */ + /* UNIX redefines some POSIX thread functions to use */ + /* mangled names. Anyway, it's safe to undef them before redefining. */ +# undef pthread_create +# undef pthread_join +# undef pthread_detach +# define pthread_create GC_pthread_create +# define pthread_join GC_pthread_join +# define pthread_detach GC_pthread_detach + +# ifndef GC_NO_PTHREAD_SIGMASK +# undef pthread_sigmask +# define pthread_sigmask GC_pthread_sigmask +# endif +# ifndef GC_NO_DLOPEN +# undef dlopen +# define dlopen GC_dlopen +# endif +# ifndef GC_NO_PTHREAD_CANCEL +# undef pthread_cancel +# define pthread_cancel GC_pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# undef pthread_exit +# define pthread_exit GC_pthread_exit +# endif +#endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_PTHREADS */ + +#endif /* GC_PTHREAD_REDIRECTS_H */ diff --git a/bdwgc/include/gc_tiny_fl.h b/bdwgc/include/gc_tiny_fl.h new file mode 100644 index 000000000..0382b4179 --- /dev/null +++ b/bdwgc/include/gc_tiny_fl.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1999-2005 Hewlett-Packard Development Company, L.P. + * + * 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. + */ + +#ifndef GC_TINY_FL_H +#define GC_TINY_FL_H +/* + * Constants and data structures for "tiny" free lists. + * These are used for thread-local allocation or in-lined allocators. + * Each global free list also essentially starts with one of these. + * However, global free lists are known to the GC. "Tiny" free lists + * are basically private to the client. Their contents are viewed as + * "in use" and marked accordingly by the core of the GC. + * + * Note that inlined code might know about the layout of these and the constants + * involved. Thus any change here may invalidate clients, and such changes should + * be avoided. Hence we keep this as simple as possible. + */ + +/* + * We always set GC_GRANULE_BYTES to twice the length of a pointer. + * This means that all allocation requests are rounded up to the next + * multiple of 16 on 64-bit architectures or 8 on 32-bit architectures. + * This appears to be a reasonable compromise between fragmentation overhead + * and space usage for mark bits (usually mark bytes). + * On many 64-bit architectures some memory references require 16-byte + * alignment, making this necessary anyway. + * For a few 32-bit architecture (e.g. x86), we may also need 16-byte alignment + * for certain memory references. But currently that does not seem to be the + * default for all conventional malloc implementations, so we ignore that + * problem. + * It would always be safe, and often useful, to be able to allocate very + * small objects with smaller alignment. But that would cost us mark bit + * space, so we no longer do so. + */ +#ifndef GC_GRANULE_BYTES + /* GC_GRANULE_BYTES should not be overridden in any instances of the GC */ + /* 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(__ILP32__)) \ + || defined(__alpha__) || defined(__powerpc64__) \ + || defined(__arch64__) +# define GC_GRANULE_BYTES 16 +# define GC_GRANULE_WORDS 2 +# else +# define GC_GRANULE_BYTES 8 +# define GC_GRANULE_WORDS 2 +# endif +#endif /* !GC_GRANULE_BYTES */ + +#if GC_GRANULE_WORDS == 2 +# define GC_WORDS_TO_GRANULES(n) ((n)>>1) +#else +# define GC_WORDS_TO_GRANULES(n) ((n)*sizeof(void *)/GC_GRANULE_BYTES) +#endif + +/* A "tiny" free list header contains TINY_FREELISTS pointers to */ +/* singly linked lists of objects of different sizes, the ith one */ +/* containing objects i granules in size. Note that there is a list */ +/* of size zero objects. */ +#ifndef GC_TINY_FREELISTS +# if GC_GRANULE_BYTES == 16 +# define GC_TINY_FREELISTS 25 +# else +# define GC_TINY_FREELISTS 33 /* Up to and including 256 bytes */ +# endif +#endif /* !GC_TINY_FREELISTS */ + +/* The ith free list corresponds to size i*GC_GRANULE_BYTES */ +/* Internally to the collector, the index can be computed with */ +/* ROUNDED_UP_GRANULES. Externally, we don't know whether */ +/* DONT_ADD_BYTE_AT_END is set, but the client should know. */ + +/* Convert a free list index to the actual size of objects */ +/* on that list, including extra space we added. Not an */ +/* inverse of the above. */ +#define GC_RAW_BYTES_FROM_INDEX(i) ((i) * GC_GRANULE_BYTES) + +#endif /* GC_TINY_FL_H */ diff --git a/bdwgc/include/gc_typed.h b/bdwgc/include/gc_typed.h new file mode 100644 index 000000000..f91c7bcec --- /dev/null +++ b/bdwgc/include/gc_typed.h @@ -0,0 +1,122 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright 1996 Silicon Graphics. All rights reserved. + * + * 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. + */ + +/* + * Some simple primitives for allocation with explicit type information. + * Facilities for dynamic type inference may be added later. + * Should be used only for extremely performance critical applications, + * or if conservative collector leakage is otherwise a problem (unlikely). + * Note that this is implemented completely separately from the rest + * of the collector, and is not linked in unless referenced. + * This does not currently support GC_DEBUG in any interesting way. + */ + +#ifndef GC_TYPED_H +#define GC_TYPED_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +typedef GC_word * GC_bitmap; + /* The least significant bit of the first word is one if */ + /* the first word in the object may be a pointer. */ + +#define GC_WORDSZ (8 * sizeof(GC_word)) +#define GC_get_bit(bm, index) \ + (((bm)[(index) / GC_WORDSZ] >> ((index) % GC_WORDSZ)) & 1) +#define GC_set_bit(bm, index) \ + ((bm)[(index) / GC_WORDSZ] |= (GC_word)1 << ((index) % GC_WORDSZ)) +#define GC_WORD_OFFSET(t, f) (offsetof(t,f) / sizeof(GC_word)) +#define GC_WORD_LEN(t) (sizeof(t) / sizeof(GC_word)) +#define GC_BITMAP_SIZE(t) ((GC_WORD_LEN(t) + GC_WORDSZ - 1) / GC_WORDSZ) + +typedef GC_word GC_descr; + +GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * /* GC_bitmap bm */, + size_t /* len (number_of_bits_in_bitmap) */); + /* Return a type descriptor for the object whose layout */ + /* is described by the argument. */ + /* The least significant bit of the first word is one */ + /* if the first word in the object may be a pointer. */ + /* The second argument specifies the number of */ + /* meaningful bits in the bitmap. The actual object */ + /* may be larger (but not smaller). Any additional */ + /* words in the object are assumed not to contain */ + /* pointers. */ + /* Returns a conservative approximation in the */ + /* (unlikely) case of insufficient memory to build */ + /* the descriptor. Calls to GC_make_descriptor */ + /* may consume some amount of a finite resource. This */ + /* is intended to be called once per type, not once */ + /* per allocation. */ + +/* It is possible to generate a descriptor for a C type T with */ +/* word aligned pointer fields f1, f2, ... as follows: */ +/* */ +/* GC_descr T_descr; */ +/* GC_word T_bitmap[GC_BITMAP_SIZE(T)] = {0}; */ +/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f1)); */ +/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f2)); */ +/* ... */ +/* T_descr = GC_make_descriptor(T_bitmap, GC_WORD_LEN(T)); */ + +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 size may NOT be less than the number of */ + /* meaningful bits in the bitmap of d multiplied by */ + /* sizeof GC_word. The returned object is cleared. */ + /* The returned object may NOT be passed to GC_realloc. */ + +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 GC_ATTR_MALLOC GC_ATTR_CALLOC_SIZE(1, 2) 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 */ + /* alignment required for pointers. E.g. on a 32-bit */ + /* machine with 16-bit aligned pointers, size_in_bytes */ + /* must be a multiple of 2. The element size may NOT */ + /* be less than the number of meaningful bits in the */ + /* bitmap of d multiplied by sizeof GC_word. */ + /* Returned object is cleared. */ + +#ifdef GC_DEBUG +# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) ((void)(d), GC_MALLOC(bytes)) +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) \ + ((void)(d), GC_MALLOC((n) * (bytes))) +#else +# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) \ + GC_malloc_explicitly_typed(bytes, d) +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) \ + GC_calloc_explicitly_typed(n, bytes, d) +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_TYPED_H */ diff --git a/bdwgc/include/gc_version.h b/bdwgc/include/gc_version.h new file mode 100644 index 000000000..bdd3dd196 --- /dev/null +++ b/bdwgc/include/gc_version.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +/* This should never be included directly; it is included only from gc.h. */ +#if defined(GC_H) + +/* The policy regarding version numbers: development code has odd */ +/* "minor" number (and "micro" part is 0); when development is finished */ +/* and a release is prepared, "minor" number is incremented (keeping */ +/* "micro" number still zero), whenever a defect is fixed a new release */ +/* is prepared incrementing "micro" part to odd value (the most stable */ +/* release has the biggest "micro" number). */ + +/* The version here should match that in configure/configure.ac */ +/* Eventually this one may become unnecessary. For now we need */ +/* it to keep the old-style build process working. */ +#define GC_TMP_VERSION_MAJOR 8 +#define GC_TMP_VERSION_MINOR 2 +#define GC_TMP_VERSION_MICRO 4 /* 8.2.4 */ + +#ifdef GC_VERSION_MAJOR +# if GC_TMP_VERSION_MAJOR != GC_VERSION_MAJOR \ + || GC_TMP_VERSION_MINOR != GC_VERSION_MINOR \ + || GC_TMP_VERSION_MICRO != GC_VERSION_MICRO +# error Inconsistent version info. Check README.md, include/gc_version.h and configure.ac. +# endif +#else +# define GC_VERSION_MAJOR GC_TMP_VERSION_MAJOR +# define GC_VERSION_MINOR GC_TMP_VERSION_MINOR +# define GC_VERSION_MICRO GC_TMP_VERSION_MICRO +#endif /* !GC_VERSION_MAJOR */ + +#endif diff --git a/bdwgc/include/include.am b/bdwgc/include/include.am new file mode 100644 index 000000000..4e8e9dfce --- /dev/null +++ b/bdwgc/include/include.am @@ -0,0 +1,47 @@ +# +# 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. + +## Process this file with automake to produce part of Makefile.in. + +# installed headers +# +pkginclude_HEADERS += \ + include/gc.h \ + include/gc_backptr.h \ + include/gc_config_macros.h \ + include/gc_inline.h \ + include/gc_mark.h \ + include/gc_tiny_fl.h \ + include/gc_typed.h \ + include/gc_version.h \ + include/javaxfc.h \ + include/leak_detector.h + +# headers which are not installed +# +dist_noinst_HEADERS += \ + include/private/darwin_semaphore.h \ + include/private/darwin_stop_world.h \ + include/private/dbg_mlc.h \ + include/private/gc_alloc_ptrs.h \ + include/private/gc_atomic_ops.h \ + include/private/gc_hdrs.h \ + include/private/gc_locks.h \ + include/private/gc_pmark.h \ + include/private/gc_priv.h \ + include/private/gcconfig.h \ + include/private/pthread_stop_world.h \ + include/private/pthread_support.h \ + include/private/specific.h \ + include/private/thread_local_alloc.h + +# unprefixed header +include_HEADERS += \ + include/extra/gc.h diff --git a/bdwgc/include/javaxfc.h b/bdwgc/include/javaxfc.h new file mode 100644 index 000000000..40ff5b7fb --- /dev/null +++ b/bdwgc/include/javaxfc.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +#ifndef GC_JAVAXFC_H +#define GC_JAVAXFC_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* + * Invoke all remaining finalizers that haven't yet been run. (Since the + * notifier is not called, this should be called from a separate thread.) + * This function is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * This is problematic for several reasons: + * 1) It means that finalizers, and all methods called by them, + * must be prepared to deal with objects that have been finalized in + * spite of the fact that they are still referenced by statically + * allocated pointer variables. + * 2) It may mean that we get stuck in an infinite loop running + * finalizers which create new finalizable objects, though that's + * probably unlikely. + * Thus this is not recommended for general use. + */ +GC_API void GC_CALL GC_finalize_all(void); + +#ifdef GC_THREADS + /* External thread suspension support. No thread suspension count */ + /* (so a thread which has been suspended numerous times will be */ + /* resumed with the very first call to GC_resume_thread). */ + /* Acquire the allocation lock. Thread should be registered in GC */ + /* (otherwise no-op, GC_is_thread_suspended returns false). */ + /* Unimplemented on some platforms. Not recommended for general use. */ +# ifndef GC_SUSPEND_THREAD_ID +# define GC_SUSPEND_THREAD_ID void* +# endif + GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID); + GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID); + GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID); +#endif /* GC_THREADS */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_JAVAXFC_H */ diff --git a/bdwgc/include/leak_detector.h b/bdwgc/include/leak_detector.h new file mode 100644 index 000000000..f8161b27e --- /dev/null +++ b/bdwgc/include/leak_detector.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2000-2011 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +#ifndef GC_LEAK_DETECTOR_H +#define GC_LEAK_DETECTOR_H + +/* Include leak_detector.h (e.g., via GCC --include directive) */ +/* to turn libgc into a leak detector. */ + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif +#include "gc.h" + +#ifndef GC_DONT_INCLUDE_STDLIB + /* We ensure stdlib.h and string.h are included before */ + /* redirecting malloc() and the accompanying functions. */ +# include +# include +#endif + +#undef malloc +#define malloc(n) GC_MALLOC(n) +#undef calloc +#define calloc(m,n) GC_MALLOC((m)*(n)) +#undef free +#define free(p) GC_FREE(p) +#undef realloc +#define realloc(p,n) GC_REALLOC(p,n) + +#undef strdup +#define strdup(s) GC_STRDUP(s) +#undef strndup +#define strndup(s,n) GC_STRNDUP(s,n) + +#ifdef GC_REQUIRE_WCSDUP + /* The collector should be built with GC_REQUIRE_WCSDUP */ + /* defined as well to redirect wcsdup(). */ +# include +# undef wcsdup +# define wcsdup(s) GC_WCSDUP(s) +#endif + +#undef memalign +#define memalign(a,n) GC_memalign(a,n) +#undef posix_memalign +#define posix_memalign(p,a,n) GC_posix_memalign(p,a,n) + +#ifndef CHECK_LEAKS +# define CHECK_LEAKS() GC_gcollect() + /* Note 1: CHECK_LEAKS does not have GC prefix (preserved for */ + /* backward compatibility). */ + /* Note 2: GC_gcollect() is also called automatically in the */ + /* leak-finding mode at program exit. */ +#endif + +#endif /* GC_LEAK_DETECTOR_H */ diff --git a/bdwgc/include/private/darwin_semaphore.h b/bdwgc/include/private/darwin_semaphore.h new file mode 100644 index 000000000..6431f439e --- /dev/null +++ b/bdwgc/include/private/darwin_semaphore.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +#ifndef GC_DARWIN_SEMAPHORE_H +#define GC_DARWIN_SEMAPHORE_H + +#if !defined(GC_DARWIN_THREADS) && !defined(GC_WIN32_THREADS) +# error darwin_semaphore.h included for improper target +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* This is a very simple semaphore implementation based on pthreads. */ +/* It is not async-signal safe. But this is not a problem because */ +/* signals are not used to suspend threads on the target. */ + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int value; +} sem_t; + +GC_INLINE int sem_init(sem_t *sem, int pshared, int value) { + if (pshared != 0) { + errno = EPERM; /* unsupported */ + return -1; + } + sem->value = value; + if (pthread_mutex_init(&sem->mutex, NULL) != 0) + return -1; + if (pthread_cond_init(&sem->cond, NULL) != 0) { + (void)pthread_mutex_destroy(&sem->mutex); + return -1; + } + return 0; +} + +GC_INLINE int sem_post(sem_t *sem) { + if (pthread_mutex_lock(&sem->mutex) != 0) + return -1; + sem->value++; + if (pthread_cond_signal(&sem->cond) != 0) { + (void)pthread_mutex_unlock(&sem->mutex); + return -1; + } + return pthread_mutex_unlock(&sem->mutex) != 0 ? -1 : 0; +} + +GC_INLINE int sem_wait(sem_t *sem) { + if (pthread_mutex_lock(&sem->mutex) != 0) + return -1; + while (sem->value == 0) { + if (pthread_cond_wait(&sem->cond, &sem->mutex) != 0) { + (void)pthread_mutex_unlock(&sem->mutex); + return -1; + } + } + sem->value--; + return pthread_mutex_unlock(&sem->mutex) != 0 ? -1 : 0; +} + +GC_INLINE int sem_destroy(sem_t *sem) { + return pthread_cond_destroy(&sem->cond) != 0 + || pthread_mutex_destroy(&sem->mutex) != 0 ? -1 : 0; +} + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/bdwgc/include/private/darwin_stop_world.h b/bdwgc/include/private/darwin_stop_world.h new file mode 100644 index 000000000..ee97cbf66 --- /dev/null +++ b/bdwgc/include/private/darwin_stop_world.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +#ifndef GC_DARWIN_STOP_WORLD_H +#define GC_DARWIN_STOP_WORLD_H + +#if !defined(GC_DARWIN_THREADS) +# error darwin_stop_world.h included without GC_DARWIN_THREADS defined +#endif + +#include +#include + +EXTERN_C_BEGIN + +struct thread_stop_info { + mach_port_t mach_thread; + ptr_t stack_ptr; /* Valid only when thread is in a "blocked" state. */ +}; + +#ifndef DARWIN_DONT_PARSE_STACK + GC_INNER ptr_t GC_FindTopOfStack(unsigned long); +#endif + +#ifdef MPROTECT_VDB + GC_INNER void GC_mprotect_stop(void); + GC_INNER void GC_mprotect_resume(void); +# ifndef GC_NO_THREADS_DISCOVERY + GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread); +# endif +#endif + +#if defined(PARALLEL_MARK) && !defined(GC_NO_THREADS_DISCOVERY) + GC_INNER GC_bool GC_is_mach_marker(thread_act_t); +#endif + +EXTERN_C_END + +#endif diff --git a/bdwgc/include/private/dbg_mlc.h b/bdwgc/include/private/dbg_mlc.h new file mode 100644 index 000000000..e38475a62 --- /dev/null +++ b/bdwgc/include/private/dbg_mlc.h @@ -0,0 +1,182 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 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. + * + * 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. + */ + +/* + * This is mostly an internal header file. Typical clients should + * not use it. Clients that define their own object kinds with + * debugging allocators will probably want to include this, however. + * No attempt is made to keep the namespace clean. This should not be + * included from header files that are frequently included by clients. + */ + +#ifndef GC_DBG_MLC_H +#define GC_DBG_MLC_H + +#include "gc_priv.h" +#ifdef KEEP_BACK_PTRS +# include "gc_backptr.h" +#endif + +EXTERN_C_BEGIN + +#if CPP_WORDSZ == 32 +# define START_FLAG (word)0xfedcedcb +# define END_FLAG (word)0xbcdecdef +#else +# define START_FLAG GC_WORD_C(0xFEDCEDCBfedcedcb) +# define END_FLAG GC_WORD_C(0xBCDECDEFbcdecdef) +#endif + /* Stored both one past the end of user object, and one before */ + /* the end of the object as seen by the allocator. */ + +#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) \ + || defined(MAKE_BACK_GRAPH) + /* Pointer "source"s that aren't real locations. */ + /* Used in oh_back_ptr fields and as "source" */ + /* argument to some marking functions. */ +# define NOT_MARKED (ptr_t)0 +# define MARKED_FOR_FINALIZATION ((ptr_t)(word)2) + /* Object was marked because it is finalizable. */ +# define MARKED_FROM_REGISTER ((ptr_t)(word)4) + /* Object was marked from a register. Hence the */ + /* source of the reference doesn't have an address. */ +#endif /* KEEP_BACK_PTRS || PRINT_BLACK_LIST */ + +/* Object header */ +typedef struct { +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + /* We potentially keep two different kinds of back */ + /* pointers. KEEP_BACK_PTRS stores a single back */ + /* pointer in each reachable object to allow reporting */ + /* of why an object was retained. MAKE_BACK_GRAPH */ + /* builds a graph containing the inverse of all */ + /* "points-to" edges including those involving */ + /* objects that have just become unreachable. This */ + /* allows detection of growing chains of unreachable */ + /* objects. It may be possible to eventually combine */ + /* both, but for now we keep them separate. Both */ + /* kinds of back pointers are hidden using the */ + /* following macros. In both cases, the plain version */ + /* is constrained to have an least significant bit of 1, */ + /* to allow it to be distinguished from a free list */ + /* link. This means the plain version must have an */ + /* lsb of 0. */ + /* Note that blocks dropped by black-listing will */ + /* also have the lsb clear once debugging has */ + /* started. */ + /* We're careful never to overwrite a value with lsb 0. */ +# if ALIGNMENT == 1 + /* Fudge back pointer to be even. */ +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(~1 & (word)(p)) +# else +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(p) +# endif +# ifdef KEEP_BACK_PTRS + GC_hidden_pointer oh_back_ptr; +# endif +# ifdef MAKE_BACK_GRAPH + GC_hidden_pointer oh_bg_ptr; +# endif +# if defined(KEEP_BACK_PTRS) != defined(MAKE_BACK_GRAPH) + /* Keep double-pointer-sized alignment. */ + word oh_dummy; +# endif +# endif + const char * oh_string; /* object descriptor string (file name) */ + signed_word oh_int; /* object descriptor integer (line number) */ +# ifdef NEED_CALLINFO + struct callinfo oh_ci[NFRAMES]; +# endif +# ifndef SHORT_DBG_HDRS + word oh_sz; /* Original malloc arg. */ + word oh_sf; /* start flag */ +# endif /* SHORT_DBG_HDRS */ +} oh; +/* The size of the above structure is assumed not to de-align things, */ +/* and to be a multiple of the word length. */ + +#ifdef SHORT_DBG_HDRS +# define DEBUG_BYTES (sizeof (oh)) +# define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES +#else + /* Add space for END_FLAG, but use any extra space that was already */ + /* added to catch off-the-end pointers. */ + /* 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 + +/* Round bytes to words without adding extra byte at end. */ +#define SIMPLE_ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1) + +/* ADD_CALL_CHAIN stores a (partial) call chain into an object */ +/* header; it should be called with the allocation lock held. */ +/* PRINT_CALL_CHAIN prints the call chain stored in an object */ +/* to stderr. It requires that we do not hold the lock. */ +#if defined(SAVE_CALL_CHAIN) + struct callinfo; + GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +#elif defined(GC_ADD_CALLER) + struct callinfo; + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +#else +# define ADD_CALL_CHAIN(base, ra) +# define PRINT_CALL_CHAIN(base) +#endif + +#ifdef GC_ADD_CALLER +# define OPT_RA ra, +#else +# define OPT_RA +#endif + +/* Check whether object with base pointer p has debugging info */ +/* p is assumed to point to a legitimate object in our part */ +/* of the heap. */ +#ifdef SHORT_DBG_HDRS +# define GC_has_other_debug_info(p) 1 +#else + GC_INNER int GC_has_other_debug_info(ptr_t p); +#endif + +#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) +# if defined(SHORT_DBG_HDRS) && !defined(CPPCHECK) +# error Non-ptr stored in object results in GC_HAS_DEBUG_INFO malfunction + /* We may mistakenly conclude that p has a debugging wrapper. */ +# endif +# if defined(PARALLEL_MARK) && defined(KEEP_BACK_PTRS) +# define GC_HAS_DEBUG_INFO(p) \ + ((AO_load((volatile AO_t *)(p)) & 1) != 0 \ + && GC_has_other_debug_info(p) > 0) + /* Atomic load is used as GC_store_back_pointer */ + /* stores oh_back_ptr atomically (p might point */ + /* to the field); this prevents a TSan warning. */ +# else +# define GC_HAS_DEBUG_INFO(p) \ + ((*(word *)(p) & 1) && GC_has_other_debug_info(p) > 0) +# endif +#else +# define GC_HAS_DEBUG_INFO(p) (GC_has_other_debug_info(p) > 0) +#endif /* !KEEP_BACK_PTRS && !MAKE_BACK_GRAPH */ + +EXTERN_C_END + +#endif /* GC_DBG_MLC_H */ diff --git a/bdwgc/include/private/gc_alloc_ptrs.h b/bdwgc/include/private/gc_alloc_ptrs.h new file mode 100644 index 000000000..a31c18327 --- /dev/null +++ b/bdwgc/include/private/gc_alloc_ptrs.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. + * Copyright (c) 2018-2021 Ivan Maidanski + * + * 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. + */ + +/* This file is kept for a binary compatibility purpose only. */ + +#ifndef GC_ALLOC_PTRS_H +#define GC_ALLOC_PTRS_H + +#include "gc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef GC_API_PRIV +# define GC_API_PRIV GC_API +#endif + +/* Some compilers do not accept "const" together with the dllimport */ +/* attribute, so the symbols below are exported as non-constant ones. */ +#ifndef GC_APIVAR_CONST +# if defined(GC_BUILD) || !defined(GC_DLL) +# define GC_APIVAR_CONST const +# else +# define GC_APIVAR_CONST /* empty */ +# endif +#endif + +GC_API_PRIV void ** GC_APIVAR_CONST GC_objfreelist_ptr; +GC_API_PRIV void ** GC_APIVAR_CONST GC_aobjfreelist_ptr; +GC_API_PRIV void ** GC_APIVAR_CONST GC_uobjfreelist_ptr; + +#ifdef GC_ATOMIC_UNCOLLECTABLE + GC_API_PRIV void ** GC_APIVAR_CONST GC_auobjfreelist_ptr; +#endif + +/* Manually update the number of bytes allocated during the current */ +/* collection cycle and the number of explicitly deallocated bytes of */ +/* memory since the last collection, respectively. Both functions are */ +/* unsynchronized, GC_call_with_alloc_lock() should be used to avoid */ +/* data races. */ +GC_API_PRIV void GC_CALL GC_incr_bytes_allocd(size_t /* bytes */); +GC_API_PRIV void GC_CALL GC_incr_bytes_freed(size_t /* bytes */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_ALLOC_PTRS_H */ diff --git a/bdwgc/include/private/gc_atomic_ops.h b/bdwgc/include/private/gc_atomic_ops.h new file mode 100644 index 000000000..eaa28cb19 --- /dev/null +++ b/bdwgc/include/private/gc_atomic_ops.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 Ivan Maidanski + * + * 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. + */ + +/* This is a private GC header which provides an implementation of */ +/* libatomic_ops subset primitives sufficient for GC assuming that GCC */ +/* atomic intrinsics are available (and have correct implementation). */ +/* This is enabled by defining GC_BUILTIN_ATOMIC macro. Otherwise, */ +/* libatomic_ops library is used to define the primitives. */ + +#ifndef GC_ATOMIC_OPS_H +#define GC_ATOMIC_OPS_H + +#ifdef GC_BUILTIN_ATOMIC + +# include "gc.h" /* for GC_word */ + +# ifdef __cplusplus + extern "C" { +# endif + + typedef GC_word AO_t; + +# ifdef GC_PRIVATE_H /* have GC_INLINE */ +# define AO_INLINE GC_INLINE +# else +# define AO_INLINE static __inline +# endif + + typedef unsigned char AO_TS_t; +# define AO_TS_CLEAR 0 +# define AO_TS_INITIALIZER (AO_TS_t)AO_TS_CLEAR +# if defined(__GCC_ATOMIC_TEST_AND_SET_TRUEVAL) && !defined(CPPCHECK) +# define AO_TS_SET __GCC_ATOMIC_TEST_AND_SET_TRUEVAL +# else +# define AO_TS_SET (AO_TS_t)1 /* true */ +# endif +# define AO_CLEAR(p) __atomic_clear(p, __ATOMIC_RELEASE) +# define AO_test_and_set_acquire(p) \ + (__atomic_test_and_set(p, __ATOMIC_ACQUIRE) ? AO_TS_SET : AO_TS_CLEAR) +# define AO_HAVE_test_and_set_acquire + +# define AO_compiler_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) +# define AO_nop_full() __atomic_thread_fence(__ATOMIC_SEQ_CST) +# define AO_HAVE_nop_full + +# define AO_fetch_and_add(p, v) __atomic_fetch_add(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_fetch_and_add +# define AO_fetch_and_add1(p) AO_fetch_and_add(p, 1) +# define AO_HAVE_fetch_and_add1 + +# define AO_or(p, v) (void)__atomic_or_fetch(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_or + +# define AO_load(p) __atomic_load_n(p, __ATOMIC_RELAXED) +# define AO_HAVE_load +# define AO_load_acquire(p) __atomic_load_n(p, __ATOMIC_ACQUIRE) +# define AO_HAVE_load_acquire +# define AO_load_acquire_read(p) AO_load_acquire(p) +# define AO_HAVE_load_acquire_read + +# define AO_store(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_store +# define AO_store_release(p, v) __atomic_store_n(p, v, __ATOMIC_RELEASE) +# define AO_HAVE_store_release +# define AO_store_release_write(p, v) AO_store_release(p, v) +# define AO_HAVE_store_release_write + +# define AO_char_load(p) __atomic_load_n(p, __ATOMIC_RELAXED) +# define AO_HAVE_char_load +# define AO_char_store(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_char_store + +# ifdef AO_REQUIRE_CAS + AO_INLINE int + AO_compare_and_swap(volatile AO_t *p, AO_t ov, AO_t nv) + { + return (int)__atomic_compare_exchange_n(p, &ov, nv, 0, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + } + + AO_INLINE int + AO_compare_and_swap_release(volatile AO_t *p, AO_t ov, AO_t nv) + { + return (int)__atomic_compare_exchange_n(p, &ov, nv, 0, + __ATOMIC_RELEASE, __ATOMIC_RELAXED); + } +# define AO_HAVE_compare_and_swap_release +# endif + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +# ifndef NO_LOCKFREE_AO_OR + /* __atomic_or_fetch is assumed to be lock-free. */ +# define HAVE_LOCKFREE_AO_OR 1 +# endif + +#else + /* Fallback to libatomic_ops. */ +# include "atomic_ops.h" + + /* AO_compiler_barrier, AO_load and AO_store should be defined for */ + /* all targets; the rest of the primitives are guaranteed to exist */ + /* only if AO_REQUIRE_CAS is defined (or if the corresponding */ + /* AO_HAVE_x macro is defined). x86/x64 targets have AO_nop_full, */ + /* AO_load_acquire, AO_store_release, at least. */ +# if (!defined(AO_HAVE_load) || !defined(AO_HAVE_store)) && !defined(CPPCHECK) +# error AO_load or AO_store is missing; probably old version of atomic_ops +# endif + +#endif /* !GC_BUILTIN_ATOMIC */ + +#endif /* GC_ATOMIC_OPS_H */ diff --git a/bdwgc/include/private/gc_hdrs.h b/bdwgc/include/private/gc_hdrs.h new file mode 100644 index 000000000..a6e7037a5 --- /dev/null +++ b/bdwgc/include/private/gc_hdrs.h @@ -0,0 +1,215 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#ifndef GC_HEADERS_H +#define GC_HEADERS_H + +#if CPP_WORDSZ != 32 && CPP_WORDSZ < 36 && !defined(CPPCHECK) +# error Get a real machine +#endif + +EXTERN_C_BEGIN + +typedef struct hblkhdr hdr; + +/* + * The 2 level tree data structure that is used to find block headers. + * If there are more than 32 bits in a pointer, the top level is a hash + * table. + * + * This defines HDR, GET_HDR, and SET_HDR, the main macros used to + * retrieve and set object headers. + * + * We take advantage of a header lookup + * cache. This is a locally declared direct mapped cache, used inside + * the marker. The HC_GET_HDR macro uses and maintains this + * cache. Assuming we get reasonable hit rates, this shaves a few + * memory references from each pointer validation. + */ + +#if CPP_WORDSZ > 32 +# define HASH_TL +#endif + +/* Define appropriate out-degrees for each of the two tree levels */ +#if defined(LARGE_CONFIG) || !defined(SMALL_CONFIG) +# define LOG_BOTTOM_SZ 10 +#else +# define LOG_BOTTOM_SZ 11 + /* Keep top index size reasonable with smaller blocks. */ +#endif +#define BOTTOM_SZ (1 << LOG_BOTTOM_SZ) + +#ifndef HASH_TL +# define LOG_TOP_SZ (WORDSZ - LOG_BOTTOM_SZ - LOG_HBLKSIZE) +#else +# define LOG_TOP_SZ 11 +#endif +#define TOP_SZ (1 << LOG_TOP_SZ) + +/* #define COUNT_HDR_CACHE_HITS */ + +#ifdef COUNT_HDR_CACHE_HITS + extern word GC_hdr_cache_hits; /* used for debugging/profiling */ + extern word GC_hdr_cache_misses; +# define HC_HIT() (void)(++GC_hdr_cache_hits) +# define HC_MISS() (void)(++GC_hdr_cache_misses) +#else +# define HC_HIT() /* empty */ +# define HC_MISS() /* empty */ +#endif + +typedef struct hce { + word block_addr; /* right shifted by LOG_HBLKSIZE */ + hdr * hce_hdr; +} hdr_cache_entry; + +#define HDR_CACHE_SIZE 8 /* power of 2 */ + +#define DECLARE_HDR_CACHE \ + hdr_cache_entry hdr_cache[HDR_CACHE_SIZE] + +#define INIT_HDR_CACHE BZERO(hdr_cache, sizeof(hdr_cache)) + +#define HCE(h) \ + (hdr_cache + (((word)(h) >> LOG_HBLKSIZE) & (HDR_CACHE_SIZE-1))) + +#define HCE_VALID_FOR(hce, h) ((hce) -> block_addr == \ + ((word)(h) >> LOG_HBLKSIZE)) + +#define HCE_HDR(h) ((hce) -> hce_hdr) + +#ifdef PRINT_BLACK_LIST + GC_INNER hdr * GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, + ptr_t source); +# define HEADER_CACHE_MISS(p, hce, source) \ + GC_header_cache_miss(p, hce, source) +#else + GC_INNER hdr * GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce); +# define HEADER_CACHE_MISS(p, hce, source) GC_header_cache_miss(p, hce) +#endif + +/* Set hhdr to the header for p. Analogous to GET_HDR below, */ +/* except that in the case of large objects, it gets the header for */ +/* the object beginning if GC_all_interior_pointers is set. */ +/* 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) \ + { /* cannot use do-while(0) here */ \ + hdr_cache_entry * hce = HCE(p); \ + if (EXPECT(HCE_VALID_FOR(hce, p), TRUE)) { \ + HC_HIT(); \ + hhdr = hce -> hce_hdr; \ + } else { \ + hhdr = HEADER_CACHE_MISS(p, hce, source); \ + if (NULL == hhdr) break; /* go to the enclosing loop end */ \ + } \ + } + +typedef struct bi { + hdr * index[BOTTOM_SZ]; + /* + * The bottom level index contains one of three kinds of values: + * 0 means we're not responsible for this block, + * or this is a block other than the first one in a free block. + * 1 < (long)X <= MAX_JUMP means the block starts at least + * X * HBLKSIZE bytes before the current address. + * A valid pointer points to a hdr structure. (The above can't be + * valid pointers due to the GET_MEM return convention.) + */ + struct bi * asc_link; /* All indices are linked in */ + /* ascending order... */ + struct bi * desc_link; /* ... and in descending order. */ + word key; /* high order address bits. */ +# ifdef HASH_TL + struct bi * hash_link; /* Hash chain link. */ +# endif +} bottom_index; + +/* bottom_index GC_all_nils; - really part of GC_arrays */ + +/* extern bottom_index * GC_top_index []; - really part of GC_arrays */ + /* Each entry points to a bottom_index. */ + /* On a 32 bit machine, it points to */ + /* the index for a set of high order */ + /* bits equal to the index. For longer */ + /* addresses, we hash the high order */ + /* bits to compute the index in */ + /* GC_top_index, and each entry points */ + /* to a hash chain. */ + /* The last entry in each chain is */ + /* GC_all_nils. */ + + +#define MAX_JUMP (HBLKSIZE - 1) + +#define HDR_FROM_BI(bi, p) \ + ((bi)->index[((word)(p) >> LOG_HBLKSIZE) & (BOTTOM_SZ - 1)]) +#ifndef HASH_TL +# define BI(p) (GC_top_index \ + [(word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE)]) +# define HDR_INNER(p) HDR_FROM_BI(BI(p),p) +# ifdef SMALL_CONFIG +# define HDR(p) GC_find_header((ptr_t)(p)) +# else +# define HDR(p) HDR_INNER(p) +# endif +# 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); \ + } 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 + +/* Is the result a forwarding address to someplace closer to the */ +/* beginning of the block or NULL? */ +#define IS_FORWARDING_ADDR_OR_NIL(hhdr) ((size_t) (hhdr) <= MAX_JUMP) + +/* Get an HBLKSIZE aligned address closer to the beginning of the block */ +/* h. Assumes hhdr == HDR(h) and IS_FORWARDING_ADDR(hhdr). */ +#define FORWARDED_ADDR(h, hhdr) ((struct hblk *)(h) - (size_t)(hhdr)) + +EXTERN_C_END + +#endif /* GC_HEADERS_H */ diff --git a/bdwgc/include/private/gc_locks.h b/bdwgc/include/private/gc_locks.h new file mode 100644 index 000000000..cb1f45c79 --- /dev/null +++ b/bdwgc/include/private/gc_locks.h @@ -0,0 +1,282 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * + * + * 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. + */ + +#ifndef GC_LOCKS_H +#define GC_LOCKS_H + +/* + * Mutual exclusion between allocator/collector routines. + * Needed if there is more than one allocator thread. + * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK. + * + * Note that I_HOLD_LOCK and I_DONT_HOLD_LOCK are used only positively + * in assertions, and may return TRUE in the "don't know" case. + */ +# ifdef THREADS + +# ifdef PCR +# include +# include +# endif + + EXTERN_C_BEGIN + +# ifdef PCR + GC_EXTERN PCR_Th_ML GC_allocate_ml; +# define UNCOND_LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) +# define UNCOND_UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +# elif defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) + extern void GC_lock(void); + extern void GC_unlock(void); +# define UNCOND_LOCK() GC_lock() +# define UNCOND_UNLOCK() GC_unlock() +# endif + +# if (!defined(AO_HAVE_test_and_set_acquire) || defined(GC_RTEMS_PTHREADS) \ + || defined(SN_TARGET_PS3) \ + || defined(GC_WIN32_THREADS) || defined(BASE_ATOMIC_OPS_EMULATED) \ + || defined(LINT2)) && defined(GC_PTHREADS) +# define USE_PTHREAD_LOCKS +# undef USE_SPIN_LOCK +# if defined(LINT2) && !defined(NO_PTHREAD_TRYLOCK) +# define NO_PTHREAD_TRYLOCK +# endif +# endif + +# if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS) +# define NO_THREAD (DWORD)(-1) + GC_EXTERN CRITICAL_SECTION GC_allocate_ml; +# ifdef GC_ASSERTIONS + 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()) +# ifdef THREAD_SANITIZER +# define I_DONT_HOLD_LOCK() TRUE /* Conservatively say yes */ +# else +# define I_DONT_HOLD_LOCK() (!GC_need_to_lock \ + || GC_lock_holder != GetCurrentThreadId()) +# endif +# define UNCOND_LOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + EnterCriticalSection(&GC_allocate_ml); \ + SET_LOCK_HOLDER(); } +# 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 */ +# elif defined(GC_PTHREADS) + EXTERN_C_END +# include + EXTERN_C_BEGIN + /* Posix allows pthread_t to be a struct, though it rarely is. */ + /* Unfortunately, we need to use a pthread_t to index a data */ + /* structure. It also helps if comparisons don't involve a */ + /* function call. Hence we introduce platform-dependent macros */ + /* to compare pthread_t ids and to map them to integers. */ + /* The mapping to integers does not need to result in different */ + /* integers for each thread, though that should be true as much */ + /* as possible. */ + /* Refine to exclude platforms on which pthread_t is struct. */ +# if !defined(GC_WIN32_PTHREADS) +# define NUMERIC_THREAD_ID(id) ((unsigned long)(id)) +# define THREAD_EQUAL(id1, id2) ((id1) == (id2)) +# define NUMERIC_THREAD_ID_UNIQUE +# elif defined(__WINPTHREADS_VERSION_MAJOR) /* winpthreads */ +# define NUMERIC_THREAD_ID(id) ((unsigned long)(id)) +# define THREAD_EQUAL(id1, id2) ((id1) == (id2)) +# ifndef _WIN64 + /* NUMERIC_THREAD_ID is 32-bit and not unique on Win64. */ +# define NUMERIC_THREAD_ID_UNIQUE +# endif +# else /* pthreads-win32 */ +# define NUMERIC_THREAD_ID(id) ((unsigned long)(word)(id.p)) + /* Using documented internal details of pthreads-win32 library. */ + /* Faster than pthread_equal(). Should not change with */ + /* future versions of pthreads-win32 library. */ +# define THREAD_EQUAL(id1, id2) ((id1.p == id2.p) && (id1.x == id2.x)) +# undef NUMERIC_THREAD_ID_UNIQUE + /* Generic definitions based on pthread_equal() always work but */ + /* will result in poor performance (as NUMERIC_THREAD_ID is */ + /* defined to just a constant) and weak assertion checking. */ +# endif +# define NO_THREAD ((unsigned long)(-1l)) + /* != NUMERIC_THREAD_ID(pthread_self()) for any thread */ + +# ifdef SN_TARGET_PSP2 + EXTERN_C_END +# include "psp2-support.h" + EXTERN_C_BEGIN + GC_EXTERN WapiMutex GC_allocate_ml_PSP2; +# define UNCOND_LOCK() { int res; GC_ASSERT(I_DONT_HOLD_LOCK()); \ + res = PSP2_MutexLock(&GC_allocate_ml_PSP2); \ + GC_ASSERT(0 == res); (void)res; \ + SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() { int res; GC_ASSERT(I_HOLD_LOCK()); \ + UNSET_LOCK_HOLDER(); \ + res = PSP2_MutexUnlock(&GC_allocate_ml_PSP2); \ + GC_ASSERT(0 == res); (void)res; } + +# elif (!defined(THREAD_LOCAL_ALLOC) || defined(USE_SPIN_LOCK)) \ + && !defined(USE_PTHREAD_LOCKS) + /* In the THREAD_LOCAL_ALLOC case, the allocation lock tends to */ + /* be held for long periods, if it is held at all. Thus spinning */ + /* and sleeping for fixed periods are likely to result in */ + /* significant wasted time. We thus rely mostly on queued locks. */ +# undef USE_SPIN_LOCK +# define USE_SPIN_LOCK + GC_EXTERN volatile AO_TS_t GC_allocate_lock; + GC_INNER void GC_lock(void); + /* Allocation lock holder. Only set if acquired by client through */ + /* GC_call_with_alloc_lock. */ +# ifdef GC_ASSERTIONS +# define UNCOND_LOCK() \ + { 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() \ + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + AO_CLEAR(&GC_allocate_lock); } +# else +# define UNCOND_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 */ +# ifndef USE_PTHREAD_LOCKS +# define USE_PTHREAD_LOCKS +# endif +# endif /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */ +# ifdef USE_PTHREAD_LOCKS + EXTERN_C_END +# include + EXTERN_C_BEGIN + GC_EXTERN pthread_mutex_t GC_allocate_ml; +# ifdef GC_ASSERTIONS + GC_INNER void GC_lock(void); +# 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() pthread_mutex_lock(&GC_allocate_ml) +# else + GC_INNER void GC_lock(void); +# 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 */ +# 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() \ + (!GC_need_to_lock \ + || GC_lock_holder == NUMERIC_THREAD_ID(pthread_self())) +# if !defined(NUMERIC_THREAD_ID_UNIQUE) || defined(THREAD_SANITIZER) +# define I_DONT_HOLD_LOCK() TRUE /* Conservatively say yes */ +# else +# define I_DONT_HOLD_LOCK() \ + (!GC_need_to_lock \ + || GC_lock_holder != NUMERIC_THREAD_ID(pthread_self())) +# endif +# endif /* GC_ASSERTIONS */ +# ifndef GC_WIN32_THREADS + GC_EXTERN volatile GC_bool GC_collecting; +# ifdef AO_HAVE_char_store +# define ENTER_GC() AO_char_store((unsigned char*)&GC_collecting, TRUE) +# define EXIT_GC() AO_char_store((unsigned char*)&GC_collecting, FALSE) +# else +# define ENTER_GC() (void)(GC_collecting = TRUE) +# define EXIT_GC() (void)(GC_collecting = FALSE) +# endif +# endif +# endif /* GC_PTHREADS */ +# if defined(GC_ALWAYS_MULTITHREADED) \ + && (defined(USE_PTHREAD_LOCKS) || defined(USE_SPIN_LOCK)) +# define GC_need_to_lock TRUE +# define set_need_to_lock() (void)0 +# else +# if defined(GC_ALWAYS_MULTITHREADED) && !defined(CPPCHECK) +# error Runtime initialization of GC lock is needed! +# endif +# undef GC_ALWAYS_MULTITHREADED + GC_EXTERN GC_bool GC_need_to_lock; +# ifdef THREAD_SANITIZER + /* To workaround TSan false positive (e.g., when */ + /* GC_pthread_create is called from multiple threads in */ + /* parallel), do not set GC_need_to_lock if it is already set. */ +# define set_need_to_lock() \ + (void)(*(GC_bool volatile *)&GC_need_to_lock \ + ? FALSE \ + : (GC_need_to_lock = TRUE)) +# else +# define set_need_to_lock() (void)(GC_need_to_lock = TRUE) + /* We are multi-threaded now. */ +# endif +# endif + + EXTERN_C_END + +# else /* !THREADS */ +# 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) +# if (defined(LINT2) && defined(USE_PTHREAD_LOCKS)) \ + || defined(GC_ALWAYS_MULTITHREADED) + /* 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() 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 +# define ENTER_GC() +# define EXIT_GC() +# endif + +# ifndef DCL_LOCK_STATE +# define DCL_LOCK_STATE +# endif + +#endif /* GC_LOCKS_H */ diff --git a/bdwgc/include/private/gc_pmark.h b/bdwgc/include/private/gc_pmark.h new file mode 100644 index 000000000..d86af0c67 --- /dev/null +++ b/bdwgc/include/private/gc_pmark.h @@ -0,0 +1,485 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2001 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. + * + * 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. + * + */ + +/* Private declarations of GC marker data structures and macros */ + +/* + * Declarations of mark stack. Needed by marker and client supplied mark + * routines. Transitively include gc_priv.h. + */ +#ifndef GC_PMARK_H +#define GC_PMARK_H + +#if defined(HAVE_CONFIG_H) && !defined(GC_PRIVATE_H) + /* When gc_pmark.h is included from gc_priv.h, some of macros might */ + /* be undefined in gcconfig.h, so skip config.h in this case. */ +# include "config.h" +#endif + +#ifndef GC_BUILD +# define GC_BUILD +#endif + +#if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \ + && !defined(_GNU_SOURCE) && defined(GC_PTHREADS) \ + && !defined(GC_NO_PTHREAD_SIGMASK) +# define _GNU_SOURCE 1 +#endif + +#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) +# include "dbg_mlc.h" +#endif + +#include "../gc_mark.h" +#include "gc_priv.h" + +EXTERN_C_BEGIN + +/* The real declarations of the following is in gc_priv.h, so that */ +/* we can avoid scanning the following table. */ +/* +mark_proc GC_mark_procs[MAX_MARK_PROCS]; +*/ + +#ifndef MARK_DESCR_OFFSET +# define MARK_DESCR_OFFSET sizeof(word) +#endif + +/* + * Mark descriptor stuff that should remain private for now, mostly + * because it's hard to export WORDSZ without including gcconfig.h. + */ +#define BITMAP_BITS (WORDSZ - GC_DS_TAG_BITS) +#define PROC(descr) \ + (GC_mark_procs[((descr) >> GC_DS_TAG_BITS) & (GC_MAX_MARK_PROCS-1)]) +#define ENV(descr) \ + ((descr) >> (GC_DS_TAG_BITS + GC_LOG_MAX_MARK_PROCS)) +#define MAX_ENV \ + (((word)1 << (WORDSZ - GC_DS_TAG_BITS - GC_LOG_MAX_MARK_PROCS)) - 1) + +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) + +#ifdef PARALLEL_MARK + /* + * Allow multiple threads to participate in the marking process. + * This works roughly as follows: + * The main mark stack never shrinks, but it can grow. + * + * The initiating threads holds the GC lock, and sets GC_help_wanted. + * + * Other threads: + * 1) update helper_count (while holding mark_lock.) + * 2) allocate a local mark stack + * repeatedly: + * 3) Steal a global mark stack entry by atomically replacing + * its descriptor with 0. + * 4) Copy it to the local stack. + * 5) Mark on the local stack until it is empty, or + * it may be profitable to copy it back. + * 6) If necessary, copy local stack to global one, + * holding mark lock. + * 7) Stop when the global mark stack is empty. + * 8) decrement helper_count (holding mark_lock). + * + * This is an experiment to see if we can do something along the lines + * of the University of Tokyo SGC in a less intrusive, though probably + * also less performant, way. + */ + + /* GC_mark_stack_top is protected by mark lock. */ + + /* + * GC_notify_all_marker() is used when GC_help_wanted is first set, + * when the last helper becomes inactive, + * when something is added to the global mark stack, and just after + * GC_mark_no is incremented. + * This could be split into multiple CVs (and probably should be to + * scale to really large numbers of processors.) + */ +#endif /* PARALLEL_MARK */ + +GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp); + +/* Push the object obj with corresponding heap block header hhdr onto */ +/* the mark stack. Returns the updated mark_stack_top value. */ +GC_INLINE mse * GC_push_obj(ptr_t obj, hdr * hhdr, mse * mark_stack_top, + mse * mark_stack_limit) +{ + word descr = hhdr -> hb_descr; + + GC_ASSERT(!HBLK_IS_FREE(hhdr)); + if (descr != 0) { + mark_stack_top++; + 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.w = descr; + } + return mark_stack_top; +} + +/* Push the contents of current onto the mark stack if it is a valid */ +/* ptr to a currently unmarked object. Mark it. */ +#define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, source) \ + do { \ + hdr * my_hhdr; \ + HC_GET_HDR(current, my_hhdr, source); /* contains "break" */ \ + mark_stack_top = GC_push_contents_hdr(current, mark_stack_top, \ + mark_stack_limit, \ + source, my_hhdr, TRUE); \ + } while (0) + +/* Set mark bit, exit (using "break" statement) if it is already set. */ +#ifdef USE_MARK_BYTES +# if defined(PARALLEL_MARK) && defined(AO_HAVE_char_store) \ + && !defined(BASE_ATOMIC_OPS_EMULATED) + /* There is a race here, and we may set the bit twice in the */ + /* concurrent case. This can result in the object being pushed */ + /* twice. But that is only a performance issue. */ +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + volatile unsigned char * mark_byte_addr = \ + (unsigned char *)(hhdr)->hb_marks + (bit_no); \ + /* Unordered atomic load and store are sufficient here. */ \ + if (AO_char_load(mark_byte_addr) != 0) \ + break; /* go to the enclosing loop end */ \ + AO_char_store(mark_byte_addr, 1); \ + } +# else +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + char * mark_byte_addr = (char *)(hhdr)->hb_marks + (bit_no); \ + if (*mark_byte_addr != 0) break; /* go to the enclosing loop end */ \ + *mark_byte_addr = 1; \ + } +# endif /* !PARALLEL_MARK */ +#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: */ +# ifdef THREAD_SANITIZER +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + if (!((word)AO_load((volatile AO_t *)(addr)) & (bits))) { \ + /* Atomic load is just to avoid TSan false positive. */ \ + AO_or((volatile AO_t *)(addr), (AO_t)(bits)); \ + } else { \ + break; /* go to the enclosing loop end */ \ + } \ + } +# else +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + if (!(*(addr) & (bits))) { \ + AO_or((volatile AO_t *)(addr), (AO_t)(bits)); \ + } else { \ + break; /* go to the enclosing loop end */ \ + } \ + } +# endif /* !THREAD_SANITIZER */ +# else +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + word old = *(addr); \ + word my_bits = (bits); \ + if ((old & my_bits) != 0) \ + break; /* go to the enclosing loop end */ \ + *(addr) = old | my_bits; \ + } +# endif /* !PARALLEL_MARK */ +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + word * mark_word_addr = (hhdr)->hb_marks + divWORDSZ(bit_no); \ + OR_WORD_EXIT_IF_SET(mark_word_addr, \ + (word)1 << modWORDSZ(bit_no)); /* contains "break" */ \ + } +#endif /* !USE_MARK_BYTES */ + +#ifdef PARALLEL_MARK +# define INCR_MARKS(hhdr) \ + AO_store(&hhdr->hb_n_marks, AO_load(&hhdr->hb_n_marks) + 1) +#else +# define INCR_MARKS(hhdr) (void)(++hhdr->hb_n_marks) +#endif + +#ifdef ENABLE_TRACE +# define TRACE(source, cmd) \ + if (GC_trace_addr != 0 && (ptr_t)(source) == GC_trace_addr) cmd +# define TRACE_TARGET(target, cmd) \ + if (GC_trace_addr != NULL && GC_is_heap_ptr(GC_trace_addr) \ + && (target) == *(ptr_t *)GC_trace_addr) cmd +#else +# define TRACE(source, cmd) +# define TRACE_TARGET(source, cmd) +#endif + +#if defined(I386) && defined(__GNUC__) && !defined(NACL) +# define LONG_MULT(hprod, lprod, x, y) \ + do { \ + __asm__ __volatile__("mull %2" : "=a"(lprod), "=d"(hprod) \ + : "g"(y), "0"(x)); \ + } while (0) +#else +# if defined(__int64) && !defined(__GNUC__) && !defined(CPPCHECK) +# define ULONG_MULT_T unsigned __int64 +# else +# define ULONG_MULT_T unsigned long long +# endif +# define LONG_MULT(hprod, lprod, x, y) \ + do { \ + ULONG_MULT_T prod = (ULONG_MULT_T)(x) * (ULONG_MULT_T)(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 */ +/* push the contents of the object on the mark stack. Current points */ +/* to the beginning of the object. We rely on the fact that the */ +/* preceding header calculation will succeed for a pointer past the */ +/* first page of an object, only if it is in fact a valid pointer */ +/* to the object. Thus we can omit the otherwise necessary tests */ +/* here. Note in particular that the "displ" value is the displacement */ +/* from the beginning of the heap block, which may itself be in the */ +/* interior of a large object. */ +GC_INLINE mse * GC_push_contents_hdr(ptr_t current, mse * mark_stack_top, + mse * mark_stack_limit, ptr_t source, + hdr * hhdr, GC_bool 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 the */ + /* first block, then we are in the all_interior_pointers case, and */ + /* it is safe to use any displacement value. */ + ptr_t base = current; +# ifdef MARK_BIT_PER_GRANULE + size_t gran_displ = BYTES_TO_GRANULES(displ); + size_t gran_offset = hhdr -> hb_map[gran_displ]; + size_t byte_offset = displ & (GRANULE_BYTES - 1); + + /* The following always fails for large block references. */ + if (EXPECT((gran_offset | byte_offset) != 0, FALSE)) +# else + unsigned32 gran_displ; /* high_prod */ + unsigned32 inv_sz = hhdr -> hb_inv_sz; +# endif /* MARK_BIT_PER_OBJ */ + + { +# ifdef MARK_BIT_PER_GRANULE + if ((hhdr -> hb_flags & LARGE_BLOCK) != 0) +# else + if (EXPECT(inv_sz == LARGE_INV_SZ, FALSE)) +# endif /* MARK_BIT_PER_OBJ */ + { + /* gran_offset is bogus. */ + size_t obj_displ; + + base = (ptr_t)hhdr->hb_block; + obj_displ = current - base; + if (obj_displ != displ) { + GC_ASSERT(obj_displ < hhdr -> hb_sz); + /* Must be in all_interior_pointer case, not first block */ + /* already did validity check on cache miss. */ + } else if (do_offset_check && !GC_valid_offsets[obj_displ]) { + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); + break; + } + GC_ASSERT(hhdr -> hb_sz > HBLKSIZE + || hhdr -> hb_block == HBLKPTR(current)); + GC_ASSERT((word)hhdr->hb_block <= (word)current); + gran_displ = 0; + } else { +# ifdef MARK_BIT_PER_GRANULE + size_t obj_displ = GRANULES_TO_BYTES(gran_offset) + byte_offset; +# else + unsigned32 low_prod; + + LONG_MULT(gran_displ, low_prod, (unsigned32)displ, inv_sz); + if ((low_prod >> 16) != 0) +# endif /* MARK_BIT_PER_OBJ */ + { +# if defined(MARK_BIT_PER_OBJ) \ + && !defined(MARK_BIT_PER_GRANULE) /* for cppcheck */ + size_t obj_displ; + + /* Accurate enough if HBLKSIZE <= 2**15. */ + GC_STATIC_ASSERT(HBLKSIZE <= (1 << 15)); + obj_displ = (((low_prod >> 16) + 1) * (size_t)hhdr->hb_sz) >> 16; +# endif + if (do_offset_check && !GC_valid_offsets[obj_displ]) { + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); + break; + } +# ifdef MARK_BIT_PER_GRANULE + gran_displ -= gran_offset; +# endif + base -= obj_displ; + } + } + } +# ifdef MARK_BIT_PER_GRANULE + GC_ASSERT(hhdr == GC_find_header(base)); + GC_ASSERT(gran_displ % BYTES_TO_GRANULES(hhdr -> hb_sz) == 0); +# else + /* May get here for pointer to start of block not at the */ + /* beginning of object. If so, it is valid, and we are fine. */ + GC_ASSERT(gran_displ <= HBLK_OBJS(hhdr -> hb_sz)); +# endif /* MARK_BIT_PER_OBJ */ + TRACE(source, GC_log_printf("GC #%lu: passed validity tests\n", + (unsigned long)GC_gc_no)); + SET_MARK_BIT_EXIT_IF_SET(hhdr, gran_displ); /* contains "break" */ + TRACE(source, GC_log_printf("GC #%lu: previously unmarked\n", + (unsigned long)GC_gc_no)); + TRACE_TARGET(base, GC_log_printf("GC #%lu: marking %p from %p instead\n", + (unsigned long)GC_gc_no, (void *)base, + (void *)source)); + INCR_MARKS(hhdr); + GC_STORE_BACK_PTR(source, base); + mark_stack_top = GC_push_obj(base, hhdr, mark_stack_top, + mark_stack_limit); + } while (0); + return mark_stack_top; +} + +#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) +# define PUSH_ONE_CHECKED_STACK(p, source) \ + GC_mark_and_push_stack((ptr_t)(p), (ptr_t)(source)) +#else +# define PUSH_ONE_CHECKED_STACK(p, source) \ + GC_mark_and_push_stack((ptr_t)(p)) +#endif + +/* + * Push a single value onto mark stack. Mark from the object pointed to by p. + * Invoke FIXUP_POINTER(p) before any further processing. + * P is considered valid even if it is an interior pointer. + * Previously marked objects are not pushed. Hence we make progress even + * if the mark stack overflows. + */ + +#ifdef NEED_FIXUP_POINTER + /* Try both the raw version and the fixed up one. */ +# define GC_PUSH_ONE_STACK(p, source) \ + 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 ((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) \ + 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,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 */ +/* about one page worth of work. Return the new mark stack */ +/* top entry. */ +GC_INNER mse * GC_mark_from(mse * top, mse * bottom, mse *limit); + +#define MARK_FROM_MARK_STACK() \ + GC_mark_stack_top = GC_mark_from(GC_mark_stack_top, \ + 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 + * real_ptr. That is the job of the caller, if appropriate. + * Note that this is called with the mutator running, but + * with us holding the allocation lock. This is safe only if the + * mutator needs the allocation lock to reveal hidden pointers. + * 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)) { /* empty */ } \ + } \ + } while (0) + + /* Current state of marking, as follows.*/ + + /* We say something is dirty if it was */ + /* written since the last time we */ + /* retrieved dirty bits. We say it's */ + /* grungy if it was marked dirty in the */ + /* last set of bits we retrieved. */ + + /* Invariant "I": all roots and marked */ + /* objects p are either dirty, or point */ + /* to objects q that are either marked */ + /* or a pointer to q appears in a range */ + /* on the mark stack. */ + +#define MS_NONE 0 /* No marking in progress. "I" holds. */ + /* Mark stack is empty. */ + +#define MS_PUSH_RESCUERS 1 /* Rescuing objects are currently */ + /* being pushed. "I" holds, except */ + /* that grungy roots may point to */ + /* unmarked objects, as may marked */ + /* grungy objects above GC_scan_ptr. */ + +#define MS_PUSH_UNCOLLECTABLE 2 /* "I" holds, except that marked */ + /* uncollectible objects above */ + /* GC_scan_ptr may point to unmarked */ + /* objects. Roots may point to */ + /* unmarked objects. */ + +#define MS_ROOTS_PUSHED 3 /* "I" holds, mark stack may be nonempty. */ + +#define MS_PARTIALLY_INVALID 4 /* "I" may not hold, e.g. because of */ + /* the mark stack overflow. However, */ + /* marked heap objects below */ + /* GC_scan_ptr point to marked or */ + /* stacked objects. */ + +#define MS_INVALID 5 /* "I" may not hold. */ + +EXTERN_C_END + +#endif /* GC_PMARK_H */ diff --git a/bdwgc/include/private/gc_priv.h b/bdwgc/include/private/gc_priv.h new file mode 100644 index 000000000..32b073737 --- /dev/null +++ b/bdwgc/include/private/gc_priv.h @@ -0,0 +1,3126 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2008-2021 Ivan Maidanski + * + * 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. + */ + +#ifndef GC_PRIVATE_H +#define GC_PRIVATE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if !defined(GC_BUILD) && !defined(NOT_GCBUILD) +# define GC_BUILD +#endif + +#if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__) \ + || defined(__CYGWIN__) || defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) \ + || defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG) \ + || defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)) && !defined(_GNU_SOURCE) + /* Can't test LINUX, since this must be defined before other includes. */ +# define _GNU_SOURCE 1 +#endif + +#if defined(__INTERIX) && !defined(_ALL_SOURCE) +# define _ALL_SOURCE 1 +#endif + +#if (defined(DGUX) && defined(GC_THREADS) || defined(DGUX386_THREADS) \ + || defined(GC_DGUX386_THREADS)) && !defined(_USING_POSIX4A_DRAFT10) +# define _USING_POSIX4A_DRAFT10 1 +#endif + +#if defined(__MINGW32__) && !defined(__MINGW_EXCPT_DEFINE_PSDK) \ + && defined(__i386__) && defined(GC_EXTERN) /* defined in gc.c */ + /* See the description in mark.c. */ +# define __MINGW_EXCPT_DEFINE_PSDK 1 +#endif + +# if defined(NO_DEBUGGING) && !defined(GC_ASSERTIONS) && !defined(NDEBUG) + /* To turn off assertion checking (in atomic_ops.h). */ +# define NDEBUG 1 +# endif + +#ifndef GC_H +# include "../gc.h" +#endif + +#include +#if !defined(sony_news) +# include +#endif + +#ifdef DGUX +# include +# include +# include +#endif /* DGUX */ + +#ifdef BSD_TIME +# include +# include +# include +#endif /* BSD_TIME */ + +#ifdef PARALLEL_MARK +# define AO_REQUIRE_CAS +# if !defined(__GNUC__) && !defined(AO_ASSUME_WINDOWS98) +# define AO_ASSUME_WINDOWS98 +# endif +#endif + +#include "../gc_tiny_fl.h" +#include "../gc_mark.h" + +typedef GC_word word; +typedef GC_signed_word signed_word; +typedef unsigned int unsigned32; + +typedef int GC_bool; +#define TRUE 1 +#define FALSE 0 + +#ifndef PTR_T_DEFINED + typedef char * ptr_t; /* A generic pointer to which we can add */ + /* byte displacements and which can be used */ + /* for address comparisons. */ +# define PTR_T_DEFINED +#endif + +#ifndef SIZE_MAX +# include +#endif +#if defined(SIZE_MAX) && !defined(CPPCHECK) +# define GC_SIZE_MAX ((size_t)SIZE_MAX) + /* Extra cast to workaround some buggy SIZE_MAX definitions. */ +#else +# define GC_SIZE_MAX (~(size_t)0) +#endif + +#if GC_GNUC_PREREQ(3, 0) && !defined(LINT2) +# define EXPECT(expr, outcome) __builtin_expect(expr,outcome) + /* Equivalent to (expr), but predict that usually (expr)==outcome. */ +#else +# define EXPECT(expr, outcome) (expr) +#endif /* __GNUC__ */ + +/* Saturated addition of size_t values. Used to avoid value wrap */ +/* around on overflow. The arguments should have no side effects. */ +#define SIZET_SAT_ADD(a, b) \ + (EXPECT((a) < GC_SIZE_MAX - (b), TRUE) ? (a) + (b) : GC_SIZE_MAX) + +#include "gcconfig.h" + +#if !defined(GC_ATOMIC_UNCOLLECTABLE) && defined(ATOMIC_UNCOLLECTABLE) + /* For compatibility with old-style naming. */ +# define GC_ATOMIC_UNCOLLECTABLE +#endif + +#ifndef GC_INNER + /* This tagging macro must be used at the start of every variable */ + /* definition which is declared with GC_EXTERN. Should be also used */ + /* 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 (located in "extra" folder). */ +# if defined(GC_DLL) && defined(__GNUC__) && !defined(MSWIN32) \ + && !defined(MSWINCE) && !defined(CYGWIN32) +# if GC_GNUC_PREREQ(4, 0) && !defined(GC_NO_VISIBILITY) + /* See the corresponding GC_API definition. */ +# define GC_INNER __attribute__((__visibility__("hidden"))) +# else + /* The attribute is unsupported. */ +# define GC_INNER /* empty */ +# endif +# else +# define GC_INNER /* empty */ +# endif + +# define GC_EXTERN extern GC_INNER + /* Used only for the GC-scope variables (prefixed with "GC_") */ + /* declared in the header files. Must not be used for thread-local */ + /* variables. Must not be used in gcconfig.h. Shouldn't be used for */ + /* the debugging-only or profiling-only variables. Currently, not */ + /* used for the variables accessed from the "dated" source files */ + /* (specific.c/h, and in the "extra" folder). */ + /* The corresponding variable definition must start with GC_INNER. */ +#endif /* !GC_INNER */ + +#ifdef __cplusplus + /* Register storage specifier is deprecated in C++11. */ +# define REGISTER /* empty */ +#else + /* Used only for several local variables in the performance-critical */ + /* functions. Should not be used for new code. */ +# define REGISTER register +#endif + +#if defined(CPPCHECK) +# define MACRO_BLKSTMT_BEGIN { +# define MACRO_BLKSTMT_END } +# define LOCAL_VAR_INIT_OK =0 /* to avoid "uninit var" false positive */ +#else +# define MACRO_BLKSTMT_BEGIN do { +# define MACRO_BLKSTMT_END } while (0) +# define LOCAL_VAR_INIT_OK /* empty */ +#endif + +#if defined(M68K) && defined(__GNUC__) + /* By default, __alignof__(word) is 2 on m68k. Use this attribute to */ + /* have proper word alignment (i.e. 4-byte on a 32-bit arch). */ +# define GC_ATTR_WORD_ALIGNED __attribute__((__aligned__(sizeof(word)))) +#else +# define GC_ATTR_WORD_ALIGNED /* empty */ +#endif + +#ifndef HEADERS_H +# include "gc_hdrs.h" +#endif + +#ifndef GC_ATTR_NO_SANITIZE_ADDR +# ifndef ADDRESS_SANITIZER +# define GC_ATTR_NO_SANITIZE_ADDR /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize("address"))) +# else +# define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize_address)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_ADDR */ + +#ifndef GC_ATTR_NO_SANITIZE_MEMORY +# ifndef MEMORY_SANITIZER +# define GC_ATTR_NO_SANITIZE_MEMORY /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# else +# define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_MEMORY */ + +#ifndef GC_ATTR_NO_SANITIZE_THREAD +# ifndef THREAD_SANITIZER +# define GC_ATTR_NO_SANITIZE_THREAD /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread"))) +# else + /* It seems that no_sanitize_thread attribute has no effect if the */ + /* function is inlined (as of gcc 11.1.0, at least). */ +# define GC_ATTR_NO_SANITIZE_THREAD \ + GC_ATTR_NOINLINE __attribute__((no_sanitize_thread)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_THREAD */ + +#ifndef GC_ATTR_UNUSED +# if GC_GNUC_PREREQ(3, 4) +# define GC_ATTR_UNUSED __attribute__((__unused__)) +# else +# define GC_ATTR_UNUSED /* empty */ +# endif +#endif /* !GC_ATTR_UNUSED */ + +#ifdef HAVE_CONFIG_H + /* The "inline" keyword is determined by Autoconf AC_C_INLINE. */ +# define GC_INLINE static inline +#elif defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__DMC__) \ + || (GC_GNUC_PREREQ(3, 0) && defined(__STRICT_ANSI__)) \ + || defined(__WATCOMC__) +# define GC_INLINE static __inline +#elif GC_GNUC_PREREQ(3, 0) || defined(__sun) +# define GC_INLINE static inline +#else +# define GC_INLINE static +#endif + +#ifndef GC_ATTR_NOINLINE +# if GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_NOINLINE __attribute__((__noinline__)) +# elif _MSC_VER >= 1400 +# define GC_ATTR_NOINLINE __declspec(noinline) +# else +# define GC_ATTR_NOINLINE /* empty */ +# endif +#endif + +#ifndef GC_API_OSCALL + /* This is used to identify GC routines called by name from OS. */ +# if defined(__GNUC__) +# if GC_GNUC_PREREQ(4, 0) && !defined(GC_NO_VISIBILITY) + /* Same as GC_API if GC_DLL. */ +# define GC_API_OSCALL extern __attribute__((__visibility__("default"))) +# else + /* The attribute is unsupported. */ +# define GC_API_OSCALL extern +# endif +# else +# define GC_API_OSCALL GC_API +# endif +#endif + +#ifndef GC_API_PRIV +# define GC_API_PRIV GC_API +#endif + +#if defined(THREADS) && !defined(NN_PLATFORM_CTR) +# include "gc_atomic_ops.h" +# ifndef AO_HAVE_compiler_barrier +# define AO_HAVE_compiler_barrier 1 +# endif +#endif + +#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +# include +#endif + +#include "gc_locks.h" + +#define GC_WORD_MAX (~(word)0) + +# ifdef STACK_GROWS_DOWN +# define COOLER_THAN > +# define HOTTER_THAN < +# define MAKE_COOLER(x,y) if ((word)((x) + (y)) > (word)(x)) {(x) += (y);} \ + else (x) = (ptr_t)GC_WORD_MAX +# define MAKE_HOTTER(x,y) (x) -= (y) +# else +# define COOLER_THAN < +# define HOTTER_THAN > +# define MAKE_COOLER(x,y) if ((word)((x) - (y)) < (word)(x)) {(x) -= (y);} \ + else (x) = 0 +# define MAKE_HOTTER(x,y) (x) += (y) +# endif + +#if defined(AMIGA) && defined(__SASC) +# define GC_FAR __far +#else +# define GC_FAR +#endif + + +/*********************************/ +/* */ +/* Definitions for conservative */ +/* collector */ +/* */ +/*********************************/ + +/*********************************/ +/* */ +/* Easily changeable parameters */ +/* */ +/*********************************/ + +/* #define ALL_INTERIOR_POINTERS */ + /* Forces all pointers into the interior of an */ + /* object to be considered valid. Also causes the */ + /* sizes of all objects to be inflated by at least */ + /* one byte. This should suffice to guarantee */ + /* that in the presence of a compiler that does */ + /* not perform garbage-collector-unsafe */ + /* optimizations, all portable, strictly ANSI */ + /* conforming C programs should be safely usable */ + /* with malloc replaced by GC_malloc and free */ + /* calls removed. There are several disadvantages: */ + /* 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" */ + /* by integers, etc. Under SunOS 4.X with a */ + /* statically linked libc, we empirically */ + /* observed that it would be difficult to */ + /* allocate individual objects > 100 KB. */ + /* Even if only smaller objects are allocated, */ + /* more swap space is likely to be needed. */ + /* Fortunately, much of this will never be */ + /* touched. */ + /* If you can easily avoid using this option, do. */ + /* If not, try to keep individual objects small. */ + /* This is now really controlled at startup, */ + /* through GC_all_interior_pointers. */ + +EXTERN_C_BEGIN + +#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_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 GC_TOGGLE_REFS_NOT_NEEDED + GC_INNER void GC_process_togglerefs(void); + /* Process the toggle-refs before GC starts. */ +# endif +# 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 + /* Explicitly instruct the code analysis tool that */ + /* GC_all_interior_pointers is assumed to have only 0 or 1 value. */ +# define EXTRA_BYTES ((size_t)(GC_all_interior_pointers? 1 : 0)) +# else +# define EXTRA_BYTES (size_t)GC_all_interior_pointers +# endif +# define MAX_EXTRA_BYTES 1 +#else +# define EXTRA_BYTES 0 +# define MAX_EXTRA_BYTES 0 +#endif + + +# ifndef LARGE_CONFIG +# define MINHINCR 16 /* Minimum heap increment, in blocks of HBLKSIZE */ + /* Must be multiple of largest page size. */ +# define MAXHINCR 2048 /* Maximum heap increment, in blocks */ +# else +# define MINHINCR 64 +# define MAXHINCR 4096 +# endif + +# define BL_LIMIT GC_black_list_spacing + /* If we need a block of N bytes, and we have */ + /* a block of N + BL_LIMIT bytes available, */ + /* and N > BL_LIMIT, */ + /* but all possible positions in it are */ + /* blacklisted, we just use it anyway (and */ + /* print a warning, if warnings are enabled). */ + /* This risks subsequently leaking the block */ + /* due to a false reference. But not using */ + /* the block risks unreasonable immediate */ + /* heap growth. */ + +/*********************************/ +/* */ +/* Stack saving for debugging */ +/* */ +/*********************************/ + +#ifdef NEED_CALLINFO + struct callinfo { + word ci_pc; /* Caller, not callee, pc */ +# if NARGS > 0 + word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ +# endif +# if (NFRAMES * (NARGS + 1)) % 2 == 1 + /* Likely alignment problem. */ + word ci_dummy; +# endif + }; +#endif + +#ifdef SAVE_CALL_CHAIN + /* Fill in the pc and argument information for up to NFRAMES of my */ + /* callers. Ignore my frame and my callers frame. */ + GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +#endif + +EXTERN_C_END + +/*********************************/ +/* */ +/* OS interface routines */ +/* */ +/*********************************/ + +#ifndef NO_CLOCK +#ifdef BSD_TIME +# undef CLOCK_TYPE +# undef GET_TIME +# undef MS_TIME_DIFF +# define CLOCK_TYPE struct timeval +# define CLOCK_TYPE_INITIALIZER { 0, 0 } +# 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)((long)(a.tv_sec-b.tv_sec) * 1000 \ + + (long)(a.tv_usec - b.tv_usec) / 1000 \ + - (a.tv_usec < b.tv_usec \ + && (long)(a.tv_usec - b.tv_usec) % 1000 != 0 ? 1 : 0))) + /* "a" time is expected to be not earlier than */ + /* "b" one; the result has unsigned long type. */ +# define NS_FRAC_TIME_DIFF(a, b) ((unsigned long) \ + ((a.tv_usec < b.tv_usec \ + && (long)(a.tv_usec - b.tv_usec) % 1000 != 0 ? 1000L : 0) \ + + (long)(a.tv_usec - b.tv_usec) % 1000) * 1000) + /* The total time difference could be computed as */ + /* MS_TIME_DIFF(a,b)*1000000+NS_FRAC_TIME_DIFF(a,b).*/ + +#elif defined(MSWIN32) || defined(MSWINCE) || defined(WINXP_USE_PERF_COUNTER) +# if defined(MSWINRT_FLAVOR) || defined(WINXP_USE_PERF_COUNTER) +# define CLOCK_TYPE ULONGLONG +# define GET_TIME(x) \ + do { \ + LARGE_INTEGER freq, tc; \ + if (!QueryPerformanceFrequency(&freq)) \ + ABORT("QueryPerformanceFrequency requires WinXP+"); \ + /* Note: two standalone if statements are needed to */ \ + /* avoid MS VC false warning about potentially */ \ + /* uninitialized tc variable. */ \ + if (!QueryPerformanceCounter(&tc)) \ + ABORT("QueryPerformanceCounter failed"); \ + x = (CLOCK_TYPE)((double)tc.QuadPart/freq.QuadPart * 1e9); \ + } while (0) + /* TODO: Call QueryPerformanceFrequency once at GC init. */ +# define MS_TIME_DIFF(a, b) ((unsigned long)(((a) - (b)) / 1000000UL)) +# define NS_FRAC_TIME_DIFF(a, b) ((unsigned long)(((a) - (b)) % 1000000UL)) +# else +# define CLOCK_TYPE DWORD +# define GET_TIME(x) (void)(x = GetTickCount()) +# define MS_TIME_DIFF(a, b) ((unsigned long)((a) - (b))) +# define NS_FRAC_TIME_DIFF(a, b) 0UL +# endif /* !WINXP_USE_PERF_COUNTER */ + +#elif defined(NN_PLATFORM_CTR) +# define CLOCK_TYPE long long + EXTERN_C_BEGIN + CLOCK_TYPE n3ds_get_system_tick(void); + CLOCK_TYPE n3ds_convert_tick_to_ms(CLOCK_TYPE tick); + EXTERN_C_END +# define GET_TIME(x) (void)(x = n3ds_get_system_tick()) +# define MS_TIME_DIFF(a,b) ((unsigned long)n3ds_convert_tick_to_ms((a)-(b))) +# define NS_FRAC_TIME_DIFF(a, b) 0UL /* TODO: implement it */ + +#elif defined(NINTENDO_SWITCH) \ + || (((defined(LINUX) && defined(__USE_POSIX199309)) \ + || defined(CYGWIN32)) && defined(_POSIX_TIMERS)) +# include +# define HAVE_CLOCK_GETTIME 1 +# define CLOCK_TYPE struct timespec +# define CLOCK_TYPE_INITIALIZER { 0, 0 } +# if defined(_POSIX_MONOTONIC_CLOCK) && !defined(NINTENDO_SWITCH) +# define GET_TIME(x) \ + do { \ + if (clock_gettime(CLOCK_MONOTONIC, &x) == -1) \ + ABORT("clock_gettime failed"); \ + } while (0) +# else +# define GET_TIME(x) \ + do { \ + if (clock_gettime(CLOCK_REALTIME, &x) == -1) \ + ABORT("clock_gettime failed"); \ + } while (0) +# endif +# define MS_TIME_DIFF(a, b) \ + /* a.tv_nsec - b.tv_nsec is in range -1e9 to 1e9 exclusively */ \ + ((unsigned long)((a).tv_nsec + (1000000L*1000 - (b).tv_nsec)) / 1000000UL \ + + ((unsigned long)((a).tv_sec - (b).tv_sec) * 1000UL) - 1000UL) +# define NS_FRAC_TIME_DIFF(a, b) \ + ((unsigned long)((a).tv_nsec + (1000000L*1000 - (b).tv_nsec)) % 1000000UL) + +#else /* !BSD_TIME && !LINUX && !NN_PLATFORM_CTR && !MSWIN32 */ +# include +# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC) +# include +# define CLOCKS_PER_SEC CLK_TCK +# endif +# if !defined(CLOCKS_PER_SEC) +# define CLOCKS_PER_SEC 1000000 + /* This is technically a bug in the implementation. */ + /* ANSI requires that CLOCKS_PER_SEC be defined. But at least */ + /* under SunOS 4.1.1, it isn't. Also note that the combination of */ + /* ANSI C and POSIX is incredibly gross here. The type clock_t */ + /* is used by both clock() and times(). But on some machines */ + /* these use different notions of a clock tick, CLOCKS_PER_SEC */ + /* seems to apply only to clock. Hence we use it here. On many */ + /* machines, including SunOS, clock actually uses units of */ + /* microseconds (which are not really clock ticks). */ +# endif +# define CLOCK_TYPE clock_t +# 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) + /* Avoid using double type since some targets (like ARM) might */ + /* require -lm option for double-to-long conversion. */ +# define NS_FRAC_TIME_DIFF(a, b) (CLOCKS_PER_SEC <= 1000 ? 0UL \ + : (unsigned long)(CLOCKS_PER_SEC <= (clock_t)1000000UL \ + ? (((a) - (b)) * ((clock_t)1000000UL / CLOCKS_PER_SEC) % 1000) * 1000 \ + : (CLOCKS_PER_SEC <= (clock_t)1000000UL * 1000 \ + ? ((a) - (b)) * ((clock_t)1000000UL * 1000 / CLOCKS_PER_SEC) \ + : (((a) - (b)) * (clock_t)1000000UL * 1000) / CLOCKS_PER_SEC) \ + % (clock_t)1000000UL)) +#endif /* !BSD_TIME && !MSWIN32 */ +# ifndef CLOCK_TYPE_INITIALIZER + /* This is used to initialize CLOCK_TYPE variables (to some value) */ + /* to avoid "variable might be uninitialized" compiler warnings. */ +# define CLOCK_TYPE_INITIALIZER 0 +# endif +#endif /* !NO_CLOCK */ + +/* We use bzero and bcopy internally. They may not be available. */ +# if defined(SPARC) && defined(SUNOS4) \ + || (defined(M68K) && defined(NEXT)) || defined(VAX) +# define BCOPY_EXISTS +# elif defined(AMIGA) || defined(DARWIN) +# include +# define BCOPY_EXISTS +# elif defined(MACOS) && defined(POWERPC) +# include +# define bcopy(x,y,n) BlockMoveData(x, y, n) +# define bzero(x,n) BlockZero(x, n) +# define BCOPY_EXISTS +# endif + +# if !defined(BCOPY_EXISTS) || defined(CPPCHECK) +# include +# define BCOPY(x,y,n) memcpy(y, x, (size_t)(n)) +# define BZERO(x,n) memset(x, 0, (size_t)(n)) +# else +# define BCOPY(x,y,n) bcopy((void *)(x),(void *)(y),(size_t)(n)) +# define BZERO(x,n) bzero((void *)(x),(size_t)(n)) +# endif + +#ifdef PCR +# include "th/PCR_ThCtl.h" +#endif + +EXTERN_C_BEGIN + +/* + * Stop and restart mutator threads. + */ +# ifdef PCR +# define STOP_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_stopNormal, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# define START_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# else +# if defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) + GC_INNER void GC_stop_world(void); + GC_INNER void GC_start_world(void); +# define STOP_WORLD() GC_stop_world() +# define START_WORLD() GC_start_world() +# else + /* Just do a sanity check: we are not inside GC_do_blocking(). */ +# define STOP_WORLD() GC_ASSERT(GC_blocked_sp == NULL) +# define START_WORLD() +# endif +# endif + +#ifdef THREADS + GC_EXTERN GC_on_thread_event_proc GC_on_thread_event; +#endif + +/* Abandon ship */ +# if defined(SMALL_CONFIG) || defined(PCR) +# define GC_on_abort(msg) (void)0 /* be silent on abort */ +# else + GC_API_PRIV GC_abort_func GC_on_abort; +# endif +# if defined(CPPCHECK) +# define ABORT(msg) { GC_on_abort(msg); abort(); } +# elif defined(PCR) +# define ABORT(s) PCR_Base_Panic(s) +# else +# if defined(MSWIN_XBOX1) && !defined(DebugBreak) +# define DebugBreak() __debugbreak() +# elif defined(MSWINCE) && !defined(DebugBreak) \ + && (!defined(UNDER_CE) || (defined(__MINGW32CE__) && !defined(ARM32))) + /* This simplifies linking for WinCE (and, probably, doesn't */ + /* hurt debugging much); use -DDebugBreak=DebugBreak to override */ + /* this behavior if really needed. This is also a workaround for */ + /* x86mingw32ce toolchain (if it is still declaring DebugBreak() */ + /* instead of defining it as a macro). */ +# define DebugBreak() _exit(-1) /* there is no abort() in WinCE */ +# endif +# if defined(MSWIN32) && (defined(NO_DEBUGGING) || defined(LINT2)) + /* A more user-friendly abort after showing fatal message. */ +# define ABORT(msg) (GC_on_abort(msg), _exit(-1)) + /* Exit on error without running "at-exit" callbacks. */ +# elif defined(MSWINCE) && defined(NO_DEBUGGING) +# define ABORT(msg) (GC_on_abort(msg), ExitProcess(-1)) +# elif defined(MSWIN32) || defined(MSWINCE) +# if defined(_CrtDbgBreak) && defined(_DEBUG) && defined(_MSC_VER) +# define ABORT(msg) { GC_on_abort(msg); \ + _CrtDbgBreak() /* __debugbreak() */; } +# else +# 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. */ +# endif +# 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) \ + MACRO_BLKSTMT_BEGIN \ + GC_ERRINFO_PRINTF(C_msg /* + */ C_fmt "\n", arg1); \ + ABORT(C_msg); \ + MACRO_BLKSTMT_END +#define ABORT_ARG2(C_msg, C_fmt, arg1, arg2) \ + MACRO_BLKSTMT_BEGIN \ + GC_ERRINFO_PRINTF(C_msg /* + */ C_fmt "\n", arg1, arg2); \ + ABORT(C_msg); \ + MACRO_BLKSTMT_END +#define ABORT_ARG3(C_msg, C_fmt, arg1, arg2, arg3) \ + MACRO_BLKSTMT_BEGIN \ + GC_ERRINFO_PRINTF(C_msg /* + */ C_fmt "\n", \ + arg1, arg2, arg3); \ + ABORT(C_msg); \ + MACRO_BLKSTMT_END + +/* Same as ABORT but does not have 'no-return' attribute. */ +/* 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() (GC_on_abort(NULL), exit(1 /* EXIT_FAILURE */)) +# endif + +/* Print warning message, e.g. almost out of memory. */ +/* The argument (if any) format specifier should be: */ +/* "%s", "%p", "%"WARN_PRIdPTR or "%"WARN_PRIuPTR. */ +#define WARN(msg, arg) \ + (*GC_current_warn_proc)((/* no const */ char *)("GC Warning: " msg), \ + (word)(arg)) +GC_EXTERN GC_warn_proc GC_current_warn_proc; + +/* Print format type macro for decimal signed_word value passed WARN(). */ +/* This could be redefined for Win64 or LLP64, but typically should */ +/* not be done as the WARN format string is, possibly, processed on the */ +/* client side, so non-standard print type modifiers (like MS "I64d") */ +/* should be avoided here if possible. */ +#ifndef WARN_PRIdPTR + /* Assume sizeof(void *) == sizeof(long) or a little-endian machine. */ +# define WARN_PRIdPTR "ld" +# define WARN_PRIuPTR "lu" +#endif + +/* A tagging macro (for a code static analyzer) to indicate that the */ +/* string obtained from an untrusted source (e.g., argv[], getenv) is */ +/* safe to use in a vulnerable operation (e.g., open, exec). */ +#define TRUSTED_STRING(s) (char*)COVERT_DATAFLOW(s) + +/* Get environment entry */ +#ifdef GC_READ_ENV_FILE + GC_INNER char * GC_envfile_getenv(const char *name); +# define GETENV(name) GC_envfile_getenv(name) +#elif defined(NO_GETENV) && !defined(CPPCHECK) +# define GETENV(name) NULL +#elif defined(EMPTY_GETENV_RESULTS) + /* Workaround for a reputed Wine bug. */ + GC_INLINE char * fixed_getenv(const char *name) + { + char *value = getenv(name); + return value != NULL && *value != '\0' ? value : NULL; + } +# define GETENV(name) fixed_getenv(name) +#else +# define GETENV(name) getenv(name) +#endif + +EXTERN_C_END + +#if defined(DARWIN) +# include +# ifndef MAC_OS_X_VERSION_MAX_ALLOWED +# include + /* Include this header just to import the above macro. */ +# endif +# if defined(POWERPC) +# if CPP_WORDSZ == 32 +# define GC_THREAD_STATE_T ppc_thread_state_t +# else +# define GC_THREAD_STATE_T ppc_thread_state64_t +# define GC_MACH_THREAD_STATE PPC_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT +# endif +# elif defined(I386) || defined(X86_64) +# if CPP_WORDSZ == 32 +# if defined(i386_THREAD_STATE_COUNT) && !defined(x86_THREAD_STATE32_COUNT) + /* Use old naming convention for 32-bit x86. */ +# define GC_THREAD_STATE_T i386_thread_state_t +# define GC_MACH_THREAD_STATE i386_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT i386_THREAD_STATE_COUNT +# else +# define GC_THREAD_STATE_T x86_thread_state32_t +# define GC_MACH_THREAD_STATE x86_THREAD_STATE32 +# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT +# endif +# else +# define GC_THREAD_STATE_T x86_thread_state64_t +# define GC_MACH_THREAD_STATE x86_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT +# endif +# elif defined(ARM32) && defined(ARM_UNIFIED_THREAD_STATE) \ + && !defined(CPPCHECK) +# define GC_THREAD_STATE_T arm_unified_thread_state_t +# define GC_MACH_THREAD_STATE ARM_UNIFIED_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT ARM_UNIFIED_THREAD_STATE_COUNT +# elif defined(ARM32) +# define GC_THREAD_STATE_T arm_thread_state_t +# ifdef ARM_MACHINE_THREAD_STATE_COUNT +# define GC_MACH_THREAD_STATE ARM_MACHINE_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT ARM_MACHINE_THREAD_STATE_COUNT +# endif +# elif defined(AARCH64) +# define GC_THREAD_STATE_T arm_thread_state64_t +# define GC_MACH_THREAD_STATE ARM_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT +# elif !defined(CPPCHECK) +# error define GC_THREAD_STATE_T +# endif +# ifndef GC_MACH_THREAD_STATE +# define GC_MACH_THREAD_STATE MACHINE_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT MACHINE_THREAD_STATE_COUNT +# endif + +# if CPP_WORDSZ == 32 +# define GC_MACH_HEADER mach_header +# define GC_MACH_SECTION section +# define GC_GETSECTBYNAME getsectbynamefromheader +# else +# define GC_MACH_HEADER mach_header_64 +# define GC_MACH_SECTION section_64 +# define GC_GETSECTBYNAME getsectbynamefromheader_64 +# endif + + /* Try to work out the right way to access thread state structure */ + /* members. The structure has changed its definition in different */ + /* Darwin versions. This now defaults to the (older) names */ + /* without __, thus hopefully, not breaking any existing */ + /* Makefile.direct builds. */ +# if __DARWIN_UNIX03 +# define THREAD_FLD_NAME(x) __ ## x +# else +# define THREAD_FLD_NAME(x) x +# endif +# if defined(ARM32) && defined(ARM_UNIFIED_THREAD_STATE) +# define THREAD_FLD(x) ts_32.THREAD_FLD_NAME(x) +# else +# define THREAD_FLD(x) THREAD_FLD_NAME(x) +# endif +#endif /* DARWIN */ + +#include + +#if __STDC_VERSION__ >= 201112L +# include /* for static_assert */ +#endif + +EXTERN_C_BEGIN + +/*********************************/ +/* */ +/* Word-size-dependent defines */ +/* */ +/*********************************/ + +#if CPP_WORDSZ == 32 +# define WORDS_TO_BYTES(x) ((x)<<2) +# define BYTES_TO_WORDS(x) ((x)>>2) +# define LOGWL ((word)5) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x1f) /* n mod size of word */ +# if ALIGNMENT != 4 +# define UNALIGNED_PTRS +# endif +#endif + +#if CPP_WORDSZ == 64 +# define WORDS_TO_BYTES(x) ((x)<<3) +# define BYTES_TO_WORDS(x) ((x)>>3) +# define LOGWL ((word)6) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x3f) /* n mod size of word */ +# if ALIGNMENT != 8 +# define UNALIGNED_PTRS +# endif +#endif + +/* The first TINY_FREELISTS free lists correspond to the first */ +/* TINY_FREELISTS multiples of GRANULE_BYTES, i.e. we keep */ +/* separate free lists for each multiple of GRANULE_BYTES */ +/* up to (TINY_FREELISTS-1) * GRANULE_BYTES. After that they */ +/* may be spread out further. */ + +#define GRANULE_BYTES GC_GRANULE_BYTES +#define TINY_FREELISTS GC_TINY_FREELISTS + +#define WORDSZ ((word)CPP_WORDSZ) +#define SIGNB ((word)1 << (WORDSZ-1)) +#define BYTES_PER_WORD ((word)(sizeof (word))) +#define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */ + +#if GRANULE_BYTES == 8 +# define BYTES_TO_GRANULES(n) ((n)>>3) +# define GRANULES_TO_BYTES(n) ((n)<<3) +# if CPP_WORDSZ == 64 +# define GRANULES_TO_WORDS(n) (n) +# elif CPP_WORDSZ == 32 +# define GRANULES_TO_WORDS(n) ((n)<<1) +# else +# define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +# endif +#elif GRANULE_BYTES == 16 +# define BYTES_TO_GRANULES(n) ((n)>>4) +# define GRANULES_TO_BYTES(n) ((n)<<4) +# if CPP_WORDSZ == 64 +# define GRANULES_TO_WORDS(n) ((n)<<1) +# elif CPP_WORDSZ == 32 +# define GRANULES_TO_WORDS(n) ((n)<<2) +# else +# define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +# endif +#else +# error Bad GRANULE_BYTES value +#endif + +/*********************/ +/* */ +/* Size Parameters */ +/* */ +/*********************/ + +/* Heap block size, bytes. Should be power of 2. */ +/* Incremental GC with MPROTECT_VDB currently requires the */ +/* page size to be a multiple of HBLKSIZE. Since most modern */ +/* architectures support variable page sizes down to 4 KB, and */ +/* x86 is generally 4 KB, we now default to 4 KB, except for */ +/* Alpha: Seems to be used with 8 KB pages. */ +/* SMALL_CONFIG: Want less block-level fragmentation. */ +#ifndef HBLKSIZE +# if defined(LARGE_CONFIG) || !defined(SMALL_CONFIG) +# ifdef ALPHA +# define CPP_LOG_HBLKSIZE 13 +# elif defined(SN_TARGET_PSP2) +# define CPP_LOG_HBLKSIZE 16 /* page size is set to 64 KB */ +# else +# define CPP_LOG_HBLKSIZE 12 +# endif +# else +# define CPP_LOG_HBLKSIZE 10 +# endif +#else +# if HBLKSIZE == 512 +# define CPP_LOG_HBLKSIZE 9 +# elif HBLKSIZE == 1024 +# define CPP_LOG_HBLKSIZE 10 +# elif HBLKSIZE == 2048 +# define CPP_LOG_HBLKSIZE 11 +# elif HBLKSIZE == 4096 +# define CPP_LOG_HBLKSIZE 12 +# elif HBLKSIZE == 8192 +# define CPP_LOG_HBLKSIZE 13 +# elif HBLKSIZE == 16384 +# define CPP_LOG_HBLKSIZE 14 +# elif !defined(CPPCHECK) +# error Bad HBLKSIZE value +# endif +# undef HBLKSIZE +#endif + +# define CPP_HBLKSIZE (1 << CPP_LOG_HBLKSIZE) +# define LOG_HBLKSIZE ((size_t)CPP_LOG_HBLKSIZE) +# define HBLKSIZE ((size_t)CPP_HBLKSIZE) + +#define GC_SQRT_SIZE_MAX ((((size_t)1) << (WORDSZ / 2)) - 1) + +/* Max size objects supported by freelist (larger objects are */ +/* allocated directly with allchblk(), by rounding to the next */ +/* multiple of HBLKSIZE). */ +#define CPP_MAXOBJBYTES (CPP_HBLKSIZE/2) +#define MAXOBJBYTES ((size_t)CPP_MAXOBJBYTES) +#define CPP_MAXOBJWORDS BYTES_TO_WORDS(CPP_MAXOBJBYTES) +#define MAXOBJWORDS ((size_t)CPP_MAXOBJWORDS) +#define CPP_MAXOBJGRANULES BYTES_TO_GRANULES(CPP_MAXOBJBYTES) +#define MAXOBJGRANULES ((size_t)CPP_MAXOBJGRANULES) + +# define divHBLKSZ(n) ((n) >> LOG_HBLKSIZE) + +# define HBLK_PTR_DIFF(p,q) divHBLKSZ((ptr_t)p - (ptr_t)q) + /* Equivalent to subtracting 2 hblk pointers. */ + /* We do it this way because a compiler should */ + /* find it hard to use an integer division */ + /* instead of a shift. The bundled SunOS 4.1 */ + /* o.w. sometimes pessimizes the subtraction to */ + /* involve a call to .div. */ + +# define modHBLKSZ(n) ((n) & (HBLKSIZE-1)) + +# define HBLKPTR(objptr) ((struct hblk *)(((word)(objptr)) \ + & ~(word)(HBLKSIZE-1))) +# define HBLKDISPL(objptr) (((size_t) (objptr)) & (HBLKSIZE-1)) + +/* Round up allocation size (in bytes) to a multiple of a granule. */ +#define ROUNDUP_GRANULE_SIZE(lb) /* lb should have no side-effect */ \ + (SIZET_SAT_ADD(lb, GRANULE_BYTES - 1) & ~(GRANULE_BYTES - 1)) + +/* Round up byte allocation requests to integral number of words, etc. */ +# define ROUNDED_UP_GRANULES(lb) /* lb should have no side-effect */ \ + BYTES_TO_GRANULES(SIZET_SAT_ADD(lb, GRANULE_BYTES - 1 + EXTRA_BYTES)) +# if MAX_EXTRA_BYTES == 0 +# define SMALL_OBJ(bytes) EXPECT((bytes) <= (MAXOBJBYTES), TRUE) +# else +# define SMALL_OBJ(bytes) \ + (EXPECT((bytes) <= (MAXOBJBYTES - MAX_EXTRA_BYTES), TRUE) \ + || (bytes) <= MAXOBJBYTES - EXTRA_BYTES) + /* This really just tests bytes <= MAXOBJBYTES - EXTRA_BYTES. */ + /* But we try to avoid looking up EXTRA_BYTES. */ +# endif +# define ADD_SLOP(lb) /* lb should have no side-effect */ \ + SIZET_SAT_ADD(lb, EXTRA_BYTES) + +/* + * Hash table representation of sets of pages. + * Implements a map from aligned HBLKSIZE chunks of the address space to one + * bit each. + * This assumes it is OK to spuriously set bits, e.g. because multiple + * addresses are represented by a single location. + * Used by black-listing code, and perhaps by dirty bit maintenance code. + */ + +#ifndef LOG_PHT_ENTRIES +# ifdef LARGE_CONFIG +# if CPP_WORDSZ == 32 +# define LOG_PHT_ENTRIES 20 /* Collisions likely at 1M blocks, */ + /* which is >= 4 GB. Each table takes */ + /* 128 KB, some of which may never be */ + /* touched. */ +# else +# define LOG_PHT_ENTRIES 21 /* Collisions likely at 2M blocks, */ + /* which is >= 8 GB. Each table takes */ + /* 256 KB, some of which may never be */ + /* touched. */ +# endif +# elif !defined(SMALL_CONFIG) +# define LOG_PHT_ENTRIES 18 /* Collisions are likely if heap grows */ + /* to more than 256K hblks >= 1 GB. */ + /* Each hash table occupies 32 KB. */ + /* Even for somewhat smaller heaps, */ + /* say half that, collisions may be an */ + /* issue because we blacklist */ + /* addresses outside the heap. */ +# else +# define LOG_PHT_ENTRIES 15 /* Collisions are likely if heap grows */ + /* to more than 32K hblks (128 MB). */ + /* Each hash table occupies 4 KB. */ +# endif +#endif /* !LOG_PHT_ENTRIES */ + +# define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES) +# define PHT_SIZE (PHT_ENTRIES >> LOGWL) +typedef word page_hash_table[PHT_SIZE]; + +# define PHT_HASH(addr) ((((word)(addr)) >> LOG_HBLKSIZE) & (PHT_ENTRIES - 1)) + +# define get_pht_entry_from_index(bl, index) \ + (((bl)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) +# define set_pht_entry_from_index(bl, index) \ + (void)((bl)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index)) + +#if defined(THREADS) && defined(AO_HAVE_or) + /* And, one more version for GC_add_to_black_list_normal/stack */ + /* (invoked indirectly by GC_do_local_mark) and */ + /* async_set_pht_entry_from_index (invoked by GC_dirty or the write */ + /* fault handler). */ +# define set_pht_entry_from_index_concurrent(bl, index) \ + AO_or((volatile AO_t *)&(bl)[divWORDSZ(index)], \ + (AO_t)((word)1 << modWORDSZ(index))) +#else +# define set_pht_entry_from_index_concurrent(bl, index) \ + set_pht_entry_from_index(bl, index) +#endif + + +/********************************************/ +/* */ +/* H e a p B l o c k s */ +/* */ +/********************************************/ + +/* heap block header */ +#define HBLKMASK (HBLKSIZE-1) + +#define MARK_BITS_PER_HBLK (HBLKSIZE/GRANULE_BYTES) + /* upper bound */ + /* We allocate 1 bit per allocation granule. */ + /* If MARK_BIT_PER_GRANULE is defined, we use */ + /* every nth bit, where n is the number of */ + /* allocation granules per object. If */ + /* MARK_BIT_PER_OBJ is defined, we only use the */ + /* initial group of mark bits, and it is safe */ + /* to allocate smaller header for large objects. */ + +union word_ptr_ao_u { + word w; + signed_word sw; + void *vp; +# ifdef PARALLEL_MARK + 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. */ +struct hblkhdr { + struct hblk * hb_next; /* Link field for hblk free list */ + /* and for lists of chunks waiting to be */ + /* reclaimed. */ + struct hblk * hb_prev; /* Backwards link for free list. */ + struct hblk * hb_block; /* The corresponding block. */ + unsigned char hb_obj_kind; + /* Kind of objects in the block. Each kind */ + /* identifies a mark procedure and a set of */ + /* list headers. Sometimes called regions. */ + unsigned char hb_flags; +# define IGNORE_OFF_PAGE 1 /* Ignore pointers that do not */ + /* point to the first hblk of */ + /* this object. */ +# define WAS_UNMAPPED 2 /* This is a free block, which has */ + /* been unmapped from the address */ + /* space. */ + /* GC_remap must be invoked on it */ + /* before it can be reallocated. */ + /* Only set with USE_MUNMAP. */ +# define FREE_BLK 4 /* Block is free, i.e. not in use. */ +# ifdef ENABLE_DISCLAIM +# define HAS_DISCLAIM 8 + /* This kind has a callback on reclaim. */ +# define MARK_UNCONDITIONALLY 0x10 + /* Mark from all objects, marked or */ + /* not. Used to mark objects needed by */ + /* reclaim notifier. */ +# endif +# ifdef MARK_BIT_PER_GRANULE +# define LARGE_BLOCK 0x20 +# endif + unsigned short hb_last_reclaimed; + /* Value of GC_gc_no when block was */ + /* last allocated or swept. May wrap. */ + /* For a free block, this is maintained */ + /* only for USE_MUNMAP, and indicates */ + /* when the header was allocated, or */ + /* when the size of the block last */ + /* changed. */ +# ifdef MARK_BIT_PER_OBJ + unsigned32 hb_inv_sz; /* A good upper bound for 2**32/hb_sz. */ + /* For large objects, we use */ + /* LARGE_INV_SZ. */ +# define LARGE_INV_SZ (1 << 16) +# endif + word hb_sz; /* If in use, size in bytes, of objects in the block. */ + /* if free, the size in bytes of the whole block. */ + /* We assume that this is convertible to signed_word */ + /* without generating a negative result. We avoid */ + /* generating free blocks larger than that. */ + word hb_descr; /* object descriptor for marking. See */ + /* gc_mark.h. */ +# ifdef MARK_BIT_PER_GRANULE + unsigned short * hb_map; /* Essentially a table of remainders */ + /* mod BYTES_TO_GRANULES(hb_sz), except */ + /* for large blocks. See GC_obj_map. */ +# endif +# ifdef PARALLEL_MARK + volatile AO_t hb_n_marks; /* Number of set mark bits, excluding */ + /* the one always set at the end. */ + /* Currently it is concurrently */ + /* updated and hence only approximate. */ + /* But a zero value does guarantee that */ + /* the block contains no marked */ + /* objects. */ + /* Ensuring this property means that we */ + /* never decrement it to zero during a */ + /* collection, and hence the count may */ + /* be one too high. Due to concurrent */ + /* updates, an arbitrary number of */ + /* increments, but not all of them (!) */ + /* may be lost, hence it may in theory */ + /* be much too low. */ + /* The count may also be too high if */ + /* multiple mark threads mark the */ + /* same object due to a race. */ +# else + size_t hb_n_marks; /* Without parallel marking, the count */ + /* is accurate. */ +# endif +# ifdef USE_MARK_BYTES +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK + 1) + /* Unlike the other case, this is in units of bytes. */ + /* Since we force double-word alignment, we need at most one */ + /* mark bit per 2 words. But we do allocate and set one */ + /* extra mark bit to avoid an explicit check for the */ + /* partial object at the end of each block. */ + union { + char _hb_marks[MARK_BITS_SZ]; + /* The i'th byte is 1 if the object */ + /* starting at granule i or object i is */ + /* marked, 0 o.w. */ + /* The mark bit for the "one past the */ + /* end" object is always set to avoid a */ + /* special case test in the marker. */ + word dummy; /* Force word alignment of mark bytes. */ + } _mark_byte_union; +# define hb_marks _mark_byte_union._hb_marks +# else +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK/CPP_WORDSZ + 1) + word hb_marks[MARK_BITS_SZ]; +# endif /* !USE_MARK_BYTES */ +}; + +# define ANY_INDEX 23 /* "Random" mark bit index for assertions */ + +/* heap block body */ + +# define HBLK_WORDS (HBLKSIZE/sizeof(word)) +# define HBLK_GRANULES (HBLKSIZE/GRANULE_BYTES) + +/* The number of objects in a block dedicated to a certain size. */ +/* may erroneously yield zero (instead of one) for large objects. */ +# define HBLK_OBJS(sz_in_bytes) (HBLKSIZE/(sz_in_bytes)) + +struct hblk { + char hb_body[HBLKSIZE]; +}; + +# define HBLK_IS_FREE(hdr) (((hdr) -> hb_flags & FREE_BLK) != 0) + +# define OBJ_SZ_TO_BLOCKS(lb) divHBLKSZ((lb) + HBLKSIZE-1) +# define OBJ_SZ_TO_BLOCKS_CHECKED(lb) /* lb should have no side-effect */ \ + divHBLKSZ(SIZET_SAT_ADD(lb, HBLKSIZE - 1)) + /* Size of block (in units of HBLKSIZE) needed to hold objects of */ + /* given lb (in bytes). The checked variant prevents wrap around. */ + +/* Object free list link */ +# define obj_link(p) (*(void **)(p)) + +# define LOG_MAX_MARK_PROCS 6 +# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) + +/* Root sets. Logically private to mark_rts.c. But we don't want the */ +/* tables scanned, so we put them here. */ +/* MAX_ROOT_SETS is the maximum number of ranges that can be */ +/* registered as static roots. */ +# ifdef LARGE_CONFIG +# define MAX_ROOT_SETS 8192 +# elif !defined(SMALL_CONFIG) +# define MAX_ROOT_SETS 2048 +# else +# define MAX_ROOT_SETS 512 +# endif + +# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) +/* Maximum number of segments that can be excluded from root sets. */ + +/* + * Data structure for excluded static roots. + */ +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +struct roots { + ptr_t r_start;/* multiple of word size */ + ptr_t r_end; /* multiple of word size and greater than r_start */ +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + struct roots * r_next; +# endif + GC_bool r_tmp; + /* Delete before registering new dynamic libraries */ +}; + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + /* Size of hash table index to roots. */ +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */ +#endif + +#if (!defined(MAX_HEAP_SECTS) || defined(CPPCHECK)) \ + && (defined(CYGWIN32) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(USE_PROC_FOR_LIBRARIES)) +# ifdef LARGE_CONFIG +# if CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 81920 +# else +# define MAX_HEAP_SECTS 7680 +# endif +# elif defined(SMALL_CONFIG) && !defined(USE_PROC_FOR_LIBRARIES) +# if defined(PARALLEL_MARK) && (defined(MSWIN32) || defined(CYGWIN32)) +# define MAX_HEAP_SECTS 384 +# else +# define MAX_HEAP_SECTS 128 /* Roughly 256 MB (128*2048*1024) */ +# endif +# elif CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 1024 /* Roughly 8 GB */ +# else +# define MAX_HEAP_SECTS 512 /* Roughly 4 GB */ +# 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; + +typedef int mark_state_t; /* Current state of marking. */ + /* Used to remember where we are during */ + /* concurrent marking. */ + +struct disappearing_link; +struct finalizable_object; + +struct dl_hashtbl_s { + struct disappearing_link **head; + word entries; + unsigned log_size; +}; + +struct fnlz_roots_s { + struct finalizable_object **fo_head; + /* List of objects that should be finalized now: */ + struct finalizable_object *finalize_now; +}; + +union toggle_ref_u { + /* The lowest bit is used to distinguish between choices. */ + void *strong_ref; + GC_hidden_pointer weak_ref; +}; + +/* Extended descriptors. GC_typed_mark_proc understands these. */ +/* These are used for simple objects that are larger than what */ +/* can be described by a BITMAP_BITS sized bitmap. */ +typedef struct { + word ed_bitmap; /* lsb corresponds to first word. */ + GC_bool ed_continued; /* next entry is continuation. */ +} typed_ext_descr_t; + +struct HeapSect { + ptr_t hs_start; + size_t hs_bytes; +}; + +/* Lists of all heap blocks and free lists */ +/* as well as other random data structures */ +/* that should not be scanned by the */ +/* collector. */ +/* These are grouped together in a struct */ +/* so that they can be easily skipped by the */ +/* GC_mark routine. */ +/* The ordering is weird to make GC_malloc */ +/* faster by keeping the important fields */ +/* sufficiently close together that a */ +/* single load of a base register will do. */ +/* Scalars that could easily appear to */ +/* be pointers are also put here. */ +/* The main fields should precede any */ +/* conditionally included fields, so that */ +/* gc_inline.h will work even if a different */ +/* set of macros is defined when the client is */ +/* compiled. */ + +struct _GC_arrays { + word _heapsize; /* Heap size in bytes (value never goes down). */ + word _requested_heapsize; /* Heap size due to explicit expansion. */ + ptr_t _last_heap_addr; + word _large_free_bytes; + /* Total bytes contained in blocks on large object free */ + /* list. */ + word _large_allocd_bytes; + /* Total number of bytes in allocated large objects blocks. */ + /* For the purposes of this counter and the next one only, a */ + /* large object is one that occupies a block of at least */ + /* 2*HBLKSIZE. */ + word _max_large_allocd_bytes; + /* Maximum number of bytes that were ever allocated in */ + /* 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 bytes allocated before this */ + /* collection cycle. */ +# define GC_our_mem_bytes GC_arrays._our_mem_bytes + word _our_mem_bytes; +# ifndef SEPARATE_GLOBALS +# define GC_bytes_allocd GC_arrays._bytes_allocd + word _bytes_allocd; + /* Number of bytes allocated during this collection cycle. */ +# endif + word _bytes_dropped; + /* Number of black-listed bytes dropped during GC cycle */ + /* as a result of repeated scanning during allocation */ + /* attempts. These are treated largely as allocated, */ + /* even though they are not useful to the client. */ + word _bytes_finalized; + /* Approximate number of bytes in objects (and headers) */ + /* that became ready for finalization in the last */ + /* collection. */ + word _bytes_freed; + /* Number of explicitly deallocated bytes of memory */ + /* since last collection. */ + word _finalizer_bytes_freed; + /* Bytes of memory explicitly deallocated while */ + /* finalizers were running. Used to approximate memory */ + /* explicitly deallocated by finalizers. */ + bottom_index *_all_bottom_indices; + /* Pointer to the first (lowest address) bottom_index; */ + /* assumes the lock is held. */ + bottom_index *_all_bottom_indices_end; + /* Pointer to the last (highest address) bottom_index; */ + /* assumes the lock is held. */ + ptr_t _scratch_free_ptr; + hdr *_hdr_free_list; + ptr_t _scratch_end_ptr; + /* GC_scratch_end_ptr is end point of the current scratch area. */ +# if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX)) +# define USE_SCRATCH_LAST_END_PTR +# define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr + ptr_t _scratch_last_end_ptr; + /* GC_scratch_last_end_ptr is the end point of the last */ + /* obtained scratch area. */ + /* Used by GC_register_dynamic_libraries(). */ +# endif +# if defined(GC_ASSERTIONS) || defined(INCLUDE_LINUX_THREAD_DESCR) \ + || (defined(KEEP_BACK_PTRS) && ALIGNMENT == 1) +# define SET_REAL_HEAP_BOUNDS +# define GC_least_real_heap_addr GC_arrays._least_real_heap_addr +# define GC_greatest_real_heap_addr GC_arrays._greatest_real_heap_addr + word _least_real_heap_addr; + word _greatest_real_heap_addr; + /* Similar to GC_least/greatest_plausible_heap_addr but */ + /* do not include future (potential) heap expansion. */ + /* Both variables are zero initially. */ +# endif + 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 + word _composite_in_use; /* Number of bytes in the accessible */ + /* composite objects. */ + word _atomic_in_use; /* Number of bytes in the accessible */ + /* atomic objects. */ +# ifdef USE_MUNMAP +# define GC_unmapped_bytes GC_arrays._unmapped_bytes + word _unmapped_bytes; +# ifdef COUNT_UNMAPPED_REGIONS +# define GC_num_unmapped_regions GC_arrays._num_unmapped_regions + signed_word _num_unmapped_regions; +# endif +# else +# define GC_unmapped_bytes 0 +# endif + bottom_index * _all_nils; +# define GC_scan_ptr GC_arrays._scan_ptr + struct hblk * _scan_ptr; +# ifdef PARALLEL_MARK +# define GC_main_local_mark_stack GC_arrays._main_local_mark_stack + mse *_main_local_mark_stack; +# define GC_first_nonempty GC_arrays._first_nonempty + volatile AO_t _first_nonempty; + /* Lowest entry on mark stack that may be */ + /* nonempty. Updated only by initiating thread. */ +# endif +# define GC_mark_stack_size GC_arrays._mark_stack_size + size_t _mark_stack_size; +# define GC_mark_state GC_arrays._mark_state + mark_state_t _mark_state; /* Initialized to MS_NONE (0). */ +# define GC_mark_stack_too_small GC_arrays._mark_stack_too_small + GC_bool _mark_stack_too_small; + /* We need a larger mark stack. May be set by */ + /* client supplied mark routines. */ +# define GC_objects_are_marked GC_arrays._objects_are_marked + GC_bool _objects_are_marked; + /* Are there collectible marked objects in the heap? */ +# ifdef ENABLE_TRACE +# define GC_trace_addr GC_arrays._trace_addr + ptr_t _trace_addr; +# endif +# define GC_capacity_heap_sects GC_arrays._capacity_heap_sects + size_t _capacity_heap_sects; +# define GC_n_heap_sects GC_arrays._n_heap_sects + word _n_heap_sects; /* Number of separately added heap sections. */ +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# define GC_n_heap_bases GC_arrays._n_heap_bases + word _n_heap_bases; /* See GC_heap_bases. */ +# endif +# ifdef USE_PROC_FOR_LIBRARIES +# define GC_n_memory GC_arrays._n_memory + word _n_memory; /* Number of GET_MEM allocated memory sections. */ +# endif +# ifdef GC_GCJ_SUPPORT +# define GC_gcjobjfreelist GC_arrays._gcjobjfreelist + ptr_t *_gcjobjfreelist; +# endif +# define GC_fo_entries GC_arrays._fo_entries + word _fo_entries; +# ifndef GC_NO_FINALIZATION +# define GC_dl_hashtbl GC_arrays._dl_hashtbl +# define GC_fnlz_roots GC_arrays._fnlz_roots +# define GC_log_fo_table_size GC_arrays._log_fo_table_size +# ifndef GC_LONG_REFS_NOT_NEEDED +# define GC_ll_hashtbl GC_arrays._ll_hashtbl + struct dl_hashtbl_s _ll_hashtbl; +# endif + struct dl_hashtbl_s _dl_hashtbl; + struct fnlz_roots_s _fnlz_roots; + unsigned _log_fo_table_size; +# ifndef GC_TOGGLE_REFS_NOT_NEEDED +# define GC_toggleref_arr GC_arrays._toggleref_arr +# define GC_toggleref_array_size GC_arrays._toggleref_array_size +# define GC_toggleref_array_capacity GC_arrays._toggleref_array_capacity + union toggle_ref_u *_toggleref_arr; + size_t _toggleref_array_size; + size_t _toggleref_array_capacity; +# endif +# endif +# ifdef TRACE_BUF +# define GC_trace_buf_ptr GC_arrays._trace_buf_ptr + int _trace_buf_ptr; +# endif +# ifdef ENABLE_DISCLAIM +# define GC_finalized_kind GC_arrays._finalized_kind + int _finalized_kind; +# endif +# define n_root_sets GC_arrays._n_root_sets +# define GC_excl_table_entries GC_arrays._excl_table_entries + int _n_root_sets; /* GC_static_roots[0..n_root_sets) contains the */ + /* valid root sets. */ + size_t _excl_table_entries; /* Number of entries in use. */ +# ifdef THREADS +# define GC_roots_were_cleared GC_arrays._roots_were_cleared + GC_bool _roots_were_cleared; +# endif +# define GC_explicit_typing_initialized GC_arrays._explicit_typing_initialized +# define GC_ed_size GC_arrays._ed_size +# define GC_avail_descr GC_arrays._avail_descr +# define GC_ext_descriptors GC_arrays._ext_descriptors +# ifdef AO_HAVE_load_acquire + volatile AO_t _explicit_typing_initialized; +# else + GC_bool _explicit_typing_initialized; +# endif + size_t _ed_size; /* Current size of above arrays. */ + size_t _avail_descr; /* Next available slot. */ + typed_ext_descr_t *_ext_descriptors; /* Points to array of extended */ + /* descriptors. */ + 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 */ + /* by DS_PROC mark descriptors. See gc_mark.h. */ + char _modws_valid_offsets[sizeof(word)]; + /* GC_valid_offsets[i] ==> */ + /* GC_modws_valid_offsets[i%sizeof(word)] */ +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) +# define GC_root_index GC_arrays._root_index + struct roots * _root_index[RT_SIZE]; +# endif +# ifdef SAVE_CALL_CHAIN +# define GC_last_stack GC_arrays._last_stack + struct callinfo _last_stack[NFRAMES]; + /* Stack at last garbage collection. Useful for */ + /* debugging mysterious object disappearances. In the */ + /* multi-threaded case, we currently only save the */ + /* calling stack. */ +# endif +# ifndef SEPARATE_GLOBALS +# define GC_objfreelist GC_arrays._objfreelist + void *_objfreelist[MAXOBJGRANULES+1]; + /* free list for objects */ +# define GC_aobjfreelist GC_arrays._aobjfreelist + void *_aobjfreelist[MAXOBJGRANULES+1]; + /* free list for atomic objects */ +# endif + void *_uobjfreelist[MAXOBJGRANULES+1]; + /* Uncollectible but traced objects. */ + /* Objects on this and _auobjfreelist */ + /* are always marked, except during */ + /* garbage collections. */ +# ifdef GC_ATOMIC_UNCOLLECTABLE +# define GC_auobjfreelist GC_arrays._auobjfreelist + void *_auobjfreelist[MAXOBJGRANULES+1]; + /* Atomic uncollectible but traced objects. */ +# endif + size_t _size_map[MAXOBJBYTES+1]; + /* Number of granules to allocate when asked for a certain */ + /* number of bytes. Should be accessed with the allocation */ + /* lock held. */ +# ifdef MARK_BIT_PER_GRANULE +# define GC_obj_map GC_arrays._obj_map + unsigned short * _obj_map[MAXOBJGRANULES + 1]; + /* If not NULL, then a pointer to a map of valid */ + /* object addresses. */ + /* _obj_map[sz_in_granules][i] is */ + /* i % sz_in_granules. */ + /* This is now used purely to replace a */ + /* division in the marker by a table lookup. */ + /* _obj_map[0] is used for large objects and */ + /* contains all nonzero entries. This gets us */ + /* out of the marker fast path without an extra */ + /* test. */ +# define MAP_LEN BYTES_TO_GRANULES(HBLKSIZE) +# endif +# define VALID_OFFSET_SZ HBLKSIZE + char _valid_offsets[VALID_OFFSET_SZ]; + /* GC_valid_offsets[i] == TRUE ==> i */ + /* is registered as a displacement. */ +# ifndef GC_DISABLE_INCREMENTAL +# define GC_grungy_pages GC_arrays._grungy_pages + page_hash_table _grungy_pages; /* Pages that were dirty at last */ + /* GC_read_dirty. */ +# define GC_dirty_pages GC_arrays._dirty_pages + volatile page_hash_table _dirty_pages; + /* Pages dirtied since last GC_read_dirty. */ +# endif +# if (defined(CHECKSUMS) && (defined(GWW_VDB) || defined(SOFT_VDB))) \ + || defined(PROC_VDB) +# define GC_written_pages GC_arrays._written_pages + page_hash_table _written_pages; /* Pages ever dirtied */ +# endif +# define GC_heap_sects GC_arrays._heap_sects + struct HeapSect *_heap_sects; /* Heap segments potentially */ + /* client objects. */ +# if defined(USE_PROC_FOR_LIBRARIES) +# define GC_our_memory GC_arrays._our_memory + struct HeapSect _our_memory[MAX_HEAP_SECTS]; + /* All GET_MEM allocated */ + /* memory. Includes block */ + /* headers and the like. */ +# endif +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# define GC_heap_bases GC_arrays._heap_bases + ptr_t _heap_bases[MAX_HEAP_SECTS]; + /* Start address of memory regions obtained from kernel. */ +# endif +# ifdef MSWINCE +# define GC_heap_lengths GC_arrays._heap_lengths + word _heap_lengths[MAX_HEAP_SECTS]; + /* Committed lengths of memory regions obtained from kernel. */ +# endif + struct roots _static_roots[MAX_ROOT_SETS]; + struct exclusion _excl_table[MAX_EXCLUSIONS]; + /* Block header index; see gc_headers.h */ + bottom_index * _top_index[TOP_SZ]; +}; + +GC_API_PRIV GC_FAR struct _GC_arrays GC_arrays; + +#define GC_all_nils GC_arrays._all_nils +#define GC_atomic_in_use GC_arrays._atomic_in_use +#define GC_bytes_allocd_before_gc GC_arrays._bytes_allocd_before_gc +#define GC_bytes_dropped GC_arrays._bytes_dropped +#define GC_bytes_finalized GC_arrays._bytes_finalized +#define GC_bytes_freed GC_arrays._bytes_freed +#define GC_composite_in_use GC_arrays._composite_in_use +#define GC_excl_table GC_arrays._excl_table +#define GC_finalizer_bytes_freed GC_arrays._finalizer_bytes_freed +#define GC_heapsize GC_arrays._heapsize +#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_large_allocd_bytes GC_arrays._max_large_allocd_bytes +#define GC_modws_valid_offsets GC_arrays._modws_valid_offsets +#define GC_requested_heapsize GC_arrays._requested_heapsize +#define GC_all_bottom_indices GC_arrays._all_bottom_indices +#define GC_all_bottom_indices_end GC_arrays._all_bottom_indices_end +#define GC_scratch_free_ptr GC_arrays._scratch_free_ptr +#define GC_hdr_free_list GC_arrays._hdr_free_list +#define GC_scratch_end_ptr GC_arrays._scratch_end_ptr +#define GC_size_map GC_arrays._size_map +#define GC_static_roots GC_arrays._static_roots +#define GC_top_index GC_arrays._top_index +#define GC_uobjfreelist GC_arrays._uobjfreelist +#define GC_valid_offsets GC_arrays._valid_offsets + +#define beginGC_arrays ((ptr_t)(&GC_arrays)) +#define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays)) +#define USED_HEAP_SIZE (GC_heapsize - GC_large_free_bytes) + +/* Object kinds: */ +#ifndef MAXOBJKINDS +# define MAXOBJKINDS 16 +#endif +GC_EXTERN struct obj_kind { + void **ok_freelist; /* Array of free list headers for this kind of */ + /* object. Point either to GC_arrays or to */ + /* storage allocated with GC_scratch_alloc. */ + struct hblk **ok_reclaim_list; + /* List headers for lists of blocks waiting to */ + /* be swept. Indexed by object size in */ + /* granules. */ + word ok_descriptor; /* Descriptor template for objects in this */ + /* block. */ + GC_bool ok_relocate_descr; + /* Add object size in bytes to descriptor */ + /* template to obtain descriptor. Otherwise */ + /* template is used as is. */ + GC_bool ok_init; /* Clear objects before putting them on the free list. */ +# ifdef ENABLE_DISCLAIM + GC_bool ok_mark_unconditionally; + /* Mark from all, including unmarked, objects */ + /* in block. Used to protect objects reachable */ + /* from reclaim notifiers. */ + int (GC_CALLBACK *ok_disclaim_proc)(void * /*obj*/); + /* The disclaim procedure is called before obj */ + /* 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, 0 +# else +# define OK_DISCLAIM_INITZ /* empty */ +# endif /* !ENABLE_DISCLAIM */ +} GC_obj_kinds[MAXOBJKINDS]; + +#define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds[0])) +#define endGC_obj_kinds (beginGC_obj_kinds + sizeof(GC_obj_kinds)) + +/* Variables that used to be in GC_arrays, but need to be accessed by */ +/* inline allocation code. If they were in GC_arrays, the inlined */ +/* allocation code would include GC_arrays offsets (as it did), which */ +/* introduce maintenance problems. */ + +#ifdef SEPARATE_GLOBALS + extern word GC_bytes_allocd; + /* 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[0])) +# define endGC_objfreelist (beginGC_objfreelist + sizeof(GC_objfreelist)) + + extern ptr_t GC_aobjfreelist[MAXOBJGRANULES+1]; + /* free list for atomic (PTRFREE) objects */ +# define beginGC_aobjfreelist ((ptr_t)(&GC_aobjfreelist[0])) +# define endGC_aobjfreelist (beginGC_aobjfreelist + sizeof(GC_aobjfreelist)) +#endif /* SEPARATE_GLOBALS */ + +/* Predefined kinds: */ +#define PTRFREE 0 +#define NORMAL 1 +#define UNCOLLECTABLE 2 +#ifdef GC_ATOMIC_UNCOLLECTABLE +# define AUNCOLLECTABLE 3 +# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE) +# define GC_N_KINDS_INITIAL_VALUE 4 +#else +# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE) +# define GC_N_KINDS_INITIAL_VALUE 3 +#endif + +GC_EXTERN unsigned GC_n_kinds; + +GC_EXTERN size_t GC_page_size; + +/* Round up allocation size to a multiple of a page size. */ +/* GC_setpagesize() is assumed to be already invoked. */ +#define ROUNDUP_PAGESIZE(lb) /* lb should have no side-effect */ \ + (SIZET_SAT_ADD(lb, GC_page_size - 1) & ~(GC_page_size - 1)) + +/* Same as above but used to make GET_MEM() argument safe. */ +#ifdef MMAP_SUPPORTED +# define ROUNDUP_PAGESIZE_IF_MMAP(lb) ROUNDUP_PAGESIZE(lb) +#else +# define ROUNDUP_PAGESIZE_IF_MMAP(lb) (lb) +#endif + +#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + GC_EXTERN SYSTEM_INFO GC_sysinfo; + GC_INNER GC_bool GC_is_heap_base(void *p); +#endif + +GC_EXTERN word GC_black_list_spacing; + /* Average number of bytes between blacklisted */ + /* blocks. Approximate. */ + /* Counts only blocks that are */ + /* "stack-blacklisted", i.e. that are */ + /* problematic in the interior of an object. */ + +#ifdef GC_GCJ_SUPPORT + extern struct hblk * GC_hblkfreelist[]; + extern word GC_free_bytes[]; /* Both remain visible to GNU GCJ. */ +#endif + +GC_EXTERN word GC_root_size; /* Total size of registered root sections. */ + +GC_EXTERN GC_bool GC_debugging_started; + /* GC_debug_malloc has been called. */ + +/* This is used by GC_do_blocking[_inner](). */ +struct blocking_data { + GC_fn_type fn; + void * client_data; /* and result */ +}; + +/* This is used by GC_call_with_gc_active(), GC_push_all_stack_sections(). */ +struct GC_traced_stack_sect_s { + ptr_t saved_stack_ptr; +# ifdef IA64 + ptr_t saved_backing_store_ptr; + ptr_t backing_store_end; +# endif + struct GC_traced_stack_sect_s *prev; +}; + +#ifdef THREADS + /* Process all "traced stack sections" - scan entire stack except for */ + /* frames belonging to the user functions invoked by GC_do_blocking. */ + GC_INNER void GC_push_all_stack_sections(ptr_t lo, ptr_t hi, + struct GC_traced_stack_sect_s *traced_stack_sect); + GC_EXTERN word GC_total_stacksize; /* updated on every push_all_stacks */ +# ifdef STACKPTR_CORRECTOR_AVAILABLE + GC_EXTERN GC_sp_corrector_proc GC_sp_corrector; +# endif +#else + GC_EXTERN ptr_t GC_blocked_sp; + GC_EXTERN struct GC_traced_stack_sect_s *GC_traced_stack_sect; + /* Points to the "frame" data held in stack by */ + /* the innermost GC_call_with_gc_active(). */ + /* NULL if no such "frame" active. */ +#endif /* !THREADS */ + +#if defined(E2K) || defined(IA64) + /* Similar to GC_push_all_stack_sections() but for IA-64 registers store. */ + GC_INNER void GC_push_all_register_sections(ptr_t bs_lo, ptr_t bs_hi, + int eager, struct GC_traced_stack_sect_s *traced_stack_sect); +#endif + +/* Marks are in a reserved area in */ +/* each heap block. Each word has one mark bit associated */ +/* with it. Only those corresponding to the beginning of an */ +/* object are used. */ + +/* Mark bit operations */ + +/* + * Retrieve, set, clear the nth mark bit in a given heap block. + * + * (Recall that bit n corresponds to nth object or allocation granule + * relative to the beginning of the block, including unused words) + */ + +#ifdef USE_MARK_BYTES +# define mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n]) +# define set_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 1) +# define clear_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 0) +#else +/* Set mark bit correctly, even if mark bits may be concurrently */ +/* accessed. */ +# if defined(PARALLEL_MARK) || (defined(THREAD_SANITIZER) && defined(THREADS)) + /* Workaround TSan false positive: there is no race between */ + /* mark_bit_from_hdr and set_mark_bit_from_hdr when n is different */ + /* (alternatively, USE_MARK_BYTES could be used). If TSan is off, */ + /* AO_or() is used only if we set USE_MARK_BITS explicitly. */ +# define OR_WORD(addr, bits) AO_or((volatile AO_t *)(addr), (AO_t)(bits)) +# else +# define OR_WORD(addr, bits) (void)(*(addr) |= (bits)) +# endif +# define mark_bit_from_hdr(hhdr,n) \ + (((hhdr)->hb_marks[divWORDSZ(n)] >> modWORDSZ(n)) & (word)1) +# define set_mark_bit_from_hdr(hhdr,n) \ + OR_WORD((hhdr)->hb_marks+divWORDSZ(n), (word)1 << modWORDSZ(n)) +# define clear_mark_bit_from_hdr(hhdr,n) \ + ((hhdr)->hb_marks[divWORDSZ(n)] &= ~((word)1 << modWORDSZ(n))) +#endif /* !USE_MARK_BYTES */ + +#ifdef MARK_BIT_PER_OBJ +# define MARK_BIT_NO(offset, sz) (((word)(offset))/(sz)) + /* Get the mark bit index corresponding to the given byte */ + /* offset and size (in bytes). */ +# define MARK_BIT_OFFSET(sz) 1 + /* Spacing between useful mark bits. */ +# define IF_PER_OBJ(x) x +# define FINAL_MARK_BIT(sz) ((sz) > MAXOBJBYTES? 1 : HBLK_OBJS(sz)) + /* Position of final, always set, mark bit. */ +#else /* MARK_BIT_PER_GRANULE */ +# define MARK_BIT_NO(offset, sz) BYTES_TO_GRANULES((word)(offset)) +# define MARK_BIT_OFFSET(sz) BYTES_TO_GRANULES(sz) +# define IF_PER_OBJ(x) +# define FINAL_MARK_BIT(sz) \ + ((sz) > MAXOBJBYTES ? MARK_BITS_PER_HBLK \ + : BYTES_TO_GRANULES((sz) * HBLK_OBJS(sz))) +#endif + +/* Important internal collector routines */ + +GC_INNER ptr_t GC_approx_sp(void); + +GC_INNER GC_bool GC_should_collect(void); + +void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data), + word client_data); + /* Invoke fn(hbp, client_data) for each */ + /* allocated heap block. */ +GC_INNER struct hblk * GC_next_block(struct hblk *h, GC_bool allow_free); + /* Get the next block whose address is at least */ + /* h. Returned block is managed by GC. The */ + /* block must be in use unless allow_free is */ + /* true. Return 0 if there is no such block. */ +GC_INNER struct hblk * GC_prev_block(struct hblk * h); + /* Get the last (highest address) block whose */ + /* address is at most h. Returned block is */ + /* managed by GC, but may or may not be in use. */ + /* Return 0 if there is no such block. */ +GC_INNER void GC_mark_init(void); +GC_INNER void GC_clear_marks(void); + /* Clear mark bits for all heap objects. */ +GC_INNER void GC_invalidate_mark_state(void); + /* Tell the marker that marked */ + /* objects may point to unmarked */ + /* ones, and roots may point to */ + /* unmarked objects. Reset mark stack. */ +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 */ + /* quickly if no collection is in progress. */ + /* Return TRUE if mark phase finished. */ +GC_INNER void GC_initiate_gc(void); + /* initiate collection. */ + /* If the mark state is invalid, this */ + /* becomes full collection. Otherwise */ + /* it's partial. */ + +GC_INNER GC_bool GC_collection_in_progress(void); + /* Collection is in progress, or was abandoned. */ + +#define GC_PUSH_ALL_SYM(sym) \ + GC_push_all((/* no volatile */ void *)&(sym), \ + (/* no volatile */ void *)(&(sym) + 1)) + +GC_INNER void GC_push_all_stack(ptr_t b, ptr_t t); + /* As GC_push_all but consider */ + /* interior pointers as valid. */ + +#ifdef NO_VDB_FOR_STATIC_ROOTS +# define GC_push_conditional_static(b, t, all) \ + ((void)(all), GC_push_all(b, t)) +#else + /* Same as GC_push_conditional (does either of GC_push_all or */ + /* GC_push_selected depending on the third argument) but the caller */ + /* guarantees the region belongs to the registered static roots. */ + GC_INNER void GC_push_conditional_static(void *b, void *t, GC_bool all); +#endif + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) + /* GC_mark_local does not handle memory protection faults yet. So, */ + /* the static data regions are scanned immediately by GC_push_roots. */ + GC_INNER void GC_push_conditional_eager(void *bottom, void *top, + GC_bool all); +#endif + + /* In the threads case, we push part of the current thread stack */ + /* with GC_push_all_eager when we push the registers. This gets the */ + /* callee-save registers that may disappear. The remainder of the */ + /* stacks are scheduled for scanning in *GC_push_other_roots, which */ + /* is thread-package-specific. */ + +GC_INNER void GC_push_roots(GC_bool all, ptr_t cold_gc_frame); + /* Push all or dirty roots. */ + +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. Remains externally */ + /* visible as used by some well-known 3rd-party */ + /* software (e.g., ECL) currently. */ + +#ifdef THREADS + void GC_push_thread_structures(void); +#endif +GC_EXTERN void (*GC_push_typed_structures)(void); + /* A pointer such that we can avoid linking in */ + /* the typed allocation support if unused. */ + +GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), + volatile ptr_t arg); + +#if defined(E2K) || defined(IA64) || defined(SPARC) + /* Cause all stacked registers to be saved in memory. Return a */ + /* pointer to the top of the corresponding memory stack. */ + ptr_t GC_save_regs_in_stack(void); +#endif + +#ifdef E2K + /* Copy the full procedure stack to the provided buffer (with the */ + /* given capacity). Returns either the required buffer size if it */ + /* is bigger than capacity, otherwise the amount of copied bytes. */ + /* May be called from a signal handler. */ + GC_INNER size_t GC_get_procedure_stack(ptr_t, size_t); + +# if defined(CPPCHECK) +# define PS_ALLOCA_BUF(sz) NULL +# define ALLOCA_SAFE_LIMIT 0 +# else +# define PS_ALLOCA_BUF(sz) alloca(sz) /* cannot return NULL */ +# ifndef ALLOCA_SAFE_LIMIT +# define ALLOCA_SAFE_LIMIT (HBLKSIZE*256) +# endif +# endif /* !CPPCHECK */ + + /* Copy procedure (register) stack to a stack-allocated or */ + /* memory-mapped buffer. Usable from a signal handler. */ + /* FREE_PROCEDURE_STACK_LOCAL() must be called with the same */ + /* *pbuf and *psz values before the caller function returns */ + /* (thus, the buffer is valid only within the function). */ +# define GET_PROCEDURE_STACK_LOCAL(pbuf, psz) \ + do { \ + size_t capacity = 0; \ + GC_ASSERT(GC_page_size != 0); \ + for (*(pbuf) = NULL; ; capacity = *(psz)) { \ + *(psz) = GC_get_procedure_stack(*(pbuf), capacity); \ + if (*(psz) <= capacity) break; \ + if (*(psz) > ALLOCA_SAFE_LIMIT \ + || EXPECT(capacity != 0, FALSE)) { \ + /* Deallocate old buffer if any. */ \ + if (EXPECT(capacity > ALLOCA_SAFE_LIMIT, FALSE)) \ + GC_unmap_procedure_stack_buf(*(pbuf),capacity); \ + *(psz) = ROUNDUP_PAGESIZE(*(psz)); \ + *(pbuf) = GC_mmap_procedure_stack_buf(*(psz)); \ + } else { \ + /* Allocate buffer on the stack if not large. */ \ + *(pbuf) = PS_ALLOCA_BUF(*(psz)); \ + } \ + } \ + if (capacity > ALLOCA_SAFE_LIMIT \ + && EXPECT(((capacity - *(psz)) \ + & ~(GC_page_size-1)) != 0, FALSE)) { \ + /* Ensure sz value passed to munmap() later */ \ + /* matches that passed to mmap() above. */ \ + *(psz) = capacity - (GC_page_size - 1); \ + } \ + } while (0) + + /* Indicate that the buffer with copied procedure stack is not needed. */ +# define FREE_PROCEDURE_STACK_LOCAL(buf, sz) \ + (void)((sz) > ALLOCA_SAFE_LIMIT \ + ? (GC_unmap_procedure_stack_buf(buf, sz), 0) : 0) + + GC_INNER ptr_t GC_mmap_procedure_stack_buf(size_t); + GC_INNER void GC_unmap_procedure_stack_buf(ptr_t, size_t); + +# ifdef THREADS + /* Allocate a buffer in the GC heap (as an atomic object) and copy */ + /* procedure stack there. Assumes the GC allocation lock is held. */ + /* May trigger a collection (thus, cannot be used in GC_push_roots */ + /* or in a signal handler). The buffer should be freed with */ + /* GC_INTERNAL_FREE later when not needed (or, alternatively, it */ + /* could be just garbage-collected). */ + /* Similar to GET_PROCEDURE_STACK_LOCAL in other aspects. */ + GC_INNER size_t GC_alloc_and_get_procedure_stack(ptr_t *pbuf); +# endif + + /* Load value and get tag of the target memory. */ +# if defined(__ptr64__) +# define LOAD_TAGGED_VALUE(v, tag, p) \ + do { \ + word val; \ + __asm__ __volatile__ ( \ + "ldd, sm %[adr], 0x0, %[val]\n\t" \ + "gettagd %[val], %[tag]\n" \ + : [val] "=r" (val), \ + [tag] "=r" (tag) \ + : [adr] "r" (p)); \ + v = val; \ + } while (0) +# elif !defined(CPPCHECK) +# error Unsupported -march for e2k target +# endif + +# define LOAD_WORD_OR_CONTINUE(v, p) \ + { \ + int tag LOCAL_VAR_INIT_OK; \ + LOAD_TAGGED_VALUE(v, tag, p); \ + if (tag != 0) continue; \ + } +#else +# define LOAD_WORD_OR_CONTINUE(v, p) (void)(v = *(word *)(p)) +#endif /* !E2K */ + +#if defined(AMIGA) || defined(MACOS) || defined(GC_DARWIN_THREADS) + void GC_push_one(word p); + /* If p points to an object, mark it */ + /* and push contents on the mark stack */ + /* Pointer recognition test always */ + /* accepts interior pointers, i.e. this */ + /* is appropriate for pointers found on */ + /* stack. */ +#endif + +#ifdef GC_WIN32_THREADS + /* Same as GC_push_one but for a sequence of registers. */ + GC_INNER void GC_push_many_regs(const word *regs, unsigned count); +#endif + +#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) + GC_INNER void GC_mark_and_push_stack(ptr_t p, ptr_t source); + /* Ditto, omits plausibility test */ +#else + GC_INNER void GC_mark_and_push_stack(ptr_t p); +#endif + +GC_INNER void GC_clear_hdr_marks(hdr * hhdr); + /* Clear the mark bits in a header */ +GC_INNER void GC_set_hdr_marks(hdr * hhdr); + /* Set the mark bits in a header */ +GC_INNER void GC_set_fl_marks(ptr_t p); + /* Set all mark bits associated with */ + /* a free list. */ +#if defined(GC_ASSERTIONS) && 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. */ +#endif +void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp); +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER void GC_remove_roots_subregion(ptr_t b, ptr_t e); +#endif +GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish); +#if defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(CYGWIN32) || defined(PCR) + GC_INNER void GC_register_dynamic_libraries(void); + /* Add dynamic library data sections to the root set. */ +#endif +GC_INNER void GC_cond_register_dynamic_libraries(void); + /* Remove and reregister dynamic libraries if we're */ + /* configured to do that at each GC. */ + +/* Machine dependent startup routines */ +ptr_t GC_get_main_stack_base(void); /* Cold end of stack. */ +#ifdef IA64 + GC_INNER ptr_t GC_get_register_stack_base(void); + /* Cold end of register stack. */ +#endif +void GC_register_data_segments(void); + +#ifdef THREADS + GC_INNER void GC_thr_init(void); + GC_INNER void GC_init_parallel(void); +# ifndef DONT_USE_ATEXIT + GC_INNER GC_bool GC_is_main_thread(void); +# endif +#else + GC_INNER GC_bool GC_is_static_root(void *p); + /* Is the address p in one of the registered static */ + /* root sections? */ +# ifdef TRACE_BUF + void GC_add_trace_entry(char *kind, word arg1, word arg2); +# endif +#endif /* !THREADS */ + +/* Black listing: */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_normal(word p, ptr_t source); + /* Register bits as a possible future false */ + /* reference from the heap or static data */ +# 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)) + 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)) +#else + GC_INNER void GC_add_to_black_list_normal(word p); +# 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)) + 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 */ + +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. 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); + /* Approximately undo the effect of the above. */ + /* This actually loses some information, but */ + /* only in a reasonably safe way. */ + +GC_INNER ptr_t GC_scratch_alloc(size_t bytes); + /* GC internal memory allocation for */ + /* small objects. Deallocation is not */ + /* possible. May return NULL. */ + +#ifdef GWW_VDB + /* GC_scratch_recycle_no_gww() not used. */ +#else +# define GC_scratch_recycle_no_gww GC_scratch_recycle_inner +#endif +GC_INNER void GC_scratch_recycle_inner(void *ptr, size_t bytes); + /* Reuse the memory region by the heap. */ + +/* Heap block layout maps: */ +#ifdef MARK_BIT_PER_GRANULE + GC_INNER GC_bool GC_add_map_entry(size_t sz); + /* Add a heap block map for objects of */ + /* size sz to obj_map. */ + /* Return FALSE on failure. */ +#endif + +GC_INNER void GC_register_displacement_inner(size_t offset); + /* Version of GC_register_displacement */ + /* that assumes lock is already held. */ + +/* hblk allocation: */ +GC_INNER void GC_new_hblk(size_t size_in_granules, int kind); + /* Allocate a new heap block, and build */ + /* a free list in it. */ + +GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t words, GC_bool clear, + ptr_t list); + /* Build a free list for objects of */ + /* size sz in block h. Append list to */ + /* end of the free lists. Possibly */ + /* clear objects on the list. Normally */ + /* called by GC_new_hblk, but also */ + /* called explicitly without GC lock. */ + +GC_INNER struct hblk * GC_allochblk(size_t size_in_bytes, int kind, + unsigned flags); + /* Allocate a heap block, inform */ + /* the marker that block is valid */ + /* for objects of indicated size. */ + +GC_INNER ptr_t GC_alloc_large(size_t lb, int k, unsigned flags); + /* Allocate a large block of size lb bytes. */ + /* The block is not cleared. flags argument */ + /* should be 0 or IGNORE_OFF_PAGE. */ + /* Calls GC_allochblk to do the actual */ + /* allocation, but also triggers GC and/or */ + /* heap expansion as appropriate. */ + /* Does not update GC_bytes_allocd, but does */ + /* other accounting. */ + +GC_INNER void GC_freehblk(struct hblk * p); + /* Deallocate a heap block and mark it */ + /* as invalid. */ + +/* Miscellaneous GC routines. */ +GC_INNER GC_bool GC_expand_hp_inner(word n); +GC_INNER void GC_start_reclaim(GC_bool abort_if_found); + /* Restore unmarked objects to free */ + /* lists, or (if abort_if_found is */ + /* TRUE) report them. */ + /* Sweeping of small object pages is */ + /* largely deferred. */ +GC_INNER void GC_continue_reclaim(word sz, int kind); + /* Sweep pages of the given size and */ + /* kind, as long as possible, and */ + /* as long as the corr. free list is */ + /* empty. Sz is in granules. */ + +GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old); + /* Reclaim all blocks. Abort (in a */ + /* consistent state) if f returns TRUE. */ +GC_INNER ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz, + GC_bool init, ptr_t list, + signed_word *count); + /* Rebuild free list in hbp with */ + /* header hhdr, with objects of size sz */ + /* bytes. Add list to the end of the */ + /* free list. Add the number of */ + /* reclaimed bytes to *count. */ +GC_INNER GC_bool GC_block_empty(hdr * hhdr); + /* Block completely unmarked? */ +GC_INNER int GC_CALLBACK GC_never_stop_func(void); + /* Always returns 0 (FALSE). */ +GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func f); + + /* Collect; caller must have acquired */ + /* lock. Collection is aborted if f */ + /* returns TRUE. Returns TRUE if it */ + /* completes successfully. */ +#define GC_gcollect_inner() \ + (void)GC_try_to_collect_inner(GC_never_stop_func) + +#ifdef THREADS + GC_EXTERN GC_bool GC_in_thread_creation; + /* We may currently be in thread creation or destruction. */ + /* Only set to TRUE while allocation lock is held. */ + /* When set, it is OK to run GC from unknown thread. */ +#endif + +GC_EXTERN GC_bool GC_is_initialized; /* GC_init() has been run. */ + +GC_INNER void GC_collect_a_little_inner(int n); + /* Do n units worth of garbage */ + /* collection work, if appropriate. */ + /* A unit is an amount appropriate for */ + /* HBLKSIZE bytes of allocation. */ + +GC_INNER void * GC_generic_malloc_inner(size_t lb, int k); + /* Allocate an object of the given */ + /* kind but assuming lock already held. */ +#if defined(DBG_HDRS_ALL) || defined(GC_GCJ_SUPPORT) \ + || !defined(GC_NO_FINALIZATION) + GC_INNER void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k); + /* Allocate an object, where the client */ + /* guarantees that there will always be */ + /* a pointer to the beginning (i.e. */ + /* within the first hblk) of the object */ + /* while it is live. */ +#endif + +GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, + GC_bool ignore_off_page, GC_bool retry); + +GC_INNER ptr_t GC_allocobj(size_t sz, int kind); + /* Make the indicated */ + /* free list nonempty, and return its */ + /* head. Sz is in granules. */ + +#ifdef GC_ADD_CALLER + /* 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_HAVE_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_EXTRAS "unknown", 0 +#endif + +#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. */ +#if defined(THREAD_LOCAL_ALLOC) && defined(GC_GCJ_SUPPORT) + GC_INNER void * GC_core_gcj_malloc(size_t, void *); +#endif + +GC_INNER void GC_init_headers(void); +GC_INNER struct hblkhdr * GC_install_header(struct hblk *h); + /* Install a header for block h. */ + /* Return 0 on failure, or the header */ + /* otherwise. */ +GC_INNER GC_bool GC_install_counts(struct hblk * h, size_t sz); + /* Set up forwarding counts for block */ + /* h of size sz. */ + /* Return FALSE on failure. */ +GC_INNER void GC_remove_header(struct hblk * h); + /* Remove the header for block h. */ +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); + +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER void GC_add_to_our_memory(ptr_t p, size_t bytes); + /* Add a chunk to GC_our_memory. */ +#else +# define GC_add_to_our_memory(p, bytes) \ + (GC_our_mem_bytes += (bytes), (void)(p)) +#endif + +GC_INNER void GC_print_all_errors(void); + /* Print smashed and leaked objects, if any. */ + /* Clear the lists of such objects. */ + +GC_EXTERN void (*GC_check_heap)(void); + /* Check that all objects in the heap with */ + /* debugging info are intact. */ + /* Add any that are not to GC_smashed list. */ +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 (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); + /* Print an address map of the process. */ +#endif + +#ifndef SHORT_DBG_HDRS + GC_EXTERN GC_bool GC_findleak_delay_free; + /* Do not immediately deallocate object on */ + /* free() in the leak-finding mode, just mark */ + /* it as freed (and deallocate it after GC). */ + GC_INNER GC_bool GC_check_leaked(ptr_t base); /* from dbg_mlc.c */ +#endif + +#ifdef AO_HAVE_store + GC_EXTERN volatile AO_t GC_have_errors; +# define GC_SET_HAVE_ERRORS() AO_store(&GC_have_errors, (AO_t)TRUE) +# define get_have_errors() ((GC_bool)AO_load(&GC_have_errors)) + /* The barriers are not needed. */ +#else + GC_EXTERN GC_bool GC_have_errors; +# define GC_SET_HAVE_ERRORS() (void)(GC_have_errors = TRUE) +# define get_have_errors() GC_have_errors +#endif /* We saw a smashed or leaked object. */ + /* Call error printing routine */ + /* occasionally. It is OK to read it */ + /* without acquiring the lock. */ + /* If set to true, it is never cleared. */ + +#define VERBOSE 2 +#if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) + /* 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 /* 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 + +#ifdef KEEP_BACK_PTRS + GC_EXTERN long GC_backtraces; +#endif + +#ifdef LINT2 +# define GC_RAND_MAX (~0U >> 1) + GC_API_PRIV long GC_random(void); +#endif + +GC_EXTERN GC_bool GC_print_back_height; + +#ifdef MAKE_BACK_GRAPH + void GC_print_back_graph_stats(void); +#endif + +#ifdef THREADS + GC_INNER void GC_free_inner(void * p); +#endif + +/* Macros used for collector internal allocation. */ +/* These assume the collector lock is held. */ +#ifdef DBG_HDRS_ALL + GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k); + GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, + int k); +# define GC_INTERNAL_MALLOC GC_debug_generic_malloc_inner +# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \ + GC_debug_generic_malloc_inner_ignore_off_page +# ifdef THREADS + GC_INNER void GC_debug_free_inner(void * p); +# define GC_INTERNAL_FREE GC_debug_free_inner +# else +# define GC_INTERNAL_FREE GC_debug_free +# endif +#else +# define GC_INTERNAL_MALLOC GC_generic_malloc_inner +# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \ + GC_generic_malloc_inner_ignore_off_page +# ifdef THREADS +# define GC_INTERNAL_FREE GC_free_inner +# else +# define GC_INTERNAL_FREE GC_free +# endif +#endif /* !DBG_HDRS_ALL */ + +#ifdef USE_MUNMAP + /* Memory unmapping: */ + GC_INNER void GC_unmap_old(void); + GC_INNER void GC_merge_unmapped(void); + GC_INNER void GC_unmap(ptr_t start, size_t bytes); + GC_INNER void GC_remap(ptr_t start, size_t bytes); + GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, + size_t bytes2); + +# ifndef NOT_GCBUILD + /* Compute end address for an unmap operation on the indicated block. */ + GC_INLINE ptr_t GC_unmap_end(ptr_t start, size_t bytes) + { + return (ptr_t)((word)(start + bytes) & ~(GC_page_size - 1)); + } +# endif +#endif /* USE_MUNMAP */ + +#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 + +#ifdef GC_DISABLE_INCREMENTAL +# define GC_incremental FALSE +# define GC_auto_incremental FALSE +# define GC_manual_vdb FALSE +# define GC_dirty(p) (void)(p) +# define REACHABLE_AFTER_DIRTY(p) (void)(p) + +#else /* !GC_DISABLE_INCREMENTAL */ + GC_EXTERN GC_bool GC_incremental; + /* Using incremental/generational collection. */ + /* Assumes dirty bits are being maintained. */ + + /* Virtual dirty bit implementation: */ + /* Each implementation exports the following: */ + GC_INNER void GC_read_dirty(GC_bool output_unneeded); + /* Retrieve dirty bits. Set output_unneeded to */ + /* indicate that reading of the retrieved dirty */ + /* bits is not planned till the next retrieval. */ + GC_INNER GC_bool GC_page_was_dirty(struct hblk *h); + /* Read retrieved dirty bits. */ + + GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, + GC_bool pointerfree); + /* h is about to be written or allocated. Ensure that */ + /* it is not write protected by the virtual dirty bit */ + /* implementation. I.e., this is a call that: */ + /* - hints that [h, h+nblocks) is about to be written; */ + /* - guarantees that protection is removed; */ + /* - may speed up some dirty bit implementations; */ + /* - may be essential if we need to ensure that */ + /* pointer-free system call buffers in the heap are */ + /* not protected. */ + +# if !defined(NO_VDB_FOR_STATIC_ROOTS) && !defined(PROC_VDB) + GC_INNER GC_bool GC_is_vdb_for_static_roots(void); + /* Is VDB working for static roots? */ +# endif + +# ifdef CAN_HANDLE_FORK +# if defined(PROC_VDB) || defined(SOFT_VDB) + GC_INNER void GC_dirty_update_child(void); + /* Update pid-specific resources (like /proc file */ + /* descriptors) needed by the dirty bits implementation */ + /* after fork in the child process. */ +# else +# define GC_dirty_update_child() (void)0 +# endif +# endif /* CAN_HANDLE_FORK */ + + GC_INNER GC_bool GC_dirty_init(void); + /* Returns true if dirty bits are maintained (otherwise */ + /* it is OK to be called again if the client invokes */ + /* GC_enable_incremental once more). */ + + GC_EXTERN GC_bool GC_manual_vdb; + /* The incremental collection is in the manual VDB */ + /* mode. Assumes GC_incremental is true. Should not */ + /* be modified once GC_incremental is set to true. */ + +# define GC_auto_incremental (GC_incremental && !GC_manual_vdb) + + GC_INNER void GC_dirty_inner(const void *p); /* does not require locking */ +# define GC_dirty(p) (GC_manual_vdb ? GC_dirty_inner(p) : (void)0) +# define REACHABLE_AFTER_DIRTY(p) GC_reachable_here(p) +#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))) + +/* Debugging print routines: */ +void GC_print_block_list(void); +void GC_print_hblkfreelist(void); +void GC_print_heap_sects(void); +void GC_print_static_roots(void); + +#ifdef KEEP_BACK_PTRS + GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest); + GC_INNER void GC_marked_for_finalization(ptr_t dest); +# define GC_STORE_BACK_PTR(source, dest) GC_store_back_pointer(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) GC_marked_for_finalization(dest) +#else +# define GC_STORE_BACK_PTR(source, dest) (void)(source) +# define GC_MARKED_FOR_FINALIZATION(dest) +#endif + +/* Make arguments appear live to compiler */ +void GC_noop6(word, word, word, word, word, word); + +GC_API void GC_CALL GC_noop1(word); + +#ifndef GC_ATTR_FORMAT_PRINTF +# if GC_GNUC_PREREQ(3, 0) +# define GC_ATTR_FORMAT_PRINTF(spec_argnum, first_checked) \ + __attribute__((__format__(__printf__, spec_argnum, first_checked))) +# else +# define GC_ATTR_FORMAT_PRINTF(spec_argnum, first_checked) +# endif +#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, */ + /* 1 KB total output length. */ + /* (We use sprintf. Hopefully that doesn't */ + /* 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. */ +GC_API_PRIV void GC_log_printf(const char * format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + +#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 */ + +#if defined(SMALL_CONFIG) || defined(GC_ANDROID_LOG) +# define GC_ERRINFO_PRINTF GC_INFOLOG_PRINTF +#else +# define GC_ERRINFO_PRINTF GC_log_printf +#endif + +/* 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. */ + +GC_EXTERN long GC_large_alloc_warn_interval; /* defined in misc.c */ + +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 */ +#endif + +#ifdef MSWIN32 + GC_EXTERN GC_bool GC_no_win32_dlls; /* defined in os_dep.c */ + GC_EXTERN GC_bool GC_wnt; /* Is Windows NT derivative; */ + /* defined and set in os_dep.c. */ +#endif + +#ifdef THREADS +# if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE) + GC_EXTERN CRITICAL_SECTION GC_write_cs; /* defined in misc.c */ +# ifdef GC_ASSERTIONS + GC_EXTERN GC_bool GC_write_disabled; + /* defined in win32_threads.c; */ + /* protected by GC_write_cs. */ + +# endif +# endif /* MSWIN32 || MSWINCE */ +# if defined(GC_DISABLE_INCREMENTAL) || defined(HAVE_LOCKFREE_AO_OR) +# define GC_acquire_dirty_lock() (void)0 +# define GC_release_dirty_lock() (void)0 +# else + /* Acquire the spin lock we use to update dirty bits. */ + /* Threads should not get stopped holding it. But we may */ + /* acquire and release it during GC_remove_protection call. */ +# define GC_acquire_dirty_lock() \ + do { /* empty */ \ + } while (AO_test_and_set_acquire(&GC_fault_handler_lock) == AO_TS_SET) +# define GC_release_dirty_lock() AO_CLEAR(&GC_fault_handler_lock) + GC_EXTERN volatile AO_TS_t GC_fault_handler_lock; + /* defined in os_dep.c */ +# endif +# ifdef MSWINCE + GC_EXTERN GC_bool GC_dont_query_stack_min; + /* Defined and set in os_dep.c. */ +# endif +#elif defined(IA64) + GC_EXTERN ptr_t GC_save_regs_ret_val; /* defined in mach_dep.c. */ + /* Previously set to backing store pointer. */ +#endif /* !THREADS */ + +#ifdef THREAD_LOCAL_ALLOC + GC_EXTERN GC_bool GC_world_stopped; /* defined in alloc.c */ + GC_INNER void GC_mark_thread_local_free_lists(void); +#endif + +#if defined(GLIBC_2_19_TSX_BUG) && defined(PARALLEL_MARK) + /* Parse string like [.[]] and return major value. */ + GC_INNER int GC_parse_version(int *pminor, const char *pverstr); +#endif + +#if defined(MPROTECT_VDB) && defined(GWW_VDB) + GC_INNER GC_bool GC_gww_dirty_init(void); + /* Returns TRUE if GetWriteWatch is available. */ + /* May be called repeatedly. */ +#endif + +#if defined(CHECKSUMS) || defined(PROC_VDB) + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h); + /* Could the page contain valid heap pointers? */ +#endif + +#ifdef CHECKSUMS +# if defined(MPROTECT_VDB) && !defined(DARWIN) + void GC_record_fault(struct hblk * h); +# endif + void GC_check_dirty(void); +#endif + +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p); + +GC_INNER void GC_setpagesize(void); + +GC_INNER void GC_initialize_offsets(void); /* defined in obj_map.c */ + +GC_INNER void GC_bl_init(void); +GC_INNER void GC_bl_init_no_interiors(void); /* defined in blacklst.c */ + +GC_INNER void GC_start_debugging_inner(void); /* defined in dbg_mlc.c. */ + /* Should not be called if GC_debugging_started. */ + +/* Store debugging info into p. Return displaced pointer. */ +/* Assumes we hold the allocation lock. */ +GC_INNER void *GC_store_debug_info_inner(void *p, word sz, const char *str, + int linenum); + +#ifdef REDIRECT_MALLOC +# ifdef GC_LINUX_THREADS + GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp); + /* from os_dep.c */ +# endif +#elif defined(USE_WINALLOC) + GC_INNER void GC_add_current_malloc_heap(void); +#endif /* !REDIRECT_MALLOC */ + +#ifdef MAKE_BACK_GRAPH + GC_INNER void GC_build_back_graph(void); + GC_INNER void GC_traverse_back_graph(void); +#endif + +#ifdef MSWIN32 + GC_INNER void GC_init_win32(void); +#endif + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + GC_INNER void * GC_roots_present(ptr_t); + /* The type is a lie, since the real type doesn't make sense here, */ + /* and we only test for NULL. */ +#endif + +#ifdef GC_WIN32_THREADS + GC_INNER void GC_get_next_stack(char *start, char * limit, char **lo, + char **hi); +# if defined(MPROTECT_VDB) && !defined(CYGWIN32) + GC_INNER void GC_set_write_fault_handler(void); +# endif +# if defined(WRAP_MARK_SOME) && !defined(GC_PTHREADS) + GC_INNER GC_bool GC_started_thread_while_stopped(void); + /* Did we invalidate mark phase with an unexpected thread start? */ +# endif +#endif /* GC_WIN32_THREADS */ + +#ifdef THREADS +# ifndef GC_NO_FINALIZATION + GC_INNER void GC_reset_finalizer_nested(void); + GC_INNER unsigned char *GC_check_finalizer_nested(void); +# endif + GC_INNER void GC_do_blocking_inner(ptr_t data, void * context); + GC_INNER void GC_push_all_stacks(void); +# ifdef USE_PROC_FOR_LIBRARIES + GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi); +# endif +# if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && defined(IA64) + GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound); +# endif +#endif /* THREADS */ + +#ifdef DYNAMIC_LOADING + GC_INNER GC_bool GC_register_main_static_data(void); +# ifdef DARWIN + GC_INNER void GC_init_dyld(void); +# endif +#endif /* DYNAMIC_LOADING */ + +#ifdef SEARCH_FOR_DATA_START + GC_INNER void GC_init_linux_data_start(void); + void * GC_find_limit(void *, int); +#endif + +#if defined(NETBSD) && defined(__ELF__) + GC_INNER void GC_init_netbsd_elf(void); + void * GC_find_limit(void *, int); +#endif + +#ifdef UNIX_LIKE + GC_INNER void GC_set_and_save_fault_handler(void (*handler)(int)); +#endif + +#ifdef NEED_PROC_MAPS +# if defined(DYNAMIC_LOADING) && defined(USE_PROC_FOR_LIBRARIES) + GC_INNER const char *GC_parse_map_entry(const char *maps_ptr, + ptr_t *start, ptr_t *end, + const char **prot, + unsigned *maj_dev, + const char **mapping_name); +# endif +# if defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) + GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr, + ptr_t *startp, ptr_t *endp); +# endif + GC_INNER const char *GC_get_maps(void); +#endif /* NEED_PROC_MAPS */ + +#ifdef GC_ASSERTIONS +# 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) +#endif + +/* Check a compile time assertion at compile time. */ +#if _MSC_VER >= 1700 +# define GC_STATIC_ASSERT(expr) \ + static_assert(expr, "static assertion failed: " #expr) +#elif defined(static_assert) && __STDC_VERSION__ >= 201112L +# define GC_STATIC_ASSERT(expr) static_assert(expr, #expr) +#elif defined(mips) && !defined(__GNUC__) +/* DOB: MIPSPro C gets an internal error taking the sizeof an array type. + This code works correctly (ugliness is to avoid "unused var" warnings) */ +# define GC_STATIC_ASSERT(expr) \ + do { if (0) { char j[(expr)? 1 : -1]; j[0]='\0'; j[0]=j[0]; } } while(0) +#else + /* The error message for failure is a bit baroque, but ... */ +# define GC_STATIC_ASSERT(expr) (void)sizeof(char[(expr)? 1 : -1]) +#endif + +/* Runtime check for an argument declared as non-null is actually not null. */ +#if GC_GNUC_PREREQ(4, 0) + /* Workaround tautological-pointer-compare Clang warning. */ +# define NONNULL_ARG_NOT_NULL(arg) (*(volatile void **)&(arg) != NULL) +#else +# define NONNULL_ARG_NOT_NULL(arg) (NULL != (arg)) +#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_named(NULL); \ + } 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. */ + +# define GC_markers_m1 GC_parallel + /* Number of mark threads we would like to have */ + /* excluding the initiating thread. */ + + GC_EXTERN GC_bool GC_parallel_mark_disabled; + /* A flag to temporarily avoid parallel marking.*/ + + /* The mark lock and condition variable. If the GC lock is also */ + /* acquired, the GC lock must be acquired first. The mark lock is */ + /* used to both protect some variables used by the parallel */ + /* marker, and to protect GC_fl_builder_count, below. */ + /* GC_notify_all_marker() is called when */ + /* the state of the parallel marker changes */ + /* in some significant way (see gc_mark.h for details). The */ + /* latter set of events includes incrementing GC_mark_no. */ + /* GC_notify_all_builder() is called when GC_fl_builder_count */ + /* reaches 0. */ + + GC_INNER void GC_wait_for_markers_init(void); + GC_INNER void GC_acquire_mark_lock(void); + GC_INNER void GC_release_mark_lock(void); + GC_INNER void GC_notify_all_builder(void); + GC_INNER void GC_wait_for_reclaim(void); + + GC_EXTERN signed_word GC_fl_builder_count; /* Protected by mark lock. */ + + GC_INNER void GC_notify_all_marker(void); + GC_INNER void GC_wait_marker(void); + GC_EXTERN word GC_mark_no; /* Protected by mark lock. */ + + GC_INNER void GC_help_marker(word my_mark_no); + /* Try to help out parallel marker for mark cycle */ + /* my_mark_no. Returns if the mark cycle finishes or */ + /* was already done, or there was nothing to do for */ + /* some other reason. */ + + GC_INNER void GC_start_mark_threads_inner(void); +#endif /* PARALLEL_MARK */ + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) && !defined(NACL) \ + && !defined(GC_DARWIN_THREADS) && !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. */ + /* unfortunately, there is no standard mechanism. (There is one */ + /* in Linux glibc, but it's not exported.) Thus we continue to use */ + /* the same hard-coded signals we've always used. */ +# ifdef THREAD_SANITIZER + /* Unfortunately, use of an asynchronous signal to suspend threads */ + /* leads to the situation when the signal is not delivered (is */ + /* stored to pending_signals in TSan runtime actually) while the */ + /* destination thread is blocked in pthread_mutex_lock. Thus, we */ + /* use some synchronous one instead (which is again unlikely to be */ + /* used by clients directly). */ +# define SIG_SUSPEND SIGSYS +# elif (defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)) \ + && !defined(GC_USESIGRT_SIGNALS) +# if defined(SPARC) && !defined(SIGPWR) + /* SPARC/Linux doesn't properly define SIGPWR in . */ + /* It is aliased to SIGLOST in asm/signal.h, though. */ +# define SIG_SUSPEND SIGLOST +# else + /* Linuxthreads itself uses SIGUSR1 and SIGUSR2. */ +# define SIG_SUSPEND SIGPWR +# endif +# elif defined(GC_FREEBSD_THREADS) && defined(__GLIBC__) \ + && !defined(GC_USESIGRT_SIGNALS) +# define SIG_SUSPEND (32+6) +# elif (defined(GC_FREEBSD_THREADS) || defined(HURD) || defined(RTEMS)) \ + && !defined(GC_USESIGRT_SIGNALS) +# define SIG_SUSPEND SIGUSR1 + /* SIGTSTP and SIGCONT could be used alternatively on FreeBSD. */ +# elif defined(GC_OPENBSD_THREADS) && !defined(GC_USESIGRT_SIGNALS) +# ifndef GC_OPENBSD_UTHREADS +# define SIG_SUSPEND SIGXFSZ +# endif +# elif defined(_SIGRTMIN) && !defined(CPPCHECK) +# define SIG_SUSPEND _SIGRTMIN + 6 +# else +# define SIG_SUSPEND SIGRTMIN + 6 +# endif +#endif /* GC_PTHREADS && !SIG_SUSPEND */ + +#if defined(GC_PTHREADS) && !defined(GC_SEM_INIT_PSHARED) +# define GC_SEM_INIT_PSHARED 0 +#endif + +/* 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))) \ + && !defined(GC_NO_SIGSETJMP) +# if defined(SUNOS5SIGS) && !defined(FREEBSD) && !defined(LINUX) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif + /* Define SETJMP and friends to be the version that restores */ + /* the signal mask. */ +# define SETJMP(env) sigsetjmp(env, 1) +# define LONGJMP(env, val) siglongjmp(env, val) +# define JMP_BUF sigjmp_buf +#else +# ifdef ECOS +# 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 || GC_NO_SIGSETJMP */ + +/* Do we need the GC_find_limit machinery to find the end of a */ +/* data segment. */ +#if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START) \ + || ((defined(SVR4) || defined(AIX) || defined(DGUX) \ + || (defined(LINUX) && defined(SPARC))) && !defined(PCR)) +# define NEED_FIND_LIMIT +#endif + +#if defined(DATASTART_USES_BSDGETDATASTART) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# if !defined(PCR) +# define NEED_FIND_LIMIT +# endif + GC_INNER ptr_t GC_FreeBSDGetDataStart(size_t, ptr_t); +# define DATASTART_IS_FUNC +#endif /* DATASTART_USES_BSDGETDATASTART */ + +#if ((defined(NETBSD) && defined(__ELF__)) \ + || (defined(OPENBSD) && !defined(GC_OPENBSD_UTHREADS))) \ + && !defined(NEED_FIND_LIMIT) + /* Used by GC_init_netbsd_elf or GC_register_data_segments in os_dep.c. */ +# define NEED_FIND_LIMIT +#endif + +#if defined(IA64) && !defined(NEED_FIND_LIMIT) +# define NEED_FIND_LIMIT + /* May be needed for register backing store base. */ +#endif + +#if defined(NEED_FIND_LIMIT) \ + || (defined(WRAP_MARK_SOME) && defined(__GNUC__)) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) + GC_EXTERN JMP_BUF GC_jmp_buf; + + /* Set up a handler for address faults which will longjmp to */ + /* GC_jmp_buf. */ + GC_INNER void GC_setup_temporary_fault_handler(void); + + /* Undo the effect of GC_setup_temporary_fault_handler. */ + GC_INNER void GC_reset_fault_handler(void); +#endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES */ + +/* Some convenience macros for cancellation support. */ +#ifdef CANCEL_SAFE +# if defined(GC_ASSERTIONS) \ + && (defined(USE_COMPILER_TLS) \ + || (defined(LINUX) && !defined(ARM32) && GC_GNUC_PREREQ(3, 3) \ + || defined(HPUX) /* and probably others ... */)) + extern __thread unsigned char GC_cancel_disable_count; +# define NEED_CANCEL_DISABLE_COUNT +# define INCR_CANCEL_DISABLE() ++GC_cancel_disable_count +# define DECR_CANCEL_DISABLE() --GC_cancel_disable_count +# define ASSERT_CANCEL_DISABLED() GC_ASSERT(GC_cancel_disable_count > 0) +# else +# define INCR_CANCEL_DISABLE() +# define DECR_CANCEL_DISABLE() +# define ASSERT_CANCEL_DISABLED() (void)0 +# endif /* !GC_ASSERTIONS */ +# define DISABLE_CANCEL(state) \ + do { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); \ + INCR_CANCEL_DISABLE(); } while (0) +# define RESTORE_CANCEL(state) \ + do { ASSERT_CANCEL_DISABLED(); \ + pthread_setcancelstate(state, NULL); \ + DECR_CANCEL_DISABLE(); } while (0) +#else +# define DISABLE_CANCEL(state) (void)0 +# define RESTORE_CANCEL(state) (void)0 +# define ASSERT_CANCEL_DISABLED() (void)0 +#endif /* !CANCEL_SAFE */ + +EXTERN_C_END + +#endif /* GC_PRIVATE_H */ diff --git a/bdwgc/include/private/gcconfig.h b/bdwgc/include/private/gcconfig.h new file mode 100644 index 000000000..f173d5a13 --- /dev/null +++ b/bdwgc/include/private/gcconfig.h @@ -0,0 +1,3382 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 2000-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * 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. + */ + +/* + * This header is private to the gc. It is almost always included from + * gc_priv.h. However it is possible to include it by itself if just the + * configuration macros are needed. In that + * case, a few declarations relying on types declared in gc_priv.h will be + * omitted. + */ + +#ifndef GCCONFIG_H +#define GCCONFIG_H + +#ifdef CPPCHECK +# undef CLOCKS_PER_SEC +# undef FIXUP_POINTER +# undef POINTER_MASK +# undef POINTER_SHIFT +# undef REDIRECT_REALLOC +# undef _MAX_PATH +#endif + +#ifndef PTR_T_DEFINED + typedef char * ptr_t; +# define PTR_T_DEFINED +#endif + +#if !defined(sony_news) +# include /* For size_t, etc. */ +#endif + +/* Note: Only wrap our own declarations, and not the included headers. */ +/* In this case, wrap our entire file, but temporarily unwrap/rewrap */ +/* around #includes. Types and macros do not need such wrapping, only */ +/* the declared global data and functions. */ +#ifdef __cplusplus +# define EXTERN_C_BEGIN extern "C" { +# define EXTERN_C_END } /* extern "C" */ +#else +# define EXTERN_C_BEGIN /* empty */ +# define EXTERN_C_END /* empty */ +#endif + +EXTERN_C_BEGIN + +/* Convenient internal macro to test version of Clang. */ +#if defined(__clang__) && defined(__clang_major__) +# define GC_CLANG_PREREQ(major, minor) \ + ((__clang_major__ << 16) + __clang_minor__ >= ((major) << 16) + (minor)) +# define GC_CLANG_PREREQ_FULL(major, minor, patchlevel) \ + (GC_CLANG_PREREQ(major, (minor) + 1) \ + || (__clang_major__ == (major) && __clang_minor__ == (minor) \ + && __clang_patchlevel__ >= (patchlevel))) +#else +# define GC_CLANG_PREREQ(major, minor) 0 /* FALSE */ +# define GC_CLANG_PREREQ_FULL(major, minor, patchlevel) 0 +#endif + +#ifdef LINT2 + /* A macro (based on a tricky expression) to prevent false warnings */ + /* like "Array compared to 0", "Comparison of identical expressions", */ + /* "Untrusted loop bound" output by some static code analysis tools. */ + /* The argument should not be a literal value. The result is */ + /* converted to word type. (Actually, GC_word is used instead of */ + /* word type as the latter might be undefined at the place of use.) */ +# define COVERT_DATAFLOW(w) (~(GC_word)(w)^(~(GC_word)0)) +#else +# define COVERT_DATAFLOW(w) ((GC_word)(w)) +#endif + +/* Machine dependent parameters. Some tuning parameters can be found */ +/* near the top of gc_priv.h. */ + +/* Machine specific parts contributed by various people. See README file. */ + +#if defined(__ANDROID__) && !defined(HOST_ANDROID) + /* __ANDROID__ macro is defined by Android NDK gcc. */ +# define HOST_ANDROID 1 +#endif + +#if defined(TIZEN) && !defined(HOST_TIZEN) +# define HOST_TIZEN 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(HOST_ANDROID)) \ + && !defined(LINUX) && !defined(__native_client__) +# define LINUX +# endif + +/* And one for NetBSD: */ +# if defined(__NetBSD__) +# define NETBSD +# endif + +/* And one for OpenBSD: */ +# if defined(__OpenBSD__) +# define OPENBSD +# endif + +/* And one for FreeBSD: */ +# if (defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(__FreeBSD_kernel__)) && !defined(FREEBSD) \ + && !defined(GC_NO_FREEBSD) /* Orbis compiler defines __FreeBSD__ */ +# define FREEBSD +# endif + +/* And one for Darwin: */ +# if defined(macosx) || (defined(__APPLE__) && defined(__MACH__)) +# define DARWIN + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif + +/* Determine the machine type: */ +# if defined(__native_client__) +# define NACL +# if !defined(__portable_native_client__) && !defined(__arm__) +# define I386 +# define mach_type_known +# else + /* Here we will rely upon arch-specific defines. */ +# endif +# endif +# if defined(__aarch64__) +# define AARCH64 +# if !defined(LINUX) && !defined(DARWIN) && !defined(FREEBSD) \ + && !defined(NETBSD) && !defined(NN_BUILD_TARGET_PLATFORM_NX) \ + && !defined(OPENBSD) && !defined(_WIN32) +# define NOSYS +# define mach_type_known +# endif +# endif +# if defined(__arm) || defined(__arm__) || defined(__thumb__) +# define ARM32 +# if defined(NACL) +# define mach_type_known +# elif !defined(LINUX) && !defined(NETBSD) && !defined(FREEBSD) \ + && !defined(OPENBSD) && !defined(DARWIN) && !defined(_WIN32) \ + && !defined(__CEGCC__) && !defined(NN_PLATFORM_CTR) \ + && !defined(GC_NO_NOSYS) && !defined(SN_TARGET_PSP2) \ + && !defined(SYMBIAN) +# define NOSYS +# define mach_type_known +# endif +# endif +# if defined(sun) && defined(mc68000) && !defined(CPPCHECK) +# error SUNOS4 no longer supported +# endif +# if defined(hp9000s300) && !defined(CPPCHECK) +# error M68K based HP machines no longer supported +# endif +# if defined(OPENBSD) && defined(m68k) +# define M68K +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__sparc__) +# define SPARC +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__arm__) +# define ARM32 +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__aarch64__) +# define AARCH64 +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__sh__) +# define SH +# define mach_type_known +# endif +# if defined(NETBSD) && (defined(m68k) || defined(__m68k__)) +# define M68K +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__powerpc__) +# define POWERPC +# define mach_type_known +# endif +# if defined(NETBSD) && (defined(__arm32__) || defined(__arm__)) +# define ARM32 +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__aarch64__) +# define AARCH64 +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__sh__) +# define SH +# define mach_type_known +# endif +# if defined(vax) || defined(__vax__) +# define VAX +# ifdef ultrix +# define ULTRIX +# else +# define BSD +# endif +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__vax__) +# define VAX +# define mach_type_known +# endif +# if (defined(mips) || defined(__mips) || defined(_mips)) && !defined(__TANDEM) +# define MIPS +# if defined(nec_ews) || defined(_nec_ews) +# define EWS4800 +# endif +# if !defined(LINUX) && !defined(EWS4800) && !defined(NETBSD) \ + && !defined(OPENBSD) && !defined(FREEBSD) && !defined(_WIN32_WCE) \ + && !defined(__CEGCC__) && !defined(__MINGW32CE__) +# if defined(ultrix) || defined(__ultrix) +# define ULTRIX +# else +# define IRIX5 /* or IRIX 6.X */ +# endif +# endif /* !LINUX */ +# if defined(NETBSD) && defined(__MIPSEL__) +# undef ULTRIX +# endif +# define mach_type_known +# endif +# if defined(__QNX__) +# define I386 +# define mach_type_known +# endif +# if defined(__NIOS2__) || defined(__NIOS2) || defined(__nios2__) +# define NIOS2 /* Altera NIOS2 */ +# define mach_type_known +# endif +# if defined(__or1k__) +# define OR1K /* OpenRISC/or1k */ +# define mach_type_known +# endif +# if defined(DGUX) && (defined(i386) || defined(__i386__)) +# define I386 +# ifndef _USING_DGUX +# define _USING_DGUX +# endif +# define mach_type_known +# endif +# if defined(sequent) && (defined(i386) || defined(__i386__)) +# define I386 +# define SEQUENT +# define mach_type_known +# endif +# if (defined(sun) || defined(__sun)) && (defined(i386) || defined(__i386__)) +# define I386 +# define SOLARIS +# define mach_type_known +# endif +# if (defined(sun) || defined(__sun)) && defined(__amd64) +# define X86_64 +# define SOLARIS +# define mach_type_known +# endif +# if (defined(__OS2__) || defined(__EMX__)) && defined(__32BIT__) +# define I386 +# define OS2 +# define mach_type_known +# endif +# if defined(ibm032) && !defined(CPPCHECK) +# error IBM PC/RT no longer supported +# endif +# if (defined(sun) || defined(__sun)) && (defined(sparc) || defined(__sparc)) + /* Test for SunOS 5.x */ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define SPARC +# define SOLARIS +# define mach_type_known +# elif defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ + && !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) +# define SPARC +# define DRSNX +# define mach_type_known +# endif +# if defined(_IBMR2) +# define POWERPC +# define AIX +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__sparc__) +# define SPARC +# define mach_type_known +# endif +# if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386) + /* The above test may need refinement */ +# define I386 +# if defined(_SCO_ELF) +# define SCO_ELF +# else +# define SCO +# endif +# define mach_type_known +# endif +# if defined(_AUX_SOURCE) && !defined(CPPCHECK) +# error A/UX no longer supported +# endif +# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \ + || defined(hppa) || defined(__hppa__) +# define HP_PA +# if !defined(LINUX) && !defined(HPUX) && !defined(OPENBSD) +# define HPUX +# endif +# define mach_type_known +# endif +# if defined(__ia64) && (defined(_HPUX_SOURCE) || defined(__HP_aCC)) +# define IA64 +# ifndef HPUX +# define HPUX +# endif +# define mach_type_known +# endif +# if (defined(__BEOS__) || defined(__HAIKU__)) && defined(_X86_) +# define I386 +# define HAIKU +# define mach_type_known +# endif +# if defined(__HAIKU__) && (defined(__amd64__) || defined(__x86_64__)) +# define X86_64 +# define HAIKU +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__amd64__) +# define X86_64 +# define mach_type_known +# endif +# if defined(LINUX) && (defined(i386) || defined(__i386__)) +# define I386 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__x86_64__) +# define X86_64 +# define mach_type_known +# endif +# if defined(LINUX) && (defined(__ia64__) || defined(__ia64)) +# define IA64 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__e2k__) +# define E2K +# 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 +# endif +# if defined(LINUX) && defined(__cris__) +# ifndef CRIS +# define CRIS +# endif +# define mach_type_known +# endif +# if defined(LINUX) && defined(__loongarch__) +# define LOONGARCH +# define mach_type_known +# endif +# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__) \ + || defined(powerpc64) || defined(__powerpc64__)) +# define POWERPC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__mc68000__) +# define M68K +# define mach_type_known +# endif +# if defined(LINUX) && (defined(sparc) || defined(__sparc__)) +# define SPARC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__sh__) +# define SH +# define mach_type_known +# endif +# if defined(LINUX) && defined(__avr32__) +# define AVR32 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__m32r__) +# define M32R +# define mach_type_known +# endif +# if defined(__alpha) || defined(__alpha__) +# define ALPHA +# if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) \ + && !defined(FREEBSD) +# define OSF1 /* a.k.a Digital Unix */ +# endif +# define mach_type_known +# endif +# if defined(_AMIGA) && !defined(AMIGA) +# define AMIGA +# endif +# ifdef AMIGA +# define M68K +# define mach_type_known +# endif +# 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__) \ + && !defined(SYMBIAN) +# define POWERPC +# define MACOS +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__powerpc__) +# define POWERPC +# define mach_type_known +# endif +# if defined(DARWIN) +# if defined(__ppc__) || defined(__ppc64__) +# define POWERPC +# define mach_type_known +# elif defined(__x86_64__) || defined(__x86_64) +# define X86_64 +# define mach_type_known +# elif defined(__i386__) +# define I386 +# define mach_type_known +# elif defined(__arm__) +# define ARM32 +# define mach_type_known +# elif defined(__aarch64__) +# define AARCH64 +# define mach_type_known +# endif +# endif +# if defined(__rtems__) && (defined(i386) || defined(__i386__)) +# define I386 +# define RTEMS +# define mach_type_known +# endif +# if defined(NeXT) && defined(mc68000) +# define M68K +# define NEXT +# define mach_type_known +# endif +# if defined(NeXT) && (defined(i386) || defined(__i386__)) +# define I386 +# define NEXT +# define mach_type_known +# endif +# if defined(OPENBSD) && (defined(i386) || defined(__i386__)) +# define I386 +# 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(__amd64__) || defined(__x86_64__)) +# define X86_64 +# define mach_type_known +# endif +# if defined(FREEBSD) && defined(__sparc__) +# 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(FREEBSD) && defined(__aarch64__) +# define AARCH64 +# define mach_type_known +# endif +# if defined(FREEBSD) && (defined(mips) || defined(__mips) || defined(_mips)) +# define MIPS +# define mach_type_known +# endif +# if defined(bsdi) && (defined(i386) || defined(__i386__)) +# define I386 +# define BSDI +# define mach_type_known +# endif +# if !defined(mach_type_known) && defined(__386BSD__) +# define I386 +# define THREE86BSD +# define mach_type_known +# endif +# if defined(_CX_UX) && defined(_M88K) +# define M88K +# define CX_UX +# define mach_type_known +# endif +# if defined(DGUX) && defined(m88k) +# define M88K + /* DGUX defined */ +# define mach_type_known +# endif +# if defined(_WIN32_WCE) || defined(__CEGCC__) || defined(__MINGW32CE__) + /* SH3, SH4, MIPS already defined for corresponding architectures */ +# if defined(SH3) || defined(SH4) +# define SH +# endif +# if defined(x86) || defined(__i386__) +# define I386 +# endif +# if defined(_M_ARM) || defined(ARM) || defined(_ARM_) +# define ARM32 +# endif +# define MSWINCE +# define mach_type_known +# else +# if ((defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300)) \ + || (defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) \ + && !defined(__INTERIX) && !defined(SYMBIAN)) +# if defined(__LP64__) || defined(_M_X64) +# define X86_64 +# elif defined(_M_ARM) +# define ARM32 +# elif defined(_M_ARM64) +# define AARCH64 +# else /* _M_IX86 */ +# define I386 +# endif +# ifdef _XBOX_ONE +# define MSWIN_XBOX1 +# else +# ifndef MSWIN32 +# define MSWIN32 /* or Win64 */ +# endif +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define MSWINRT_FLAVOR +# endif +# endif +# define mach_type_known +# endif +# if defined(_MSC_VER) && defined(_M_IA64) +# define IA64 +# define MSWIN32 /* Really Win64, but we do not treat 64-bit */ + /* variants as a different platform. */ +# endif +# endif +# if defined(__DJGPP__) +# define I386 +# ifndef DJGPP +# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# endif +# define mach_type_known +# endif +# if defined(__CYGWIN32__) || defined(__CYGWIN__) +# if defined(__LP64__) +# define X86_64 +# else +# define I386 +# endif +# define CYGWIN32 +# define mach_type_known +# endif +# if defined(__INTERIX) +# define I386 +# define INTERIX +# define mach_type_known +# endif +# if defined(__MINGW32__) && !defined(mach_type_known) +# define I386 +# define MSWIN32 +# define mach_type_known +# endif +# if defined(__BORLANDC__) +# define I386 +# define MSWIN32 +# define mach_type_known +# endif +# if defined(_UTS) && !defined(mach_type_known) +# define S370 +# define UTS4 +# define mach_type_known +# endif +# if defined(__pj__) && !defined(CPPCHECK) +# error PicoJava no longer supported + /* The implementation had problems, and I haven't heard of users */ + /* in ages. If you want it resurrected, let me know. */ +# endif +# if defined(__embedded__) && defined(PPC) +# define POWERPC +# define NOSYS +# define mach_type_known +# endif + +# if defined(__WATCOMC__) && defined(__386__) +# define I386 +# if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW) +# if defined(__OS2__) +# define OS2 +# else +# if defined(__WINDOWS_386__) || defined(__NT__) +# define MSWIN32 +# else +# define DOS4GW +# endif +# endif +# endif +# define mach_type_known +# endif +# if defined(__s390__) && defined(LINUX) +# define S390 +# define mach_type_known +# endif +# if defined(__GNU__) +# if defined(__i386__) +/* The Debian Hurd running on generic PC */ +# define HURD +# define I386 +# define mach_type_known +# endif +# endif +# if defined(__x86_64__) && defined(__GNU__) +# define HURD +# define X86_64 +# define mach_type_known +# endif +# if defined(__TANDEM) + /* Nonstop S-series */ + /* FIXME: Should recognize Integrity series? */ +# define MIPS +# define NONSTOP +# define mach_type_known +# endif +# if defined(__arc__) && defined(LINUX) +# define ARC +# define mach_type_known +# endif +# if defined(__hexagon__) && defined(LINUX) +# define HEXAGON +# define mach_type_known +# endif +# if defined(__tile__) && defined(LINUX) +# ifdef __tilegx__ +# define TILEGX +# else +# define TILEPRO +# endif +# define mach_type_known +# endif +# if defined(__riscv) && (defined(FREEBSD) || defined(LINUX) \ + || defined(OPENBSD)) +# define RISCV +# define mach_type_known +# endif + +# if defined(SN_TARGET_PSP2) +# define mach_type_known +# endif + +# if defined(NN_PLATFORM_CTR) +# define mach_type_known +# endif + +# if defined(NN_BUILD_TARGET_PLATFORM_NX) +# define NINTENDO_SWITCH +# define mach_type_known +# endif + +# if defined(SYMBIAN) +# define mach_type_known +# endif + +# if defined(__EMSCRIPTEN__) || defined(EMSCRIPTEN) +# ifndef EMSCRIPTEN +# define EMSCRIPTEN +# endif +# define I386 +# define mach_type_known +# endif + +/* Feel free to add more clauses here */ + +/* Or manually define the machine type here. A machine type is */ +/* characterized by the architecture. Some */ +/* machine types are further subdivided by OS. */ +/* Macros such as LINUX, FREEBSD, etc. distinguish them. */ +/* SYSV on an M68K actually means A/UX. */ +/* The distinction in these cases is usually the stack starting address */ +# if !defined(mach_type_known) && !defined(CPPCHECK) +# error The collector has not been ported to this machine/OS combination +# endif + /* Mapping is: M68K ==> Motorola 680X0 */ + /* (NEXT, and SYSV (A/UX), */ + /* MACOS and AMIGA variants) */ + /* I386 ==> Intel 386 */ + /* (SEQUENT, OS2, SCO, LINUX, NETBSD, */ + /* FREEBSD, THREE86BSD, MSWIN32, */ + /* BSDI, SOLARIS, NEXT and others) */ + /* NS32K ==> Encore Multimax */ + /* MIPS ==> R2000 through R14K */ + /* (many variants) */ + /* VAX ==> DEC VAX */ + /* (BSD, ULTRIX variants) */ + /* HP_PA ==> HP9000/700 & /800 */ + /* HP/UX, LINUX */ + /* SPARC ==> SPARC v7/v8/v9 */ + /* (SOLARIS, LINUX, DRSNX variants) */ + /* ALPHA ==> DEC Alpha */ + /* (OSF1 and LINUX variants) */ + /* LOONGARCH ==> Loongson LoongArch */ + /* (LINUX 32- and 64-bit variants) */ + /* M88K ==> Motorola 88XX0 */ + /* (CX_UX and DGUX) */ + /* S370 ==> 370-like machine */ + /* running Amdahl UTS4 */ + /* S390 ==> 390-like machine */ + /* running LINUX */ + /* AARCH64 ==> ARM AArch64 */ + /* (LP64 and ILP32 variants) */ + /* E2K ==> Elbrus 2000 */ + /* running LINUX */ + /* ARM32 ==> Intel StrongARM */ + /* (many variants) */ + /* IA64 ==> Intel IPF */ + /* (e.g. Itanium) */ + /* (LINUX and HPUX) */ + /* SH ==> Hitachi SuperH */ + /* (LINUX & MSWINCE) */ + /* X86_64 ==> AMD x86-64 */ + /* POWERPC ==> IBM/Apple PowerPC */ + /* (MACOS(<=9),DARWIN(incl.MACOSX),*/ + /* LINUX, NETBSD, AIX, NOSYS */ + /* variants) */ + /* Handles 32 and 64-bit variants. */ + /* ARC ==> Synopsys ARC */ + /* AVR32 ==> Atmel RISC 32-bit */ + /* CRIS ==> Axis Etrax */ + /* M32R ==> Renesas M32R */ + /* NIOS2 ==> Altera NIOS2 */ + /* HEXAGON ==> Qualcomm Hexagon */ + /* OR1K ==> OpenRISC/or1k */ + /* RISCV ==> RISC-V 32/64-bit */ + /* TILEPRO ==> Tilera TILEPro */ + /* TILEGX ==> Tilera TILE-Gx */ + + +/* + * For each architecture and OS, the following need to be defined: + * + * CPP_WORDSZ is a simple integer constant representing the word size. + * in bits. We assume byte addressability, where a byte has 8 bits. + * We also assume CPP_WORDSZ is either 32 or 64. + * (We care about the length of pointers, not hardware + * bus widths. Thus a 64 bit processor with a C compiler that uses + * 32 bit pointers should use CPP_WORDSZ of 32, not 64. Default is 32.) + * + * MACH_TYPE is a string representation of the machine type. + * OS_TYPE is analogous for the OS. + * + * ALIGNMENT is the largest N, such that + * all pointer are guaranteed to be aligned on N byte boundaries. + * defining it to be 1 will always work, but perform poorly. + * + * DATASTART is the beginning of the data segment. + * On some platforms SEARCH_FOR_DATA_START is defined. + * The latter will cause GC_data_start to + * be set to an address determined by accessing data backwards from _end + * until an unmapped page is found. DATASTART will be defined to be + * GC_data_start. + * 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[]". + * 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-*-* + * without any special options. + * + * STACKBOTTOM is the cold end of the stack, which is usually the + * highest address in the stack. + * Under PCR or OS/2, we have other ways of finding thread stacks. + * For each machine, the following should: + * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and + * 2) define exactly one of + * STACKBOTTOM (should be defined to be an expression) + * LINUX_STACKBOTTOM + * HEURISTIC1 + * HEURISTIC2 + * If STACKBOTTOM is defined, then its value will be used directly (as the + * stack bottom). If LINUX_STACKBOTTOM is defined, then it will be determined + * with a method appropriate for most Linux systems. Currently we look + * first for __libc_stack_end (currently only if USE_LIBC_PRIVATES is + * defined), and if that fails read it from /proc. (If USE_LIBC_PRIVATES + * is not defined and NO_PROC_STAT is defined, we revert to HEURISTIC2.) + * If either of the last two macros are defined, then STACKBOTTOM is computed + * during collector startup using one of the following two heuristics: + * HEURISTIC1: Take an address inside GC_init's frame, and round it up to + * the next multiple of STACK_GRAN. + * HEURISTIC2: Take an address inside GC_init's frame, increment it repeatedly + * in small steps (decrement if STACK_GROWS_UP), and read the value + * at each location. Remember the value when the first + * Segmentation violation or Bus error is signaled. Round that + * to the nearest plausible page boundary, and use that instead + * of STACKBOTTOM. + * + * Gustavo Rodriguez-Rivera points out that on most (all?) Unix machines, + * the value of environ is a pointer that can serve as STACKBOTTOM. + * I expect that HEURISTIC2 can be replaced by this approach, which + * interferes far less with debugging. However it has the disadvantage + * that it's confused by a putenv call before the collector is initialized. + * This could be dealt with by intercepting putenv ... + * + * If no expression for STACKBOTTOM can be found, and neither of the above + * heuristics are usable, the collector can still be used with all of the above + * undefined, provided one of the following is done: + * 1) GC_mark_roots can be changed to somehow mark from the correct stack(s) + * without reference to STACKBOTTOM. This is appropriate for use in + * conjunction with thread packages, since there will be multiple stacks. + * (Allocating thread stacks in the heap, and treating them as ordinary + * heap data objects is also possible as a last resort. However, this is + * likely to introduce significant amounts of excess storage retention + * unless the dead parts of the thread stacks are periodically cleared.) + * 2) Client code may set GC_stackbottom before calling any GC_ routines. + * If the author of the client code controls the main program, this is + * easily accomplished by introducing a new main program, setting + * GC_stackbottom to the address of a local variable, and then calling + * the original main program. The new main program would read something + * like (provided real_main() is not inlined by the compiler): + * + * #include "gc.h" + * + * main(argc, argv, envp) + * int argc; + * char **argv, **envp; + * { + * volatile int dummy; + * + * GC_stackbottom = (ptr_t)(&dummy); + * return(real_main(argc, argv, envp)); + * } + * + * + * Each architecture may also define the style of virtual dirty bit + * implementation to be used: + * GWW_VDB: Use Win32 GetWriteWatch primitive. + * MPROTECT_VDB: Write protect the heap and catch faults. + * PROC_VDB: Use the SVR4 /proc primitives to read dirty bits. + * SOFT_VDB: Use the Linux /proc primitives to track dirty bits. + * + * The first and second one may be combined, in which case a runtime + * selection will be made, based on GetWriteWatch availability. + * + * An architecture may define DYNAMIC_LOADING if dyn_load.c + * defined GC_register_dynamic_libraries() for the architecture. + * + * An architecture may define PREFETCH(x) to preload the cache with *x. + * This defaults to GCC built-in operation (or a no-op for other compilers). + * + * GC_PREFETCH_FOR_WRITE(x) is used if *x is about to be written. + * + * An architecture may also define CLEAR_DOUBLE(x) to be a fast way to + * clear the two words at GC_malloc-aligned address x. By default, + * word stores of 0 are used instead. + * + * HEAP_START may be defined as the initial address hint for mmap-based + * allocation. + */ + +/* If available, we can use __builtin_unwind_init() to push the */ +/* relevant registers onto the stack. */ +# if GC_GNUC_PREREQ(2, 8) \ + && !GC_GNUC_PREREQ(11, 0) /* broken at least in 11.2.0 on cygwin64 */ \ + && !defined(__INTEL_COMPILER) && !defined(__PATHCC__) \ + && !defined(__FUJITSU) /* for FX10 system */ \ + && !(defined(POWERPC) && defined(DARWIN)) /* for MacOS X 10.3.9 */ \ + && !defined(E2K) && !defined(RTEMS) \ + && !defined(__ARMCC_VERSION) /* does not exist in armcc gnu emu */ \ + && (!defined(__clang__) \ + || GC_CLANG_PREREQ(8, 0) /* was no-op in clang-3 at least */) +# define HAVE_BUILTIN_UNWIND_INIT +# endif + +/* The common OS-specific definitions (should be applicable to */ +/* all (or most, at least) supported architectures). */ + +# ifdef CYGWIN32 +# define OS_TYPE "CYGWIN32" +# define RETRY_GET_THREAD_CONTEXT +# ifdef USE_WINALLOC +# define GWW_VDB +# elif defined(USE_MMAP) +# define USE_MMAP_ANON +# endif +# endif /* CYGWIN32 */ + +# ifdef DARWIN +# define OS_TYPE "DARWIN" +# define DYNAMIC_LOADING + /* TODO: see get_end(3), get_etext() and get_end() should not be used. */ + /* These aren't used when dyld support is enabled (it is by default). */ +# define DATASTART ((ptr_t)get_etext()) +# define DATAEND ((ptr_t)get_end()) +# define USE_MMAP_ANON + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)getpagesize() + /* There seems to be some issues with trylock hanging on darwin. */ + /* TODO: This should be looked into some more. */ +# define NO_PTHREAD_TRYLOCK +# endif /* DARWIN */ + +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif +# if !defined(ALPHA) && !defined(SPARC) + extern char etext[]; +# define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext) +# define DATASTART_USES_BSDGETDATASTART +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# endif +# endif /* FREEBSD */ + +# ifdef HAIKU +# define OS_TYPE "HAIKU" +# define DYNAMIC_LOADING +# define MPROTECT_VDB + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)B_PAGE_SIZE +# endif /* HAIKU */ + +# ifdef HPUX +# define OS_TYPE "HPUX" + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# ifdef USE_MMAP +# define USE_MMAP_ANON +# endif +# define DYNAMIC_LOADING + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGE_SIZE) +# endif /* HPUX */ + +# ifdef LINUX +# define OS_TYPE "LINUX" + EXTERN_C_END +# include /* for __GLIBC__ */ + EXTERN_C_BEGIN +# if defined(FORCE_MPROTECT_BEFORE_MADVISE) \ + || defined(PREFER_MMAP_PROT_NONE) +# define COUNT_UNMAPPED_REGIONS +# endif +# define RETRY_TKILL_ON_EAGAIN +# if !defined(MIPS) && !defined(POWERPC) +# define LINUX_STACKBOTTOM +# endif +# if defined(__ELF__) && !defined(IA64) +# define DYNAMIC_LOADING +# endif +# if defined(__ELF__) && !defined(ARC) && !defined(RISCV) \ + && !defined(S390) && !defined(TILEGX) && !defined(TILEPRO) + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# endif /* LINUX */ + +# ifdef MACOS +# define OS_TYPE "MACOS" +# ifndef __LOWMEM__ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif + /* See os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t)LMGetCurStackBase()) +# define DATAEND /* not needed */ +# endif /* MACOS */ + +# ifdef MSWIN32 +# define OS_TYPE "MSWIN32" + /* STACKBOTTOM and DATASTART are handled specially in os_dep.c. */ +# define DATAEND /* not needed */ +# define GWW_VDB +# endif + +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define DATAEND /* not needed */ +# endif + +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# elif !defined(MIPS) /* TODO: probably do not exclude it */ + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# endif /* NETBSD */ + +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t)get_etext()) +# define DATASTART_IS_FUNC +# define DATAEND /* not needed */ +# endif + +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# if !defined(M68K) /* TODO: probably do not exclude it */ +# ifndef GC_OPENBSD_THREADS +# define HEURISTIC2 +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# endif +# endif /* OPENBSD */ + +# ifdef SOLARIS +# define OS_TYPE "SOLARIS" + extern int _etext[], _end[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(_end)) +# if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) +# define USE_MMAP 1 + /* Otherwise we now use calloc. Mmap may result in the */ + /* heap interleaved with thread stacks, which can result in */ + /* excessive blacklisting. Sbrk is unusable since it */ + /* doesn't interact correctly with the system malloc. */ +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# ifndef GC_THREADS +# define MPROTECT_VDB +# endif +# define DYNAMIC_LOADING + /* Define STACKBOTTOM as (ptr_t)_start worked through 2.7, */ + /* but reportedly breaks under 2.8. It appears that the stack */ + /* base is a property of the executable, so this should not */ + /* break old executables. */ + /* HEURISTIC1 reportedly no longer works under Solaris 2.7. */ + /* HEURISTIC2 probably works, but this appears to be preferable.*/ + /* Apparently USRSTACK is defined to be USERLIMIT, but in some */ + /* installations that's undefined. We work around this with a */ + /* gross hack: */ + EXTERN_C_END +# include +# include + EXTERN_C_BEGIN +# ifdef USERLIMIT + /* This should work everywhere, but doesn't. */ +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif /* SOLARIS */ + +# define STACK_GRAN 0x1000000 + +# ifdef SYMBIAN +# define MACH_TYPE "SYMBIAN" +# define OS_TYPE "SYMBIAN" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define DATASTART (ptr_t)ALIGNMENT /* cannot be null */ +# define DATAEND (ptr_t)ALIGNMENT +# endif + +# ifdef M68K +# define MACH_TYPE "M68K" +# define ALIGNMENT 2 +# ifdef OPENBSD +# define HEURISTIC2 +# ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef LINUX +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# endif +# ifdef __ELF__ +# if defined(__GLIBC__) && __GLIBC__ >= 2 +# define SEARCH_FOR_DATA_START +# else /* !GLIBC2 */ + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif /* !GLIBC2 */ +# else + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef AMIGA +# define OS_TYPE "AMIGA" + /* STACKBOTTOM and DATASTART handled specially */ + /* in os_dep.c */ +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef MACOS +# define GETPAGESIZE() 4096 +# endif +# ifdef NEXT +# define STACKBOTTOM ((ptr_t)0x4000000) +# endif +# endif + +# ifdef POWERPC +# define MACH_TYPE "POWERPC" +# ifdef MACOS +# define ALIGNMENT 2 /* Still necessary? Could it be 4? */ +# endif +# ifdef LINUX +# if defined(__powerpc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define ALIGNMENT 4 +# endif + /* HEURISTIC1 has been reliably reported to fail for a 32-bit */ + /* executable on a 64 bit kernel. */ +# if defined(__bg__) + /* The Linux Compute Node Kernel (used on BlueGene systems) */ + /* does not support LINUX_STACKBOTTOM way. */ +# define HEURISTIC2 +# define NO_PTHREAD_GETATTR_NP +# else +# define LINUX_STACKBOTTOM +# endif +# define SEARCH_FOR_DATA_START +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# endif +# ifndef SOFT_VDB +# define SOFT_VDB +# endif +# endif +# ifdef DARWIN +# if defined(__ppc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t)0x7fff5fc00000) +# define CACHE_LINE_SIZE 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define ALIGNMENT 4 +# define STACKBOTTOM ((ptr_t)0xc0000000) +# endif +# define MPROTECT_VDB +# if defined(USE_PPC_PREFETCH) && defined(__GNUC__) + /* The performance impact of prefetches is untested */ +# define PREFETCH(x) \ + __asm__ __volatile__ ("dcbt 0,%0" : : "r" ((const void *) (x))) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x))) +# endif +# endif +# ifdef OPENBSD +# if defined(__powerpc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# else +# define ALIGNMENT 4 +# endif +# endif +# ifdef FREEBSD +# if defined(__powerpc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define ALIGNMENT 4 +# endif +# endif +# ifdef NETBSD +# define ALIGNMENT 4 +# endif +# ifdef SN_TARGET_PS3 +# define OS_TYPE "SN_TARGET_PS3" +# define NO_GETENV +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + extern int _end[]; + extern int __bss_start; +# define DATASTART ((ptr_t)(__bss_start)) +# define DATAEND ((ptr_t)(_end)) +# 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" +# undef ALIGNMENT /* in case it's defined */ +# undef IA64 + /* DOB: some AIX installs stupidly define IA64 in */ + /* /usr/include/sys/systemcfg.h */ +# ifdef __64BIT__ +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t)0x1000000000000000) +# else +# define ALIGNMENT 4 +# define CPP_WORDSZ 32 +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) +# endif +# define USE_MMAP_ANON + /* From AIX linker man page: + _text Specifies the first location of the program. + _etext Specifies the first location after the program. + _data Specifies the first location of the data. + _edata Specifies the first location after the initialized data + _end or end Specifies the first location after all data. + */ + extern int _data[], _end[]; +# define DATASTART ((ptr_t)((ulong)_data)) +# define DATAEND ((ptr_t)((ulong)_end)) + extern int errno; +# define DYNAMIC_LOADING + /* For really old versions of AIX, this may have to be removed. */ +# endif +# ifdef NOSYS +# define OS_TYPE "NOSYS" +# define ALIGNMENT 4 + extern void __end[], __dso_handle[]; +# define DATASTART ((ptr_t)__dso_handle) /* OK, that's ugly. */ +# define DATAEND ((ptr_t)(__end)) + /* Stack starts at 0xE0000000 for the simulator. */ +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 +# define HEURISTIC1 +# endif +# endif + +# ifdef NACL +# define OS_TYPE "NACL" +# if defined(__GLIBC__) +# define DYNAMIC_LOADING +# endif +# define DATASTART ((ptr_t)0x10020000) + extern int _end[]; +# define DATAEND ((ptr_t)_end) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# define HEURISTIC1 +# define NO_PTHREAD_GETATTR_NP +# define USE_MMAP_ANON +# define GETPAGESIZE() 65536 +# define MAX_NACL_GC_THREADS 1024 +# endif + +# ifdef VAX +# define MACH_TYPE "VAX" +# define ALIGNMENT 4 /* Pointers are longword aligned by 4.2 C compiler */ + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# ifdef BSD +# define OS_TYPE "BSD" +# define HEURISTIC1 + /* HEURISTIC2 may be OK, but it's hard to test. */ +# endif +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define STACKBOTTOM ((ptr_t)0x7fffc800) +# endif +# endif + +# ifdef SPARC +# define MACH_TYPE "SPARC" +# if defined(__arch64__) || defined(__sparcv9) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define ELF_CLASS ELFCLASS64 +# else +# define ALIGNMENT 4 /* Required by hardware */ +# define CPP_WORDSZ 32 +# endif + /* Don't define USE_ASM_PUSH_REGS. We do use an asm helper, but */ + /* not to push the registers on the mark stack. */ +# ifdef SOLARIS +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# define PROC_VDB +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGESIZE) + /* getpagesize() appeared to be missing from at least */ + /* one Solaris 5.4 installation. Weird. */ +# endif +# ifdef DRSNX +# define OS_TYPE "DRSNX" + extern int etext[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)etext) +# define DATASTART_IS_FUNC +# define MPROTECT_VDB +# define STACKBOTTOM ((ptr_t)0xdfff0000) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# if !defined(__ELF__) && !defined(CPPCHECK) +# error Linux SPARC a.out not supported +# endif +# define SVR4 + extern int _etext[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# ifdef __arch64__ +# define DATASTART GC_SysVGetDataStart(0x100000, (ptr_t)_etext) +# else +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# endif +# define DATASTART_IS_FUNC +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef FREEBSD + extern char etext[]; + extern char edata[]; +# if !defined(CPPCHECK) + extern char end[]; +# endif +# define NEED_FIND_LIMIT +# define DATASTART ((ptr_t)(&etext)) + void * GC_find_limit(void *, int); +# define DATAEND (ptr_t)GC_find_limit(DATASTART, TRUE) +# define DATAEND_IS_FUNC +# define GC_HAVE_DATAREGION2 +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) +# endif +# endif + +# ifdef I386 +# define MACH_TYPE "I386" +# if (defined(__LP64__) || defined(_WIN64)) && !defined(CPPCHECK) +# error This should be handled as X86_64 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + /* Appears to hold for all "32 bit" compilers */ + /* except Borland. The -a4 option fixes */ + /* Borland. For Watcom the option is -zp4. */ +# endif +# ifdef SEQUENT +# define OS_TYPE "SEQUENT" + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# define STACKBOTTOM ((ptr_t)0x3ffff000) +# endif +# ifdef EMSCRIPTEN +# define OS_TYPE "EMSCRIPTEN" +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + /* Emscripten does emulate mmap and munmap, but those should */ + /* not be used in the collector, since WebAssembly lacks the */ + /* native support of memory mapping. Use sbrk() instead. */ +# undef USE_MMAP +# undef USE_MUNMAP +# define STACK_GROWS_DOWN +# if defined(GC_THREADS) && !defined(CPPCHECK) +# error No threads support yet +# endif +# endif +# if defined(__QNX__) +# define OS_TYPE "QNX" +# define SA_RESTART 0 +# define HEURISTIC1 + extern char etext[]; + extern int _end[]; +# define DATASTART ((ptr_t)etext) +# define DATAEND ((ptr_t)_end) +# endif +# ifdef HAIKU + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# endif +# ifdef SOLARIS +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext) + /* At least in Solaris 2.5, PROC_VDB gives wrong values for */ + /* dirty bits. It appears to be fixed in 2.8 and 2.9. */ +# ifdef SOLARIS25_PROC_VDB_BUG_FIXED +# define PROC_VDB +# endif +# endif +# ifdef SCO +# define OS_TYPE "SCO" + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0x3fffff) & ~0x3fffff) \ + + ((word)(etext) & 0xfff)) +# define STACKBOTTOM ((ptr_t)0x7ffffffc) +# endif +# ifdef SCO_ELF +# define OS_TYPE "SCO_ELF" + extern int etext[]; +# define DATASTART ((ptr_t)(etext)) +# define STACKBOTTOM ((ptr_t)0x08048000) +# define DYNAMIC_LOADING +# define ELF_CLASS ELFCLASS32 +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + extern int _etext, _end; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)(&_etext)) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(&_end)) +# define STACK_GROWS_DOWN +# define HEURISTIC2 + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGESIZE) +# define DYNAMIC_LOADING +# ifndef USE_MMAP +# define USE_MMAP 1 +# endif +# define MAP_FAILED (void *) ((word)-1) +# define HEAP_START (ptr_t)0x40000000 +# endif /* DGUX */ +# ifdef LINUX +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in the incremental mode, */ + /* possibly because the Linux threads implementation */ + /* itself is a malloc client and cannot deal with the */ + /* signals. fread() uses malloc too. */ +# endif +# define HEAP_START (ptr_t)0x1000 + /* This encourages mmap to give us low addresses, */ + /* thus allowing the heap to grow to ~3 GB. */ +# ifdef __ELF__ +# if defined(__GLIBC__) && __GLIBC__ >= 2 \ + || defined(HOST_ANDROID) || defined(HOST_TIZEN) +# define SEARCH_FOR_DATA_START +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif +# if !defined(GC_NO_SIGSETJMP) && (defined(HOST_TIZEN) \ + || (defined(HOST_ANDROID) \ + && !(GC_GNUC_PREREQ(4, 8) || GC_CLANG_PREREQ(3, 2) \ + || __ANDROID_API__ >= 18))) + /* Older Android NDK releases lack sigsetjmp in x86 libc */ + /* (setjmp is used instead to find data_start). The bug */ + /* is fixed in Android NDK r8e (so, ok to use sigsetjmp */ + /* if gcc4.8+, clang3.2+ or Android API level 18+). */ +# define GC_NO_SIGSETJMP 1 +# endif +# else + extern int etext[]; +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# endif +# ifdef USE_I686_PREFETCH +# define PREFETCH(x) \ + __asm__ __volatile__ ("prefetchnta %0" : : "m"(*(char *)(x))) + /* Empirically prefetcht0 is much more effective at reducing */ + /* cache miss stalls for the targeted load instructions. But it */ + /* seems to interfere enough with other cache traffic that the */ + /* net result is worse than prefetchnta. */ +# ifdef FORCE_WRITE_PREFETCH + /* Using prefetches for write seems to have a slight negative */ + /* impact on performance, at least for a PIII/500. */ +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("prefetcht0 %0" : : "m"(*(char *)(x))) +# else +# define GC_NO_PREFETCH_FOR_WRITE +# endif +# elif defined(USE_3DNOW_PREFETCH) +# define PREFETCH(x) \ + __asm__ __volatile__ ("prefetch %0" : : "m"(*(char *)(x))) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("prefetchw %0" : : "m"(*(char *)(x))) +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) \ + && !defined(GLIBC_TSX_BUG_FIXED) + /* Workaround lock elision implementation for some glibc. */ +# define GLIBC_2_19_TSX_BUG + EXTERN_C_END +# include /* for gnu_get_libc_version() */ + EXTERN_C_BEGIN +# endif +# ifndef SOFT_VDB +# define SOFT_VDB +# endif +# endif +# ifdef CYGWIN32 +# define WOW64_THREAD_CONTEXT_WORKAROUND +# define DATASTART ((ptr_t)GC_DATASTART) /* From gc.h */ +# define DATAEND ((ptr_t)GC_DATAEND) +# ifndef USE_WINALLOC +# /* MPROTECT_VDB does not work, it leads to a spurious exit. */ +# ifdef USE_MMAP +# define NEED_FIND_LIMIT +# endif +# endif +# endif +# ifdef INTERIX +# define OS_TYPE "INTERIX" + extern int _data_start__[]; + extern int _bss_end__[]; +# define DATASTART ((ptr_t)_data_start__) +# define DATAEND ((ptr_t)_bss_end__) +# define STACKBOTTOM ({ ptr_t rv; \ + __asm__ __volatile__ ("movl %%fs:4, %%eax" \ + : "=a" (rv)); \ + rv; }) +# define USE_MMAP_ANON +# endif +# ifdef OS2 +# define OS_TYPE "OS2" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. OS2 actually has the right */ + /* system call! */ +# define DATAEND /* not needed */ +# endif +# ifdef MSWIN32 +# define WOW64_THREAD_CONTEXT_WORKAROUND +# define RETRY_GET_THREAD_CONTEXT +# define MPROTECT_VDB +# endif +# ifdef MSWINCE + /* Nothing specific. */ +# endif +# ifdef DJGPP +# define OS_TYPE "DJGPP" + EXTERN_C_END +# include "stubinfo.h" + EXTERN_C_BEGIN + extern int etext[]; + extern int _stklen; + extern int __djgpp_stack_limit; +# define DATASTART ((ptr_t)((((word)(etext)) + 0x1ff) & ~0x1ff)) +/* #define STACKBOTTOM ((ptr_t)((word)_stubinfo+_stubinfo->size+_stklen)) */ +# define STACKBOTTOM ((ptr_t)((word)__djgpp_stack_limit + _stklen)) + /* This may not be right. */ +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef FREEBSD +# ifdef __GLIBC__ + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef THREE86BSD +# define OS_TYPE "THREE86BSD" +# define HEURISTIC2 + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# ifdef BSDI +# define OS_TYPE "BSDI" +# define HEURISTIC2 + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# ifdef NEXT +# define STACKBOTTOM ((ptr_t)0xc0000000) +# endif +# ifdef RTEMS +# define OS_TYPE "RTEMS" + EXTERN_C_END +# include + EXTERN_C_BEGIN + extern int etext[]; + void *rtems_get_stack_bottom(void); +# define InitStackBottom rtems_get_stack_bottom() +# define DATASTART ((ptr_t)etext) +# define STACKBOTTOM ((ptr_t)InitStackBottom) +# endif +# ifdef DOS4GW +# define OS_TYPE "DOS4GW" + extern long __nullarea; + extern char _end; + extern char *_STACKTOP; + /* Depending on calling conventions Watcom C either precedes */ + /* or does not precedes with underscore names of C-variables. */ + /* Make sure startup code variables always have the same names. */ + #pragma aux __nullarea "*"; + #pragma aux _end "*"; +# define STACKBOTTOM ((ptr_t)_STACKTOP) + /* confused? me too. */ +# define DATASTART ((ptr_t)(&__nullarea)) +# define DATAEND ((ptr_t)(&_end)) +# endif +# ifdef HURD +# define OS_TYPE "HURD" +# define STACK_GROWS_DOWN +# define HEURISTIC2 +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +/* # define MPROTECT_VDB Not quite working yet? */ +# define DYNAMIC_LOADING +# define USE_MMAP_ANON +# endif +# ifdef DARWIN +# define DARWIN_DONT_PARSE_STACK 1 +# define STACKBOTTOM ((ptr_t)0xc0000000) +# define MPROTECT_VDB +# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) + /* iPhone/iPad simulator */ +# define NO_DYLD_BIND_FULLY_IMAGE +# endif +# endif /* DARWIN */ +# endif + +# ifdef NS32K +# define MACH_TYPE "NS32K" +# define ALIGNMENT 4 + extern char **environ; +# define DATASTART ((ptr_t)(&environ)) + /* hideous kludge: environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ +# define STACKBOTTOM ((ptr_t)0xfffff000) /* for Encore */ +# endif + +# ifdef LOONGARCH +# define MACH_TYPE "LoongArch" +# ifdef LINUX +# pragma weak __data_start + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# define CPP_WORDSZ _LOONGARCH_SZPTR +# define ALIGNMENT (_LOONGARCH_SZPTR/8) +# endif +# endif /* LOONGARCH */ + +# ifdef MIPS +# define MACH_TYPE "MIPS" +# ifdef LINUX +# pragma weak __data_start + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# else +# define ALIGNMENT 4 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 || __GLIBC__ > 2 +# define LINUX_STACKBOTTOM +# else +# define STACKBOTTOM ((ptr_t)0x7fff8000) +# endif +# endif /* Linux */ +# ifdef EWS4800 +# define OS_TYPE "EWS4800" +# define HEURISTIC2 +# if defined(_MIPS_SZPTR) && (_MIPS_SZPTR == 64) + extern int _fdata[], _end[]; +# define DATASTART ((ptr_t)_fdata) +# define DATAEND ((ptr_t)_end) +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# else + extern int etext[], edata[]; +# if !defined(CPPCHECK) + extern int end[]; +# endif + extern int _DYNAMIC_LINKING[], _gp[]; +# define DATASTART ((ptr_t)((((word)(etext) + 0x3ffff) & ~0x3ffff) \ + + ((word)(etext) & 0xffff))) +# define DATAEND ((ptr_t)(edata)) +# define GC_HAVE_DATAREGION2 +# define DATASTART2 (_DYNAMIC_LINKING \ + ? (ptr_t)(((word)_gp + 0x8000 + 0x3ffff) & ~0x3ffff) \ + : (ptr_t)edata) +# define DATAEND2 ((ptr_t)(end)) +# define ALIGNMENT 4 +# endif +# endif +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define HEURISTIC2 +# define DATASTART ((ptr_t)0x10000000) + /* Could probably be slightly higher since */ + /* startup code allocates lots of stuff. */ +# define ALIGNMENT 4 +# endif +# ifdef IRIX5 +# define OS_TYPE "IRIX5" +# define HEURISTIC2 + extern int _fdata[]; +# define DATASTART ((ptr_t)(_fdata)) +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x30000000 +# else +# define HEAP_START DATASTART +# endif + /* Lowest plausible heap address. */ + /* In the MMAP case, we map there. */ + /* In either case it is used to identify */ + /* heap sections so they're not */ + /* considered as roots. */ +/*# define MPROTECT_VDB DOB: this should work, but there is evidence */ +/* of recent breakage. */ +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# else +# define ALIGNMENT 4 +# endif +# define DYNAMIC_LOADING +# endif +# ifdef MSWINCE +# define ALIGNMENT 4 +# endif +# ifdef NETBSD +# define ALIGNMENT 4 +# ifndef __ELF__ +# define DATASTART ((ptr_t)0x10000000) +# define STACKBOTTOM ((ptr_t)0x7ffff000) +# endif +# endif +# ifdef OPENBSD +# define CPP_WORDSZ 64 /* all OpenBSD/mips platforms are 64-bit */ +# define ALIGNMENT 8 +# endif +# ifdef FREEBSD +# define ALIGNMENT 4 +# endif +# ifdef NONSTOP +# define OS_TYPE "NONSTOP" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define DATASTART ((ptr_t)0x08000000) + extern char **environ; +# define DATAEND ((ptr_t)(environ - 0x10)) +# define STACKBOTTOM ((ptr_t)0x4fffffff) +# endif +# endif + +# ifdef NIOS2 +# define CPP_WORDSZ 32 +# define MACH_TYPE "NIOS2" +# ifdef LINUX + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# define ALIGNMENT 4 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# endif +# endif /* NIOS2 */ + +# ifdef OR1K +# define CPP_WORDSZ 32 +# define MACH_TYPE "OR1K" +# ifdef LINUX + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# define ALIGNMENT 4 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# endif +# endif /* OR1K */ + +# ifdef HP_PA +# define MACH_TYPE "HP_PA" +# ifdef __LP64__ +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# define STACK_GROWS_UP +# ifdef HPUX +# ifndef GC_THREADS +# define MPROTECT_VDB +# endif +# 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 */ + /* 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 */ + /* combinations. (Thanks to Raymond X.T. Nijssen for uncovering */ + /* this.) */ + /* This technique also doesn't work with HP/UX 11.xx. The */ + /* stack size is settable using the kernel maxssiz variable, */ + /* and in 11.23 and latter, the size can be set dynamically. */ + /* It also doesn't handle SHMEM_MAGIC binaries which have */ + /* stack and data in the first quadrant. */ +# define STACKBOTTOM ((ptr_t)0x7b033000) /* from /etc/conf/h/param.h */ +# elif defined(USE_ENVIRON_POINTER) + /* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2 */ + /* to this. Note that the GC must be initialized before the */ + /* first putenv call. Unfortunately, some clients do not obey. */ + extern char ** environ; +# define STACKBOTTOM ((ptr_t)environ) +# elif !defined(HEURISTIC2) + /* This uses pst_vm_status support. */ +# define HPUX_MAIN_STACKBOTTOM +# define NEED_FIND_LIMIT +# endif +# ifndef __GNUC__ +# define PREFETCH(x) do { \ + register long addr = (long)(x); \ + (void) _asm ("LDW", 0, 0, addr, 0); \ + } while (0) +# endif +# endif /* HPUX */ +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# endif /* HP_PA */ + +# ifdef ALPHA +# define MACH_TYPE "ALPHA" +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifdef NETBSD +# define ELFCLASS32 32 +# define ELFCLASS64 64 +# define ELF_CLASS ELFCLASS64 +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef FREEBSD + /* MPROTECT_VDB is not yet supported at all on FreeBSD/alpha. */ +/* Handle unmapped hole alpha*-*-freebsd[45]* puts between etext and edata. */ + extern char etext[]; + extern char edata[]; +# if !defined(CPPCHECK) + extern char end[]; +# endif +# define NEED_FIND_LIMIT +# define DATASTART ((ptr_t)(&etext)) + void * GC_find_limit(void *, int); +# define DATAEND (ptr_t)GC_find_limit(DATASTART, TRUE) +# define DATAEND_IS_FUNC +# define GC_HAVE_DATAREGION2 +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) +# endif +# ifdef OSF1 +# define OS_TYPE "OSF1" +# define DATASTART ((ptr_t)0x140000000) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) + extern char ** environ; + EXTERN_C_END +# include + EXTERN_C_BEGIN + /* round up from the value of environ to the nearest page boundary */ + /* Probably breaks if putenv is called before collector */ + /* initialization. */ +# define STACKBOTTOM ((ptr_t)(((word)(environ) | (getpagesize()-1))+1)) +/* # define HEURISTIC2 */ + /* Normally HEURISTIC2 is too conservative, since */ + /* the text segment immediately follows the stack. */ + /* Hence we give an upper pound. */ + /* This is currently unused, since we disabled HEURISTIC2 */ + extern int __start[]; +# define HEURISTIC2_LIMIT ((ptr_t)((word)(__start) & ~(getpagesize()-1))) +# ifndef GC_OSF1_THREADS + /* Unresolved signal issues with threads. */ +# define MPROTECT_VDB +# endif +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# ifdef __ELF__ +# define SEARCH_FOR_DATA_START +# else +# define DATASTART ((ptr_t)0x140000000) + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB + /* Has only been superficially tested. May not */ + /* work on all versions. */ +# endif +# endif +# endif /* ALPHA */ + +# ifdef IA64 +# define MACH_TYPE "IA64" +# ifdef HPUX +# ifdef _ILP32 +# define CPP_WORDSZ 32 + /* Requires 8 byte alignment for malloc */ +# define ALIGNMENT 4 +# else +# if !defined(_LP64) && !defined(CPPCHECK) +# error Unknown ABI +# endif +# define CPP_WORDSZ 64 + /* Requires 16 byte alignment for malloc */ +# define ALIGNMENT 8 +# endif + /* Note that the GC must be initialized before the 1st putenv call. */ + extern char ** environ; +# define STACKBOTTOM ((ptr_t)environ) +# define HPUX_STACKBOTTOM + /* The following was empirically determined, and is probably */ + /* not very robust. */ + /* Note that the backing store base seems to be at a nice */ + /* address minus one page. */ +# define BACKING_STORE_DISPLACEMENT 0x1000000 +# define BACKING_STORE_ALIGNMENT 0x1000 + extern ptr_t GC_register_stackbottom; +# define BACKING_STORE_BASE GC_register_stackbottom + /* Known to be wrong for recent HP/UX versions!!! */ +# endif +# ifdef LINUX +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 + /* The following works on NUE and older kernels: */ + /* define STACKBOTTOM ((ptr_t)0xa000000000000000l) */ + /* TODO: LINUX_STACKBOTTOM does not work on NUE. */ + /* We also need the base address of the register stack */ + /* backing store. */ + extern ptr_t GC_register_stackbottom; +# define BACKING_STORE_BASE GC_register_stackbottom +# define SEARCH_FOR_DATA_START +# ifdef __GNUC__ +# define DYNAMIC_LOADING +# else + /* In the Intel compiler environment, we seem to end up with */ + /* statically linked executables and an undefined reference */ + /* to _DYNAMIC */ +# endif +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB + /* Requires Linux 2.3.47 or later. */ +# endif +# ifdef __GNUC__ +# ifndef __INTEL_COMPILER +# define PREFETCH(x) \ + __asm__ (" lfetch [%0]": : "r"(x)) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ (" lfetch.excl [%0]": : "r"(x)) +# define CLEAR_DOUBLE(x) \ + __asm__ (" stf.spill [%0]=f0": : "r"((void *)(x))) +# else + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define PREFETCH(x) __lfetch(__lfhint_none, (x)) +# define GC_PREFETCH_FOR_WRITE(x) __lfetch(__lfhint_nta, (x)) +# define CLEAR_DOUBLE(x) __stf_spill((void *)(x), 0) +# endif /* __INTEL_COMPILER */ +# endif +# endif +# ifdef MSWIN32 + /* FIXME: This is a very partial guess. There is no port, yet. */ +# if defined(_WIN64) +# define CPP_WORDSZ 64 +# else +# define CPP_WORDSZ 32 /* Is this possible? */ +# endif +# define ALIGNMENT 8 +# endif +# endif + +# ifdef E2K +# define MACH_TYPE "E2K" +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifdef LINUX + extern int __dso_handle[]; +# define DATASTART ((ptr_t)__dso_handle) +# endif +# endif /* E2K */ + +# ifdef M88K +# define MACH_TYPE "M88K" +# define ALIGNMENT 4 +# define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */ + extern int etext[]; +# ifdef CX_UX +# define OS_TYPE "CX_UX" +# define DATASTART ((ptr_t)((((word)(etext) + 0x3fffff) & ~0x3fffff) \ + + 0x10000)) +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)etext) +# define DATASTART_IS_FUNC +# endif +# endif + +# ifdef S370 + /* If this still works, and if anyone cares, this should probably */ + /* be moved to the S390 category. */ +# define MACH_TYPE "S370" +# define ALIGNMENT 4 /* Required by hardware */ +# ifdef UTS4 +# define OS_TYPE "UTS4" + extern int etext[]; + extern int _etext[]; + extern int _end[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(_end)) +# define HEURISTIC2 +# endif +# endif + +# ifdef S390 +# define MACH_TYPE "S390" +# ifndef __s390x__ +# define ALIGNMENT 4 +# define CPP_WORDSZ 32 +# else +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# endif +# ifdef LINUX + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)(__data_start)) + extern int _end[] __attribute__((__weak__)); +# define DATAEND ((ptr_t)(_end)) +# define CACHE_LINE_SIZE 256 +# define GETPAGESIZE() 4096 +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# endif +# ifndef SOFT_VDB +# define SOFT_VDB +# endif +# endif +# endif /* S390 */ + +# ifdef AARCH64 +# define MACH_TYPE "AARCH64" +# ifdef __ILP32__ +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# else +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifdef LINUX +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# endif +# if defined(HOST_ANDROID) +# define SEARCH_FOR_DATA_START +# else + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)__data_start) +# endif +# endif +# ifdef DARWIN + /* iOS */ +# define DARWIN_DONT_PARSE_STACK 1 +# define STACKBOTTOM ((ptr_t)0x16fdfffff) + /* MPROTECT_VDB causes use of non-public API like exc_server, */ + /* this could be a reason for blocking the client application in */ + /* the store. */ +# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) +# define NO_DYLD_BIND_FULLY_IMAGE +# endif +# endif +# ifdef FREEBSD + /* Nothing specific. */ +# endif +# ifdef NETBSD +# define ELF_CLASS ELFCLASS64 +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef NINTENDO_SWITCH +# define OS_TYPE "NINTENDO_SWITCH" + extern int __bss_end[]; +# define NO_HANDLE_FORK 1 +# define DATASTART (ptr_t)ALIGNMENT /* cannot be null */ +# define DATAEND (ptr_t)(&__bss_end) + void *switch_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)switch_get_stack_bottom()) +# endif +# ifdef MSWIN32 /* UWP */ + /* TODO: Enable MPROTECT_VDB */ +# endif +# ifdef NOSYS +# define OS_TYPE "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 +# if defined(NACL) +# define MACH_TYPE "NACL" +# else +# define MACH_TYPE "ARM32" +# endif +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef LINUX +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# endif +# if defined(__GLIBC__) && __GLIBC__ >= 2 \ + || defined(HOST_ANDROID) || defined(HOST_TIZEN) +# define SEARCH_FOR_DATA_START +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif +# endif +# ifdef MSWINCE + /* Nothing specific. */ +# endif +# ifdef FREEBSD + /* Nothing specific. */ +# endif +# ifdef DARWIN + /* iOS */ +# define DARWIN_DONT_PARSE_STACK 1 +# define STACKBOTTOM ((ptr_t)0x30000000) + /* MPROTECT_VDB causes use of non-public API. */ +# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) +# define NO_DYLD_BIND_FULLY_IMAGE +# endif +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef SN_TARGET_PSP2 +# define OS_TYPE "SN_TARGET_PSP2" +# define NO_HANDLE_FORK 1 +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + void *psp2_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)psp2_get_stack_bottom()) +# endif +# ifdef NN_PLATFORM_CTR +# define OS_TYPE "NN_PLATFORM_CTR" + extern unsigned char Image$$ZI$$ZI$$Base[]; +# define DATASTART (ptr_t)(Image$$ZI$$ZI$$Base) + extern unsigned char Image$$ZI$$ZI$$Limit[]; +# define DATAEND (ptr_t)(Image$$ZI$$ZI$$Limit) + void *n3ds_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)n3ds_get_stack_bottom()) +# endif +# ifdef MSWIN32 /* UWP */ + /* TODO: Enable MPROTECT_VDB */ +# endif +# ifdef NOSYS +# define OS_TYPE "NOSYS" + /* __data_start is usually defined in the target linker script. */ + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) + /* __stack_base__ is set in newlib/libc/sys/arm/crt0.S */ + extern void *__stack_base__; +# define STACKBOTTOM ((ptr_t)(__stack_base__)) +# endif +#endif + +# ifdef CRIS +# define MACH_TYPE "CRIS" +# define CPP_WORDSZ 32 +# define ALIGNMENT 1 +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# endif /* CRIS */ + +# if defined(SH) && !defined(SH4) +# define MACH_TYPE "SH" +# define ALIGNMENT 4 +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef MSWINCE + /* Nothing specific. */ +# endif +# endif + +# ifdef SH4 +# define MACH_TYPE "SH4" +# define ALIGNMENT 4 +# ifdef MSWINCE + /* Nothing specific. */ +# endif +# endif + +# ifdef AVR32 +# define MACH_TYPE "AVR32" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# endif /* AVR32 */ + +# ifdef M32R +# define CPP_WORDSZ 32 +# define MACH_TYPE "M32R" +# define ALIGNMENT 4 +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# endif /* M32R */ + +# ifdef X86_64 +# define MACH_TYPE "X86_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 +# ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 64 +# endif +# ifdef PLATFORM_GETMEM +# define OS_TYPE "PLATFORM_GETMEM" +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + EXTERN_C_END +# include + EXTERN_C_BEGIN + void *platform_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)platform_get_stack_bottom()) +# endif +# ifdef LINUX +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in the incremental mode, */ + /* possibly because the Linux threads implementation */ + /* itself is a malloc client and cannot deal with the */ + /* signals. fread() uses malloc too. */ +# endif +# define SEARCH_FOR_DATA_START +# if defined(__GLIBC__) && !defined(__UCLIBC__) + /* A workaround for GCF (Google Cloud Function) which does */ + /* not support mmap() for "/dev/zero". Should not cause any */ + /* harm to other targets. */ +# define USE_MMAP_ANON +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) \ + && !defined(GETCONTEXT_FPU_BUG_FIXED) + /* At present, there's a bug in GLibc getcontext() on */ + /* Linux/x64 (it clears FPU exception mask). We define this */ + /* macro to workaround it. */ + /* TODO: This seems to be fixed in GLibc v2.14. */ +# define GETCONTEXT_FPU_EXCMASK_BUG +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) \ + && !defined(GLIBC_TSX_BUG_FIXED) + /* Workaround lock elision implementation for some glibc. */ +# define GLIBC_2_19_TSX_BUG + EXTERN_C_END +# include /* for gnu_get_libc_version() */ + EXTERN_C_BEGIN +# endif +# ifndef SOFT_VDB +# define SOFT_VDB +# endif +# endif +# ifdef DARWIN +# define DARWIN_DONT_PARSE_STACK 1 +# define STACKBOTTOM ((ptr_t)0x7fff5fc00000) +# define MPROTECT_VDB +# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) + /* iPhone/iPad simulator */ +# define NO_DYLD_BIND_FULLY_IMAGE +# endif +# endif +# ifdef FREEBSD +# ifdef __GLIBC__ + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# if defined(__DragonFly__) + /* DragonFly BSD still has vm.max_proc_mmap, according to */ + /* its mmap(2) man page. */ +# define COUNT_UNMAPPED_REGIONS +# endif +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef HAIKU +# define HEURISTIC2 +# define SEARCH_FOR_DATA_START +# endif +# ifdef SOLARIS +# define ELF_CLASS ELFCLASS64 +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext) +# ifdef SOLARIS25_PROC_VDB_BUG_FIXED +# define PROC_VDB +# endif +# endif +# ifdef HURD +# define OS_TYPE "HURD" +# define HEURISTIC2 +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# define DYNAMIC_LOADING +# define USE_MMAP_ANON +# endif +# ifdef CYGWIN32 +# ifndef USE_WINALLOC +# if defined(THREAD_LOCAL_ALLOC) + /* TODO: For an unknown reason, thread-local allocations */ + /* lead to spurious process exit after the fault handler is */ + /* once invoked. */ +# else +# define MPROTECT_VDB +# endif +# endif +# endif +# ifdef MSWIN_XBOX1 +# define OS_TYPE "MSWIN_XBOX1" +# define NO_GETENV +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + LONG64 durango_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)durango_get_stack_bottom()) +# define GETPAGESIZE() 4096 +# ifndef USE_MMAP +# define USE_MMAP 1 +# endif + /* The following is from sys/mman.h: */ +# define PROT_NONE 0 +# define PROT_READ 1 +# define PROT_WRITE 2 +# define PROT_EXEC 4 +# define MAP_PRIVATE 2 +# define MAP_FIXED 0x10 +# define MAP_FAILED ((void *)-1) +# endif +# ifdef MSWIN32 +# define RETRY_GET_THREAD_CONTEXT +# if !defined(__GNUC__) || defined(__INTEL_COMPILER) \ + || (GC_GNUC_PREREQ(4, 7) && !defined(__MINGW64__)) + /* Older GCC and Mingw-w64 (both GCC and Clang) do not */ + /* support SetUnhandledExceptionFilter() properly on x64. */ +# define MPROTECT_VDB +# endif +# endif +# endif /* X86_64 */ + +# ifdef ARC +# define CPP_WORDSZ 32 +# define MACH_TYPE "ARC" +# define ALIGNMENT 4 +# define CACHE_LINE_SIZE 64 +# ifdef LINUX + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)__data_start) +# endif +# endif /* ARC */ + +# ifdef HEXAGON +# define CPP_WORDSZ 32 +# define MACH_TYPE "HEXAGON" +# define ALIGNMENT 4 +# ifdef LINUX +# if !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# endif +# if defined(__GLIBC__) +# define SEARCH_FOR_DATA_START +# elif !defined(CPPCHECK) +# error Unknown Hexagon libc configuration +# endif +# endif +# endif /* HEXAGON */ + +# ifdef TILEPRO +# define CPP_WORDSZ 32 +# define MACH_TYPE "TILEPro" +# define ALIGNMENT 4 +# define PREFETCH(x) __insn_prefetch(x) +# define CACHE_LINE_SIZE 64 +# ifdef LINUX + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) +# endif +# endif /* TILEPRO */ + +# ifdef TILEGX +# define CPP_WORDSZ (__SIZEOF_POINTER__ * 8) +# define MACH_TYPE "TILE-Gx" +# define ALIGNMENT __SIZEOF_POINTER__ +# if CPP_WORDSZ < 64 +# define CLEAR_DOUBLE(x) (*(long long *)(x) = 0) +# endif +# define PREFETCH(x) __insn_prefetch_l1(x) +# define CACHE_LINE_SIZE 64 +# ifdef LINUX + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) +# endif +# endif /* TILEGX */ + +# ifdef RISCV +# define MACH_TYPE "RISC-V" +# define CPP_WORDSZ __riscv_xlen /* 32 or 64 */ +# define ALIGNMENT (CPP_WORDSZ/8) +# ifdef FREEBSD + /* Nothing specific. */ +# endif +# ifdef LINUX + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)__data_start) +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# endif /* RISCV */ + +#if defined(__GLIBC__) && !defined(DONT_USE_LIBC_PRIVATES) + /* Use glibc's stack-end marker. */ +# define USE_LIBC_PRIVATES +#endif + +#ifdef NO_RETRY_GET_THREAD_CONTEXT +# undef RETRY_GET_THREAD_CONTEXT +#endif + +#if defined(LINUX_STACKBOTTOM) && defined(NO_PROC_STAT) \ + && !defined(USE_LIBC_PRIVATES) + /* This combination will fail, since we have no way to get */ + /* the stack bottom. Use HEURISTIC2 instead. */ +# undef LINUX_STACKBOTTOM +# define HEURISTIC2 + /* This may still fail on some architectures like IA64. */ + /* We tried ... */ +#endif + +#if defined(USE_MMAP_ANON) && !defined(USE_MMAP) +# define USE_MMAP 1 +#elif (defined(LINUX) || defined(OPENBSD)) && defined(USE_MMAP) + /* The kernel may do a somewhat better job merging mappings etc. */ + /* with anonymous mappings. */ +# define USE_MMAP_ANON +#endif + +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && !defined(USE_PROC_FOR_LIBRARIES) + /* Nptl allocates thread stacks with mmap, which is fine. But it */ + /* keeps a cache of thread stacks. Thread stacks contain the */ + /* thread control blocks. These in turn contain a pointer to */ + /* (sizeof (void *) from the beginning of) the dtv for thread-local */ + /* storage, which is calloc allocated. If we don't scan the cached */ + /* thread stacks, we appear to lose the dtv. This tends to */ + /* result in something that looks like a bogus dtv count, which */ + /* tends to result in a memset call on a block that is way too */ + /* large. Sometimes we're lucky and the process just dies ... */ + /* There seems to be a similar issue with some other memory */ + /* allocated by the dynamic loader. */ + /* This should be avoidable by either: */ + /* - Defining USE_PROC_FOR_LIBRARIES here. */ + /* That performs very poorly, precisely because we end up */ + /* scanning cached stacks. */ + /* - Have calloc look at its callers. */ + /* In spite of the fact that it is gross and disgusting. */ + /* In fact neither seems to suffice, probably in part because */ + /* even with USE_PROC_FOR_LIBRARIES, we don't scan parts of stack */ + /* segments that appear to be out of bounds. Thus we actually */ + /* do both, which seems to yield the best results. */ +# define USE_PROC_FOR_LIBRARIES +#endif + +#ifndef STACK_GROWS_UP +# define STACK_GROWS_DOWN +#endif + +#ifndef CPP_WORDSZ +# define CPP_WORDSZ 32 +#endif + +#ifndef OS_TYPE +# define OS_TYPE "" +#endif + +#ifndef DATAEND +# if !defined(CPPCHECK) + extern int end[]; +# endif +# define DATAEND ((ptr_t)(end)) +#endif + +/* Workaround for Android NDK clang 3.5+ (as of NDK r10e) which does */ +/* not provide correct _end symbol. Unfortunately, alternate __end__ */ +/* symbol is provided only by NDK "bfd" linker. */ +#if defined(HOST_ANDROID) && defined(__clang__) \ + && !defined(BROKEN_UUENDUU_SYM) +# undef DATAEND +# pragma weak __end__ + extern int __end__[]; +# define DATAEND (__end__ != 0 ? (ptr_t)__end__ : (ptr_t)_end) +#endif + +#if (defined(SVR4) || defined(HOST_ANDROID) || defined(HOST_TIZEN)) \ + && !defined(GETPAGESIZE) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGESIZE) +#endif + +#ifndef GETPAGESIZE +# if defined(AIX) || defined(IRIX5) || defined(LINUX) || defined(SOLARIS) \ + || defined(NETBSD) || defined(FREEBSD) || defined(HPUX) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif +# define GETPAGESIZE() (unsigned)getpagesize() +#endif + +#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ + && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ + || defined(ARM32) || defined(I386) /* but not x32 */) + /* tkill() exists only on arm32/mips(32)/x86. */ + /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ +# define USE_TKILL_ON_ANDROID +#endif + +#if defined(SOLARIS) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. */ + /* Probably others also qualify. */ +# define SVR4 +#endif + +#if defined(MPROTECT_VDB) && defined(__GLIBC__) \ + && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)) +# error glibc too old? +#endif + +#if defined(SOLARIS) || defined(DRSNX) + /* OS has SOLARIS style semi-undocumented interface */ + /* to dynamic loader. */ +# define SOLARISDL + /* OS has SOLARIS style signal handlers. */ +# define SUNOS5SIGS +#endif + +#if defined(HPUX) +# define SUNOS5SIGS +#endif + +#if defined(FREEBSD) && (defined(__DragonFly__) || __FreeBSD__ >= 4 \ + || __FreeBSD_kernel__ >= 4 || defined(__GLIBC__)) +# define SUNOS5SIGS +#endif + +#if !defined(GC_EXPLICIT_SIGNALS_UNBLOCK) && defined(SUNOS5SIGS) \ + && !defined(GC_NO_PTHREAD_SIGMASK) +# define GC_EXPLICIT_SIGNALS_UNBLOCK +#endif + +#if !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) && defined(GC_NO_PTHREAD_SIGMASK) +# define NO_SIGNALS_UNBLOCK_IN_MAIN +#endif + +#if !defined(NO_MARKER_SPECIAL_SIGMASK) \ + && (defined(NACL) || defined(GC_WIN32_PTHREADS)) + /* Either there is no pthread_sigmask(), or GC marker thread cannot */ + /* steal and drop user signal calls. */ +# define NO_MARKER_SPECIAL_SIGMASK +#endif + +#ifdef GC_NETBSD_THREADS +# define SIGRTMIN 33 +# define SIGRTMAX 63 + /* It seems to be necessary to wait until threads have restarted. */ + /* But it is unclear why that is the case. */ +# define GC_NETBSD_THREADS_WORKAROUND +#endif + +#ifdef GC_OPENBSD_THREADS + EXTERN_C_END +# include + EXTERN_C_BEGIN + /* 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(HAIKU) || defined(HURD) \ + || defined(AIX) || defined(DARWIN) || defined(OSF1) +# define UNIX_LIKE /* Basic Unix-like system calls work. */ +#endif + +#if defined(CPPCHECK) +# undef CPP_WORDSZ +# define CPP_WORDSZ (__SIZEOF_POINTER__ * 8) +#elif CPP_WORDSZ != 32 && CPP_WORDSZ != 64 +# error Bad word size +#endif + +#if !defined(ALIGNMENT) && !defined(CPPCHECK) +# error Undefined ALIGNMENT +#endif + +#ifdef PCR +# undef DYNAMIC_LOADING +# undef STACKBOTTOM +# undef HEURISTIC1 +# undef HEURISTIC2 +# undef PROC_VDB +# undef MPROTECT_VDB +# define PCR_VDB +#endif + +#if !defined(STACKBOTTOM) && (defined(ECOS) || defined(NOSYS)) \ + && !defined(CPPCHECK) +# error Undefined STACKBOTTOM +#endif + +#ifdef IGNORE_DYNAMIC_LOADING +# undef DYNAMIC_LOADING +#endif + +#if defined(SMALL_CONFIG) && !defined(GC_DISABLE_INCREMENTAL) + /* Presumably not worth the space it takes. */ +# define GC_DISABLE_INCREMENTAL +#endif + +#if (defined(MSWIN32) || defined(MSWINCE)) && !defined(USE_WINALLOC) + /* USE_WINALLOC is only an option for Cygwin. */ +# define USE_WINALLOC 1 +#endif + +#ifdef USE_WINALLOC +# undef USE_MMAP +#endif + +#if defined(DARWIN) || defined(FREEBSD) || defined(HAIKU) \ + || defined(IRIX5) || defined(LINUX) || defined(NETBSD) \ + || defined(OPENBSD) || defined(SOLARIS) \ + || ((defined(CYGWIN32) || defined(USE_MMAP) || defined(USE_MUNMAP)) \ + && !defined(USE_WINALLOC)) + /* Try both sbrk and mmap, in that order. */ +# define MMAP_SUPPORTED +#endif + +/* Xbox One (DURANGO) may not need to be this aggressive, but the */ +/* default is likely too lax under heavy allocation pressure. */ +/* The platform does not have a virtual paging system, so it does not */ +/* have a large virtual address space that a standard x64 platform has. */ +#if defined(USE_MUNMAP) && !defined(MUNMAP_THRESHOLD) \ + && (defined(SN_TARGET_PS3) \ + || defined(SN_TARGET_PSP2) || defined(MSWIN_XBOX1)) +# define MUNMAP_THRESHOLD 2 +#endif + +#if defined(USE_MUNMAP) && defined(COUNT_UNMAPPED_REGIONS) \ + && !defined(GC_UNMAPPED_REGIONS_SOFT_LIMIT) + /* The default limit of vm.max_map_count on Linux is ~65530. */ + /* There is approximately one mapped region to every unmapped region. */ + /* Therefore if we aim to use up to half of vm.max_map_count for the */ + /* GC (leaving half for the rest of the process) then the number of */ + /* unmapped regions should be one quarter of vm.max_map_count. */ +# if defined(__DragonFly__) +# define GC_UNMAPPED_REGIONS_SOFT_LIMIT (1000000 / 4) +# else +# define GC_UNMAPPED_REGIONS_SOFT_LIMIT 16384 +# endif +#endif + +#if defined(GC_DISABLE_INCREMENTAL) || defined(DEFAULT_VDB) +# undef GWW_VDB +# undef MPROTECT_VDB +# undef PCR_VDB +# undef PROC_VDB +# undef SOFT_VDB +#endif + +#ifdef NO_GWW_VDB +# undef GWW_VDB +#endif + +#ifdef NO_MPROTECT_VDB +# undef MPROTECT_VDB +#endif + +#ifdef NO_SOFT_VDB +# undef SOFT_VDB +#endif + +#if defined(SOFT_VDB) && defined(SOFT_VDB_LINUX_VER_STATIC_CHECK) + EXTERN_C_END +# include /* for LINUX_VERSION[_CODE] */ + EXTERN_C_BEGIN +# if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + /* Not reliable in kernels prior to v3.18. */ +# undef SOFT_VDB +# endif +#endif /* SOFT_VDB */ + +#ifdef GC_DISABLE_INCREMENTAL +# undef CHECKSUMS +#endif + +#ifdef USE_GLOBAL_ALLOC + /* Cannot pass MEM_WRITE_WATCH to GlobalAlloc(). */ +# undef GWW_VDB +#endif + +#if defined(BASE_ATOMIC_OPS_EMULATED) + /* GC_write_fault_handler() cannot use lock-based atomic primitives */ + /* as this could lead to a deadlock. */ +# undef MPROTECT_VDB +#endif + +#if defined(USE_PROC_FOR_LIBRARIES) && defined(GC_LINUX_THREADS) + /* Incremental GC based on mprotect is incompatible with /proc roots. */ +# undef MPROTECT_VDB +#endif + +#if defined(MPROTECT_VDB) && defined(GC_PREFER_MPROTECT_VDB) + /* Choose MPROTECT_VDB manually (if multiple strategies available). */ +# undef PCR_VDB +# undef PROC_VDB + /* GWW_VDB, SOFT_VDB are handled in os_dep.c. */ +#endif + +#ifdef PROC_VDB + /* Mutually exclusive VDB implementations (for now). */ +# undef MPROTECT_VDB + /* For a test purpose only. */ +# undef SOFT_VDB +#endif + +#if defined(MPROTECT_VDB) && !defined(MSWIN32) && !defined(MSWINCE) +# include /* for SA_SIGINFO, SIGBUS */ +#endif + +#if defined(SIGBUS) && !defined(HAVE_SIGBUS) && !defined(CPPCHECK) +# define HAVE_SIGBUS +#endif + +#ifndef SA_SIGINFO +# define NO_SA_SIGACTION +#endif + +#if (defined(NO_SA_SIGACTION) || defined(GC_NO_SIGSETJMP)) \ + && defined(MPROTECT_VDB) && !defined(DARWIN) \ + && !defined(MSWIN32) && !defined(MSWINCE) +# undef MPROTECT_VDB +#endif + +#if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) \ + && !defined(GWW_VDB) && !defined(SOFT_VDB) && !defined(DEFAULT_VDB) \ + && !defined(GC_DISABLE_INCREMENTAL) +# define DEFAULT_VDB +#endif + +#if !defined(PROC_VDB) && !defined(SOFT_VDB) \ + && !defined(NO_VDB_FOR_STATIC_ROOTS) + /* Cannot determine whether a static root page is dirty? */ +# define NO_VDB_FOR_STATIC_ROOTS +#endif + +#if ((defined(UNIX_LIKE) && (defined(DARWIN) || defined(HAIKU) \ + || defined(HURD) || defined(OPENBSD) \ + || defined(ARM32) \ + || defined(AVR32) || defined(MIPS) \ + || defined(NIOS2) || defined(OR1K))) \ + || (defined(LINUX) && !defined(__gnu_linux__)) \ + || (defined(RTEMS) && defined(I386)) || defined(HOST_ANDROID)) \ + && !defined(NO_GETCONTEXT) +# define NO_GETCONTEXT 1 +#endif + +#ifndef PREFETCH +# if GC_GNUC_PREREQ(3, 0) && !defined(NO_PREFETCH) +# define PREFETCH(x) __builtin_prefetch((x), 0, 0) +# else +# define PREFETCH(x) (void)0 +# endif +#endif + +#ifndef GC_PREFETCH_FOR_WRITE +# if GC_GNUC_PREREQ(3, 0) && !defined(GC_NO_PREFETCH_FOR_WRITE) +# define GC_PREFETCH_FOR_WRITE(x) __builtin_prefetch((x), 1) +# else +# define GC_PREFETCH_FOR_WRITE(x) (void)0 +# endif +#endif + +#ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 32 /* Wild guess */ +#endif + +#ifndef STATIC +# ifdef GC_ASSERTIONS +# define STATIC /* ignore to aid debugging (or profiling) */ +# else +# define STATIC static +# endif +#endif + +#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) \ + || !defined(SMALL_CONFIG)) +# define NEED_PROC_MAPS +#endif + +#if defined(LINUX) || defined(HURD) || defined(__GLIBC__) +# define REGISTER_LIBRARIES_EARLY + /* We sometimes use dl_iterate_phdr, which may acquire an internal */ + /* lock. This isn't safe after the world has stopped. So we must */ + /* call GC_register_dynamic_libraries before stopping the world. */ + /* For performance reasons, this may be beneficial on other */ + /* platforms as well, though it should be avoided on Windows. */ +#endif /* LINUX */ + +#if defined(SEARCH_FOR_DATA_START) + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +#endif + +#ifndef HEAP_START +# define HEAP_START ((ptr_t)0) +#endif + +#ifndef CLEAR_DOUBLE +# define CLEAR_DOUBLE(x) (((word*)(x))[0] = 0, ((word*)(x))[1] = 0) +#endif + +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && !defined(INCLUDE_LINUX_THREAD_DESCR) + /* Will not work, since libc and the dynamic loader use thread */ + /* locals, sometimes as the only reference. */ +# define INCLUDE_LINUX_THREAD_DESCR +#endif + +#if !defined(CPPCHECK) +# if defined(GC_IRIX_THREADS) && !defined(IRIX5) +# error Inconsistent configuration +# endif +# if defined(GC_LINUX_THREADS) && !defined(LINUX) && !defined(NACL) +# error Inconsistent configuration +# endif +# if defined(GC_NETBSD_THREADS) && !defined(NETBSD) +# error Inconsistent configuration +# endif +# if defined(GC_FREEBSD_THREADS) && !defined(FREEBSD) +# error Inconsistent configuration +# endif +# if defined(GC_SOLARIS_THREADS) && !defined(SOLARIS) +# error Inconsistent configuration +# endif +# if defined(GC_HPUX_THREADS) && !defined(HPUX) +# error Inconsistent configuration +# endif +# if defined(GC_AIX_THREADS) && !defined(_AIX) +# error Inconsistent configuration +# endif +# if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) && !defined(MSWIN32) \ + && !defined(MSWINCE) && !defined(MSWIN_XBOX1) +# error Inconsistent configuration +# endif +# if defined(GC_WIN32_PTHREADS) && defined(CYGWIN32) +# error Inconsistent configuration +# endif +#endif /* !CPPCHECK */ + +#if defined(PCR) || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) \ + || ((defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(SN_TARGET_PS3) \ + || defined(SN_TARGET_PSP2)) && defined(GC_THREADS)) +# define THREADS +#endif + +#if defined(PARALLEL_MARK) && !defined(THREADS) && !defined(CPPCHECK) +# error Invalid config: PARALLEL_MARK requires GC_THREADS +#endif + +#if defined(GWW_VDB) && !defined(USE_WINALLOC) && !defined(CPPCHECK) +# error Invalid config: GWW_VDB requires USE_WINALLOC +#endif + +#if (defined(MSWIN32) || defined(MSWINCE) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS))) \ + && !defined(NO_CRT) && !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. */ + /* TODO: Should we also define it for Cygwin? */ +# define WRAP_MARK_SOME +#endif + +#if defined(PARALLEL_MARK) && !defined(DEFAULT_STACK_MAYBE_SMALL) \ + && (defined(HPUX) || defined(GC_DGUX386_THREADS) \ + || defined(NO_GETCONTEXT) /* e.g. musl */) + /* TODO: Test default stack size in configure. */ +# define DEFAULT_STACK_MAYBE_SMALL +#endif + +#ifdef PARALLEL_MARK + /* The minimum stack size for a marker thread. */ +# define MIN_STACK_SIZE (8 * HBLKSIZE * sizeof(word)) +#endif + +#if defined(HOST_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 + +/* Outline pthread primitives to use in GC_get_[main_]stack_base. */ +#if ((defined(FREEBSD) && defined(__GLIBC__)) /* kFreeBSD */ \ + || defined(LINUX) || defined(NETBSD) || defined(HOST_ANDROID)) \ + && !defined(NO_PTHREAD_GETATTR_NP) +# define HAVE_PTHREAD_GETATTR_NP 1 +#elif defined(FREEBSD) && !defined(__GLIBC__) \ + && !defined(NO_PTHREAD_ATTR_GET_NP) +# define HAVE_PTHREAD_NP_H 1 /* requires include pthread_np.h */ +# define HAVE_PTHREAD_ATTR_GET_NP 1 +#endif + +#if defined(GC_PTHREADS) && !defined(E2K) && !defined(IA64) \ + && (!defined(DARWIN) || defined(DARWIN_DONT_PARSE_STACK)) \ + && !defined(SN_TARGET_PSP2) && !defined(REDIRECT_MALLOC) + /* Note: unimplemented in case of redirection of malloc() because */ + /* the client-provided function might call some pthreads primitive */ + /* which, in turn, may use malloc() internally. */ +# define STACKPTR_CORRECTOR_AVAILABLE +#endif + +#if defined(UNIX_LIKE) && defined(THREADS) && !defined(NO_CANCEL_SAFE) \ + && !defined(HOST_ANDROID) + /* Make the code cancellation-safe. This basically means that we */ + /* ensure that cancellation requests are ignored while we are in */ + /* the collector. This applies only to Posix deferred cancellation; */ + /* we don't handle Posix asynchronous cancellation. */ + /* Note that this only works if pthread_setcancelstate is */ + /* async-signal-safe, at least in the absence of asynchronous */ + /* cancellation. This appears to be true for the glibc version, */ + /* though it is not documented. Without that assumption, there */ + /* seems to be no way to safely wait in a signal handler, which */ + /* we need to do for thread suspension. */ + /* Also note that little other code appears to be cancellation-safe. */ + /* Hence it may make sense to turn this off for performance. */ +# define CANCEL_SAFE +#endif + +#ifdef CANCEL_SAFE +# define IF_CANCEL(x) x +#else +# 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 + +/* Workaround "failed to create new win32 semaphore" Cygwin fatal error */ +/* during semaphores fixup-after-fork. */ +#if defined(CYGWIN32) && defined(GC_WIN32_THREADS) \ + && defined(CAN_HANDLE_FORK) && !defined(EMULATE_PTHREAD_SEMAPHORE) \ + && !defined(CYGWIN_SEM_FIXUP_AFTER_FORK_BUG_FIXED) +# define EMULATE_PTHREAD_SEMAPHORE +#endif + +#if defined(CAN_HANDLE_FORK) && !defined(CAN_CALL_ATFORK) \ + && !defined(GC_NO_CAN_CALL_ATFORK) && !defined(HOST_TIZEN) \ + && !defined(HURD) && (!defined(HOST_ANDROID) || __ANDROID_API__ >= 21) + /* Have working pthread_atfork(). */ +# define CAN_CALL_ATFORK +#endif + +#if !defined(CAN_HANDLE_FORK) && !defined(HAVE_NO_FORK) \ + && !(defined(CYGWIN32) || defined(SOLARIS) || defined(UNIX_LIKE)) +# define HAVE_NO_FORK +#endif + +#if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \ + && defined(PARALLEL_MARK) + /* Minimize compare-and-swap usage. */ +# define USE_MARK_BYTES +#endif + +#if (defined(MSWINCE) && !defined(__CEGCC__) || defined(MSWINRT_FLAVOR)) \ + && !defined(NO_GETENV) +# define NO_GETENV +#endif + +#if (defined(NO_GETENV) || defined(MSWINCE)) && !defined(NO_GETENV_WIN32) +# define NO_GETENV_WIN32 +#endif + +#if !defined(MSGBOX_ON_ERROR) && !defined(NO_MSGBOX_ON_ERROR) \ + && !defined(SMALL_CONFIG) && defined(MSWIN32) \ + && !defined(MSWINRT_FLAVOR) && !defined(MSWIN_XBOX1) + /* Show a Windows message box with "OK" button on a GC fatal error. */ + /* Client application is terminated once the user clicks the button. */ +# define MSGBOX_ON_ERROR +#endif + +#ifndef STRTOULL +# if defined(_WIN64) && !defined(__GNUC__) +# define STRTOULL _strtoui64 +# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) +# define STRTOULL strtoull +# else + /* strtoul() fits since sizeof(long) >= sizeof(word). */ +# define STRTOULL strtoul +# endif +#endif /* !STRTOULL */ + +#ifndef GC_WORD_C +# if defined(_WIN64) && !defined(__GNUC__) +# define GC_WORD_C(val) val##ui64 +# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) +# define GC_WORD_C(val) val##ULL +# else +# define GC_WORD_C(val) ((word)val##UL) +# endif +#endif /* !GC_WORD_C */ + +#if defined(__has_feature) + /* __has_feature() is supported. */ +# if __has_feature(address_sanitizer) && !defined(ADDRESS_SANITIZER) +# define ADDRESS_SANITIZER +# endif +# if __has_feature(memory_sanitizer) && !defined(MEMORY_SANITIZER) +# define MEMORY_SANITIZER +# endif +# if __has_feature(thread_sanitizer) && !defined(THREAD_SANITIZER) +# define THREAD_SANITIZER +# endif +#else +# ifdef __SANITIZE_ADDRESS__ + /* GCC v4.8+ */ +# define ADDRESS_SANITIZER +# endif +#endif /* !__has_feature */ + +#if defined(SPARC) +# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ + /* include assembly code to do it well. */ +#endif + +/* Can we save call chain in objects for debugging? */ +/* SET NFRAMES (# of saved frames) and NARGS (#of args for each */ +/* frame) to reasonable values for the platform. */ +/* Set SAVE_CALL_CHAIN if we can. SAVE_CALL_COUNT can be specified */ +/* at build time, though we feel free to adjust it slightly. */ +/* Define NEED_CALLINFO if we either save the call stack or */ +/* GC_ADD_CALLER is defined. */ +/* GC_CAN_SAVE_CALL_STACKS is set in gc.h. */ +#if defined(SPARC) +# define CAN_SAVE_CALL_ARGS +#endif +#if (defined(I386) || defined(X86_64)) \ + && (defined(LINUX) || defined(__GLIBC__)) + /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ + /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ +# define CAN_SAVE_CALL_ARGS +#endif + +#if defined(SAVE_CALL_COUNT) && !defined(GC_ADD_CALLER) \ + && defined(GC_CAN_SAVE_CALL_STACKS) +# define SAVE_CALL_CHAIN +#endif +#ifdef SAVE_CALL_CHAIN +# if defined(SAVE_CALL_NARGS) && defined(CAN_SAVE_CALL_ARGS) +# define NARGS SAVE_CALL_NARGS +# else +# define NARGS 0 /* Number of arguments to save for each call. */ +# endif +#endif +#ifdef SAVE_CALL_CHAIN +# if !defined(SAVE_CALL_COUNT) || defined(CPPCHECK) +# define NFRAMES 6 /* Number of frames to save. Even for */ + /* alignment reasons. */ +# else +# define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1) +# endif +# define NEED_CALLINFO +#endif /* SAVE_CALL_CHAIN */ +#ifdef GC_ADD_CALLER +# define NFRAMES 1 +# define NARGS 0 +# 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 1 +#endif + +#if defined(MAKE_BACK_GRAPH) && !defined(DBG_HDRS_ALL) +# define DBG_HDRS_ALL 1 +#endif + +#if defined(POINTER_MASK) && !defined(POINTER_SHIFT) +# define POINTER_SHIFT 0 +#endif + +#if defined(POINTER_SHIFT) && !defined(POINTER_MASK) +# define POINTER_MASK ((word)(signed_word)(-1)) +#endif + +#if !defined(FIXUP_POINTER) && defined(POINTER_MASK) +# define FIXUP_POINTER(p) (p = ((p) & POINTER_MASK) << POINTER_SHIFT) +#endif + +#if defined(FIXUP_POINTER) +# define NEED_FIXUP_POINTER +#else +# define FIXUP_POINTER(p) +#endif + +#if !defined(MARK_BIT_PER_GRANULE) && !defined(MARK_BIT_PER_OBJ) +# define MARK_BIT_PER_GRANULE /* Usually faster */ +#endif + +/* Some static sanity tests. */ +#if !defined(CPPCHECK) +# if defined(MARK_BIT_PER_GRANULE) && defined(MARK_BIT_PER_OBJ) +# error Define only one of MARK_BIT_PER_GRANULE and MARK_BIT_PER_OBJ +# endif +# if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN) +# error Only one of STACK_GROWS_UP and STACK_GROWS_DOWN should be defined +# endif +# if !defined(STACK_GROWS_UP) && !defined(STACK_GROWS_DOWN) +# error One of STACK_GROWS_UP and STACK_GROWS_DOWN should be defined +# endif +# if defined(REDIRECT_MALLOC) && defined(THREADS) && !defined(LINUX) \ + && !defined(REDIRECT_MALLOC_IN_HEADER) + /* May work on other platforms (e.g. Darwin) provided the client */ + /* ensures all the client threads are registered with the GC, */ + /* e.g. by using the preprocessor-based interception of the thread */ + /* primitives (i.e., define GC_THREADS and include gc.h from all */ + /* the client files those are using pthread_create and friends). */ +# endif +#endif /* !CPPCHECK */ + +#ifdef GC_PRIVATE_H + /* This relies on some type definitions from gc_priv.h, from */ + /* where it's normally included. */ + /* */ + /* How to get heap memory from the OS: */ + /* Note that sbrk()-like allocation is preferred, since it */ + /* usually makes it possible to merge consecutively allocated */ + /* chunks. It also avoids unintended recursion with */ + /* REDIRECT_MALLOC macro defined. */ + /* GET_MEM() argument should be of size_t type and have */ + /* no side-effect. GET_MEM() returns HBLKSIZE-aligned chunk; */ + /* 0 is taken to mean failure. */ + /* In case of MMAP_SUPPORTED, the argument must also be */ + /* a multiple of a physical page size. */ + /* GET_MEM is currently not assumed to retrieve 0 filled space, */ + /* though we should perhaps take advantage of the case in which */ + /* does. */ + struct hblk; /* See gc_priv.h. */ +# if defined(PCR) + char * real_malloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR(real_malloc(SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(OS2) + void * os2_alloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(NEXT) || defined(DOS4GW) || defined(NONSTOP) \ + || (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) \ + || (defined(SOLARIS) && !defined(USE_MMAP)) || defined(RTEMS) \ + || defined(__CC_ARM) +# define GET_MEM(bytes) HBLKPTR((size_t)calloc(1, \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size - 1) +# elif defined(MSWIN_XBOX1) + ptr_t GC_durango_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_durango_get_mem(bytes) +# elif defined(MSWIN32) || defined(CYGWIN32) + ptr_t GC_win32_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) +# elif defined(MACOS) +# if defined(USE_TEMPORARY_MEMORY) + Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory); +# define GET_MEM(bytes) HBLKPTR(GC_MacTemporaryNewPtr( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size), true) \ + + GC_page_size-1) +# else +# define GET_MEM(bytes) HBLKPTR(NewPtrClear(SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# endif +# elif defined(MSWINCE) + ptr_t GC_wince_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes) +# elif defined(AMIGA) && defined(GC_AMIGA_FASTALLOC) + void *GC_amiga_get_mem(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((size_t)GC_amiga_get_mem( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(PLATFORM_GETMEM) + void *platform_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)platform_get_mem(bytes) +# elif defined(SN_TARGET_PS3) + void *ps3_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)ps3_get_mem(bytes) +# elif defined(SN_TARGET_PSP2) + void *psp2_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)psp2_get_mem(bytes) +# elif defined(NINTENDO_SWITCH) + void *switch_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)switch_get_mem(bytes) +# elif defined(HAIKU) + ptr_t GC_haiku_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)GC_haiku_get_mem(bytes) +# elif defined(EMSCRIPTEN_TINY) + void *emmalloc_memalign(size_t alignment, size_t size); +# define GET_MEM(bytes) (struct hblk*)emmalloc_memalign(GC_page_size, bytes) +# else + ptr_t GC_unix_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +# endif +#endif /* GC_PRIVATE_H */ + +EXTERN_C_END + +#endif /* GCCONFIG_H */ diff --git a/bdwgc/include/private/msvc_dbg.h b/bdwgc/include/private/msvc_dbg.h new file mode 100644 index 000000000..10dd1035c --- /dev/null +++ b/bdwgc/include/private/msvc_dbg.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2004-2005 Andrei Polushin + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef GC_MSVC_DBG_H +#define GC_MSVC_DBG_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#if !MSVC_DBG_DLL +#define MSVC_DBG_EXPORT +#elif MSVC_DBG_BUILD +#define MSVC_DBG_EXPORT __declspec(dllexport) +#else +#define MSVC_DBG_EXPORT __declspec(dllimport) +#endif + +#ifndef MAX_SYM_NAME +#define MAX_SYM_NAME 2000 +#endif + +typedef void* HANDLE; +typedef struct _CONTEXT CONTEXT; + +MSVC_DBG_EXPORT size_t GetStackFrames(size_t skip, void* frames[], size_t maxFrames); +MSVC_DBG_EXPORT size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread, CONTEXT* context, size_t skip, void* frames[], size_t maxFrames); + +MSVC_DBG_EXPORT size_t GetModuleNameFromAddress(void* address, char* moduleName, size_t size); +MSVC_DBG_EXPORT size_t GetModuleNameFromStack(size_t skip, char* moduleName, size_t size); + +MSVC_DBG_EXPORT size_t GetSymbolNameFromAddress(void* address, char* symbolName, size_t size, size_t* offsetBytes); +MSVC_DBG_EXPORT size_t GetSymbolNameFromStack(size_t skip, char* symbolName, size_t size, size_t* offsetBytes); + +MSVC_DBG_EXPORT size_t GetFileLineFromAddress(void* address, char* fileName, size_t size, size_t* lineNumber, size_t* offsetBytes); +MSVC_DBG_EXPORT size_t GetFileLineFromStack(size_t skip, char* fileName, size_t size, size_t* lineNumber, size_t* offsetBytes); + +MSVC_DBG_EXPORT size_t GetDescriptionFromAddress(void* address, const char* format, char* description, size_t size); +MSVC_DBG_EXPORT size_t GetDescriptionFromStack(void*const frames[], size_t count, const char* format, char* description[], size_t size); + +/* Compatibility with */ +MSVC_DBG_EXPORT int backtrace(void* addresses[], int count); +MSVC_DBG_EXPORT char** backtrace_symbols(void*const addresses[], int count); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_MSVC_DBG_H */ diff --git a/bdwgc/include/private/pthread_stop_world.h b/bdwgc/include/private/pthread_stop_world.h new file mode 100644 index 000000000..ae322fb40 --- /dev/null +++ b/bdwgc/include/private/pthread_stop_world.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +#ifndef GC_PTHREAD_STOP_WORLD_H +#define GC_PTHREAD_STOP_WORLD_H + +EXTERN_C_BEGIN + +struct thread_stop_info { +# if !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) \ + && !defined(PLATFORM_STOP_WORLD) && !defined(SN_TARGET_PSP2) + volatile AO_t last_stop_count; + /* The value of GC_stop_count when the thread */ + /* last successfully handled a suspend signal. */ +# ifdef GC_ENABLE_SUSPEND_THREAD + volatile AO_t ext_suspend_cnt; + /* An odd value means thread was suspended */ + /* externally. Incremented on every call of */ + /* GC_suspend_thread() and GC_resume_thread(). */ + /* Updated with the GC lock held, but could be */ + /* read from a signal handler. */ +# endif +# endif + + ptr_t stack_ptr; /* Valid only when stopped. */ + +# ifdef NACL + /* Grab NACL_GC_REG_STORAGE_SIZE pointers off the stack when */ + /* going into a syscall. 20 is more than we need, but it's an */ + /* overestimate in case the instrumented function uses any callee */ + /* saved registers, they may be pushed to the stack much earlier. */ + /* Also, on x64 'push' puts 8 bytes on the stack even though */ + /* our pointers are 4 bytes. */ +# ifdef ARM32 + /* Space for r4-r8, r10-r12, r14. */ +# define NACL_GC_REG_STORAGE_SIZE 9 +# else +# define NACL_GC_REG_STORAGE_SIZE 20 +# endif + ptr_t reg_storage[NACL_GC_REG_STORAGE_SIZE]; +# elif defined(PLATFORM_HAVE_GC_REG_STORAGE_SIZE) + word registers[PLATFORM_GC_REG_STORAGE_SIZE]; /* used externally */ +# endif +}; + +GC_INNER void GC_stop_init(void); + +EXTERN_C_END + +#endif diff --git a/bdwgc/include/private/pthread_support.h b/bdwgc/include/private/pthread_support.h new file mode 100644 index 000000000..fa8f2636c --- /dev/null +++ b/bdwgc/include/private/pthread_support.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +#ifndef GC_PTHREAD_SUPPORT_H +#define GC_PTHREAD_SUPPORT_H + +#include "private/gc_priv.h" + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + +#if defined(GC_DARWIN_THREADS) +# include "private/darwin_stop_world.h" +#else +# include "private/pthread_stop_world.h" +#endif + +#ifdef THREAD_LOCAL_ALLOC +# include "thread_local_alloc.h" +#endif + +#ifdef THREAD_SANITIZER +# include "dbg_mlc.h" /* for oh type */ +#endif + +EXTERN_C_BEGIN + +/* We use the allocation lock to protect thread-related data structures. */ + +/* The set of all known threads. We intercept thread creation and */ +/* joins. */ +/* Protected by allocation/GC lock. */ +/* Some of this should be declared volatile, but that's inconsistent */ +/* with some library routine declarations. */ +typedef struct GC_Thread_Rep { +# ifdef THREAD_SANITIZER + char dummy[sizeof(oh)]; /* A dummy field to avoid TSan false */ + /* positive about the race between */ + /* GC_has_other_debug_info and */ + /* GC_suspend_handler_inner (which */ + /* sets stop_info.stack_ptr). */ +# endif + + struct GC_Thread_Rep * next; /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + pthread_t id; +# ifdef USE_TKILL_ON_ANDROID + pid_t kernel_id; +# endif + /* Extra bookkeeping information the stopping code uses */ + struct thread_stop_info stop_info; + + unsigned char flags; /* Protected by GC lock. */ +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is treated as detached. */ + /* Thread may really be detached, or */ + /* it may have been explicitly */ + /* registered, in which case we can */ + /* deallocate its GC_Thread_Rep once */ + /* it unregisters itself, since it */ + /* may not return a GC pointer. */ +# define MAIN_THREAD 4 /* True for the original thread only. */ +# define DISABLED_GC 0x10 /* Collections are disabled while the */ + /* thread is exiting. */ + + unsigned char thread_blocked; + /* Protected by GC lock. */ + /* Treated as a boolean value. If set, */ + /* thread will acquire GC lock before */ + /* doing any pointer manipulations, and */ + /* has set its SP value. Thus it does */ + /* not need to be sent a signal to stop */ + /* it. */ + +# ifndef GC_NO_FINALIZATION + unsigned short finalizer_skipped; + unsigned char finalizer_nested; + /* Used by GC_check_finalizer_nested() */ + /* to minimize the level of recursion */ + /* when a client finalizer allocates */ + /* memory (initially both are 0). */ +# endif + + ptr_t stack_end; /* Cold end of the stack (except for */ + /* main thread). */ + ptr_t altstack; /* The start of the alt-stack if there */ + /* is one, NULL otherwise. */ + word altstack_size; /* The size of the alt-stack if exists. */ + ptr_t stack; /* The start and size of the normal */ + /* stack (set by GC_register_altstack). */ + word stack_size; +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + ptr_t topOfStack; /* Result of GC_FindTopOfStack(0); */ + /* valid only if the thread is blocked; */ + /* non-NULL value means already set. */ +# endif +# if defined(E2K) || defined(IA64) + ptr_t backing_store_end; /* Note: may reference data in GC heap */ + ptr_t backing_store_ptr; +# endif + + struct GC_traced_stack_sect_s *traced_stack_sect; + /* Points to the "frame" data held in stack by */ + /* the innermost GC_call_with_gc_active() of */ + /* this thread. May be NULL. */ + + void * status; /* The value returned from the thread. */ + /* Used only to avoid premature */ + /* reclamation of any data it might */ + /* reference. */ + /* This is unfortunately also the */ + /* reason we need to intercept join */ + /* and detach. */ + +# ifdef THREAD_LOCAL_ALLOC + struct thread_local_freelists tlfs GC_ATTR_WORD_ALIGNED; +# endif +} * GC_thread; + +#ifndef THREAD_TABLE_SZ +# define THREAD_TABLE_SZ 256 /* Power of 2 (for speed). */ +#endif + +#if CPP_WORDSZ == 64 +# define THREAD_TABLE_INDEX(id) \ + (int)(((((NUMERIC_THREAD_ID(id) >> 8) ^ NUMERIC_THREAD_ID(id)) >> 16) \ + ^ ((NUMERIC_THREAD_ID(id) >> 8) ^ NUMERIC_THREAD_ID(id))) \ + % THREAD_TABLE_SZ) +#else +# define THREAD_TABLE_INDEX(id) \ + (int)(((NUMERIC_THREAD_ID(id) >> 16) \ + ^ (NUMERIC_THREAD_ID(id) >> 8) \ + ^ NUMERIC_THREAD_ID(id)) % THREAD_TABLE_SZ) +#endif + +GC_EXTERN volatile GC_thread GC_threads[THREAD_TABLE_SZ]; + +GC_EXTERN GC_bool GC_thr_initialized; + +GC_INNER GC_thread GC_lookup_thread(pthread_t id); + +#ifdef NACL + GC_EXTERN __thread GC_thread GC_nacl_gc_thread_self; + GC_INNER void GC_nacl_initialize_gc_thread(void); + GC_INNER void GC_nacl_shutdown_gc_thread(void); +#endif + +#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + GC_INNER void GC_unblock_gc_signals(void); +#endif + +#if defined(GC_ENABLE_SUSPEND_THREAD) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) \ + && !defined(PLATFORM_STOP_WORLD) && !defined(SN_TARGET_PSP2) + GC_INNER void GC_suspend_self_inner(GC_thread me, word suspend_cnt); + + GC_INNER void GC_suspend_self_blocked(ptr_t thread_me, void *context); + /* Wrapper over GC_suspend_self_inner. */ +#endif + +#ifdef GC_PTHREAD_START_STANDALONE +# define GC_INNER_PTHRSTART /* empty */ +#else +# define GC_INNER_PTHRSTART GC_INNER +#endif + +GC_INNER_PTHRSTART void * GC_CALLBACK GC_inner_start_routine( + struct GC_stack_base *sb, void *arg); + +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_PTHRSTART void GC_thread_exit_proc(void *); + +EXTERN_C_END + +#endif /* GC_PTHREADS && !GC_WIN32_THREADS */ + +#endif /* GC_PTHREAD_SUPPORT_H */ diff --git a/bdwgc/include/private/specific.h b/bdwgc/include/private/specific.h new file mode 100644 index 000000000..3ed75647c --- /dev/null +++ b/bdwgc/include/private/specific.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/* + * This is a reimplementation of a subset of the pthread_getspecific/setspecific + * interface. This appears to outperform the standard linuxthreads one + * by a significant margin. + * The major restriction is that each thread may only make a single + * pthread_setspecific call on a single key. (The current data structure + * doesn't really require that. The restriction should be easily removable.) + * We don't currently support the destruction functions, though that + * could be done. + * We also currently assume that only one pthread_setspecific call + * can be executed at a time, though that assumption would be easy to remove + * by adding a lock. + */ + +#ifndef GC_SPECIFIC_H +#define GC_SPECIFIC_H + +#include + +EXTERN_C_BEGIN + +/* Called during key creation or setspecific. */ +/* For the GC we already hold lock. */ +/* Currently allocated objects leak on thread exit. */ +/* That's hard to fix, but OK if we allocate garbage */ +/* collected memory. */ +#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL) + +#define TS_CACHE_SIZE 1024 +#define CACHE_HASH(n) ((((n) >> 8) ^ (n)) & (TS_CACHE_SIZE - 1)) + +#define TS_HASH_SIZE 1024 +#define HASH(p) \ + ((unsigned)((((word)(p)) >> 8) ^ (word)(p)) & (TS_HASH_SIZE - 1)) + +#ifdef GC_ASSERTIONS + /* Thread-local storage is not guaranteed to be scanned by GC. */ + /* We hide values stored in "specific" entries for a test purpose. */ + typedef GC_hidden_pointer ts_entry_value_t; +# define TS_HIDE_VALUE(p) GC_HIDE_POINTER(p) +# define TS_REVEAL_PTR(p) GC_REVEAL_POINTER(p) +#else + typedef void * ts_entry_value_t; +# define TS_HIDE_VALUE(p) (p) +# define TS_REVEAL_PTR(p) (p) +#endif + +/* An entry describing a thread-specific value for a given thread. */ +/* All such accessible structures preserve the invariant that if either */ +/* thread is a valid pthread id or qtid is a valid "quick thread id" */ +/* for a thread, then value holds the corresponding thread specific */ +/* value. This invariant must be preserved at ALL times, since */ +/* asynchronous reads are allowed. */ +typedef struct thread_specific_entry { + volatile AO_t qtid; /* quick thread id, only for cache */ + ts_entry_value_t value; + struct thread_specific_entry *next; + pthread_t thread; +} tse; + +/* We represent each thread-specific datum as two tables. The first is */ +/* a cache, indexed by a "quick thread identifier". The "quick" thread */ +/* identifier is an easy to compute value, which is guaranteed to */ +/* determine the thread, though a thread may correspond to more than */ +/* one value. We typically use the address of a page in the stack. */ +/* The second is a hash table, indexed by pthread_self(). It is used */ +/* only as a backup. */ + +/* Return the "quick thread id". Default version. Assumes page size, */ +/* or at least thread stack separation, is at least 4 KB. */ +/* 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() (((word)GC_approx_sp()) >> 12) + +#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 */ + union ptse_ao_u hash[TS_HASH_SIZE]; + pthread_mutex_t lock; +} tsd; + +typedef tsd * GC_key_t; + +#define GC_key_create(key, d) GC_key_create_inner(key) +GC_INNER int GC_key_create_inner(tsd ** key_ptr); +GC_INNER int GC_setspecific(tsd * key, void * value); +#define GC_remove_specific(key) \ + GC_remove_specific_after_fork(key, pthread_self()) +GC_INNER void GC_remove_specific_after_fork(tsd * key, pthread_t t); + +/* An internal version of getspecific that assumes a cache miss. */ +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) +{ + 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 TS_REVEAL_PTR(entry -> value); + } + return GC_slow_getspecific(key, qtid, entry_ptr); +} + +EXTERN_C_END + +#endif /* GC_SPECIFIC_H */ diff --git a/bdwgc/include/private/thread_local_alloc.h b/bdwgc/include/private/thread_local_alloc.h new file mode 100644 index 000000000..de9b7ba9c --- /dev/null +++ b/bdwgc/include/private/thread_local_alloc.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2000-2005 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. + * + * 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. + */ + +/* Included indirectly from a thread-library-specific file. */ +/* This is the interface for thread-local allocation, whose */ +/* implementation is mostly thread-library-independent. */ +/* Here we describe only the interface that needs to be known */ +/* and invoked from the thread support layer; the actual */ +/* implementation also exports GC_malloc and friends, which */ +/* are declared in gc.h. */ + +#ifndef GC_THREAD_LOCAL_ALLOC_H +#define GC_THREAD_LOCAL_ALLOC_H + +#include "private/gc_priv.h" + +#ifdef THREAD_LOCAL_ALLOC + +#include "gc_inline.h" + +#if defined(USE_HPUX_TLS) +# error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS +#endif + +#include + +EXTERN_C_BEGIN + +#if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) \ + && !defined(USE_WIN32_COMPILER_TLS) && !defined(USE_COMPILER_TLS) \ + && !defined(USE_CUSTOM_SPECIFIC) +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# if defined(CYGWIN32) && GC_GNUC_PREREQ(4, 0) +# if defined(__clang__) + /* As of Cygwin clang3.5.2, 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) \ + && GC_GNUC_PREREQ(3, 3) \ + && !(defined(__clang__) && (defined(HOST_ANDROID) \ + || (defined(AARCH64) && !GC_CLANG_PREREQ(8, 0))))) \ + || ((defined(NETBSD) && __NetBSD_Version__ >= 600000000 /* 6.0 */ \ + || defined(FREEBSD)) \ + && (GC_GNUC_PREREQ(4, 4) || GC_CLANG_PREREQ(3, 9))) \ + || (defined(HOST_ANDROID) && defined(ARM32) \ + && (GC_GNUC_PREREQ(4, 6) || GC_CLANG_PREREQ_FULL(3, 8, 256229))) +# define USE_COMPILER_TLS +# elif defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) \ + || defined(GC_AIX_THREADS) || defined(GC_DARWIN_THREADS) \ + || defined(GC_FREEBSD_THREADS) || defined(GC_NETBSD_THREADS) \ + || defined(GC_LINUX_THREADS) || defined(GC_HAIKU_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. */ +# else +# define USE_COMPILER_TLS +# endif +# else +# define USE_CUSTOM_SPECIFIC /* Use our own. */ +# endif +#endif + +#ifndef THREAD_FREELISTS_KINDS +# ifdef ENABLE_DISCLAIM +# define THREAD_FREELISTS_KINDS (NORMAL+2) +# else +# define THREAD_FREELISTS_KINDS (NORMAL+1) +# endif +#endif /* !THREAD_FREELISTS_KINDS */ + +/* One of these should be declared as the tlfs field in the */ +/* structure pointed to by a GC_thread. */ +typedef struct thread_local_freelists { + void * _freelists[THREAD_FREELISTS_KINDS][TINY_FREELISTS]; +# define ptrfree_freelists _freelists[PTRFREE] +# define normal_freelists _freelists[NORMAL] + /* Note: Preserve *_freelists names for some clients. */ +# ifdef GC_GCJ_SUPPORT + void * gcj_freelists[TINY_FREELISTS]; +# define ERROR_FL ((void *)GC_WORD_MAX) + /* Value used for gcj_freelists[-1]; allocation is */ + /* erroneous. */ +# endif + /* Free lists contain either a pointer or a small count */ + /* reflecting the number of granules allocated at that */ + /* size. */ + /* 0 ==> thread-local allocation in use, free list */ + /* empty. */ + /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */ + /* too few objects of this size have been */ + /* allocated by this thread. */ + /* >= HBLKSIZE => pointer to nonempty free list. */ + /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */ + /* local alloc, equivalent to 0. */ +# define DIRECT_GRANULES (HBLKSIZE/GRANULE_BYTES) + /* Don't use local free lists for up to this much */ + /* allocation. */ +} *GC_tlfs; + +#if defined(USE_PTHREAD_SPECIFIC) +# define GC_getspecific pthread_getspecific +# define GC_setspecific pthread_setspecific +# define GC_key_create pthread_key_create +# define GC_remove_specific(key) pthread_setspecific(key, NULL) + /* Explicitly delete the value to stop the TLS */ + /* destructor from being called repeatedly. */ +# define GC_remove_specific_after_fork(key, t) (void)0 + /* Should not need any action. */ + typedef pthread_key_t GC_key_t; +#elif defined(USE_COMPILER_TLS) || defined(USE_WIN32_COMPILER_TLS) +# define GC_getspecific(x) (x) +# define GC_setspecific(key, v) ((key) = (v), 0) +# define GC_key_create(key, d) 0 +# define GC_remove_specific(key) /* No need for cleanup on exit. */ +# define GC_remove_specific_after_fork(key, t) (void)0 + typedef void * GC_key_t; +#elif defined(USE_WIN32_SPECIFIC) +# define GC_getspecific TlsGetValue +# define GC_setspecific(key, v) !TlsSetValue(key, v) + /* We assume 0 == success, msft does the opposite. */ +# ifndef TLS_OUT_OF_INDEXES + /* this is currently missing in WinCE */ +# define TLS_OUT_OF_INDEXES (DWORD)0xFFFFFFFF +# endif +# define GC_key_create(key, d) \ + ((d) != 0 || (*(key) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? -1 : 0) +# define GC_remove_specific(key) /* No need for cleanup on exit. */ + /* Need TlsFree on process exit/detach? */ +# define GC_remove_specific_after_fork(key, t) (void)0 + typedef DWORD GC_key_t; +#elif defined(USE_CUSTOM_SPECIFIC) + EXTERN_C_END +# include "private/specific.h" + EXTERN_C_BEGIN +#else +# error implement me +#endif + +/* Each thread structure must be initialized. */ +/* This call must be made from the new thread. */ +/* Caller holds allocation lock. */ +GC_INNER void GC_init_thread_local(GC_tlfs p); + +/* Called when a thread is unregistered, or exits. */ +/* We hold the allocator lock. */ +GC_INNER void GC_destroy_thread_local(GC_tlfs p); + +/* The thread support layer must arrange to mark thread-local */ +/* free lists explicitly, since the link field is often */ +/* invisible to the marker. It knows how to find all threads; */ +/* we take care of an individual thread freelist structure. */ +GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p); + +#ifdef GC_ASSERTIONS + GC_bool GC_is_thread_tsd_valid(void *tsd); + void GC_check_tls_for(GC_tlfs p); +# if defined(USE_CUSTOM_SPECIFIC) + void GC_check_tsd_marks(tsd *key); +# endif +#endif /* GC_ASSERTIONS */ + +#ifndef GC_ATTR_TLS_FAST +# define GC_ATTR_TLS_FAST /* empty */ +#endif + +extern +#if defined(USE_COMPILER_TLS) + __thread GC_ATTR_TLS_FAST +#elif defined(USE_WIN32_COMPILER_TLS) + __declspec(thread) GC_ATTR_TLS_FAST +#endif + GC_key_t GC_thread_key; +/* This is set up by the thread_local_alloc implementation. No need */ +/* for cleanup on thread exit. But the thread support layer makes sure */ +/* that GC_thread_key is traced, if necessary. */ + +EXTERN_C_END + +#endif /* THREAD_LOCAL_ALLOC */ + +#endif /* GC_THREAD_LOCAL_ALLOC_H */ diff --git a/bdwgc/m4/gc_set_version.m4 b/bdwgc/m4/gc_set_version.m4 new file mode 100644 index 000000000..465e4f0f9 --- /dev/null +++ b/bdwgc/m4/gc_set_version.m4 @@ -0,0 +1,40 @@ +# +# 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. + +# GC_SET_VERSION +# sets and AC_DEFINEs GC_VERSION_MAJOR, GC_VERSION_MINOR and GC_VERSION_MICRO +# based on the contents of PACKAGE_VERSION; PACKAGE_VERSION must conform to +# [0-9]+[.][0-9]+[.][0-9]+ +# +AC_DEFUN([GC_SET_VERSION], [ + AC_MSG_CHECKING(GC version numbers) + GC_VERSION_MAJOR=`echo $PACKAGE_VERSION | sed 's/^\([[0-9]][[0-9]]*\)[[.]].*$/\1/g'` + GC_VERSION_MINOR=`echo $PACKAGE_VERSION | sed 's/^[[^.]]*[[.]]\([[0-9]][[0-9]]*\).*$/\1/g'` + GC_VERSION_MICRO=`echo $PACKAGE_VERSION | sed 's/^[[^.]]*[[.]][[^.]]*[[.]]\([[0-9]][[0-9]]*\)$/\1/g'` + + if test :$GC_VERSION_MAJOR: = :: \ + -o :$GC_VERSION_MINOR: = :: \ + -o :$GC_VERSION_MICRO: = :: ; + then + AC_MSG_RESULT(invalid) + AC_MSG_ERROR([nonconforming PACKAGE_VERSION='$PACKAGE_VERSION']) + fi + + AC_DEFINE_UNQUOTED([GC_VERSION_MAJOR], $GC_VERSION_MAJOR, + [The major version number of this GC release.]) + AC_DEFINE_UNQUOTED([GC_VERSION_MINOR], $GC_VERSION_MINOR, + [The minor version number of this GC release.]) + AC_DEFINE_UNQUOTED([GC_VERSION_MICRO], $GC_VERSION_MICRO, + [The micro version number of this GC release.]) + AC_MSG_RESULT(major=$GC_VERSION_MAJOR minor=$GC_VERSION_MINOR \ + micro=$GC_VERSION_MICRO) +]) + +sinclude(libtool.m4) diff --git a/bdwgc/mach_dep.c b/bdwgc/mach_dep.c new file mode 100644 index 000000000..ee120fd70 --- /dev/null +++ b/bdwgc/mach_dep.c @@ -0,0 +1,428 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#include "private/gc_priv.h" + +#if !defined(PLATFORM_MACH_DEP) && !defined(SN_TARGET_PSP2) + +#include + +#ifdef AMIGA +# ifndef __GNUC__ +# include +# else +# include +# endif +#endif + +#ifdef E2K +# include +# include +# include +# include + + GC_INNER size_t GC_get_procedure_stack(ptr_t buf, size_t buf_sz) { + word new_sz; + + GC_ASSERT(0 == buf_sz || buf != NULL); + for (;;) { + word stack_ofs; + + new_sz = 0; + if (syscall(__NR_access_hw_stacks, E2K_GET_PROCEDURE_STACK_SIZE, + NULL, NULL, 0, &new_sz) == -1) { + if (errno != EAGAIN) + ABORT_ARG1("Cannot get size of procedure stack", + ": errno= %d", errno); + continue; + } + GC_ASSERT(new_sz > 0 && new_sz % sizeof(word) == 0); + if (new_sz > buf_sz) + break; + /* Immediately read the stack right after checking its size. */ + stack_ofs = 0; + if (syscall(__NR_access_hw_stacks, E2K_READ_PROCEDURE_STACK_EX, + &stack_ofs, buf, new_sz, NULL) != -1) + break; + if (errno != EAGAIN) + ABORT_ARG2("Cannot read procedure stack", + ": new_sz= %lu, errno= %d", (unsigned long)new_sz, errno); + } + return (size_t)new_sz; + } + + ptr_t GC_save_regs_in_stack(void) { + __asm__ __volatile__ ("flushr"); + return NULL; + } + + GC_INNER ptr_t GC_mmap_procedure_stack_buf(size_t aligned_sz) + { + void *buf = mmap(NULL, aligned_sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, 0 /* fd */, 0 /* offset */); + if (MAP_FAILED == buf) + ABORT_ARG2("Could not map memory for procedure stack", + ": requested %lu bytes, errno= %d", + (unsigned long)aligned_sz, errno); + return (ptr_t)buf; + } + + GC_INNER void GC_unmap_procedure_stack_buf(ptr_t buf, size_t sz) + { + if (munmap(buf, ROUNDUP_PAGESIZE(sz)) == -1) + ABORT_ARG1("munmap failed (for procedure stack space)", + ": errno= %d", errno); + } + +# ifdef THREADS + GC_INNER size_t GC_alloc_and_get_procedure_stack(ptr_t *pbuf) + { + /* TODO: support saving from non-zero ofs in stack */ + ptr_t buf = NULL; + size_t new_sz, buf_sz; + + GC_ASSERT(I_HOLD_LOCK()); + for (buf_sz = 0; ; buf_sz = new_sz) { + new_sz = GC_get_procedure_stack(buf, buf_sz); + if (new_sz <= buf_sz) break; + + if (EXPECT(buf != NULL, FALSE)) + GC_INTERNAL_FREE(buf); + buf = (ptr_t)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(new_sz, PTRFREE); + if (NULL == buf) + ABORT("Could not allocate memory for procedure stack"); + } + *pbuf = buf; + return new_sz; + } +# endif /* THREADS */ +#endif /* E2K */ + +#if defined(MACOS) && defined(__MWERKS__) + +#if defined(POWERPC) + +# define NONVOLATILE_GPR_COUNT 19 + struct ppc_registers { + unsigned long gprs[NONVOLATILE_GPR_COUNT]; /* R13-R31 */ + }; + typedef struct ppc_registers ppc_registers; + +# if defined(CPPCHECK) + void getRegisters(ppc_registers* regs); +# else + asm static void getRegisters(register ppc_registers* regs) + { + stmw r13,regs->gprs /* save R13-R31 */ + blr + } +# endif + + static void PushMacRegisters(void) + { + ppc_registers regs; + int i; + getRegisters(®s); + for (i = 0; i < NONVOLATILE_GPR_COUNT; i++) + GC_push_one(regs.gprs[i]); + } + +#else /* M68K */ + + asm static void PushMacRegisters(void) + { + sub.w #4,sp /* reserve space for one parameter */ + move.l a2,(sp) + jsr GC_push_one + move.l a3,(sp) + jsr GC_push_one + move.l a4,(sp) + jsr GC_push_one +# if !__option(a6frames) + /* perhaps a6 should be pushed if stack frames are not being used */ + move.l a6,(sp) + jsr GC_push_one +# endif + /* skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) */ + move.l d2,(sp) + jsr GC_push_one + move.l d3,(sp) + jsr GC_push_one + move.l d4,(sp) + jsr GC_push_one + move.l d5,(sp) + jsr GC_push_one + move.l d6,(sp) + jsr GC_push_one + move.l d7,(sp) + jsr GC_push_one + add.w #4,sp /* fix stack */ + rts + } + +#endif /* M68K */ + +#endif /* MACOS && __MWERKS__ */ + +# if defined(SPARC) || defined(IA64) + /* Value returned from register flushing routine; either sp (SPARC) */ + /* or ar.bsp (IA64). */ + GC_INNER ptr_t GC_save_regs_ret_val = NULL; +# endif + +/* Routine to mark from registers that are preserved by the C compiler. */ +/* This must be ported to every new architecture. It is not optional, */ +/* and should not be used on platforms that are either UNIX-like, or */ +/* require thread support. */ + +#undef HAVE_PUSH_REGS + +#if defined(USE_ASM_PUSH_REGS) +# define HAVE_PUSH_REGS +#else /* No asm implementation */ + +# ifdef STACK_NOT_SCANNED + void GC_push_regs(void) + { + /* empty */ + } +# define HAVE_PUSH_REGS + +# elif defined(M68K) && defined(AMIGA) + /* This function is not static because it could also be */ + /* erroneously defined in .S file, so this error would be caught */ + /* by the linker. */ + void GC_push_regs(void) + { + /* AMIGA - could be replaced by generic code */ + /* a0, a1, d0 and d1 are caller save */ + +# ifdef __GNUC__ + asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ + + asm("mov.l %a2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a6,(%sp)"); asm("jsr _GC_push_one"); + /* Skip frame pointer and stack pointer */ + asm("mov.l %d2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d6,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); + + asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# else /* !__GNUC__ */ + GC_push_one(getreg(REG_A2)); + GC_push_one(getreg(REG_A3)); +# ifndef __SASC + /* Can probably be changed to #if 0 -Kjetil M. (a4=globals) */ + GC_push_one(getreg(REG_A4)); +# endif + GC_push_one(getreg(REG_A5)); + GC_push_one(getreg(REG_A6)); + /* Skip stack pointer */ + GC_push_one(getreg(REG_D2)); + GC_push_one(getreg(REG_D3)); + GC_push_one(getreg(REG_D4)); + GC_push_one(getreg(REG_D5)); + GC_push_one(getreg(REG_D6)); + GC_push_one(getreg(REG_D7)); +# endif /* !__GNUC__ */ + } +# define HAVE_PUSH_REGS + +# elif defined(MACOS) + +# if defined(M68K) && defined(THINK_C) && !defined(CPPCHECK) +# define PushMacReg(reg) \ + move.l reg,(sp) \ + jsr GC_push_one + void GC_push_regs(void) + { + asm { + sub.w #4,sp ; reserve space for one parameter. + PushMacReg(a2); + PushMacReg(a3); + PushMacReg(a4); + ; skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) + PushMacReg(d2); + PushMacReg(d3); + PushMacReg(d4); + PushMacReg(d5); + PushMacReg(d6); + PushMacReg(d7); + add.w #4,sp ; fix stack. + } + } +# define HAVE_PUSH_REGS +# undef PushMacReg +# elif defined(__MWERKS__) + void GC_push_regs(void) + { + PushMacRegisters(); + } +# define HAVE_PUSH_REGS +# endif /* __MWERKS__ */ +# endif /* MACOS */ + +#endif /* !USE_ASM_PUSH_REGS */ + +#if defined(HAVE_PUSH_REGS) && defined(THREADS) +# error GC_push_regs cannot be used with threads + /* Would fail for GC_do_blocking. There are probably other safety */ + /* issues. */ +# undef HAVE_PUSH_REGS +#endif + +#if !defined(HAVE_PUSH_REGS) && defined(UNIX_LIKE) +# include +# ifndef NO_GETCONTEXT +# if defined(DARWIN) \ + && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 /*MAC_OS_X_VERSION_10_6*/) +# include +# else +# include +# endif /* !DARWIN */ +# ifdef GETCONTEXT_FPU_EXCMASK_BUG +# include +# endif +# endif +#endif /* !HAVE_PUSH_REGS */ + +/* Ensure that either registers are pushed, or callee-save registers */ +/* are somewhere on the stack, and then call fn(arg, ctxt). */ +/* ctxt is either a pointer to a ucontext_t we generated, or NULL. */ +GC_ATTR_NO_SANITIZE_ADDR +GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), + volatile ptr_t arg) +{ + volatile int dummy; + volatile ptr_t context = 0; + +# if defined(HAVE_PUSH_REGS) + GC_push_regs(); +# elif defined(EMSCRIPTEN) + /* No-op, "registers" are pushed in GC_push_other_roots(). */ +# else +# if defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + /* Older versions of Darwin seem to lack getcontext(). */ + /* ARM and MIPS Linux often doesn't support a real */ + /* getcontext(). */ + static signed char getcontext_works = 0; /* (-1) - broken, 1 - works */ + ucontext_t ctxt; +# ifdef GETCONTEXT_FPU_EXCMASK_BUG + /* Workaround a bug (clearing the FPU exception mask) in */ + /* getcontext on Linux/x64. */ +# ifdef X86_64 + /* We manipulate FPU control word here just not to force the */ + /* client application to use -lm linker option. */ + unsigned short old_fcw; + +# if defined(CPPCHECK) + GC_noop1((word)&old_fcw); +# endif + __asm__ __volatile__ ("fstcw %0" : "=m" (*&old_fcw)); +# else + int except_mask = fegetexcept(); +# endif +# endif + + if (getcontext_works >= 0) { + if (getcontext(&ctxt) < 0) { + WARN("getcontext failed:" + " using another register retrieval method...\n", 0); + /* getcontext() is broken, do not try again. */ + /* E.g., to workaround a bug in Docker ubuntu_32bit. */ + } else { + context = (ptr_t)&ctxt; + } + if (EXPECT(0 == getcontext_works, FALSE)) + getcontext_works = context != NULL ? 1 : -1; + } +# ifdef GETCONTEXT_FPU_EXCMASK_BUG +# ifdef X86_64 + __asm__ __volatile__ ("fldcw %0" : : "m" (*&old_fcw)); + { + unsigned mxcsr; + /* And now correct the exception mask in SSE MXCSR. */ + __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr)); + mxcsr = (mxcsr & ~(FE_ALL_EXCEPT << 7)) | + ((old_fcw & FE_ALL_EXCEPT) << 7); + __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr)); + } +# else /* !X86_64 */ + if (feenableexcept(except_mask) < 0) + ABORT("feenableexcept failed"); +# endif +# endif /* GETCONTEXT_FPU_EXCMASK_BUG */ +# if defined(E2K) || defined(IA64) || defined(SPARC) + /* On a register window machine, we need to save register */ + /* contents on the stack for this to work. This may already be */ + /* subsumed by the getcontext() call. */ +# if defined(IA64) || defined(SPARC) + GC_save_regs_ret_val = +# endif + GC_save_regs_in_stack(); +# endif + if (NULL == context) /* getcontext failed */ +# endif /* !NO_GETCONTEXT */ + { +# if 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. */ + __builtin_unwind_init(); +# elif defined(NO_CRT) && defined(MSWIN32) + CONTEXT ctx; + RtlCaptureContext(&ctx); +# else + /* Generic code */ + /* The idea is due to Parag Patel at HP. */ + /* We're not sure whether he would like */ + /* to be acknowledged for it or not. */ + jmp_buf regs; + word *i = (word *)®s[0]; + ptr_t lim = (ptr_t)(®s[0]) + sizeof(regs); + + /* Setjmp doesn't always clear all of the buffer. */ + /* That tends to preserve garbage. Clear it. */ + 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 + (void) _setjmp(regs); + /* We don't want to mess with signals. According to */ + /* SUSV3, setjmp() may or may not save signal mask. */ + /* _setjmp won't, but is less portable. */ +# endif +# endif /* !HAVE_BUILTIN_UNWIND_INIT */ + } +# endif /* !HAVE_PUSH_REGS */ + /* TODO: context here is sometimes just zero. At the moment, the */ + /* callees don't really need it. */ + fn(arg, (/* no volatile */ void *)context); + /* Strongly discourage the compiler from treating the above */ + /* as a tail-call, since that would pop the register */ + /* contents before we get a chance to look at them. */ + GC_noop1(COVERT_DATAFLOW(&dummy)); +} + +#endif /* !PLATFORM_MACH_DEP && !SN_TARGET_PSP2 */ diff --git a/bdwgc/malloc.c b/bdwgc/malloc.c new file mode 100644 index 000000000..77c60ddac --- /dev/null +++ b/bdwgc/malloc.c @@ -0,0 +1,722 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * + * 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. + */ + +#include "private/gc_priv.h" +#include "gc_inline.h" /* for GC_malloc_kind */ + +#include +#include + +/* Allocate reclaim list for kind: */ +/* Return TRUE on success */ +STATIC GC_bool GC_alloc_reclaim_list(struct obj_kind *kind) +{ + struct hblk ** result = (struct hblk **) + GC_scratch_alloc((MAXOBJGRANULES+1) * sizeof(struct hblk *)); + if (result == 0) return(FALSE); + BZERO(result, (MAXOBJGRANULES+1)*sizeof(struct hblk *)); + kind -> ok_reclaim_list = result; + return(TRUE); +} + +/* Allocate a large block of size lb bytes. The block is not cleared. */ +/* flags argument should be 0 or IGNORE_OFF_PAGE. EXTRA_BYTES value */ +/* was already added to lb. */ +GC_INNER ptr_t GC_alloc_large(size_t lb, int k, unsigned flags) +{ + struct hblk * h; + word n_blocks; + ptr_t result; + GC_bool retry = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + lb = ROUNDUP_GRANULE_SIZE(lb); + n_blocks = OBJ_SZ_TO_BLOCKS_CHECKED(lb); + if (!EXPECT(GC_is_initialized, TRUE)) { + DCL_LOCK_STATE; + UNLOCK(); /* just to unset GC_lock_holder */ + GC_init(); + LOCK(); + } + /* Do our share of marking work */ + if (GC_incremental && !GC_dont_gc) { + ENTER_GC(); + GC_collect_a_little_inner((int)n_blocks); + EXIT_GC(); + } + h = GC_allochblk(lb, k, flags); +# ifdef USE_MUNMAP + if (0 == h) { + GC_merge_unmapped(); + h = GC_allochblk(lb, k, flags); + } +# endif + while (0 == h && GC_collect_or_expand(n_blocks, flags != 0, retry)) { + h = GC_allochblk(lb, k, flags); + retry = TRUE; + } + if (h == 0) { + result = 0; + } else { + size_t total_bytes = n_blocks * HBLKSIZE; + if (n_blocks > 1) { + GC_large_allocd_bytes += total_bytes; + if (GC_large_allocd_bytes > GC_max_large_allocd_bytes) + GC_max_large_allocd_bytes = GC_large_allocd_bytes; + } + /* FIXME: Do we need some way to reset GC_max_large_allocd_bytes? */ + result = h -> hb_body; + } + return result; +} + +/* Allocate a large block of size lb bytes. Clear if appropriate. */ +/* EXTRA_BYTES were already added to lb. */ +STATIC ptr_t GC_alloc_large_and_clear(size_t lb, int k, unsigned flags) +{ + ptr_t result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_alloc_large(lb, k, flags); + if (result != NULL + && (GC_debugging_started || GC_obj_kinds[k].ok_init)) { + word n_blocks = OBJ_SZ_TO_BLOCKS(lb); + + /* Clear the whole block, in case of GC_realloc call. */ + BZERO(result, n_blocks * HBLKSIZE); + } + return result; +} + +/* Fill in additional entries in GC_size_map, including the i-th one. */ +/* Note that a filled in section of the array ending at n always */ +/* has the length of at least n/4. */ +STATIC void GC_extend_size_map(size_t i) +{ + size_t orig_granule_sz = ROUNDED_UP_GRANULES(i); + size_t granule_sz; + size_t byte_sz = GRANULES_TO_BYTES(orig_granule_sz); + /* The size we try to preserve. */ + /* Close to i, unless this would */ + /* introduce too many distinct sizes. */ + size_t smaller_than_i = byte_sz - (byte_sz >> 3); + size_t low_limit; /* The lowest indexed entry we initialize. */ + size_t number_of_objs; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(0 == GC_size_map[i]); + if (0 == GC_size_map[smaller_than_i]) { + low_limit = byte_sz - (byte_sz >> 2); /* much smaller than i */ + granule_sz = orig_granule_sz; + while (GC_size_map[low_limit] != 0) + low_limit++; + } else { + low_limit = smaller_than_i + 1; + while (GC_size_map[low_limit] != 0) + low_limit++; + + granule_sz = ROUNDED_UP_GRANULES(low_limit); + granule_sz += granule_sz >> 3; + if (granule_sz < orig_granule_sz) + granule_sz = orig_granule_sz; + } + + /* For these larger sizes, we use an even number of granules. */ + /* This makes it easier to, e.g., construct a 16-byte-aligned */ + /* allocator even if GRANULE_BYTES is 8. */ + granule_sz = (granule_sz + 1) & ~1; + if (granule_sz > MAXOBJGRANULES) + granule_sz = MAXOBJGRANULES; + + /* If we can fit the same number of larger objects in a block, do so. */ + number_of_objs = HBLK_GRANULES / granule_sz; + GC_ASSERT(number_of_objs != 0); + granule_sz = (HBLK_GRANULES / number_of_objs) & ~1; + + byte_sz = GRANULES_TO_BYTES(granule_sz) - EXTRA_BYTES; + /* We may need one extra byte; do not always */ + /* fill in GC_size_map[byte_sz]. */ + + for (; low_limit <= byte_sz; low_limit++) + GC_size_map[low_limit] = granule_sz; +} + +/* Allocate lb bytes for an object of kind k. */ +/* Should not be used to directly allocate objects */ +/* that require special handling on allocation. */ +GC_INNER void * GC_generic_malloc_inner(size_t lb, int k) +{ + void *op; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(k < MAXOBJKINDS); + if (SMALL_OBJ(lb)) { + struct obj_kind * kind = GC_obj_kinds + k; + size_t lg = GC_size_map[lb]; + void ** opp = &(kind -> ok_freelist[lg]); + + op = *opp; + if (EXPECT(0 == op, FALSE)) { + if (lg == 0) { + if (!EXPECT(GC_is_initialized, TRUE)) { + DCL_LOCK_STATE; + UNLOCK(); /* just to unset GC_lock_holder */ + GC_init(); + LOCK(); + lg = GC_size_map[lb]; + } + 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 (0 == op) + return NULL; + } + } + *opp = obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + } else { + size_t lb_adjusted = ADD_SLOP(lb); + + op = (ptr_t)GC_alloc_large_and_clear(lb_adjusted, k, 0 /* flags */); + if (op != NULL) + GC_bytes_allocd += lb_adjusted; + } + + return op; +} + +#if defined(DBG_HDRS_ALL) || defined(GC_GCJ_SUPPORT) \ + || !defined(GC_NO_FINALIZATION) + /* Allocate a composite object of size n bytes. The caller */ + /* guarantees that pointers past the first hblk are not relevant. */ + GC_INNER void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k) + { + size_t lb_adjusted; + void * op; + + GC_ASSERT(I_HOLD_LOCK()); + if (lb <= HBLKSIZE) + return GC_generic_malloc_inner(lb, k); + GC_ASSERT(k < MAXOBJKINDS); + lb_adjusted = ADD_SLOP(lb); + op = GC_alloc_large_and_clear(lb_adjusted, k, IGNORE_OFF_PAGE); + if (op != NULL) + GC_bytes_allocd += lb_adjusted; + return op; + } +#endif + +#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. */ +# if defined(CPPCHECK) + size_t GC_dbg_collect_at_malloc_min_lb = 16*1024; /* e.g. */ +# else + size_t GC_dbg_collect_at_malloc_min_lb = (GC_COLLECT_AT_MALLOC); +# endif +#endif + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc(size_t lb, int k) +{ + void * result; + DCL_LOCK_STATE; + + GC_ASSERT(k < MAXOBJKINDS); + if (EXPECT(get_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(lb, k); + UNLOCK(); + } else { + size_t lg; + size_t lb_rounded; + word n_blocks; + GC_bool init; + + lg = ROUNDED_UP_GRANULES(lb); + lb_rounded = GRANULES_TO_BYTES(lg); + n_blocks = OBJ_SZ_TO_BLOCKS(lb_rounded); + init = GC_obj_kinds[k].ok_init; + LOCK(); + result = (ptr_t)GC_alloc_large(lb_rounded, k, 0); + if (0 != result) { + if (GC_debugging_started +# ifndef THREADS + || init +# endif + ) { + BZERO(result, n_blocks * HBLKSIZE); + } else { +# ifdef THREADS + /* Clear any memory that might be used for GC descriptors */ + /* before we release the lock. */ + ((word *)result)[0] = 0; + ((word *)result)[1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-2] = 0; +# endif + } + GC_bytes_allocd += lb_rounded; + } + UNLOCK(); +# ifdef THREADS + if (init && !GC_debugging_started && result != NULL) { + /* Clear the rest (i.e. excluding the initial 2 words). */ + BZERO((word *)result + 2, + n_blocks * HBLKSIZE - 2 * sizeof(word)); + } +# endif + } + if (0 == result) { + return((*GC_get_oom_fn())(lb)); + } else { + return(result); + } +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind_global(size_t lb, int k) +{ + GC_ASSERT(k < MAXOBJKINDS); + if (SMALL_OBJ(lb)) { + void *op; + void **opp; + size_t lg; + DCL_LOCK_STATE; + + GC_DBG_COLLECT_AT_MALLOC(lb); + LOCK(); + lg = GC_size_map[lb]; + opp = &GC_obj_kinds[k].ok_freelist[lg]; + op = *opp; + if (EXPECT(op != NULL, TRUE)) { + if (k == PTRFREE) { + *opp = obj_link(op); + } else { + GC_ASSERT(0 == obj_link(op) + || ((word)obj_link(op) + <= (word)GC_greatest_plausible_heap_addr + && (word)obj_link(op) + >= (word)GC_least_plausible_heap_addr)); + *opp = obj_link(op); + obj_link(op) = 0; + } + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + UNLOCK(); + return op; + } + UNLOCK(); + } + + /* We make the GC_clear_stack() call a tail one, hoping to get more */ + /* of the stack. */ + return GC_clear_stack(GC_generic_malloc(lb, k)); +} + +#if defined(THREADS) && !defined(THREAD_LOCAL_ALLOC) + GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind(size_t lb, int k) + { + return GC_malloc_kind_global(lb, k); + } +#endif + +/* Allocate lb bytes of atomic (pointer-free) data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_atomic(size_t lb) +{ + return GC_malloc_kind(lb, PTRFREE); +} + +/* Allocate lb bytes of composite (pointerful) data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc(size_t lb) +{ + return GC_malloc_kind(lb, NORMAL); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc_uncollectable( + size_t lb, int k) +{ + void *op; + DCL_LOCK_STATE; + + GC_ASSERT(k < MAXOBJKINDS); + if (SMALL_OBJ(lb)) { + void **opp; + size_t lg; + + 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. */ + LOCK(); + lg = GC_size_map[lb]; + opp = &GC_obj_kinds[k].ok_freelist[lg]; + op = *opp; + if (EXPECT(op != NULL, TRUE)) { + *opp = obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + /* Mark bit was already set on free list. It will be */ + /* cleared only temporarily during a collection, as a */ + /* result of the normal free list mark bit clearing. */ + GC_non_gc_bytes += GRANULES_TO_BYTES((word)lg); + UNLOCK(); + } else { + UNLOCK(); + op = GC_generic_malloc(lb, k); + /* For small objects, the free lists are completely marked. */ + } + GC_ASSERT(0 == op || GC_is_marked(op)); + } else { + op = GC_generic_malloc(lb, k); + if (op /* != NULL */) { /* CPPCHECK */ + hdr * hhdr = HDR(op); + + GC_ASSERT(((word)op & (HBLKSIZE - 1)) == 0); /* large block */ + /* We don't need the lock here, since we have an undisguised */ + /* pointer. We do need to hold the lock while we adjust */ + /* mark bits. */ + LOCK(); + set_mark_bit_from_hdr(hhdr, 0); /* Only object. */ +# 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 op; +} + +/* Allocate lb bytes of pointerful, traced, but not collectible data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_uncollectable(size_t lb) +{ + return GC_generic_malloc_uncollectable(lb, UNCOLLECTABLE); +} + +#ifdef GC_ATOMIC_UNCOLLECTABLE + /* 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 GC_ATTR_MALLOC void * GC_CALL + GC_malloc_atomic_uncollectable(size_t lb) + { + return GC_generic_malloc_uncollectable(lb, AUNCOLLECTABLE); + } +#endif /* GC_ATOMIC_UNCOLLECTABLE */ + +#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_MALLOC_IN_HEADER) + +# ifndef MSWINCE +# include +# endif + + /* Avoid unnecessary nested procedure calls here, by #defining some */ + /* malloc replacements. Otherwise we end up saving a meaningless */ + /* return address in the object. It also speeds things up, but it is */ + /* admittedly quite ugly. */ +# define GC_debug_malloc_replacement(lb) GC_debug_malloc(lb, GC_DBG_EXTRAS) + +# if defined(CPPCHECK) +# define REDIRECT_MALLOC_F GC_malloc /* e.g. */ +# else +# define REDIRECT_MALLOC_F REDIRECT_MALLOC +# endif + + void * malloc(size_t lb) + { + /* It might help to manually inline the GC_malloc call here. */ + /* 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 initialization can call malloc before we are ready for. */ + /* It is 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 + return (void *)REDIRECT_MALLOC_F(lb); + } + +# if defined(GC_LINUX_THREADS) + STATIC ptr_t GC_libpthread_start = 0; + STATIC ptr_t GC_libpthread_end = 0; + STATIC ptr_t GC_libld_start = 0; + STATIC ptr_t GC_libld_end = 0; + + STATIC void GC_init_lib_bounds(void) + { + IF_CANCEL(int cancel_state;) + + if (GC_libpthread_start != 0) return; + DISABLE_CANCEL(cancel_state); + GC_init(); /* if not called yet */ + if (!GC_text_mapping("libpthread-", + &GC_libpthread_start, &GC_libpthread_end)) { + /* Some libc implementations like bionic, musl and glibc 2.34 */ + /* do not have libpthread.so because the pthreads-related code */ + /* is located in libc.so, thus potential calloc calls from such */ + /* code are forwarded to real (libc) calloc without any special */ + /* handling on the libgc side. Checking glibc version at */ + /* compile time to turn off the warning seems to be fine. */ + /* TODO: Remove GC_text_mapping() call for this case. */ +# if defined(__GLIBC__) \ + && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 34)) + WARN("Failed to find libpthread.so text mapping: Expect crash\n", 0); + /* This might still work with some versions of libpthread, */ + /* so we do not abort. */ +# endif + /* Generate message only once: */ + GC_libpthread_start = (ptr_t)1; + } + if (!GC_text_mapping("ld-", &GC_libld_start, &GC_libld_end)) { + WARN("Failed to find ld.so text mapping: Expect crash\n", 0); + } + RESTORE_CANCEL(cancel_state); + } +# endif /* GC_LINUX_THREADS */ + + 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 (*GC_get_oom_fn())(GC_SIZE_MAX); /* n*lb overflow */ +# if defined(GC_LINUX_THREADS) + /* libpthread allocated some memory that is only pointed to by */ + /* 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); + /* This test does not need to ensure memory visibility, since */ + /* the bounds will be set when/if we create another thread. */ + if (!EXPECT(lib_bounds_set, TRUE)) { + GC_init_lib_bounds(); + lib_bounds_set = TRUE; + } + 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_generic_malloc_uncollectable(n * lb, UNCOLLECTABLE); + /* The two ranges are actually usually adjacent, so there may */ + /* be a way to speed this up. */ + } +# endif + return (void *)REDIRECT_MALLOC_F(n * lb); + } + +# ifndef strdup + char *strdup(const char *s) + { + size_t lb = strlen(s) + 1; + char *result = (char *)REDIRECT_MALLOC_F(lb); + if (result == 0) { + errno = ENOMEM; + return 0; + } + BCOPY(s, result, lb); + return result; + } +# endif /* !defined(strdup) */ + /* If strdup is macro defined, we assume that it actually calls malloc, */ + /* and thus the right thing will happen even without overriding it. */ + /* This seems to be true on most Linux systems. */ + +# ifndef strndup + /* This is similar to strdup(). */ + char *strndup(const char *str, size_t size) + { + char *copy; + size_t len = strlen(str); + if (len > size) + len = size; + copy = (char *)REDIRECT_MALLOC_F(len + 1); + if (copy == NULL) { + errno = ENOMEM; + return NULL; + } + if (EXPECT(len > 0, TRUE)) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; + } +# endif /* !strndup */ + +# undef GC_debug_malloc_replacement + +#endif /* REDIRECT_MALLOC */ + +/* Explicitly deallocate an object p. */ +GC_API void GC_CALL GC_free(void * p) +{ + struct hblk *h; + hdr *hhdr; + size_t sz; /* In bytes */ + size_t ngranules; /* sz in granules */ + int knd; + struct obj_kind * ok; + DCL_LOCK_STATE; + + if (p /* != NULL */) { + /* CPPCHECK */ + } else { + /* Required by ANSI. It's not my fault ... */ + return; + } + +# ifdef LOG_ALLOCS + GC_log_printf("GC_free(%p) after GC #%lu\n", + p, (unsigned long)GC_gc_no); +# endif + h = HBLKPTR(p); + hhdr = HDR(h); +# if defined(REDIRECT_MALLOC) && \ + ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \ + || defined(GC_SOLARIS_THREADS) || defined(GC_LINUX_THREADS) \ + || defined(MSWIN32)) + /* This might be called indirectly by GC_print_callers to free */ + /* the result of backtrace_symbols. */ + /* For Solaris, we have to redirect malloc calls during */ + /* initialization. For the others, this seems to happen */ + /* implicitly. */ + /* Don't try to deallocate that memory. */ + if (0 == hhdr) return; +# endif + GC_ASSERT(GC_base(p) == p); + sz = (size_t)hhdr->hb_sz; + ngranules = BYTES_TO_GRANULES(sz); + knd = hhdr -> hb_obj_kind; + ok = &GC_obj_kinds[knd]; + if (EXPECT(ngranules <= MAXOBJGRANULES, TRUE)) { + void **flh; + + LOCK(); + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz; + /* It's unnecessary to clear the mark bit. If the */ + /* object is reallocated, it doesn't matter. O.w. the */ + /* collector will do it, since it's on a free list. */ + if (ok -> ok_init && EXPECT(sz > sizeof(word), TRUE)) { + BZERO((word *)p + 1, sz-sizeof(word)); + } + flh = &(ok -> ok_freelist[ngranules]); + obj_link(p) = *flh; + *flh = (ptr_t)p; + UNLOCK(); + } else { + size_t nblocks = OBJ_SZ_TO_BLOCKS(sz); + + LOCK(); + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz; + if (nblocks > 1) { + GC_large_allocd_bytes -= nblocks * HBLKSIZE; + } + GC_freehblk(h); + UNLOCK(); + } +} + +/* Explicitly deallocate an object p when we already hold lock. */ +/* Only used for internally allocated objects, so we can take some */ +/* shortcuts. */ +#ifdef THREADS + GC_INNER void GC_free_inner(void * p) + { + struct hblk *h; + hdr *hhdr; + size_t sz; /* bytes */ + size_t ngranules; /* sz in granules */ + int knd; + struct obj_kind * ok; + + h = HBLKPTR(p); + hhdr = HDR(h); + knd = hhdr -> hb_obj_kind; + sz = (size_t)hhdr->hb_sz; + ngranules = BYTES_TO_GRANULES(sz); + ok = &GC_obj_kinds[knd]; + if (ngranules <= MAXOBJGRANULES) { + void ** flh; + + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz; + if (ok -> ok_init && EXPECT(sz > sizeof(word), TRUE)) { + BZERO((word *)p + 1, sz-sizeof(word)); + } + flh = &(ok -> ok_freelist[ngranules]); + obj_link(p) = *flh; + *flh = (ptr_t)p; + } else { + size_t nblocks = OBJ_SZ_TO_BLOCKS(sz); + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz; + if (nblocks > 1) { + GC_large_allocd_bytes -= nblocks * HBLKSIZE; + } + GC_freehblk(h); + } + } +#endif /* THREADS */ + +#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_FREE) +# define REDIRECT_FREE GC_free +#endif + +#if defined(REDIRECT_FREE) && !defined(REDIRECT_MALLOC_IN_HEADER) + +# if defined(CPPCHECK) +# define REDIRECT_FREE_F GC_free /* e.g. */ +# else +# define REDIRECT_FREE_F REDIRECT_FREE +# endif + + void free(void * p) + { +# ifndef IGNORE_FREE +# if defined(GC_LINUX_THREADS) && !defined(USE_PROC_FOR_LIBRARIES) + /* Don't bother with initialization checks. If nothing */ + /* has been initialized, the check fails, and that's safe, */ + /* 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 (((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; + } +# endif + REDIRECT_FREE_F(p); +# endif + } +#endif /* REDIRECT_FREE */ diff --git a/bdwgc/mallocx.c b/bdwgc/mallocx.c new file mode 100644 index 000000000..1f2af0bee --- /dev/null +++ b/bdwgc/mallocx.c @@ -0,0 +1,638 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * 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. + */ + +#include "private/gc_priv.h" +#include "gc_inline.h" /* for GC_malloc_kind */ + +/* + * These are extra allocation routines which are likely to be less + * frequently used than those in malloc.c. They are separate in the + * hope that the .o file will be excluded from statically linked + * executables. We should probably break this up further. + */ + +#include +#include + +#ifndef MSWINCE +# include +#endif + +/* Some externally visible but unadvertised variables to allow access to */ +/* free lists from inlined allocators without including gc_priv.h */ +/* or introducing dependencies on internal data structure layouts. */ +#include "private/gc_alloc_ptrs.h" +void ** const GC_objfreelist_ptr = GC_objfreelist; +void ** const GC_aobjfreelist_ptr = GC_aobjfreelist; +void ** const GC_uobjfreelist_ptr = GC_uobjfreelist; +# ifdef GC_ATOMIC_UNCOLLECTABLE + void ** const GC_auobjfreelist_ptr = GC_auobjfreelist; +# endif + +GC_API int GC_CALL GC_get_kind_and_size(const void * p, size_t * psize) +{ + hdr * hhdr = HDR(p); + + if (psize != NULL) { + *psize = (size_t)hhdr->hb_sz; + } + return hhdr -> hb_obj_kind; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_or_special_malloc(size_t lb, + int knd) +{ + switch(knd) { + case PTRFREE: + case NORMAL: + return GC_malloc_kind(lb, knd); + case UNCOLLECTABLE: +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: +# endif + return GC_generic_malloc_uncollectable(lb, knd); + default: + return GC_generic_malloc(lb, knd); + } +} + +/* Change the size of the block pointed to by p to contain at least */ +/* lb bytes. The object may be (and quite likely will be) moved. */ +/* The kind (e.g. atomic) is the same as that of the old. */ +/* Shrinking of large blocks is not implemented well. */ +GC_API void * GC_CALL GC_realloc(void * p, size_t lb) +{ + struct hblk * h; + hdr * hhdr; + void * result; +# if defined(_FORTIFY_SOURCE) && defined(__GNUC__) && !defined(__clang__) + volatile /* Use cleared_p instead of p as a workaround to avoid */ + /* passing alloc_size(lb) attribute associated with p */ + /* to memset (including memset call inside GC_free). */ +# endif + word cleared_p = (word)p; + size_t sz; /* Current size in bytes */ + size_t orig_sz; /* Original sz in bytes */ + int obj_kind; + + if (p == 0) return(GC_malloc(lb)); /* Required by ANSI */ + if (0 == lb) /* and p != NULL */ { +# ifndef IGNORE_FREE + GC_free(p); +# endif + return NULL; + } + h = HBLKPTR(p); + hhdr = HDR(h); + sz = (size_t)hhdr->hb_sz; + obj_kind = hhdr -> hb_obj_kind; + orig_sz = sz; + + if (sz > MAXOBJBYTES) { + /* Round it up to the next whole heap block */ + word descr = GC_obj_kinds[obj_kind].ok_descriptor; + + sz = (sz + HBLKSIZE-1) & ~HBLKMASK; + if (GC_obj_kinds[obj_kind].ok_relocate_descr) + descr += sz; + /* GC_realloc might be changing the block size while */ + /* GC_reclaim_block or GC_clear_hdr_marks is examining it. */ + /* The change to the size field is benign, in that GC_reclaim */ + /* (and GC_clear_hdr_marks) would work correctly with either */ + /* value, since we are not changing the number of objects in */ + /* the block. But seeing a half-updated value (though unlikely */ + /* to occur in practice) could be probably bad. */ + /* Using unordered atomic accesses on the size and hb_descr */ + /* fields would solve the issue. (The alternate solution might */ + /* be to initially overallocate large objects, so we do not */ + /* have to adjust the size in GC_realloc, if they still fit. */ + /* But that is probably more expensive, since we may end up */ + /* scanning a bunch of zeros during GC.) */ +# ifdef AO_HAVE_store + GC_STATIC_ASSERT(sizeof(hhdr->hb_sz) == sizeof(AO_t)); + AO_store((volatile AO_t *)&hhdr->hb_sz, (AO_t)sz); + AO_store((volatile AO_t *)&hhdr->hb_descr, (AO_t)descr); +# else + { + DCL_LOCK_STATE; + + LOCK(); + hhdr -> hb_sz = sz; + hhdr -> hb_descr = descr; + UNLOCK(); + } +# endif + +# ifdef MARK_BIT_PER_OBJ + GC_ASSERT(hhdr -> hb_inv_sz == LARGE_INV_SZ); +# endif +# ifdef MARK_BIT_PER_GRANULE + GC_ASSERT((hhdr -> hb_flags & LARGE_BLOCK) != 0 + && hhdr -> hb_map[ANY_INDEX] == 1); +# endif + if (IS_UNCOLLECTABLE(obj_kind)) GC_non_gc_bytes += (sz - orig_sz); + /* Extra area is already cleared by GC_alloc_large_and_clear. */ + } + if (ADD_SLOP(lb) <= sz) { + if (lb >= (sz >> 1)) { + if (orig_sz > lb) { + /* Clear unneeded part of object to avoid bogus pointer */ + /* tracing. */ + BZERO((ptr_t)cleared_p + lb, orig_sz - lb); + } + return(p); + } + /* shrink */ + sz = lb; + } + result = GC_generic_or_special_malloc((word)lb, obj_kind); + if (result != NULL) { + /* In case of shrink, it could also return original object. */ + /* But this gives the client warning of imminent disaster. */ + BCOPY(p, result, sz); +# ifndef IGNORE_FREE + GC_free((ptr_t)cleared_p); +# endif + } + return result; +} + +# if defined(REDIRECT_MALLOC) && !defined(REDIRECT_REALLOC) +# define REDIRECT_REALLOC GC_realloc +# endif + +# 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_EXTRAS) + +# if !defined(REDIRECT_MALLOC_IN_HEADER) + void * realloc(void * p, size_t lb) + { + return(REDIRECT_REALLOC(p, lb)); + } +# endif + +# 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 the memory. */ +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_generic_malloc_ignore_off_page(size_t lb, int k) +{ + void *result; + size_t lg; + size_t lb_rounded; + word n_blocks; + GC_bool init; + DCL_LOCK_STATE; + + if (SMALL_OBJ(lb)) + return GC_generic_malloc(lb, k); + GC_ASSERT(k < MAXOBJKINDS); + lg = ROUNDED_UP_GRANULES(lb); + lb_rounded = GRANULES_TO_BYTES(lg); + n_blocks = OBJ_SZ_TO_BLOCKS(lb_rounded); + init = GC_obj_kinds[k].ok_init; + if (EXPECT(get_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 (NULL == result) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return (*oom_fn)(lb); + } + + if (GC_debugging_started) { + BZERO(result, n_blocks * HBLKSIZE); + } else { +# ifdef THREADS + /* Clear any memory that might be used for GC descriptors */ + /* before we release the lock. */ + ((word *)result)[0] = 0; + ((word *)result)[1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-2] = 0; +# endif + } + GC_bytes_allocd += lb_rounded; + UNLOCK(); + if (init && !GC_debugging_started) { + BZERO(result, n_blocks * HBLKSIZE); + } + return(result); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_ignore_off_page(size_t lb) +{ + return GC_generic_malloc_ignore_off_page(lb, NORMAL); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_malloc_atomic_ignore_off_page(size_t lb) +{ + return GC_generic_malloc_ignore_off_page(lb, PTRFREE); +} + +/* Increment GC_bytes_allocd from code that doesn't have direct access */ +/* to GC_arrays. */ +void GC_CALL GC_incr_bytes_allocd(size_t n) +{ + GC_bytes_allocd += n; +} + +/* The same for GC_bytes_freed. */ +void GC_CALL GC_incr_bytes_freed(size_t n) +{ + GC_bytes_freed += n; +} + +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void) +{ + return (size_t)GC_bytes_freed; +} + +# ifdef PARALLEL_MARK + 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, */ + /* we add it in the next time we reacquire */ + /* the lock. (Atomically adding it doesn't */ + /* work, since we would have to atomically */ + /* update it in GC_malloc, which is too */ + /* expensive.) */ +# endif /* PARALLEL_MARK */ + +/* Return a list of 1 or more objects of the indicated size, linked */ +/* through the first word in the object. This has the advantage that */ +/* it acquires the allocation lock only once, and may greatly reduce */ +/* time wasted contending for the allocation lock. Typical usage would */ +/* be in a thread that requires many items of the same size. It would */ +/* keep its own free list in thread-local storage, and call */ +/* GC_malloc_many or friends to replenish it. (We do not round up */ +/* object sizes, since a call indicates the intention to consume many */ +/* objects of exactly this size.) */ +/* We assume that the size is a multiple of GRANULE_BYTES. */ +/* We return the free-list by assigning it to *result, since it is */ +/* not safe to return, e.g. a linked list of pointer-free objects, */ +/* since the collector would not retain the entire list if it were */ +/* invoked just as we were returning. */ +/* Note that the client should usually clear the link field. */ +GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result) +{ + void *op; + void *p; + void **opp; + size_t lw; /* Length in words. */ + size_t lg; /* Length in granules. */ + signed_word my_bytes_allocd = 0; + struct obj_kind * ok = &(GC_obj_kinds[k]); + struct hblk ** rlh; + DCL_LOCK_STATE; + + GC_ASSERT(lb != 0 && (lb & (GRANULE_BYTES-1)) == 0); + /* Currently a single object is always allocated if manual VDB. */ + /* TODO: GC_dirty should be called for each linked object (but */ + /* the last one) to support multiple objects allocation. */ + if (!SMALL_OBJ(lb) || GC_manual_vdb) { + op = GC_generic_malloc(lb, k); + if (EXPECT(0 != op, TRUE)) + obj_link(op) = 0; + *result = op; +# ifndef GC_DISABLE_INCREMENTAL + if (GC_manual_vdb && GC_is_heap_ptr(result)) { + GC_dirty_inner(result); + REACHABLE_AFTER_DIRTY(op); + } +# endif + return; + } + GC_ASSERT(k < MAXOBJKINDS); + lw = BYTES_TO_WORDS(lb); + lg = BYTES_TO_GRANULES(lb); + if (EXPECT(get_have_errors(), FALSE)) + GC_print_all_errors(); + GC_INVOKE_FINALIZERS(); + GC_DBG_COLLECT_AT_MALLOC(lb); + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + /* Do our share of marking work */ + if (GC_incremental && !GC_dont_gc) { + ENTER_GC(); + GC_collect_a_little_inner(1); + EXIT_GC(); + } + /* First see if we can reclaim a page of objects waiting to be */ + /* reclaimed. */ + rlh = ok -> ok_reclaim_list; + if (rlh != NULL) { + struct hblk * hbp; + hdr * hhdr; + + while ((hbp = rlh[lg]) != NULL) { + hhdr = HDR(hbp); + rlh[lg] = hhdr -> hb_next; + GC_ASSERT(hhdr -> hb_sz == lb); + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; +# ifdef PARALLEL_MARK + if (GC_parallel) { + 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(&GC_bytes_allocd_tmp, + (AO_t)(-my_bytes_allocd_tmp)); + GC_bytes_allocd += my_bytes_allocd_tmp; + } + GC_acquire_mark_lock(); + ++ GC_fl_builder_count; + UNLOCK(); + GC_release_mark_lock(); + } +# endif + op = GC_reclaim_generic(hbp, hhdr, lb, + ok -> ok_init, 0, &my_bytes_allocd); + if (op != 0) { +# ifdef PARALLEL_MARK + if (GC_parallel) { + *result = op; + (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(); +# ifdef THREAD_SANITIZER + GC_release_mark_lock(); + LOCK(); + GC_bytes_found += my_bytes_allocd; + UNLOCK(); +# else + GC_bytes_found += my_bytes_allocd; + /* The result may be inaccurate. */ + GC_release_mark_lock(); +# endif + (void) GC_clear_stack(0); + return; + } +# endif + /* We also reclaimed memory, so we need to adjust */ + /* that count. */ + GC_bytes_found += my_bytes_allocd; + GC_bytes_allocd += my_bytes_allocd; + goto out; + } +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + -- GC_fl_builder_count; + if (GC_fl_builder_count == 0) GC_notify_all_builder(); + GC_release_mark_lock(); + LOCK(); + /* GC lock is needed for reclaim list access. We */ + /* must decrement fl_builder_count before reacquiring */ + /* the lock. Hopefully this path is rare. */ + + rlh = ok -> ok_reclaim_list; /* reload rlh after locking */ + if (NULL == rlh) break; + } +# endif + } + } + /* Next try to use prefix of global free list if there is one. */ + /* We don't refill it, but we need to use it up before allocating */ + /* a new block ourselves. */ + opp = &(GC_obj_kinds[k].ok_freelist[lg]); + if ( (op = *opp) != 0 ) { + *opp = 0; + my_bytes_allocd = 0; + for (p = op; p != 0; p = obj_link(p)) { + my_bytes_allocd += lb; + if ((word)my_bytes_allocd >= HBLKSIZE) { + *opp = obj_link(p); + obj_link(p) = 0; + break; + } + } + GC_bytes_allocd += my_bytes_allocd; + goto out; + } + /* Next try to allocate a new block worth of objects of this size. */ + { + struct hblk *h = GC_allochblk(lb, k, 0); + if (h /* != NULL */) { /* CPPCHECK */ + if (IS_UNCOLLECTABLE(k)) GC_set_hdr_marks(HDR(h)); + GC_bytes_allocd += HBLKSIZE - HBLKSIZE % lb; +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + ++ GC_fl_builder_count; + UNLOCK(); + GC_release_mark_lock(); + + op = GC_build_fl(h, lw, + (ok -> ok_init || GC_debugging_started), 0); + + *result = op; + GC_acquire_mark_lock(); + -- GC_fl_builder_count; + if (GC_fl_builder_count == 0) GC_notify_all_builder(); + GC_release_mark_lock(); + (void) GC_clear_stack(0); + return; + } +# endif + op = GC_build_fl(h, lw, (ok -> ok_init || GC_debugging_started), 0); + goto out; + } + } + + /* As a last attempt, try allocating a single object. Note that */ + /* this may trigger a collection or expand the heap. */ + op = GC_generic_malloc_inner(lb, k); + if (0 != op) obj_link(op) = 0; + + out: + *result = op; + UNLOCK(); + (void) GC_clear_stack(0); +} + +/* Note that the "atomic" version of this would be unsafe, since the */ +/* links would not be seen by the collector. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_many(size_t lb) +{ + void *result; + + /* Add EXTRA_BYTES and round up to a multiple of a granule. */ + lb = SIZET_SAT_ADD(lb, EXTRA_BYTES + GRANULE_BYTES - 1) + & ~(GRANULE_BYTES - 1); + + GC_generic_malloc_many(lb, NORMAL, &result); + return result; +} + +#include + +/* Debug version is tricky and currently missing. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_memalign(size_t align, size_t lb) +{ + size_t new_lb; + size_t offset; + ptr_t result; + + if (align <= GRANULE_BYTES) return GC_malloc(lb); + if (align >= HBLKSIZE/2 || lb >= HBLKSIZE/2) { + if (align > HBLKSIZE) { + return (*GC_get_oom_fn())(LONG_MAX-1024); /* Fail */ + } + return GC_malloc(lb <= HBLKSIZE? HBLKSIZE : lb); + /* Will be HBLKSIZE aligned. */ + } + /* We could also try to make sure that the real rounded-up object size */ + /* is a multiple of align. That would be correct up to HBLKSIZE. */ + new_lb = SIZET_SAT_ADD(lb, align - 1); + result = (ptr_t)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; + if (!GC_all_interior_pointers) { + GC_STATIC_ASSERT(VALID_OFFSET_SZ <= HBLKSIZE); + GC_ASSERT(offset < VALID_OFFSET_SZ); + GC_register_displacement(offset); + } + } + result += offset; + GC_ASSERT((word)result % align == 0); + return result; +} + +/* 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. */ + size_t align_minus_one = align - 1; /* to workaround a cppcheck warning */ + if (align < sizeof(void *) || (align_minus_one & align) != 0) { +# ifdef MSWINCE + return ERROR_INVALID_PARAMETER; +# else + return EINVAL; +# endif + } + + if ((*memptr = GC_memalign(align, lb)) == NULL) { +# ifdef MSWINCE + return ERROR_NOT_ENOUGH_MEMORY; +# else + return ENOMEM; +# endif + } + return 0; +} + +/* provide a version of strdup() that uses the collector to allocate the + copy of the string */ +GC_API GC_ATTR_MALLOC char * GC_CALL GC_strdup(const char *s) +{ + char *copy; + size_t lb; + if (s == NULL) return NULL; + lb = strlen(s) + 1; + copy = (char *)GC_malloc_atomic(lb); + if (NULL == copy) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(s, copy, lb); + return copy; +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_strndup(const char *str, size_t size) +{ + char *copy; + size_t len = strlen(str); /* str is expected to be non-NULL */ + if (len > size) + len = size; + copy = (char *)GC_malloc_atomic(len + 1); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + if (EXPECT(len > 0, TRUE)) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; +} + +#ifdef GC_REQUIRE_WCSDUP +# include /* for wcslen() */ + + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL GC_wcsdup(const wchar_t *str) + { + size_t lb = (wcslen(str) + 1) * sizeof(wchar_t); + wchar_t *copy = (wchar_t *)GC_malloc_atomic(lb); + + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; + } +#endif /* GC_REQUIRE_WCSDUP */ + +#ifndef CPPCHECK + GC_API void * GC_CALL GC_malloc_stubborn(size_t lb) + { + return GC_malloc(lb); + } + + GC_API void GC_CALL GC_change_stubborn(const void *p GC_ATTR_UNUSED) + { + /* Empty. */ + } +#endif /* !CPPCHECK */ + +GC_API void GC_CALL GC_end_stubborn_change(const void *p) +{ + GC_dirty(p); /* entire object */ +} + +GC_API void GC_CALL GC_ptr_store_and_dirty(void *p, const void *q) +{ + *(const void **)p = q; + GC_dirty(p); + REACHABLE_AFTER_DIRTY(q); +} diff --git a/bdwgc/mark.c b/bdwgc/mark.c new file mode 100644 index 000000000..6ccc599c3 --- /dev/null +++ b/bdwgc/mark.c @@ -0,0 +1,1952 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2021 Ivan Maidanski + * + * 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. + * + */ + +#include "private/gc_pmark.h" + +#include + +/* Make arguments appear live to compiler. Put here to minimize the */ +/* risk of inlining. Used to minimize junk left in registers. */ +GC_ATTR_NOINLINE +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) +{ + /* Avoid GC_noop6 calls to be optimized away. */ +# if defined(AO_HAVE_compiler_barrier) && !defined(BASE_ATOMIC_OPS_EMULATED) + AO_compiler_barrier(); /* to serve as a special side-effect */ +# else + GC_noop1(0); +# endif +} + +volatile word GC_noop_sink; + +/* Single argument version, robust against whole program analysis. */ +GC_ATTR_NO_SANITIZE_THREAD +GC_API void GC_CALL GC_noop1(word x) +{ + GC_noop_sink = x; +} + +/* Initialize GC_obj_kinds properly and standard free lists properly. */ +/* This must be done statically since they may be accessed before */ +/* GC_init is called. */ +/* It's done here, since we need to deal with mark descriptors. */ +GC_INNER struct obj_kind GC_obj_kinds[MAXOBJKINDS] = { +/* PTRFREE */ { &GC_aobjfreelist[0], 0 /* filled in dynamically */, + /* 0 | */ GC_DS_LENGTH, FALSE, FALSE + /*, */ OK_DISCLAIM_INITZ }, +/* NORMAL */ { &GC_objfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, + /* adjusted in GC_init for EXTRA_BYTES */ + TRUE /* add length to descr */, TRUE + /*, */ OK_DISCLAIM_INITZ }, +/* UNCOLLECTABLE */ + { &GC_uobjfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, TRUE /* add length to descr */, TRUE + /*, */ OK_DISCLAIM_INITZ }, +# ifdef GC_ATOMIC_UNCOLLECTABLE + { &GC_auobjfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, FALSE, FALSE + /*, */ OK_DISCLAIM_INITZ }, +# endif +}; + +# ifndef INITIAL_MARK_STACK_SIZE +# define INITIAL_MARK_STACK_SIZE (1*HBLKSIZE) + /* INITIAL_MARK_STACK_SIZE * sizeof(mse) should be a */ + /* multiple of HBLKSIZE. */ + /* The incremental collector actually likes a larger */ + /* size, since it wants to push all marked dirty */ + /* objects before marking anything new. Currently we */ + /* let it grow dynamically. */ +# endif + +#if !defined(GC_DISABLE_INCREMENTAL) + STATIC word GC_n_rescuing_pages = 0; + /* Number of dirty pages we marked from */ + /* excludes ptrfree pages, etc. */ + /* Used for logging only. */ +#endif + +#ifdef PARALLEL_MARK + GC_INNER GC_bool GC_parallel_mark_disabled = FALSE; +#endif + +/* Is a collection in progress? Note that this can return true in the */ +/* non-incremental case, if a collection has been abandoned and the */ +/* mark state is now MS_INVALID. */ +GC_INNER GC_bool GC_collection_in_progress(void) +{ + return(GC_mark_state != MS_NONE); +} + +/* Clear all mark bits in the header. */ +GC_INNER void GC_clear_hdr_marks(hdr *hhdr) +{ + size_t last_bit; + +# ifdef AO_HAVE_load + /* Atomic access is used to avoid racing with GC_realloc. */ + last_bit = FINAL_MARK_BIT((size_t)AO_load((volatile AO_t *)&hhdr->hb_sz)); +# else + /* No race as GC_realloc holds the lock while updating hb_sz. */ + last_bit = FINAL_MARK_BIT((size_t)hhdr->hb_sz); +# endif + + BZERO(hhdr -> hb_marks, sizeof(hhdr->hb_marks)); + set_mark_bit_from_hdr(hhdr, last_bit); + hhdr -> hb_n_marks = 0; +} + +/* Set all mark bits in the header. Used for uncollectible blocks. */ +GC_INNER void GC_set_hdr_marks(hdr *hhdr) +{ + unsigned i; + size_t sz = (size_t)hhdr->hb_sz; + unsigned n_marks = (unsigned)FINAL_MARK_BIT(sz); + +# ifdef USE_MARK_BYTES + for (i = 0; i <= n_marks; i += (unsigned)MARK_BIT_OFFSET(sz)) { + hhdr -> hb_marks[i] = 1; + } +# else + for (i = 0; i < divWORDSZ(n_marks + WORDSZ); ++i) { + hhdr -> hb_marks[i] = GC_WORD_MAX; + } +# endif +# ifdef MARK_BIT_PER_OBJ + hhdr -> hb_n_marks = n_marks; +# else + hhdr -> hb_n_marks = HBLK_OBJS(sz); +# endif +} + +/* Clear all mark bits associated with block h. */ +static void clear_marks_for_block(struct hblk *h, word dummy GC_ATTR_UNUSED) +{ + hdr * hhdr = HDR(h); + + if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) return; + /* Mark bit for these is cleared only once the object is */ + /* explicitly deallocated. This either frees the block, or */ + /* the bit is cleared once the object is on the free list. */ + GC_clear_hdr_marks(hhdr); +} + +/* Slow but general routines for setting/clearing/asking about mark bits. */ +GC_API void GC_CALL GC_set_mark_bit(const void *p) +{ + struct hblk *h = HBLKPTR(p); + hdr * hhdr = HDR(h); + word bit_no = MARK_BIT_NO((ptr_t)p - (ptr_t)h, hhdr -> hb_sz); + + if (!mark_bit_from_hdr(hhdr, bit_no)) { + set_mark_bit_from_hdr(hhdr, bit_no); + ++hhdr -> hb_n_marks; + } +} + +GC_API void GC_CALL GC_clear_mark_bit(const void *p) +{ + struct hblk *h = HBLKPTR(p); + hdr * hhdr = HDR(h); + word bit_no = MARK_BIT_NO((ptr_t)p - (ptr_t)h, hhdr -> hb_sz); + + if (mark_bit_from_hdr(hhdr, bit_no)) { + size_t n_marks = hhdr -> hb_n_marks; + + GC_ASSERT(n_marks != 0); + clear_mark_bit_from_hdr(hhdr, bit_no); + n_marks--; +# ifdef PARALLEL_MARK + if (n_marks != 0 || !GC_parallel) + hhdr -> hb_n_marks = n_marks; + /* Don't decrement to zero. The counts are approximate due to */ + /* concurrency issues, but we need to ensure that a count of */ + /* zero implies an empty block. */ +# else + hhdr -> hb_n_marks = n_marks; +# endif + } +} + +GC_API int GC_CALL GC_is_marked(const void *p) +{ + struct hblk *h = HBLKPTR(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 */ +} + +/* Clear mark bits in all allocated heap blocks. This invalidates the */ +/* marker invariant, and sets GC_mark_state to reflect this. (This */ +/* implicitly starts marking to reestablish the invariant.) */ +GC_INNER void GC_clear_marks(void) +{ + GC_apply_to_all_blocks(clear_marks_for_block, (word)0); + GC_objects_are_marked = FALSE; + GC_mark_state = MS_INVALID; + GC_scan_ptr = NULL; +} + +/* Initiate a garbage collection. Initiates a full collection if the */ +/* mark state is invalid. */ +GC_INNER void GC_initiate_gc(void) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental) { +# ifdef CHECKSUMS + GC_read_dirty(FALSE); + GC_check_dirty(); +# else + GC_read_dirty(GC_mark_state == MS_INVALID); +# endif + } + GC_n_rescuing_pages = 0; +# endif + if (GC_mark_state == MS_NONE) { + GC_mark_state = MS_PUSH_RESCUERS; + } else if (GC_mark_state != MS_INVALID) { + ABORT("Unexpected state"); + } /* Else this is really a full collection, and mark bits are invalid. */ + GC_scan_ptr = NULL; +} + +#ifdef PARALLEL_MARK + STATIC void GC_do_parallel_mark(void); /* Initiate parallel marking. */ +#endif /* PARALLEL_MARK */ + +#ifdef GC_DISABLE_INCREMENTAL +# define GC_push_next_marked_dirty(h) GC_push_next_marked(h) +#else + STATIC struct hblk * GC_push_next_marked_dirty(struct hblk *h); + /* Invoke GC_push_marked on next dirty block above h. */ + /* Return a pointer just past the end of this block. */ +#endif /* !GC_DISABLE_INCREMENTAL */ +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 uncollectible pages. */ + +static void alloc_mark_stack(size_t); + +/* Perform a small amount of marking. */ +/* We try to touch roughly a page of memory. */ +/* Return TRUE if we just finished a mark phase. */ +/* Cold_gc_frame is an address inside a GC frame that */ +/* remains valid until all marking is complete. */ +/* A zero value indicates that it's OK to miss some */ +/* register values. */ +/* We hold the allocation lock. In the case of */ +/* incremental collection, the world may not be stopped.*/ +#ifdef WRAP_MARK_SOME + /* For Win32, this is called after we establish a structured */ + /* exception (or signal) handler, in case Windows unmaps one */ + /* of our root segments. See below. In either case, we */ + /* acquire the allocator lock long before we get here. */ + /* Note that this code should never generate an incremental */ + /* GC write fault. */ + STATIC GC_bool GC_mark_some_inner(ptr_t cold_gc_frame) +#else + GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) +#endif +{ + switch(GC_mark_state) { + case MS_NONE: + break; + + case MS_PUSH_RESCUERS: + 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. */ + GC_mark_stack_too_small = TRUE; + MARK_FROM_MARK_STACK(); + break; + } else { + GC_scan_ptr = GC_push_next_marked_dirty(GC_scan_ptr); + if (NULL == GC_scan_ptr) { +# if !defined(GC_DISABLE_INCREMENTAL) + GC_COND_LOG_PRINTF("Marked from %lu dirty pages\n", + (unsigned long)GC_n_rescuing_pages); +# endif + GC_push_roots(FALSE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + } + break; + + case MS_PUSH_UNCOLLECTABLE: + 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. */ + if (GC_parallel) GC_mark_stack_too_small = TRUE; +# endif + MARK_FROM_MARK_STACK(); + break; + } else { + GC_scan_ptr = GC_push_next_marked_uncollectable(GC_scan_ptr); + if (NULL == GC_scan_ptr) { + GC_push_roots(TRUE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + } + break; + + case MS_ROOTS_PUSHED: +# ifdef PARALLEL_MARK + /* Eventually, incremental marking should run */ + /* asynchronously in multiple threads, without grabbing */ + /* the allocation lock. */ + /* For now, parallel marker is disabled if there is */ + /* a chance that marking could be interrupted by */ + /* a client-supplied time limit or custom stop function. */ + if (GC_parallel && !GC_parallel_mark_disabled) { + GC_do_parallel_mark(); + 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); + } + if (GC_mark_state == MS_ROOTS_PUSHED) { + GC_mark_state = MS_NONE; + return(TRUE); + } + break; + } +# endif + if ((word)GC_mark_stack_top >= (word)GC_mark_stack) { + MARK_FROM_MARK_STACK(); + break; + } else { + GC_mark_state = MS_NONE; + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + return(TRUE); + } + + case MS_INVALID: + case MS_PARTIALLY_INVALID: + if (!GC_objects_are_marked) { + GC_mark_state = MS_PUSH_UNCOLLECTABLE; + break; + } + if ((word)GC_mark_stack_top >= (word)GC_mark_stack) { + MARK_FROM_MARK_STACK(); + break; + } + if (NULL == GC_scan_ptr && GC_mark_state == MS_INVALID) { + /* About to start a heap scan for marked objects. */ + /* Mark stack is empty. OK to reallocate. */ + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + GC_mark_state = MS_PARTIALLY_INVALID; + } + GC_scan_ptr = GC_push_next_marked(GC_scan_ptr); + if (NULL == GC_scan_ptr && GC_mark_state == MS_PARTIALLY_INVALID) { + GC_push_roots(TRUE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + break; + + default: + ABORT("GC_mark_some: bad state"); + } + return(FALSE); +} + +#ifdef WRAP_MARK_SOME + + GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) + { + GC_bool ret_val; + + /* Windows appears to asynchronously create and remove */ + /* writable memory mappings, for reasons we haven't yet */ + /* understood. Since we look for writable regions to */ + /* determine the root set, we may try to mark from an */ + /* address range that disappeared since we started the */ + /* collection. Thus we have to recover from faults here. */ + /* This code seems to be necessary for WinCE (at least in */ + /* the case we'd decide to add MEM_PRIVATE sections to */ + /* data roots in GC_register_dynamic_libraries()). */ + /* It's conceivable that this is the same issue with */ + /* terminating threads that we see with Linux and */ + /* USE_PROC_FOR_LIBRARIES. */ +# if (defined(MSWIN32) || defined(MSWINCE)) && !defined(__GNUC__) + __try { + ret_val = GC_mark_some_inner(cold_gc_frame); + } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + goto handle_ex; + } +# else + /* Here we are handling the case in which /proc is used for root */ + /* finding, and we have threads. We may find a stack for a */ + /* thread that is in the process of exiting, and disappears */ + /* while we are marking it. This seems extremely difficult to */ + /* avoid otherwise. */ +# if defined(USE_PROC_FOR_LIBRARIES) && !defined(DEFAULT_VDB) + if (GC_auto_incremental) { + static GC_bool is_warned = FALSE; + + if (!is_warned) { + is_warned = TRUE; + WARN("Incremental GC incompatible with /proc roots\n", 0); + } + /* I'm not sure if this could still work ... */ + } +# endif + GC_setup_temporary_fault_handler(); + if(SETJMP(GC_jmp_buf) != 0) goto handle_ex; + ret_val = GC_mark_some_inner(cold_gc_frame); + GC_reset_fault_handler(); +# endif + +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + /* With DllMain-based thread tracking, a thread may have */ + /* started while we were marking. This is logically equivalent */ + /* to the exception case; our results are invalid and we have */ + /* to start over. This cannot be prevented since we can't */ + /* block in DllMain. */ + if (GC_started_thread_while_stopped()) + goto handle_thr_start; +# endif + return ret_val; + +handle_ex: + /* Exception handler starts here for all cases. */ +# if !defined(MSWIN32) && !defined(MSWINCE) || defined(__GNUC__) + GC_reset_fault_handler(); +# endif + { + static word warned_gc_no; + + /* Report caught ACCESS_VIOLATION, once per collection. */ + if (warned_gc_no != GC_gc_no) { + GC_COND_LOG_PRINTF("Memory mapping disappeared at collection #%lu\n", + (unsigned long)GC_gc_no + 1); + warned_gc_no = GC_gc_no; + } + } +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + handle_thr_start: +# endif + /* We have bad roots on the stack. Discard mark stack. */ + /* Rescan from marked objects. Redetermine roots. */ +# ifdef REGISTER_LIBRARIES_EARLY + START_WORLD(); + GC_cond_register_dynamic_libraries(); + STOP_WORLD(); +# endif + GC_invalidate_mark_state(); + GC_scan_ptr = NULL; + return FALSE; + } +#endif /* WRAP_MARK_SOME */ + +GC_INNER void GC_invalidate_mark_state(void) +{ + GC_mark_state = MS_INVALID; + GC_mark_stack_top = GC_mark_stack-1; +} + +GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp) +{ + GC_mark_state = MS_INVALID; +# ifdef PARALLEL_MARK + /* We are using a local_mark_stack in parallel mode, so */ + /* do not signal the global mark stack to be resized. */ + /* That will be done if required in GC_return_mark_stack. */ + if (!GC_parallel) + GC_mark_stack_too_small = TRUE; +# else + GC_mark_stack_too_small = TRUE; +# endif + GC_COND_LOG_PRINTF("Mark stack overflow; current size: %lu entries\n", + (unsigned long)GC_mark_stack_size); + return(msp - GC_MARK_STACK_DISCARDS); +} + +/* + * Mark objects pointed to by the regions described by + * mark stack entries between mark_stack and mark_stack_top, + * inclusive. Assumes the upper limit of a mark stack entry + * is never 0. A mark stack entry never has size 0. + * We try to traverse on the order of a hblk of memory before we return. + * Caller is responsible for calling this until the mark stack is empty. + * Note that this is the most performance critical routine in the + * collector. Hence it contains all sorts of ugly hacks to speed + * things up. In particular, we avoid procedure calls on the common + * path, we take advantage of peculiarities of the mark descriptor + * encoding, we optionally maintain a cache for the block address to + * header mapping, we prefetch when an object is "grayed", etc. + */ +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD +GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, + mse *mark_stack_limit) +{ + 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 = 0; /* (Incl) limit of current candidate range. */ + word descr; + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + DECLARE_HDR_CACHE; + +# define SPLIT_RANGE_WORDS 128 /* Must be power of 2. */ + + GC_objects_are_marked = TRUE; + INIT_HDR_CACHE; +# ifdef OS2 /* Use untweaked version to circumvent compiler problem. */ + while ((word)mark_stack_top >= (word)mark_stack && credit >= 0) +# else + while (((((word)mark_stack_top - (word)mark_stack) | (word)credit) + & SIGNB) == 0) +# endif + { + current_p = mark_stack_top -> mse_start; + descr = mark_stack_top -> mse_descr.w; + retry: + /* current_p and descr describe the current object. */ + /* (*mark_stack_top) is vacant. */ + /* The following is 0 only for small objects described by a simple */ + /* length descriptor. For many applications this is the common */ + /* case, so we try to detect it quickly. */ + if (descr & ((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS) - 1)) | GC_DS_TAGS)) { + word tag = descr & GC_DS_TAGS; + + GC_STATIC_ASSERT(GC_DS_TAGS == 0x3); + switch(tag) { + case GC_DS_LENGTH: + /* Large length. */ + /* Process part of the range to avoid pushing too much on the */ + /* stack. */ + GC_ASSERT(descr < GC_greatest_real_heap_addr-GC_least_real_heap_addr + || (word)current_p + descr + <= GC_least_real_heap_addr + sizeof(word) + || (word)current_p >= GC_greatest_real_heap_addr); +# ifdef PARALLEL_MARK +# define SHARE_BYTES 2048 + if (descr > SHARE_BYTES && GC_parallel + && (word)mark_stack_top < (word)(mark_stack_limit - 1)) { + word new_size = (descr/2) & ~(word)(sizeof(word)-1); + + mark_stack_top -> mse_start = current_p; + mark_stack_top -> mse_descr.w = new_size + sizeof(word); + /* Makes sure we handle */ + /* misaligned pointers. */ + mark_stack_top++; +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%lu: large section; start %p, len %lu," + " splitting (parallel) at %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr, + (void *)(current_p + new_size)); + } +# endif + current_p += new_size; + descr -= new_size; + goto retry; + } +# endif /* PARALLEL_MARK */ + mark_stack_top -> mse_start = + limit = current_p + 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 ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%lu: large section; start %p, len %lu," + " splitting at %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr, (void *)limit); + } +# endif + /* Make sure that pointers overlapping the two ranges are */ + /* considered. */ + limit += sizeof(word) - ALIGNMENT; + break; + case GC_DS_BITMAP: + mark_stack_top--; +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + + WORDS_TO_BYTES(WORDSZ-2))) { + GC_log_printf("GC #%lu: tracing from %p bitmap descr %lu\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr); + } +# endif /* ENABLE_TRACE */ + descr &= ~GC_DS_TAGS; + credit -= WORDS_TO_BYTES(WORDSZ/2); /* guess */ + for (; descr != 0; descr <<= 1, current_p += sizeof(word)) { + if ((descr & SIGNB) == 0) continue; + LOAD_WORD_OR_CONTINUE(current, current_p); + FIXUP_POINTER(current); + 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 #%lu: considering(3) %p -> %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (void *)current); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)current, mark_stack_top, + mark_stack_limit, current_p); + } + } + continue; + case GC_DS_PROC: + mark_stack_top--; +# ifdef ENABLE_TRACE + 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 #%lu: tracing from %p, proc descr %lu\n", + (unsigned long)GC_gc_no, (void *)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)); + continue; + case GC_DS_PER_OBJECT: + if ((signed_word)descr >= 0) { + /* Descriptor is in the object. */ + descr = *(word *)(current_p + descr - GC_DS_PER_OBJECT); + } else { + /* Descriptor is in type descriptor pointed to by first */ + /* word in object. */ + ptr_t type_descr = *(ptr_t *)current_p; + /* type_descr is either a valid pointer to the descriptor */ + /* structure, or this object was on a free list. */ + /* If it was anything but the last object on the free list, */ + /* we will misinterpret the next object on the free list as */ + /* the type descriptor, and get a 0 GC descriptor, which */ + /* is ideal. Unfortunately, we need to check for the last */ + /* object case explicitly. */ + if (EXPECT(0 == type_descr, FALSE)) { + mark_stack_top--; + continue; + } + descr = *(word *)(type_descr + - ((signed_word)descr + (GC_INDIR_PER_OBJ_BIAS + - GC_DS_PER_OBJECT))); + } + if (0 == descr) { + /* Can happen either because we generated a 0 descriptor */ + /* or we saw a pointer to a free object. */ + mark_stack_top--; + continue; + } + goto retry; + } + } else { + /* Small object with length descriptor. */ + mark_stack_top--; +# ifndef SMALL_CONFIG + if (descr < sizeof(word)) + continue; +# endif +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%lu: small object; start %p, len %lu\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr); + } +# endif + limit = current_p + (word)descr; + } + /* The simple case in which we're scanning a range. */ + GC_ASSERT(!((word)current_p & (ALIGNMENT-1))); + credit -= limit - current_p; + limit -= sizeof(word); + { +# define PREF_DIST 4 + +# if !defined(SMALL_CONFIG) && !defined(E2K) + word deferred; + + /* 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 */ + /* generating slightly better code. Overall gcc code quality */ + /* for this loop is still not great. */ + for(;;) { + PREFETCH(limit - PREF_DIST*CACHE_LINE_SIZE); + GC_ASSERT((word)limit >= (word)current_p); + deferred = *(word *)limit; + FIXUP_POINTER(deferred); + limit -= ALIGNMENT; + if (deferred >= (word)least_ha && deferred < (word)greatest_ha) { + PREFETCH((ptr_t)deferred); + break; + } + 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 (deferred >= (word)least_ha && deferred < (word)greatest_ha) { + PREFETCH((ptr_t)deferred); + break; + } + if ((word)current_p > (word)limit) goto next_object; + } +# endif + + for (; (word)current_p <= (word)limit; current_p += ALIGNMENT) { + /* Empirically, unrolling this loop doesn't help a lot. */ + /* Since PUSH_CONTENTS expands to a lot of code, */ + /* we don't. */ + LOAD_WORD_OR_CONTINUE(current, current_p); + FIXUP_POINTER(current); + PREFETCH(current_p + PREF_DIST*CACHE_LINE_SIZE); + 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 #%lu: considering(1) %p -> %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (void *)current); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)current, mark_stack_top, + mark_stack_limit, current_p); + } + } + +# if !defined(SMALL_CONFIG) && !defined(E2K) + /* We still need to mark the entry we previously prefetched. */ + /* We already know that it passes the preliminary pointer */ + /* validity test. */ +# ifdef ENABLE_TRACE + if (GC_trace_addr == current_p) { + GC_log_printf("GC #%lu: considering(2) %p -> %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (void *)deferred); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)deferred, mark_stack_top, + mark_stack_limit, current_p); + next_object:; +# endif + } + } + return mark_stack_top; +} + +#ifdef PARALLEL_MARK + +STATIC GC_bool GC_help_wanted = FALSE; /* Protected by mark lock. */ +STATIC unsigned GC_helper_count = 0; /* Number of running helpers. */ + /* Protected by mark lock. */ +STATIC unsigned GC_active_count = 0; /* Number of active helpers. */ + /* Protected by mark lock. */ + /* May increase and decrease */ + /* within each mark cycle. But */ + /* once it returns to 0, it */ + /* stays zero for the cycle. */ + +GC_INNER word GC_mark_no = 0; + +#ifdef LINT2 +# define LOCAL_MARK_STACK_SIZE (HBLKSIZE / 8) +#else +# 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 */ + /* GC_mark_from. */ +#endif + +/* Wait all markers to finish initialization (i.e. store */ +/* marker_[b]sp, marker_mach_threads, GC_marker_Id). */ +GC_INNER void GC_wait_for_markers_init(void) +{ + signed_word count; + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_markers_m1 == 0) + return; + + /* Allocate the local mark stack for the thread that holds GC lock. */ +# ifndef CAN_HANDLE_FORK + GC_ASSERT(NULL == GC_main_local_mark_stack); +# else + if (NULL == GC_main_local_mark_stack) +# endif + { + size_t bytes_to_get = + ROUNDUP_PAGESIZE_IF_MMAP(LOCAL_MARK_STACK_SIZE * sizeof(mse)); + + GC_ASSERT(GC_page_size != 0); + GC_main_local_mark_stack = (mse *)GET_MEM(bytes_to_get); + if (NULL == GC_main_local_mark_stack) + ABORT("Insufficient memory for main local_mark_stack"); + GC_add_to_our_memory((ptr_t)GC_main_local_mark_stack, bytes_to_get); + } + + /* Reuse marker lock and builders count to synchronize */ + /* marker threads startup. */ + GC_acquire_mark_lock(); + GC_fl_builder_count += GC_markers_m1; + count = GC_fl_builder_count; + GC_release_mark_lock(); + if (count != 0) { + GC_ASSERT(count > 0); + GC_wait_for_reclaim(); + } +} + +/* Steal mark stack entries starting at mse low into mark stack local */ +/* until we either steal mse high, or we have max entries. */ +/* Return a pointer to the top of the local mark stack. */ +/* (*next) is replaced by a pointer to the next unscanned mark stack */ +/* entry. */ +STATIC mse * GC_steal_mark_stack(mse * low, mse * high, mse * local, + unsigned max, mse **next) +{ + mse *p; + mse *top = local - 1; + unsigned i = 0; + + 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(&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.w = descr; + top -> mse_start = p -> mse_start; + GC_ASSERT((descr & GC_DS_TAGS) != GC_DS_LENGTH + || descr < GC_greatest_real_heap_addr-GC_least_real_heap_addr + || (word)(p -> mse_start + descr) + <= GC_least_real_heap_addr + sizeof(word) + || (word)(p -> mse_start) >= GC_greatest_real_heap_addr); + /* If this is a big object, count it as */ + /* size/256 + 1 objects. */ + ++i; + if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) i += (int)(descr >> 8); + } + } + *next = p; + return top; +} + +/* Copy back a local mark stack. */ +/* low and high are inclusive bounds. */ +STATIC void GC_return_mark_stack(mse * low, mse * high) +{ + mse * my_top; + mse * my_start; + size_t stack_size; + + 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 ((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. */ + } else { + BCOPY(low, my_start, stack_size * sizeof(mse)); + GC_ASSERT((mse *)AO_load((volatile AO_t *)(&GC_mark_stack_top)) + == my_top); + AO_store_release_write((volatile AO_t *)(&GC_mark_stack_top), + (AO_t)(my_top + stack_size)); + /* Ensures visibility of previously written stack contents. */ + } + GC_release_mark_lock(); + GC_notify_all_marker(); +} + +#ifndef N_LOCAL_ITERS +# define N_LOCAL_ITERS 1 +#endif + +/* This function is only called when the local */ +/* and the main mark stacks are both empty. */ +static GC_bool has_inactive_helpers(void) +{ + GC_bool res; + + GC_acquire_mark_lock(); + res = GC_active_count < GC_helper_count; + GC_release_mark_lock(); + return res; +} + +/* Mark from the local mark stack. */ +/* On return, the local mark stack is empty. */ +/* But this may be achieved by copying the */ +/* local mark stack back into the global one. */ +/* We do not hold the mark lock. */ +STATIC void GC_do_local_mark(mse *local_mark_stack, mse *local_top) +{ + unsigned n; + + for (;;) { + 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 ((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 ((word)AO_load((volatile AO_t *)&GC_mark_stack_top) + < (word)AO_load(&GC_first_nonempty) + && (word)local_top > (word)(local_mark_stack + 1) + && has_inactive_helpers()) { + /* 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, even though */ + /* it's harder. */ + mse * new_bottom = local_mark_stack + + (local_top - local_mark_stack)/2; + 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)); + local_top -= (new_bottom - local_mark_stack); + } + } +} + +#ifndef ENTRIES_TO_GET +# define ENTRIES_TO_GET 5 +#endif + +/* 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. Caller holds the mark lock. */ +/* Caller has already incremented GC_helper_count. We decrement it, */ +/* and maintain GC_active_count. */ +STATIC void GC_mark_local(mse *local_mark_stack, int id) +{ + mse * my_first_nonempty; + + GC_active_count++; + my_first_nonempty = (mse *)AO_load(&GC_first_nonempty); + 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 %d\n", id); + GC_release_mark_lock(); + for (;;) { + size_t n_on_stack; + unsigned n_to_get; + mse * my_top; + mse * local_top; + mse * global_first_nonempty = (mse *)AO_load(&GC_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); + if ((word)my_first_nonempty < (word)global_first_nonempty) { + my_first_nonempty = global_first_nonempty; + } else if ((word)global_first_nonempty < (word)my_first_nonempty) { + (void)AO_compare_and_swap(&GC_first_nonempty, + (AO_t)global_first_nonempty, + (AO_t)my_first_nonempty); + /* If this fails, we just go ahead, without updating */ + /* GC_first_nonempty. */ + } + /* Perhaps we should also update GC_first_nonempty, if it */ + /* is less. But that would require using atomic updates. */ + my_top = (mse *)AO_load_acquire((volatile AO_t *)(&GC_mark_stack_top)); + if ((word)my_top < (word)my_first_nonempty) { + GC_acquire_mark_lock(); + my_top = GC_mark_stack_top; + /* Asynchronous modification impossible here, */ + /* since we hold mark lock. */ + n_on_stack = my_top - my_first_nonempty + 1; + if (0 == n_on_stack) { + GC_active_count--; + GC_ASSERT(GC_active_count <= GC_helper_count); + /* Other markers may redeposit objects */ + /* on the stack. */ + if (0 == GC_active_count) GC_notify_all_marker(); + while (GC_active_count > 0 + && (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 + && (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 */ + /* GC_active_count nor GC_mark_stack_top can */ + /* change. GC_first_nonempty can only be */ + /* incremented asynchronously. Thus we know that */ + /* both conditions actually held simultaneously. */ + GC_helper_count--; + if (0 == GC_helper_count) need_to_notify = TRUE; + GC_VERBOSE_LOG_PRINTF("Finished mark helper %d\n", id); + if (need_to_notify) GC_notify_all_marker(); + return; + } + /* Else there's something on the stack again, or */ + /* another helper may push something. */ + GC_active_count++; + GC_ASSERT(GC_active_count > 0); + GC_release_mark_lock(); + continue; + } else { + GC_release_mark_lock(); + } + } else { + n_on_stack = my_top - my_first_nonempty + 1; + } + n_to_get = ENTRIES_TO_GET; + if (n_on_stack < 2 * ENTRIES_TO_GET) n_to_get = 1; + local_top = GC_steal_mark_stack(my_first_nonempty, my_top, + local_mark_stack, n_to_get, + &my_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_do_local_mark(local_mark_stack, local_top); + } +} + +/* Perform Parallel mark. */ +/* We hold the GC lock, not the mark lock. */ +/* Currently runs until the mark stack is */ +/* empty. */ +STATIC void GC_do_parallel_mark(void) +{ + GC_acquire_mark_lock(); + GC_ASSERT(I_HOLD_LOCK()); + /* This could be a GC_ASSERT, but it seems safer to keep it on */ + /* 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"); + 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; + GC_help_wanted = TRUE; + GC_notify_all_marker(); + /* Wake up potential helpers. */ + GC_mark_local(GC_main_local_mark_stack, 0); + GC_help_wanted = FALSE; + /* Done; clean up. */ + while (GC_helper_count > 0) { + GC_wait_marker(); + } + /* GC_helper_count cannot be incremented while not GC_help_wanted. */ + 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(); +} + + +/* Try to help out the marker, if it's running. */ +/* We do not hold the GC lock, but the requestor does. */ +/* And we hold the mark lock. */ +GC_INNER void GC_help_marker(word my_mark_no) +{ +# define my_id my_id_mse.mse_descr.w + mse my_id_mse; /* align local_mark_stack explicitly */ + mse local_mark_stack[LOCAL_MARK_STACK_SIZE]; + /* Note: local_mark_stack is quite big (up to 128 KiB). */ + + GC_ASSERT(GC_parallel); + 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_m1) { + /* Second test is useful only if original threads can also */ + /* act as helpers. Under Linux they can't. */ + return; + } + GC_helper_count = (unsigned)my_id + 1; + GC_mark_local(local_mark_stack, (int)my_id); + /* GC_mark_local decrements GC_helper_count. */ +# undef my_id +} + +#endif /* PARALLEL_MARK */ + +/* Allocate or reallocate space for mark stack of size n entries. */ +/* May silently fail. */ +static void alloc_mark_stack(size_t n) +{ + mse * new_stack = (mse *)GC_scratch_alloc(n * sizeof(struct GC_ms_entry)); +# ifdef GWW_VDB + /* Don't recycle a stack segment obtained with the wrong flags. */ + /* Win32 GetWriteWatch requires the right kind of memory. */ + static GC_bool GC_incremental_at_stack_alloc = FALSE; + GC_bool recycle_old = !GC_auto_incremental + || GC_incremental_at_stack_alloc; + + GC_incremental_at_stack_alloc = GC_auto_incremental; +# else +# define recycle_old TRUE +# endif + + GC_mark_stack_too_small = FALSE; + if (GC_mark_stack != NULL) { + if (new_stack != 0) { + if (recycle_old) { + /* Recycle old space. */ + GC_scratch_recycle_inner(GC_mark_stack, + GC_mark_stack_size * sizeof(struct GC_ms_entry)); + } + 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; + GC_COND_LOG_PRINTF("Grew mark stack to %lu frames\n", + (unsigned long)GC_mark_stack_size); + } else { + WARN("Failed to grow mark stack to %" WARN_PRIuPTR " frames\n", n); + } + } else if (NULL == new_stack) { + GC_err_printf("No space for mark stack\n"); + EXIT(); + } else { + GC_mark_stack = new_stack; + GC_mark_stack_size = n; + GC_mark_stack_limit = new_stack + n; + } + GC_mark_stack_top = GC_mark_stack-1; +} + +GC_INNER void GC_mark_init(void) +{ + alloc_mark_stack(INITIAL_MARK_STACK_SIZE); +} + +/* + * Push all locations between b and t onto the mark stack. + * b is the first location to be checked. t is one past the last + * location to be checked. + * Should only be used if there is no possibility of mark stack + * overflow. + */ +GC_API void GC_CALL GC_push_all(void *bottom, void *top) +{ + word length; + + bottom = (void *)(((word)bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + top = (void *)((word)top & ~(ALIGNMENT-1)); + if ((word)bottom >= (word)top) return; + + GC_mark_stack_top++; + if ((word)GC_mark_stack_top >= (word)GC_mark_stack_limit) { + ABORT("Unexpected mark stack overflow"); + } + length = (word)top - (word)bottom; +# if GC_DS_TAGS > ALIGNMENT - 1 + length += GC_DS_TAGS; + length &= ~GC_DS_TAGS; +# endif + GC_mark_stack_top -> mse_start = (ptr_t)bottom; + GC_mark_stack_top -> mse_descr.w = length; +} + +#ifndef GC_DISABLE_INCREMENTAL + + /* Analogous to the above, but push only those pages h with */ + /* dirty_fn(h) != 0. We use GC_push_all to actually push the block. */ + /* Used both to selectively push dirty pages, or to push a block in */ + /* piecemeal fashion, to allow for more marking concurrency. */ + /* Will not overflow mark stack if GC_push_all pushes a small fixed */ + /* number of entries. (This is invoked only if GC_push_all pushes */ + /* a single entry, or if it marks each object before pushing it, thus */ + /* ensuring progress in the event of a stack overflow.) */ + STATIC void GC_push_selected(ptr_t bottom, ptr_t top, + GC_bool (*dirty_fn)(struct hblk *)) + { + struct hblk * h; + + bottom = (ptr_t)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + top = (ptr_t)(((word) top) & ~(ALIGNMENT-1)); + if ((word)bottom >= (word)top) return; + + h = HBLKPTR(bottom + HBLKSIZE); + if ((word)top <= (word)h) { + if ((*dirty_fn)(h-1)) { + GC_push_all(bottom, top); + } + return; + } + if ((*dirty_fn)(h-1)) { + if ((word)(GC_mark_stack_top - GC_mark_stack) + > 3 * GC_mark_stack_size / 4) { + GC_push_all(bottom, top); + return; + } + GC_push_all(bottom, h); + } + + 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) { + /* Danger of mark stack overflow. */ + GC_push_all(h, top); + return; + } else { + GC_push_all(h, h + 1); + } + } + h++; + } + + if ((ptr_t)h != top && (*dirty_fn)(h)) { + GC_push_all(h, top); + } + } + + GC_API void GC_CALL GC_push_conditional(void *bottom, void *top, int all) + { + if (!all) { + GC_push_selected((ptr_t)bottom, (ptr_t)top, GC_page_was_dirty); + } else { +# ifdef PROC_VDB + if (GC_auto_incremental) { + /* Pages that were never dirtied cannot contain pointers. */ + GC_push_selected((ptr_t)bottom, (ptr_t)top, GC_page_was_ever_dirty); + } else +# endif + /* else */ { + GC_push_all(bottom, top); + } + } + } + +# ifndef NO_VDB_FOR_STATIC_ROOTS +# ifndef PROC_VDB + /* Same as GC_page_was_dirty but h is allowed to point to some */ + /* page in the registered static roots only. Not used if */ + /* manual VDB is on. */ + STATIC GC_bool GC_static_page_was_dirty(struct hblk *h) + { + return get_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h)); + } +# endif + + GC_INNER void GC_push_conditional_static(void *bottom, void *top, + GC_bool all) + { +# ifdef PROC_VDB + /* Just redirect to the generic routine because PROC_VDB */ + /* implementation gets the dirty bits map for the whole */ + /* process memory. */ + GC_push_conditional(bottom, top, all); +# else + if (all || !GC_is_vdb_for_static_roots()) { + GC_push_all(bottom, top); + } else { + GC_push_selected((ptr_t)bottom, (ptr_t)top, + GC_static_page_was_dirty); + } +# endif + } +# endif /* !NO_VDB_FOR_STATIC_ROOTS */ + +#else + GC_API void GC_CALL GC_push_conditional(void *bottom, void *top, + int all GC_ATTR_UNUSED) + { + GC_push_all(bottom, top); + } +#endif /* GC_DISABLE_INCREMENTAL */ + +#if defined(AMIGA) || defined(MACOS) || defined(GC_DARWIN_THREADS) + void GC_push_one(word p) + { + GC_PUSH_ONE_STACK(p, MARKED_FROM_REGISTER); + } +#endif + +#ifdef GC_WIN32_THREADS + GC_INNER void GC_push_many_regs(const word *regs, unsigned count) + { + unsigned i; + for (i = 0; i < count; i++) + GC_PUSH_ONE_STACK(regs[i], MARKED_FROM_REGISTER); + } +#endif + +GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void *obj, + mse *mark_stack_ptr, mse *mark_stack_limit, void **src) +{ + hdr * hhdr; + + PREFETCH(obj); + GET_HDR(obj, hhdr); + if ((EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr), FALSE) + && (!GC_all_interior_pointers + || NULL == (hhdr = GC_find_header((ptr_t)GC_base(obj))))) + || EXPECT(HBLK_IS_FREE(hhdr), FALSE)) { + GC_ADD_TO_BLACK_LIST_NORMAL(obj, (ptr_t)src); + return mark_stack_ptr; + } + return GC_push_contents_hdr((ptr_t)obj, mark_stack_ptr, mark_stack_limit, + (ptr_t)src, hhdr, TRUE); +} + +/* Mark and push (i.e. gray) a single object p onto the main */ +/* mark stack. Consider p to be valid if it is an interior */ +/* pointer. */ +/* The object p has passed a preliminary pointer validity */ +/* test, but we do not definitely know whether it is valid. */ +/* Mark bits are NOT atomically updated. Thus this must be the */ +/* only thread setting them. */ +GC_ATTR_NO_SANITIZE_ADDR +GC_INNER void +# if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) + GC_mark_and_push_stack(ptr_t p, ptr_t source) +# else + GC_mark_and_push_stack(ptr_t p) +# define source ((ptr_t)0) +# endif +{ + hdr * hhdr; + ptr_t r = p; + + PREFETCH(p); + GET_HDR(p, hhdr); + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr), FALSE)) { + if (NULL == hhdr + || (r = (ptr_t)GC_base(p)) == NULL + || (hhdr = HDR(r)) == NULL) { + GC_ADD_TO_BLACK_LIST_STACK(p, source); + return; + } + } + if (EXPECT(HBLK_IS_FREE(hhdr), FALSE)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + return; + } +# ifdef THREADS + /* Pointer is on the stack. We may have dirtied the object */ + /* it points to, but have not called GC_dirty yet. */ + GC_dirty(p); /* entire object */ +# endif + GC_mark_stack_top = GC_push_contents_hdr(r, GC_mark_stack_top, + GC_mark_stack_limit, + source, hhdr, FALSE); + /* We silently ignore pointers to near the end of a block, */ + /* which is very mildly suboptimal. */ + /* FIXME: We should probably add a header word to address */ + /* this. */ +} +# undef source + +#ifdef TRACE_BUF + +# ifndef TRACE_ENTRIES +# define TRACE_ENTRIES 1000 +# endif + +struct trace_entry { + char * kind; + word gc_no; + word bytes_allocd; + word arg1; + word arg2; +} GC_trace_buf[TRACE_ENTRIES] = { { NULL, 0, 0, 0, 0 } }; + +void GC_add_trace_entry(char *kind, word arg1, word arg2) +{ + GC_trace_buf[GC_trace_buf_ptr].kind = kind; + GC_trace_buf[GC_trace_buf_ptr].gc_no = GC_gc_no; + GC_trace_buf[GC_trace_buf_ptr].bytes_allocd = GC_bytes_allocd; + GC_trace_buf[GC_trace_buf_ptr].arg1 = arg1 ^ 0x80000000; + GC_trace_buf[GC_trace_buf_ptr].arg2 = arg2 ^ 0x80000000; + GC_trace_buf_ptr++; + if (GC_trace_buf_ptr >= TRACE_ENTRIES) GC_trace_buf_ptr = 0; +} + +GC_API void GC_CALL GC_print_trace_inner(word gc_no) +{ + int i; + + for (i = GC_trace_buf_ptr-1; i != GC_trace_buf_ptr; i--) { + struct trace_entry *p; + + if (i < 0) i = TRACE_ENTRIES-1; + p = GC_trace_buf + i; + 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); + } + GC_printf("Trace incomplete\n"); +} + +GC_API void GC_CALL GC_print_trace(word gc_no) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_print_trace_inner(gc_no); + UNLOCK(); +} + +#endif /* TRACE_BUF */ + +/* A version of GC_push_all that treats all interior pointers as valid */ +/* and scans the entire region immediately, in case the contents change.*/ +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD +GC_API void GC_CALL GC_push_all_eager(void *bottom, void *top) +{ + word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + word * t = (word *)(((word) top) & ~(ALIGNMENT-1)); + REGISTER word *p; + REGISTER word *lim; + REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + REGISTER ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + if (top == 0) return; + + /* Check all pointers in range and push if they appear to be valid. */ + lim = t - 1 /* longword */; + for (p = b; (word)p <= (word)lim; + p = (word *)(((ptr_t)p) + ALIGNMENT)) { + REGISTER word q; + + LOAD_WORD_OR_CONTINUE(q, p); + GC_PUSH_ONE_STACK(q, p); + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +} + +GC_INNER void GC_push_all_stack(ptr_t bottom, ptr_t top) +{ +# ifndef NEED_FIXUP_POINTER + if (GC_all_interior_pointers +# if defined(THREADS) && defined(MPROTECT_VDB) + && !GC_auto_incremental +# endif + && (word)GC_mark_stack_top + < (word)(GC_mark_stack_limit - INITIAL_MARK_STACK_SIZE/8)) { + GC_push_all(bottom, top); + } else +# endif + /* else */ { + GC_push_all_eager(bottom, top); + } +} + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) + /* Similar to GC_push_conditional but scans the whole region immediately. */ + GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY + GC_ATTR_NO_SANITIZE_THREAD + GC_INNER void GC_push_conditional_eager(void *bottom, void *top, + GC_bool all) + { + word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + word * t = (word *)(((word) top) & ~(ALIGNMENT-1)); + REGISTER word *p; + REGISTER word *lim; + REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + REGISTER ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + if (top == NULL) + return; + (void)all; /* TODO: If !all then scan only dirty pages. */ + + lim = t - 1; + for (p = b; (word)p <= (word)lim; p = (word *)((ptr_t)p + ALIGNMENT)) { + REGISTER word q = *p; + + GC_PUSH_ONE_HEAP(q, p, GC_mark_stack_top); + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr + } +#endif /* WRAP_MARK_SOME && PARALLEL_MARK */ + +#if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) && \ + defined(MARK_BIT_PER_GRANULE) +# if GC_GRANULE_WORDS == 1 +# define USE_PUSH_MARKED_ACCELERATORS +# define PUSH_GRANULE(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) \ + 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_mark_stack_top); \ + } while (0) +# elif GC_GRANULE_WORDS == 4 +# define USE_PUSH_MARKED_ACCELERATORS +# define PUSH_GRANULE(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_mark_stack_top); \ + 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_mark_stack_top); \ + } while (0) +# endif +#endif /* !USE_MARK_BYTES && MARK_BIT_PER_GRANULE */ + +#ifdef USE_PUSH_MARKED_ACCELERATORS +/* Push all objects reachable from marked objects in the given block */ +/* containing objects of size 1 granule. */ +GC_ATTR_NO_SANITIZE_THREAD +STATIC void GC_push_marked1(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + /* Allow registers to be used for some frequently accessed */ + /* global variables. Otherwise aliasing issues are likely */ + /* to prevent that. */ + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)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 +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* Go through all words in block. */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + } + q += GC_GRANULE_WORDS; + mark_word >>= 1; + } + p += WORDSZ*GC_GRANULE_WORDS; + } + +# undef GC_greatest_plausible_heap_addr +# 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; +} + + +#ifndef UNALIGNED_PTRS + +/* Push all objects reachable from marked objects in the given block */ +/* of size 2 (granules) objects. */ +GC_ATTR_NO_SANITIZE_THREAD +STATIC void GC_push_marked2(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)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 +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* Go through all words in block. */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + PUSH_GRANULE(q + GC_GRANULE_WORDS); + } + q += 2 * GC_GRANULE_WORDS; + mark_word >>= 2; + } + p += WORDSZ*GC_GRANULE_WORDS; + } + +# undef GC_greatest_plausible_heap_addr +# 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; +} + +# if GC_GRANULE_WORDS < 4 +/* Push all objects reachable from marked objects in the given block */ +/* of size 4 (granules) objects. */ +/* There is a risk of mark stack overflow here. But we handle that. */ +/* And only unmarked objects get pushed, so it's not very likely. */ +GC_ATTR_NO_SANITIZE_THREAD +STATIC void GC_push_marked4(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)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 +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* Go through all words in block. */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + PUSH_GRANULE(q + GC_GRANULE_WORDS); + PUSH_GRANULE(q + 2*GC_GRANULE_WORDS); + PUSH_GRANULE(q + 3*GC_GRANULE_WORDS); + } + q += 4 * GC_GRANULE_WORDS; + mark_word >>= 4; + } + p += WORDSZ*GC_GRANULE_WORDS; + } +# undef GC_greatest_plausible_heap_addr +# 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; +} + +#endif /* GC_GRANULE_WORDS < 4 */ + +#endif /* UNALIGNED_PTRS */ + +#endif /* USE_PUSH_MARKED_ACCELERATORS */ + +/* Push all objects reachable from marked objects in the given block. */ +STATIC void GC_push_marked(struct hblk *h, hdr *hhdr) +{ + word sz = hhdr -> hb_sz; + word descr = hhdr -> hb_descr; + ptr_t p; + word bit_no; + ptr_t lim; + mse * GC_mark_stack_top_reg; + mse * mark_stack_limit = GC_mark_stack_limit; + + /* Some quick shortcuts: */ + if ((/* 0 | */ GC_DS_LENGTH) == descr) return; + if (GC_block_empty(hhdr)/* nothing marked */) return; +# if !defined(GC_DISABLE_INCREMENTAL) + GC_n_rescuing_pages++; +# endif + GC_objects_are_marked = TRUE; + if (sz > MAXOBJBYTES) { + lim = h -> hb_body; + } else { + lim = (ptr_t)((word)(h + 1)->hb_body - sz); + } + + switch(BYTES_TO_GRANULES(sz)) { +# if defined(USE_PUSH_MARKED_ACCELERATORS) + case 1: + GC_push_marked1(h, hhdr); + break; +# if !defined(UNALIGNED_PTRS) + case 2: + GC_push_marked2(h, hhdr); + break; +# if GC_GRANULE_WORDS < 4 + case 4: + GC_push_marked4(h, hhdr); + break; +# endif +# endif +# else + case 1: /* to suppress "switch statement contains no case" warning */ +# endif + default: + GC_mark_stack_top_reg = GC_mark_stack_top; + 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. */ + GC_mark_stack_top_reg = GC_push_obj(p, hhdr, GC_mark_stack_top_reg, + mark_stack_limit); + } + } + GC_mark_stack_top = GC_mark_stack_top_reg; + } +} + +#ifdef ENABLE_DISCLAIM +/* Unconditionally mark from all objects which have not been reclaimed. */ +/* 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 */ +/* any live object has a non-zero as one of the two lowest bits of the */ +/* first word. On the other hand, a reclaimed object is a members of */ +/* free-lists, and thus contains a word-aligned next-pointer as the */ +/* first word. */ + GC_ATTR_NO_SANITIZE_THREAD + STATIC void GC_push_unconditionally(struct hblk *h, hdr *hhdr) + { + word sz = hhdr -> hb_sz; + word descr = hhdr -> hb_descr; + ptr_t p; + ptr_t lim; + mse * GC_mark_stack_top_reg; + mse * mark_stack_limit = GC_mark_stack_limit; + + if ((/* 0 | */ GC_DS_LENGTH) == descr) + return; + +# if !defined(GC_DISABLE_INCREMENTAL) + GC_n_rescuing_pages++; +# endif + GC_objects_are_marked = TRUE; + if (sz > MAXOBJBYTES) + lim = h -> hb_body; + else + lim = (ptr_t)((word)(h + 1)->hb_body - sz); + + GC_mark_stack_top_reg = GC_mark_stack_top; + for (p = h -> hb_body; (word)p <= (word)lim; p += sz) + if ((*(word *)p & 0x3) != 0) + GC_mark_stack_top_reg = GC_push_obj(p, hhdr, GC_mark_stack_top_reg, + mark_stack_limit); + GC_mark_stack_top = GC_mark_stack_top_reg; + } +#endif /* ENABLE_DISCLAIM */ + +#ifndef GC_DISABLE_INCREMENTAL + /* Test whether any page in the given block is dirty. */ + STATIC GC_bool GC_block_was_dirty(struct hblk *h, hdr *hhdr) + { + word sz; + +# ifdef AO_HAVE_load + /* Atomic access is used to avoid racing with GC_realloc. */ + sz = (word)AO_load((volatile AO_t *)&(hhdr -> hb_sz)); +# else + sz = hhdr -> hb_sz; +# endif + if (sz <= MAXOBJBYTES) { + return(GC_page_was_dirty(h)); + } else { + ptr_t p = (ptr_t)h; + while ((word)p < (word)h + sz) { + if (GC_page_was_dirty((struct hblk *)p)) return(TRUE); + p += HBLKSIZE; + } + return(FALSE); + } + } +#endif /* GC_DISABLE_INCREMENTAL */ + +/* Similar to GC_push_marked, but skip over unallocated blocks */ +/* and return address of next plausible block. */ +STATIC struct hblk * GC_push_next_marked(struct hblk *h) +{ + hdr * hhdr = HDR(h); + + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_block(h, FALSE); + if (NULL == h) return NULL; + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + GC_push_marked(h, hhdr); + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); +} + +#ifndef GC_DISABLE_INCREMENTAL + /* Identical to above, but mark only from dirty pages. */ + STATIC struct hblk * GC_push_next_marked_dirty(struct hblk *h) + { + hdr * hhdr = HDR(h); + + if (!GC_incremental) ABORT("Dirty bits not set up"); + for (;;) { + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) + || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_block(h, FALSE); + if (NULL == h) return NULL; + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + if (GC_block_was_dirty(h, hhdr)) + break; + h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + hhdr = HDR(h); + } +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & MARK_UNCONDITIONALLY) != 0) { + GC_push_unconditionally(h, hhdr); + + /* Then we may ask, why not also add the MARK_UNCONDITIONALLY */ + /* case to GC_push_next_marked, which is also applied to */ + /* uncollectible blocks? But it seems to me that the function */ + /* does not need to scan uncollectible (and unconditionally */ + /* marked) blocks since those are already handled in the */ + /* MS_PUSH_UNCOLLECTABLE phase. */ + } else +# endif + /* else */ { + GC_push_marked(h, hhdr); + } + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +/* 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) +{ + hdr * hhdr = HDR(h); + + for (;;) { + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) + || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_block(h, FALSE); + if (NULL == h) return NULL; + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + if (hhdr -> hb_obj_kind == UNCOLLECTABLE) { + GC_push_marked(h, hhdr); + break; + } +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & MARK_UNCONDITIONALLY) != 0) { + GC_push_unconditionally(h, hhdr); + break; + } +# endif + h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + hhdr = HDR(h); + } + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); +} diff --git a/bdwgc/mark_rts.c b/bdwgc/mark_rts.c new file mode 100644 index 000000000..0428d5769 --- /dev/null +++ b/bdwgc/mark_rts.c @@ -0,0 +1,961 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * 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. + */ + +#include "private/gc_priv.h" + +#include + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +/* This is really declared in gc_priv.h: +struct roots { + ptr_t r_start; + ptr_t r_end; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + struct roots * r_next; +# endif + GC_bool r_tmp; + -- Delete before registering new dynamic libraries +}; + +struct roots GC_static_roots[MAX_ROOT_SETS]; +*/ + +int GC_no_dls = 0; /* Register dynamic library data segments. */ + +#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; + word size; + + for (i = 0; i < n_root_sets; i++) { + GC_printf("From %p to %p%s\n", + (void *)GC_static_roots[i].r_start, + (void *)GC_static_roots[i].r_end, + GC_static_roots[i].r_tmp ? " (temporary)" : ""); + } + 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 */ + +#ifndef THREADS + /* Primarily for debugging support: */ + /* Is the address p in one of the registered static root sections? */ + GC_INNER GC_bool GC_is_static_root(void *p) + { + static int last_root_set = MAX_ROOT_SETS; + int i; + + if (last_root_set < n_root_sets + && (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 ((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); + } +#endif /* !THREADS */ + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) +/* +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) -- Power of 2, may be != MAX_ROOT_SETS + + struct roots * GC_root_index[RT_SIZE]; + -- Hash table header. Used only to check whether a range is + -- already present. + -- really defined in gc_priv.h +*/ + + GC_INLINE int rt_hash(ptr_t addr) + { + word result = (word) addr; +# if CPP_WORDSZ > 8*LOG_RT_SIZE + result ^= result >> 8*LOG_RT_SIZE; +# endif +# if CPP_WORDSZ > 4*LOG_RT_SIZE + result ^= result >> 4*LOG_RT_SIZE; +# endif + result ^= result >> 2*LOG_RT_SIZE; + result ^= result >> LOG_RT_SIZE; + result &= (RT_SIZE-1); + return(result); + } + + /* Is a range starting at b already in the table? If so return a */ + /* pointer to it, else NULL. */ + GC_INNER void * GC_roots_present(ptr_t b) + { + int h = rt_hash(b); + struct roots *p = GC_root_index[h]; + + while (p != 0) { + if (p -> r_start == (ptr_t)b) return(p); + p = p -> r_next; + } + return NULL; + } + + /* Add the given root structure to the index. */ + GC_INLINE void add_roots_to_index(struct roots *p) + { + int h = rt_hash(p -> r_start); + + p -> r_next = GC_root_index[h]; + GC_root_index[h] = p; + } +#endif /* !MSWIN32 && !MSWINCE && !CYGWIN32 */ + +GC_INNER word GC_root_size = 0; + +GC_API void GC_CALL GC_add_roots(void *b, void *e) +{ + DCL_LOCK_STATE; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + GC_add_roots_inner((ptr_t)b, (ptr_t)e, FALSE); + UNLOCK(); +} + + +/* Add [b,e) to the root set. Adding the same interval a second time */ +/* is a moderately fast no-op, and hence benign. We do not handle */ +/* different but overlapping intervals efficiently. (We do handle */ +/* them correctly.) */ +/* Tmp specifies that the interval may be deleted before */ +/* re-registering dynamic libraries. */ +void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp) +{ + GC_ASSERT((word)b <= (word)e); + b = (ptr_t)(((word)b + (sizeof(word) - 1)) & ~(word)(sizeof(word) - 1)); + /* round b up to word boundary */ + e = (ptr_t)((word)e & ~(word)(sizeof(word) - 1)); + /* 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 */ + /* or adjacent intervals. */ + /* This could be done faster with e.g. a */ + /* balanced tree. But the execution time here is */ + /* virtually guaranteed to be dominated by the time it */ + /* takes to scan the roots. */ + { + int i; + struct roots * old = NULL; /* initialized to prevent warning. */ + + for (i = 0; i < n_root_sets; i++) { + old = GC_static_roots + i; + 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; + } + if ((word)e > (word)old->r_end) { + GC_root_size += e - old->r_end; + old -> r_end = e; + } + old -> r_tmp &= tmp; + break; + } + } + if (i < n_root_sets) { + /* merge other overlapping intervals */ + struct roots *other; + + for (i++; i < n_root_sets; i++) { + other = GC_static_roots + i; + b = other -> r_start; + e = other -> r_end; + 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; + } + if ((word)e > (word)old->r_end) { + GC_root_size += e - old->r_end; + old -> r_end = e; + } + old -> r_tmp &= other -> r_tmp; + /* Delete this entry. */ + GC_root_size -= (other -> r_end - other -> r_start); + other -> r_start = GC_static_roots[n_root_sets-1].r_start; + other -> r_end = GC_static_roots[n_root_sets-1].r_end; + n_root_sets--; + } + } + return; + } + } +# else + { + struct roots * old = (struct roots *)GC_roots_present(b); + + if (old != 0) { + if ((word)e <= (word)old->r_end) { + old -> r_tmp &= tmp; + return; /* already there */ + } + if (old -> r_tmp == tmp || !tmp) { + /* Extend the existing root. */ + GC_root_size += e - old -> r_end; + old -> r_end = e; + old -> r_tmp = tmp; + return; + } + b = old -> r_end; + } + } +# endif + 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, (void *)b, (void *)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; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + GC_static_roots[n_root_sets].r_next = 0; + add_roots_to_index(GC_static_roots + n_root_sets); +# endif + GC_root_size += e - b; + n_root_sets++; +} + +GC_API void GC_CALL GC_clear_roots(void) +{ + DCL_LOCK_STATE; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); +# ifdef THREADS + GC_roots_were_cleared = TRUE; +# endif + n_root_sets = 0; + GC_root_size = 0; +# 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, (void *)GC_static_roots[i].r_start, + (void *)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; + GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; + n_root_sets--; +} + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + STATIC void GC_rebuild_root_index(void) + { + int i; + BZERO(GC_root_index, RT_SIZE * sizeof(void *)); + for (i = 0; i < n_root_sets; i++) + add_roots_to_index(GC_static_roots + i); + } +#endif + +#if defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(PCR) || defined(CYGWIN32) +/* Internal use only; lock held. */ +STATIC void GC_remove_tmp_roots(void) +{ + int i; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + int old_n_roots = n_root_sets; +# endif + + for (i = 0; i < n_root_sets; ) { + if (GC_static_roots[i].r_tmp) { + GC_remove_root_at_pos(i); + } else { + i++; + } + } +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + if (n_root_sets < old_n_roots) + GC_rebuild_root_index(); +# endif +} +#endif + +STATIC void GC_remove_roots_inner(ptr_t b, ptr_t e); + +GC_API void GC_CALL GC_remove_roots(void *b, void *e) +{ + DCL_LOCK_STATE; + + /* Quick check whether has nothing to do */ + if ((((word)b + (sizeof(word) - 1)) & ~(word)(sizeof(word) - 1)) >= + ((word)e & ~(word)(sizeof(word) - 1))) + return; + + LOCK(); + GC_remove_roots_inner((ptr_t)b, (ptr_t)e); + UNLOCK(); +} + +/* Should only be called when the lock is held */ +STATIC void GC_remove_roots_inner(ptr_t b, ptr_t e) +{ + int i; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + int old_n_roots = n_root_sets; +# endif + + for (i = 0; i < n_root_sets; ) { + 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++; + } + } +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + if (n_root_sets < old_n_roots) + GC_rebuild_root_index(); +# endif +} + +#ifdef USE_PROC_FOR_LIBRARIES + /* Exchange the elements of the roots table. Requires rebuild of */ + /* the roots index table after the swap. */ + GC_INLINE void swap_static_roots(int i, int j) + { + ptr_t r_start = GC_static_roots[i].r_start; + ptr_t r_end = GC_static_roots[i].r_end; + GC_bool r_tmp = GC_static_roots[i].r_tmp; + + GC_static_roots[i].r_start = GC_static_roots[j].r_start; + GC_static_roots[i].r_end = GC_static_roots[j].r_end; + GC_static_roots[i].r_tmp = GC_static_roots[j].r_tmp; + /* No need to swap r_next values. */ + GC_static_roots[j].r_start = r_start; + GC_static_roots[j].r_end = r_end; + GC_static_roots[j].r_tmp = r_tmp; + } + + /* Remove given range from every static root which intersects with */ + /* the range. It is assumed GC_remove_tmp_roots is called before */ + /* this function is called repeatedly by GC_register_map_entries. */ + GC_INNER void GC_remove_roots_subregion(ptr_t b, ptr_t e) + { + int i; + GC_bool rebuild = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((word)b % sizeof(word) == 0 && (word)e % sizeof(word) == 0); + for (i = 0; i < n_root_sets; i++) { + ptr_t r_start, r_end; + + if (GC_static_roots[i].r_tmp) { + /* The remaining roots are skipped as they are all temporary. */ +# ifdef GC_ASSERTIONS + int j; + for (j = i + 1; j < n_root_sets; j++) { + GC_ASSERT(GC_static_roots[j].r_tmp); + } +# endif + break; + } + r_start = GC_static_roots[i].r_start; + r_end = GC_static_roots[i].r_end; + if (!EXPECT((word)e <= (word)r_start || (word)r_end <= (word)b, TRUE)) { +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Removing %p .. %p from root section %d (%p .. %p)\n", + (void *)b, (void *)e, + i, (void *)r_start, (void *)r_end); +# endif + if ((word)r_start < (word)b) { + GC_root_size -= r_end - b; + GC_static_roots[i].r_end = b; + /* No need to rebuild as hash does not use r_end value. */ + if ((word)e < (word)r_end) { + int j; + + if (rebuild) { + GC_rebuild_root_index(); + rebuild = FALSE; + } + GC_add_roots_inner(e, r_end, FALSE); /* updates n_root_sets */ + for (j = i + 1; j < n_root_sets; j++) + if (GC_static_roots[j].r_tmp) + break; + if (j < n_root_sets-1 && !GC_static_roots[n_root_sets-1].r_tmp) { + /* Exchange the roots to have all temporary ones at the end. */ + swap_static_roots(j, n_root_sets - 1); + rebuild = TRUE; + } + } + } else { + if ((word)e < (word)r_end) { + GC_root_size -= e - r_start; + GC_static_roots[i].r_start = e; + } else { + GC_remove_root_at_pos(i); + if (i < n_root_sets - 1 && GC_static_roots[i].r_tmp + && !GC_static_roots[i + 1].r_tmp) { + int j; + + for (j = i + 2; j < n_root_sets; j++) + if (GC_static_roots[j].r_tmp) + break; + /* Exchange the roots to have all temporary ones at the end. */ + swap_static_roots(i, j - 1); + } + i--; + } + rebuild = TRUE; + } + } + } + if (rebuild) + GC_rebuild_root_index(); + } +#endif /* USE_PROC_FOR_LIBRARIES */ + +#if !defined(NO_DEBUGGING) + /* For the debugging purpose only. */ + /* Workaround for the OS mapping and unmapping behind our back: */ + /* Is the address p in one of the temporary static root sections? */ + GC_API int GC_CALL GC_is_tmp_root(void *p) + { + static int last_root_set = MAX_ROOT_SETS; + int i; + + if (last_root_set < n_root_sets + && (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 ((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; + } + } + return(FALSE); + } +#endif /* !NO_DEBUGGING */ + +GC_INNER ptr_t GC_approx_sp(void) +{ + volatile word sp; +# if ((defined(E2K) && defined(__clang__)) \ + || (defined(S390) && (__clang_major__ < 8))) && !defined(CPPCHECK) + /* Workaround some bugs in clang: */ + /* "undefined reference to llvm.frameaddress" error (clang-9/e2k); */ + /* a crash in SystemZTargetLowering of libLLVM-3.8 (S390). */ + sp = (word)&sp; +# elif defined(CPPCHECK) || (__GNUC__ >= 4 /* GC_GNUC_PREREQ(4, 0) */ \ + && !defined(STACK_NOT_SCANNED)) + /* TODO: Use GC_GNUC_PREREQ after fixing a bug in cppcheck. */ + sp = (word)__builtin_frame_address(0); +# else + sp = (word)&sp; +# endif + /* Also force stack to grow if necessary. Otherwise the */ + /* later accesses might cause the kernel to think we're */ + /* doing something wrong. */ + return((ptr_t)sp); +} + +/* + * Data structure for excluded static roots. + * Real declaration is in gc_priv.h. + +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +struct exclusion GC_excl_table[MAX_EXCLUSIONS]; + -- Array of exclusions, ascending + -- address order. +*/ + +GC_API void GC_CALL GC_clear_exclusion_table(void) +{ + GC_excl_table_entries = 0; +} + +/* Return the first exclusion range that includes an address >= start_addr */ +/* Assumes the exclusion table contains at least one entry (namely the */ +/* GC data structures). */ +STATIC struct exclusion * GC_next_exclusion(ptr_t start_addr) +{ + size_t low = 0; + size_t high; + + GC_ASSERT(GC_excl_table_entries > 0); + high = GC_excl_table_entries - 1; + while (high > low) { + size_t mid = (low + high) >> 1; + + /* low <= mid < high */ + if ((word) GC_excl_table[mid].e_end <= (word) start_addr) { + low = mid + 1; + } else { + high = mid; + } + } + if ((word) GC_excl_table[low].e_end <= (word) start_addr) return 0; + return GC_excl_table + low; +} + +/* Should only be called when the lock is held. The range boundaries */ +/* should be properly aligned and valid. */ +GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish) +{ + struct exclusion * next; + size_t next_index; + + GC_ASSERT((word)start % sizeof(word) == 0); + GC_ASSERT((word)start < (word)finish); + + if (0 == GC_excl_table_entries) { + next = 0; + } else { + next = GC_next_exclusion((ptr_t)start); + } + if (next != NULL) { + if ((word)(next -> e_start) < (word) finish) { + /* incomplete error check. */ + ABORT("Exclusion ranges overlap"); + } + if ((word)(next -> e_start) == (word) finish) { + /* extend old range backwards */ + next -> e_start = (ptr_t)start; + return; + } + } + + next_index = GC_excl_table_entries; + if (next_index >= MAX_EXCLUSIONS) ABORT("Too many exclusions"); + if (next != NULL) { + size_t i; + + next_index = (size_t)(next - GC_excl_table); + for (i = GC_excl_table_entries; i > next_index; --i) { + GC_excl_table[i] = GC_excl_table[i-1]; + } + } + GC_excl_table[next_index].e_start = (ptr_t)start; + GC_excl_table[next_index].e_end = (ptr_t)finish; + ++GC_excl_table_entries; +} + +GC_API void GC_CALL GC_exclude_static_roots(void *b, void *e) +{ + DCL_LOCK_STATE; + + if (b == e) return; /* nothing to exclude? */ + + /* Round boundaries (in direction reverse to that of GC_add_roots). */ + b = (void *)((word)b & ~(word)(sizeof(word) - 1)); + e = (void *)(((word)e + (sizeof(word) - 1)) & ~(word)(sizeof(word) - 1)); + if (NULL == e) + e = (void *)(~(word)(sizeof(word) - 1)); /* handle overflow */ + + LOCK(); + GC_exclude_static_roots_inner(b, e); + UNLOCK(); +} + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) +# define GC_PUSH_CONDITIONAL(b, t, all) \ + (GC_parallel \ + ? GC_push_conditional_eager(b, t, all) \ + : GC_push_conditional_static(b, t, all)) +#else +# define GC_PUSH_CONDITIONAL(b, t, all) GC_push_conditional_static(b, t, all) +#endif + +/* Invoke push_conditional on ranges that are not excluded. */ +STATIC void GC_push_conditional_with_exclusions(ptr_t bottom, ptr_t top, + GC_bool all) +{ + while ((word)bottom < (word)top) { + struct exclusion *next = GC_next_exclusion(bottom); + ptr_t excl_start; + + if (0 == next + || (word)(excl_start = next -> e_start) >= (word)top) { + GC_PUSH_CONDITIONAL(bottom, top, all); + break; + } + if ((word)excl_start > (word)bottom) + GC_PUSH_CONDITIONAL(bottom, excl_start, all); + bottom = next -> e_end; + } +} + +#if defined(E2K) || defined(IA64) + /* Similar to GC_push_all_stack_sections() but for IA-64 registers store. */ + GC_INNER void GC_push_all_register_sections(ptr_t bs_lo, ptr_t bs_hi, + int eager, struct GC_traced_stack_sect_s *traced_stack_sect) + { +# ifdef E2K + (void)traced_stack_sect; /* TODO: Not implemented yet */ +# else + while (traced_stack_sect != NULL) { + ptr_t frame_bs_lo = traced_stack_sect -> backing_store_end; + GC_ASSERT((word)frame_bs_lo <= (word)bs_hi); + if (eager) { + GC_push_all_eager(frame_bs_lo, bs_hi); + } else { + GC_push_all_stack(frame_bs_lo, bs_hi); + } + bs_hi = traced_stack_sect -> saved_backing_store_ptr; + traced_stack_sect = traced_stack_sect -> prev; + } +# endif + GC_ASSERT((word)bs_lo <= (word)bs_hi); + if (eager) { + GC_push_all_eager(bs_lo, bs_hi); + } else { + GC_push_all_stack(bs_lo, bs_hi); + } + } +#endif /* E2K || IA64 */ + +#ifdef THREADS + +GC_INNER void GC_push_all_stack_sections( + ptr_t lo /* top */, ptr_t hi /* bottom */, + struct GC_traced_stack_sect_s *traced_stack_sect) +{ + while (traced_stack_sect != NULL) { + 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 */ + GC_push_all_stack(lo, (ptr_t)traced_stack_sect); +# endif + lo = traced_stack_sect -> saved_stack_ptr; + GC_ASSERT(lo != NULL); + traced_stack_sect = traced_stack_sect -> prev; + } + GC_ASSERT(!((word)hi HOTTER_THAN (word)lo)); +# ifdef STACK_GROWS_UP + /* We got them backwards! */ + GC_push_all_stack(hi, lo); +# else /* STACK_GROWS_DOWN */ + GC_push_all_stack(lo, hi); +# endif +} + +#else /* !THREADS */ + + /* Similar to GC_push_all_eager, but only the */ + /* part hotter than cold_gc_frame is scanned */ + /* immediately. Needed to ensure that callee- */ + /* save registers are not missed. */ +/* + * A version of GC_push_all that treats all interior pointers as valid + * and scans part of the area immediately, to make sure that saved + * register values are not lost. + * Cold_gc_frame delimits the stack section that must be scanned + * eagerly. A zero value indicates that no eager scanning is needed. + * We don't need to worry about the manual VDB case here, since this + * is only called in the single-threaded case. We assume that we + * cannot collect between an assignment and the corresponding + * GC_dirty() call. + */ +STATIC void GC_push_all_stack_partially_eager(ptr_t bottom, ptr_t top, + ptr_t cold_gc_frame) +{ +#ifndef NEED_FIXUP_POINTER + if (GC_all_interior_pointers) { + /* Push the hot end of the stack eagerly, so that register values */ + /* saved inside GC frames are marked before they disappear. */ + /* The rest of the marking can be deferred until later. */ + if (0 == cold_gc_frame) { + GC_push_all_stack(bottom, top); + return; + } + 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); +# else /* STACK_GROWS_UP */ + GC_push_all(bottom, cold_gc_frame + sizeof(ptr_t)); + GC_push_all_eager(cold_gc_frame, top); +# endif /* STACK_GROWS_UP */ + } else +#endif + /* else */ { + GC_push_all_eager(bottom, top); + } +# ifdef TRACE_BUF + GC_add_trace_entry("GC_push_all_stack", (word)bottom, (word)top); +# endif +} + +/* Similar to GC_push_all_stack_sections() but also uses cold_gc_frame. */ +STATIC void GC_push_all_stack_part_eager_sections( + ptr_t lo /* top */, ptr_t hi /* bottom */, ptr_t cold_gc_frame, + struct GC_traced_stack_sect_s *traced_stack_sect) +{ + GC_ASSERT(traced_stack_sect == NULL || cold_gc_frame == NULL || + (word)cold_gc_frame HOTTER_THAN (word)traced_stack_sect); + + while (traced_stack_sect != NULL) { + 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); +# else /* STACK_GROWS_DOWN */ + GC_push_all_stack_partially_eager(lo, (ptr_t)traced_stack_sect, + cold_gc_frame); +# endif + lo = traced_stack_sect -> saved_stack_ptr; + GC_ASSERT(lo != NULL); + traced_stack_sect = traced_stack_sect -> prev; + cold_gc_frame = NULL; /* Use at most once. */ + } + + 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); +# else /* STACK_GROWS_DOWN */ + GC_push_all_stack_partially_eager(lo, hi, cold_gc_frame); +# endif +} + +#endif /* !THREADS */ + +/* Push enough of the current stack eagerly to ensure that callee-save */ +/* registers saved in GC frames are scanned. In the non-threads case, */ +/* schedule entire stack for scanning. The 2nd argument is a pointer */ +/* to the (possibly null) thread context, for (currently hypothetical) */ +/* more precise stack scanning. In the presence of threads, push */ +/* enough of the current stack to ensure that callee-save registers */ +/* saved in collector frames have been seen. */ +/* TODO: Merge it with per-thread stuff. */ +STATIC void GC_push_current_stack(ptr_t cold_gc_frame, + void * context GC_ATTR_UNUSED) +{ +# if defined(THREADS) + /* cold_gc_frame is non-NULL. */ +# ifdef STACK_GROWS_DOWN + GC_push_all_eager(GC_approx_sp(), cold_gc_frame); + /* For IA64, the register stack backing store is handled */ + /* in the thread-specific code. */ +# else + GC_push_all_eager(cold_gc_frame, GC_approx_sp()); +# endif +# else + GC_push_all_stack_part_eager_sections(GC_approx_sp(), GC_stackbottom, + cold_gc_frame, GC_traced_stack_sect); +# ifdef IA64 + /* We also need to push the register stack backing store. */ + /* This should really be done in the same way as the */ + /* regular stack. For now we fudge it a bit. */ + /* Note that the backing store grows up, so we can't use */ + /* GC_push_all_stack_partially_eager. */ + { + ptr_t bsp = GC_save_regs_ret_val; + ptr_t cold_gc_bs_pointer = bsp - 2048; + 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 + && (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, + cold_gc_bs_pointer, FALSE, GC_traced_stack_sect); + GC_push_all_eager(cold_gc_bs_pointer, bsp); + } else { + GC_push_all_register_sections(BACKING_STORE_BASE, bsp, + TRUE /* eager */, GC_traced_stack_sect); + } + /* All values should be sufficiently aligned that we */ + /* don't have to worry about the boundary. */ + } +# elif defined(E2K) + /* We also need to push procedure stack store. */ + /* Procedure stack grows up. */ + { + ptr_t bs_lo; + size_t stack_size; + + GET_PROCEDURE_STACK_LOCAL(&bs_lo, &stack_size); + GC_push_all_register_sections(bs_lo, bs_lo + stack_size, + TRUE /* eager */, + GC_traced_stack_sect); + FREE_PROCEDURE_STACK_LOCAL(bs_lo, stack_size); + } +# endif +# endif /* !THREADS */ +} + +GC_INNER void (*GC_push_typed_structures)(void) = 0; + +GC_INNER void GC_cond_register_dynamic_libraries(void) +{ +# if (defined(DYNAMIC_LOADING) && !defined(MSWIN_XBOX1)) \ + || defined(CYGWIN32) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(PCR) + GC_remove_tmp_roots(); + if (!GC_no_dls) GC_register_dynamic_libraries(); +# else + GC_no_dls = TRUE; +# endif +} + +STATIC void GC_push_regs_and_stack(ptr_t cold_gc_frame) +{ +# ifdef THREADS + if (NULL == cold_gc_frame) + return; /* GC_push_all_stacks should push registers and stack */ +# endif + GC_with_callee_saves_pushed(GC_push_current_stack, cold_gc_frame); +} + +/* Call the mark routines (GC_push_one 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; a NULL value indicates */ +/* that it is OK to miss some register values. Called with the */ +/* allocation lock held. */ +GC_INNER void GC_push_roots(GC_bool all, ptr_t cold_gc_frame GC_ATTR_UNUSED) +{ + int i; + unsigned kind; + + /* Next push static data. This must happen early on, since it is */ + /* not robust against mark stack overflow. */ + /* Re-register dynamic libraries, in case one got added. */ + /* There is some argument for doing this as late as possible, */ + /* especially on Win32, where it can change asynchronously. */ + /* In those cases, we do it here. But on other platforms, it's */ + /* not safe with the world stopped, so we do it earlier. */ +# if !defined(REGISTER_LIBRARIES_EARLY) + GC_cond_register_dynamic_libraries(); +# endif + + /* Mark everything in static data areas. */ + for (i = 0; i < n_root_sets; i++) { + GC_push_conditional_with_exclusions( + GC_static_roots[i].r_start, + GC_static_roots[i].r_end, all); + } + + /* Mark all free list header blocks, if those were allocated from */ + /* the garbage collected heap. This makes sure they don't */ + /* disappear if we are not marking from static data. It also */ + /* saves us the trouble of scanning them, and possibly that of */ + /* marking the freelists. */ + for (kind = 0; kind < GC_n_kinds; kind++) { + void *base = GC_base(GC_obj_kinds[kind].ok_freelist); + if (base != NULL) { + GC_set_mark_bit(base); + } + } + + /* Mark from GC internal roots if those might otherwise have */ + /* been excluded. */ +# ifndef GC_NO_FINALIZATION + GC_push_finalizer_structures(); +# endif +# ifdef THREADS + if (GC_no_dls || GC_roots_were_cleared) + GC_push_thread_structures(); +# endif + if (GC_push_typed_structures) + GC_push_typed_structures(); + + /* Mark thread local free lists, even if their mark */ + /* descriptor excludes the link field. */ + /* If the world is not stopped, this is unsafe. It is */ + /* also unnecessary, since we will do this again with the */ + /* world stopped. */ +# if defined(THREAD_LOCAL_ALLOC) + if (GC_world_stopped) + GC_mark_thread_local_free_lists(); +# endif + + /* Now traverse stacks, and mark from register contents. */ + /* These must be done last, since they can legitimately */ + /* overflow the mark stack. This is usually done by saving */ + /* the current context on the stack, and then just tracing */ + /* from the stack. */ +# ifndef STACK_NOT_SCANNED + GC_push_regs_and_stack(cold_gc_frame); +# endif + + if (GC_push_other_roots != 0) { + /* In the threads case, this also pushes thread stacks. */ + /* Note that without interior pointer recognition lots */ + /* of stuff may have been pushed already, and this */ + /* should be careful about mark stack overflows. */ + (*GC_push_other_roots)(); + } +} diff --git a/bdwgc/misc.c b/bdwgc/misc.c new file mode 100644 index 000000000..824e39b8c --- /dev/null +++ b/bdwgc/misc.c @@ -0,0 +1,2709 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2021 Ivan Maidanski + * + * 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. + */ + +#include "private/gc_pmark.h" + +#include +#include +#include + +#ifndef MSWINCE +# include +#endif + +#ifdef GC_SOLARIS_THREADS +# include +#endif + +#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(SYMBIAN) \ + || (defined(CONSOLE_LOG) && defined(MSWIN32)) +# include +# include +# include +#endif + +#if defined(CONSOLE_LOG) && defined(MSWIN32) && !defined(__GNUC__) +# include +#endif + +#ifdef NONSTOP +# include +#endif + +#ifdef THREADS +# ifdef PCR +# include "il/PCR_IL.h" + GC_INNER PCR_Th_ML GC_allocate_ml; +# elif defined(SN_TARGET_PSP2) + GC_INNER WapiMutex GC_allocate_ml_PSP2 = { 0, NULL }; +# elif defined(GC_DEFN_ALLOCATE_ML) || defined(SN_TARGET_PS3) +# include + GC_INNER pthread_mutex_t GC_allocate_ml; +# endif + /* For other platforms with threads, the lock and possibly */ + /* GC_lock_holder variables are defined in the thread support code. */ +#endif /* THREADS */ + +#ifdef DYNAMIC_LOADING + /* We need to register the main data segment. Returns TRUE unless */ + /* this is done implicitly as part of dynamic library registration. */ +# define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data() +#elif defined(GC_DONT_REGISTER_MAIN_STATIC_DATA) +# define GC_REGISTER_MAIN_STATIC_DATA() FALSE +#else + /* Don't unnecessarily call GC_register_main_static_data() in case */ + /* dyn_load.c isn't linked in. */ +# define GC_REGISTER_MAIN_STATIC_DATA() TRUE +#endif + +#ifdef NEED_CANCEL_DISABLE_COUNT + __thread unsigned char GC_cancel_disable_count = 0; +#endif + +GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */; + +GC_INNER unsigned GC_n_mark_procs = GC_RESERVED_MARK_PROCS; + +GC_INNER unsigned GC_n_kinds = GC_N_KINDS_INITIAL_VALUE; + +GC_INNER GC_bool GC_debugging_started = FALSE; + /* defined here so we don't have to load dbg_mlc.o */ + +ptr_t GC_stackbottom = 0; + +#ifdef IA64 + ptr_t GC_register_stackbottom = 0; +#endif + +int GC_dont_gc = FALSE; + +int GC_dont_precollect = FALSE; + +GC_bool GC_quiet = 0; /* used also in pcr_interface.c */ + +#if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) + int GC_print_stats = 0; +#endif + +#ifdef GC_PRINT_BACK_HEIGHT + GC_INNER GC_bool GC_print_back_height = TRUE; +#else + GC_INNER GC_bool GC_print_back_height = FALSE; +#endif + +#ifndef NO_DEBUGGING +# ifdef GC_DUMP_REGULARLY + GC_INNER GC_bool GC_dump_regularly = TRUE; + /* Generate regular debugging dumps. */ +# else + GC_INNER GC_bool GC_dump_regularly = FALSE; +# endif +# ifndef NO_CLOCK + STATIC CLOCK_TYPE GC_init_time; + /* The time that the GC was initialized at. */ +# endif +#endif /* !NO_DEBUGGING */ + +#ifdef KEEP_BACK_PTRS + GC_INNER long GC_backtraces = 0; + /* Number of random backtraces to generate for each GC. */ +#endif + +#ifdef FIND_LEAK + int GC_find_leak = 1; +#else + int GC_find_leak = 0; +#endif + +#ifndef SHORT_DBG_HDRS +# ifdef GC_FINDLEAK_DELAY_FREE + GC_INNER GC_bool GC_findleak_delay_free = TRUE; +# else + GC_INNER GC_bool GC_findleak_delay_free = FALSE; +# endif +#endif /* !SHORT_DBG_HDRS */ + +#ifdef ALL_INTERIOR_POINTERS + int GC_all_interior_pointers = 1; +#else + 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. */ + GC_INNER GC_bool GC_force_unmap_on_gcollect = TRUE; +#else + GC_INNER GC_bool GC_force_unmap_on_gcollect = FALSE; +#endif + +#ifndef GC_LARGE_ALLOC_WARN_INTERVAL +# define GC_LARGE_ALLOC_WARN_INTERVAL 5 +#endif +GC_INNER long GC_large_alloc_warn_interval = GC_LARGE_ALLOC_WARN_INTERVAL; + /* Interval between unsuppressed warnings. */ + +STATIC void * GC_CALLBACK GC_default_oom_fn( + size_t bytes_requested GC_ATTR_UNUSED) +{ + return(0); +} + +/* All accesses to it should be synchronized to avoid data races. */ +GC_oom_func GC_oom_fn = GC_default_oom_fn; + +#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) + 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(); /* to initialize GC_manual_vdb and GC_stderr */ +# ifndef THREADS + if (GC_manual_vdb) + return; +# endif +# 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 */ +/* quantization algorithm (but we precompute it). */ +STATIC void GC_init_size_map(void) +{ + size_t i; + + /* Map size 0 to something bigger. */ + /* This avoids problems at lower levels. */ + GC_size_map[0] = 1; + for (i = 1; i <= GRANULES_TO_BYTES(TINY_FREELISTS-1) - EXTRA_BYTES; i++) { + GC_size_map[i] = ROUNDED_UP_GRANULES(i); +# ifndef _MSC_VER + GC_ASSERT(GC_size_map[i] < TINY_FREELISTS); + /* Seems to tickle bug in VC++ 2008 for x64 */ +# endif + } + /* We leave the rest of the array to be filled in on demand. */ +} + +/* + * The following is a gross hack to deal with a problem that can occur + * on machines that are sloppy about stack frame sizes, notably SPARC. + * Bogus pointers may be written to the stack and not cleared for + * a LONG time, because they always fall into holes in stack frames + * that are not written. We partially address this by clearing + * sections of the stack whenever we get control. + */ + +#ifndef SMALL_CLEAR_SIZE +# define SMALL_CLEAR_SIZE 256 /* Clear this much every time. */ +#endif + +#if defined(ALWAYS_SMALL_CLEAR_STACK) || defined(STACK_NOT_SCANNED) + GC_API void * GC_CALL GC_clear_stack(void *arg) + { +# ifndef STACK_NOT_SCANNED + word volatile dummy[SMALL_CLEAR_SIZE]; + BZERO((/* no volatile */ void *)dummy, sizeof(dummy)); +# endif + return arg; + } +#else + +# ifdef THREADS +# define BIG_CLEAR_SIZE 2048 /* Clear this much now and then. */ +# else + STATIC word GC_stack_last_cleared = 0; /* GC_no when we last did this */ + STATIC ptr_t GC_min_sp = NULL; + /* Coolest stack pointer value from which */ + /* we've already cleared the stack. */ + STATIC ptr_t GC_high_water = NULL; + /* "hottest" stack pointer value we have seen */ + /* recently. Degrades over time. */ + STATIC word GC_bytes_allocd_at_reset = 0; +# define DEGRADE_RATE 50 +# endif + +# if defined(ASM_CLEAR_CODE) + 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 erroneously defined in .S */ + /* file, so this error would be caught by the linker. */ + void *GC_clear_stack_inner(void *arg, +# if defined(__APPLE_CC__) && !GC_CLANG_PREREQ(6, 0) + volatile /* to workaround some bug */ +# endif + ptr_t limit) + { +# define CLEAR_SIZE 213 /* granularity */ + volatile word dummy[CLEAR_SIZE]; + + 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 */ + /* call is not recognized as dead code. */ +# if defined(CPPCHECK) + GC_noop1(dummy[0]); +# else + GC_noop1(COVERT_DATAFLOW(dummy)); +# endif + return(arg); + } +# endif /* !ASM_CLEAR_CODE */ + +# ifdef THREADS + /* Used to occasionally clear a bigger chunk. */ + /* TODO: Should be more random than it is ... */ + GC_ATTR_NO_SANITIZE_THREAD + static unsigned next_random_no(void) + { + static unsigned random_no = 0; + return ++random_no % 13; + } +# endif /* THREADS */ + +/* 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_API void * GC_CALL GC_clear_stack(void *arg) + { + ptr_t sp = GC_approx_sp(); /* Hotter than actual sp */ +# ifdef THREADS + word volatile dummy[SMALL_CLEAR_SIZE]; +# endif + +# define SLOP 400 + /* Extra bytes we clear every time. This clears our own */ + /* activation record, and should cause more frequent */ + /* clearing near the cold end of the stack, a good thing. */ +# define GC_SLOP 4000 + /* We make GC_high_water this much hotter than we really saw */ + /* it, to cover for GC noise etc. above our current frame. */ +# define CLEAR_THRESHOLD 100000 + /* We restart the clearing process after this many bytes of */ + /* allocation. Otherwise very heavily recursive programs */ + /* with sparse stacks may result in heaps that grow almost */ + /* without bounds. As the heap gets larger, collection */ + /* frequency decreases, thus clearing frequency would decrease, */ + /* thus more junk remains accessible, thus the heap gets */ + /* larger ... */ +# ifdef THREADS + if (next_random_no() == 0) { + ptr_t limit = sp; + + MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word)); + limit = (ptr_t)((word)limit & ~0xf); + /* Make it sufficiently aligned for assembly */ + /* implementations of GC_clear_stack_inner. */ + return GC_clear_stack_inner(arg, limit); + } + BZERO((void *)dummy, SMALL_CLEAR_SIZE*sizeof(word)); +# else + if (GC_gc_no > GC_stack_last_cleared) { + /* Start things over, so we clear the entire stack again */ + if (GC_stack_last_cleared == 0) + GC_high_water = (ptr_t)GC_stackbottom; + GC_min_sp = GC_high_water; + GC_stack_last_cleared = GC_gc_no; + GC_bytes_allocd_at_reset = GC_bytes_allocd; + } + /* Adjust GC_high_water */ + MAKE_COOLER(GC_high_water, WORDS_TO_BYTES(DEGRADE_RATE) + GC_SLOP); + if ((word)sp HOTTER_THAN (word)GC_high_water) { + GC_high_water = sp; + } + MAKE_HOTTER(GC_high_water, GC_SLOP); + { + ptr_t limit = GC_min_sp; + + MAKE_HOTTER(limit, SLOP); + 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. */ + GC_min_sp = sp; + return GC_clear_stack_inner(arg, limit); + } + } + if (GC_bytes_allocd - GC_bytes_allocd_at_reset > CLEAR_THRESHOLD) { + /* Restart clearing process, but limit how much clearing we do. */ + GC_min_sp = sp; + MAKE_HOTTER(GC_min_sp, CLEAR_THRESHOLD/4); + 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; + } +# endif + return arg; + } + +#endif /* !ALWAYS_SMALL_CLEAR_STACK && !STACK_NOT_SCANNED */ + +/* Return a pointer to the base address of p, given a pointer to a */ +/* an address within an object. Return 0 o.w. */ +GC_API void * GC_CALL GC_base(void * p) +{ + ptr_t r; + struct hblk *h; + bottom_index *bi; + hdr *candidate_hdr; + + r = (ptr_t)p; + if (!EXPECT(GC_is_initialized, TRUE)) return 0; + h = HBLKPTR(r); + GET_BI(r, bi); + candidate_hdr = HDR_FROM_BI(bi, r); + if (candidate_hdr == 0) return(0); + /* If it's a pointer to the middle of a large object, move it */ + /* to the beginning. */ + while (IS_FORWARDING_ADDR_OR_NIL(candidate_hdr)) { + h = FORWARDED_ADDR(h,candidate_hdr); + r = (ptr_t)h; + candidate_hdr = HDR(h); + } + if (HBLK_IS_FREE(candidate_hdr)) return(0); + /* Make sure r points to the beginning of the object */ + r = (ptr_t)((word)r & ~(WORDS_TO_BYTES(1) - 1)); + { + size_t offset = HBLKDISPL(r); + word sz = candidate_hdr -> hb_sz; + size_t obj_displ = offset % sz; + ptr_t limit; + + r -= obj_displ; + limit = r + sz; + if ((word)limit > (word)(h + 1) && sz <= HBLKSIZE) { + return(0); + } + if ((word)p >= (word)limit) return(0); + } + return((void *)r); +} + +/* Return TRUE if and only if p points to somewhere in GC heap. */ +GC_API int GC_CALL GC_is_heap_ptr(const void *p) +{ + bottom_index *bi; + + GC_ASSERT(GC_is_initialized); + GET_BI(p, bi); + return HDR_FROM_BI(bi, p) != 0; +} + +/* Return the size of an object, given a pointer to its base. */ +/* (For small objects this also happens to work from interior pointers, */ +/* but that shouldn't be relied upon.) */ +GC_API size_t GC_CALL GC_size(const void * p) +{ + hdr * hhdr = HDR(p); + + return (size_t)hhdr->hb_sz; +} + + +/* These getters remain unsynchronized for compatibility (since some */ +/* clients could call some of them from a GC callback holding the */ +/* allocator lock). */ +GC_API size_t GC_CALL GC_get_heap_size(void) +{ + /* ignore the memory space returned to OS (i.e. count only the */ + /* space owned by the garbage collector) */ + return (size_t)(GC_heapsize - GC_unmapped_bytes); +} + +GC_API size_t GC_CALL GC_get_obtained_from_os_bytes(void) +{ + return (size_t)GC_our_mem_bytes; +} + +GC_API size_t GC_CALL GC_get_free_bytes(void) +{ + /* ignore the memory space returned to OS */ + return (size_t)(GC_large_free_bytes - GC_unmapped_bytes); +} + +GC_API size_t GC_CALL GC_get_unmapped_bytes(void) +{ + return (size_t)GC_unmapped_bytes; +} + +GC_API size_t GC_CALL GC_get_bytes_since_gc(void) +{ + return (size_t)GC_bytes_allocd; +} + +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 + +GC_API size_t GC_CALL GC_get_size_map_at(int i) +{ + if ((unsigned)i > MAXOBJBYTES) + return GC_SIZE_MAX; + return GRANULES_TO_BYTES(GC_size_map[i]); +} + +/* 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. */ +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) +{ + DCL_LOCK_STATE; + + LOCK(); + if (pheap_size != NULL) + *pheap_size = GC_heapsize - GC_unmapped_bytes; + if (pfree_bytes != NULL) + *pfree_bytes = GC_large_free_bytes - GC_unmapped_bytes; + if (punmapped_bytes != NULL) + *punmapped_bytes = GC_unmapped_bytes; + if (pbytes_since_gc != NULL) + *pbytes_since_gc = GC_bytes_allocd; + if (ptotal_bytes != NULL) + *ptotal_bytes = GC_bytes_allocd + GC_bytes_allocd_before_gc; + UNLOCK(); +} + + GC_INNER word GC_reclaimed_bytes_before_gc = 0; + + /* Fill in GC statistics provided the destination is of enough size. */ + static void fill_prof_stats(struct GC_prof_stats_s *pstats) + { + 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)((signed_word)GC_markers_m1); +# else + 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; + pstats->expl_freed_bytes_since_gc = GC_bytes_freed; /* since gc-7.7 */ + pstats->obtained_from_os_bytes = GC_our_mem_bytes; /* since gc-8.2 */ + } + +# include /* 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 { + if (EXPECT(stats_sz > 0, TRUE)) + 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) + { + 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 { + if (EXPECT(stats_sz > 0, TRUE)) { + fill_prof_stats(&stats); + BCOPY(&stats, pstats, stats_sz); + } + return stats_sz; + } + } +# 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. */ + STATIC char *GC_envfile_content = NULL; + /* The content of the GC "env" file with CR and */ + /* LF replaced to '\0'. NULL if the file is */ + /* missing or empty. Otherwise, always ends */ + /* with '\0'. */ + STATIC unsigned GC_envfile_length = 0; + /* Length of GC_envfile_content (if non-NULL). */ + +# ifndef GC_ENVFILE_MAXLEN +# define GC_ENVFILE_MAXLEN 0x4000 +# endif + +# define GC_ENV_FILE_EXT ".gc.env" + + /* The routine initializes GC_envfile_content from the GC "env" file. */ + STATIC void GC_envfile_init(void) + { +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + HANDLE hFile; + char *content; + unsigned ofs; + unsigned len; + DWORD nBytesRead; + TCHAR path[_MAX_PATH + 0x10]; /* buffer for path + ext */ + size_t bytes_to_get; + + len = (unsigned)GetModuleFileName(NULL /* hModule */, path, + _MAX_PATH + 1); + /* If GetModuleFileName() has failed then len is 0. */ + if (len > 4 && path[len - 4] == (TCHAR)'.') { + len -= 4; /* strip executable file extension */ + } + BCOPY(TEXT(GC_ENV_FILE_EXT), &path[len], sizeof(TEXT(GC_ENV_FILE_EXT))); + hFile = CreateFile(path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL /* lpSecurityAttributes */, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL /* hTemplateFile */); + if (hFile == INVALID_HANDLE_VALUE) + return; /* the file is absent or the operation is failed */ + len = (unsigned)GetFileSize(hFile, NULL); + if (len <= 1 || len >= GC_ENVFILE_MAXLEN) { + CloseHandle(hFile); + return; /* invalid file length - ignoring the file content */ + } + /* At this execution point, GC_setpagesize() and GC_init_win32() */ + /* must already be called (for GET_MEM() to work correctly). */ + GC_ASSERT(GC_page_size != 0); + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP((size_t)len + 1); + content = (char *)GET_MEM(bytes_to_get); + if (content == NULL) { + CloseHandle(hFile); + return; /* allocation failure */ + } + GC_add_to_our_memory(content, bytes_to_get); + ofs = 0; + nBytesRead = (DWORD)-1L; + /* Last ReadFile() call should clear nBytesRead on success. */ + while (ReadFile(hFile, content + ofs, len - ofs + 1, &nBytesRead, + NULL /* lpOverlapped */) && nBytesRead != 0) { + if ((ofs += nBytesRead) > len) + break; + } + CloseHandle(hFile); + if (ofs != len || nBytesRead != 0) { + /* TODO: recycle content */ + return; /* read operation is failed - ignoring the file content */ + } + content[ofs] = '\0'; + while (ofs-- > 0) { + if (content[ofs] == '\r' || content[ofs] == '\n') + content[ofs] = '\0'; + } + GC_ASSERT(NULL == GC_envfile_content); + GC_envfile_length = len + 1; + GC_envfile_content = content; +# endif + } + + /* This routine scans GC_envfile_content for the specified */ + /* environment variable (and returns its value if found). */ + GC_INNER char * GC_envfile_getenv(const char *name) + { + char *p; + char *end_of_content; + size_t namelen; + +# ifndef NO_GETENV + p = getenv(name); /* try the standard getenv() first */ + if (p != NULL) + return *p != '\0' ? p : NULL; +# endif + p = GC_envfile_content; + if (p == NULL) + return NULL; /* "env" file is absent (or empty) */ + namelen = strlen(name); + if (namelen == 0) /* a sanity check */ + return NULL; + for (end_of_content = p + GC_envfile_length; + p != end_of_content; p += strlen(p) + 1) { + if (strncmp(p, name, namelen) == 0 && *(p += namelen) == '=') { + p++; /* the match is found; skip '=' */ + return *p != '\0' ? p : NULL; + } + /* If not matching then skip to the next line. */ + } + return NULL; /* no match found */ + } +#endif /* GC_READ_ENV_FILE */ + +GC_INNER GC_bool GC_is_initialized = FALSE; + +GC_API int GC_CALL GC_is_init_called(void) +{ + return GC_is_initialized; +} + +#if defined(GC_WIN32_THREADS) \ + && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)) + GC_INNER CRITICAL_SECTION GC_write_cs; +#endif + +#ifndef DONT_USE_ATEXIT +# if !defined(PCR) && !defined(SMALL_CONFIG) + /* A dedicated variable to avoid a garbage collection on abort. */ + /* GC_find_leak cannot be used for this purpose as otherwise */ + /* TSan finds a data race (between GC_default_on_abort and, e.g., */ + /* GC_finish_collection). */ + static GC_bool skip_gc_atexit = FALSE; +# else +# define skip_gc_atexit FALSE +# endif + + STATIC void GC_exit_check(void) + { + if (GC_find_leak && !skip_gc_atexit) { +# ifdef THREADS + /* Check that the thread executing at-exit functions is */ + /* the same as the one performed the GC initialization, */ + /* otherwise the latter thread might already be dead but */ + /* still registered and this, as a consequence, might */ + /* cause a signal delivery fail when suspending the threads */ + /* on platforms that do not guarantee ESRCH returned if */ + /* the signal is not delivered. */ + /* It should also prevent "Collecting from unknown thread" */ + /* abort in GC_push_all_stacks(). */ + if (!GC_is_main_thread() || !GC_thread_is_registered()) return; +# endif + GC_gcollect(); + } + } +#endif + +#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 (;;) { + /* empty */ + } + } + + static GC_bool installed_looping_handler = FALSE; + + static void maybe_install_looping_handler(void) + { + /* Install looping handler before the write fault handler, so we */ + /* handle write faults correctly. */ + if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) { + GC_set_and_save_fault_handler(looping_handler); + installed_looping_handler = TRUE; + } + } + +#else /* !UNIX_LIKE */ +# define maybe_install_looping_handler() +#endif + +#define GC_DEFAULT_STDOUT_FD 1 +#define GC_DEFAULT_STDERR_FD 2 + +#if !defined(OS2) && !defined(MACOS) && !defined(GC_ANDROID_LOG) \ + && !defined(NN_PLATFORM_CTR) && !defined(NINTENDO_SWITCH) \ + && (!defined(MSWIN32) || defined(CONSOLE_LOG)) && !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; + +# ifndef MSWIN32 + GC_API void GC_CALL GC_set_log_fd(int fd) + { + GC_log = fd; + } +# endif +#endif + +#ifdef MSGBOX_ON_ERROR + STATIC void GC_win32_MessageBoxA(const char *msg, const char *caption, + unsigned flags) + { +# ifndef DONT_USE_USER32_DLL + /* Use static binding to "user32.dll". */ + (void)MessageBoxA(NULL, msg, caption, flags); +# 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))(word)pfn)( + NULL /* hWnd */, msg, caption, flags); + (void)FreeLibrary(hU32); + } +# endif + } +#endif /* MSGBOX_ON_ERROR */ + +#if defined(THREADS) && defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + static void callee_saves_pushed_dummy_fn(ptr_t data GC_ATTR_UNUSED, + void * context GC_ATTR_UNUSED) {} +#endif + +#ifndef SMALL_CONFIG +# ifdef MANUAL_VDB + static GC_bool manual_vdb_allowed = TRUE; +# else + static GC_bool manual_vdb_allowed = FALSE; +# endif + + GC_API void GC_CALL GC_set_manual_vdb_allowed(int value) + { + manual_vdb_allowed = (GC_bool)value; + } + + GC_API int GC_CALL GC_get_manual_vdb_allowed(void) + { + return (int)manual_vdb_allowed; + } +#endif /* !SMALL_CONFIG */ + +STATIC word GC_parse_mem_size_arg(const char *str) +{ + word result = 0; /* bad value */ + + if (*str != '\0') { + char *endptr; + char ch; + + result = (word)STRTOULL(str, &endptr, 10); + ch = *endptr; + if (ch != '\0') { + if (*(endptr + 1) != '\0') + return 0; + /* Allow k, M or G suffix. */ + switch (ch) { + case 'K': + case 'k': + result <<= 10; + break; + case 'M': + case 'm': + result <<= 20; + break; + case 'G': + case 'g': + result <<= 30; + break; + default: + result = 0; + } + } + } + return result; +} + +#define GC_LOG_STD_NAME "gc.log" + +GC_API void GC_CALL GC_init(void) +{ + /* LOCK(); -- no longer does anything this early. */ + word initial_heap_sz; + IF_CANCEL(int cancel_state;) +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + DCL_LOCK_STATE; +# endif + + if (EXPECT(GC_is_initialized, TRUE)) return; +# ifdef REDIRECT_MALLOC + { + static GC_bool init_started = FALSE; + if (init_started) + ABORT("Redirected malloc() called during GC init"); + init_started = TRUE; + } +# endif + +# if defined(GC_INITIAL_HEAP_SIZE) && !defined(CPPCHECK) + initial_heap_sz = GC_INITIAL_HEAP_SIZE; +# else + initial_heap_sz = MINHINCR * HBLKSIZE; +# endif + + DISABLE_CANCEL(cancel_state); + /* Note that although we are nominally called with the */ + /* allocation lock held, the allocation lock is now */ + /* only really acquired once a second thread is forked.*/ + /* And the initialization code needs to run before */ + /* then. Thus we really don't hold any locks, and can */ + /* in fact safely initialize them here. */ +# ifdef THREADS +# ifndef GC_ALWAYS_MULTITHREADED + GC_ASSERT(!GC_need_to_lock); +# endif +# ifdef SN_TARGET_PS3 + { + pthread_mutexattr_t mattr; + + if (0 != pthread_mutexattr_init(&mattr)) { + ABORT("pthread_mutexattr_init failed"); + } + if (0 != pthread_mutex_init(&GC_allocate_ml, &mattr)) { + ABORT("pthread_mutex_init failed"); + } + (void)pthread_mutexattr_destroy(&mattr); + } +# endif +# endif /* THREADS */ +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) +# ifndef SPIN_COUNT +# define SPIN_COUNT 4000 +# endif +# ifdef MSWINRT_FLAVOR + InitializeCriticalSectionAndSpinCount(&GC_allocate_ml, SPIN_COUNT); +# else + { +# ifndef MSWINCE + FARPROC pfn = 0; + HMODULE hK32 = GetModuleHandle(TEXT("kernel32.dll")); + if (hK32) + pfn = GetProcAddress(hK32, + "InitializeCriticalSectionAndSpinCount"); + if (pfn) { + (*(BOOL (WINAPI *)(LPCRITICAL_SECTION, DWORD))(word)pfn)( + &GC_allocate_ml, SPIN_COUNT); + } else +# endif /* !MSWINCE */ + /* else */ InitializeCriticalSection(&GC_allocate_ml); + } +# endif +# endif /* GC_WIN32_THREADS && !GC_PTHREADS */ +# if defined(GC_WIN32_THREADS) \ + && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)) + InitializeCriticalSection(&GC_write_cs); +# endif + GC_setpagesize(); +# ifdef MSWIN32 + GC_init_win32(); +# endif +# ifdef GC_READ_ENV_FILE + GC_envfile_init(); +# endif +# if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) +# ifdef GC_PRINT_VERBOSE_STATS + /* This is useful for debugging and profiling on platforms with */ + /* missing getenv() (like WinCE). */ + GC_print_stats = VERBOSE; +# else + if (0 != GETENV("GC_PRINT_VERBOSE_STATS")) { + GC_print_stats = VERBOSE; + } else if (0 != GETENV("GC_PRINT_STATS")) { + GC_print_stats = 1; + } +# endif +# endif +# if ((defined(UNIX_LIKE) && !defined(GC_ANDROID_LOG)) \ + || (defined(CONSOLE_LOG) && defined(MSWIN32)) \ + || defined(CYGWIN32) || defined(SYMBIAN)) && !defined(SMALL_CONFIG) + { + char * file_name = TRUSTED_STRING(GETENV("GC_LOG_FILE")); +# ifdef GC_LOG_TO_FILE_ALWAYS + if (NULL == file_name) + file_name = GC_LOG_STD_NAME; +# else + if (0 != file_name) +# endif + { +# if defined(_MSC_VER) + int log_d = _open(file_name, O_CREAT | O_WRONLY | O_APPEND); +# else + int log_d = open(file_name, O_CREAT | O_WRONLY | O_APPEND, 0644); +# endif + if (log_d < 0) { + GC_err_printf("Failed to open %s as log file\n", file_name); + } else { + char *str; + GC_log = log_d; + str = GETENV("GC_ONLY_LOG_TO_FILE"); +# ifdef GC_ONLY_LOG_TO_FILE + /* The similar environment variable set to "0" */ + /* overrides the effect of the macro defined. */ + if (str != NULL && *str == '0' && *(str + 1) == '\0') +# else + /* Otherwise setting the environment variable */ + /* to anything other than "0" will prevent from */ + /* redirecting stdout/err to the log file. */ + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) +# endif + { + GC_stdout = log_d; + GC_stderr = log_d; + } + } + } + } +# endif +# if !defined(NO_DEBUGGING) && !defined(GC_DUMP_REGULARLY) + if (0 != GETENV("GC_DUMP_REGULARLY")) { + GC_dump_regularly = TRUE; + } +# endif +# ifdef KEEP_BACK_PTRS + { + char * backtraces_string = GETENV("GC_BACKTRACES"); + if (0 != backtraces_string) { + GC_backtraces = atol(backtraces_string); + if (backtraces_string[0] == '\0') GC_backtraces = 1; + } + } +# endif + if (0 != GETENV("GC_FIND_LEAK")) { + GC_find_leak = 1; + } +# ifndef SHORT_DBG_HDRS + if (0 != GETENV("GC_FINDLEAK_DELAY_FREE")) { + GC_findleak_delay_free = TRUE; + } +# endif + if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) { + GC_all_interior_pointers = 1; + } + if (0 != GETENV("GC_DONT_GC")) { +# ifdef LINT2 + GC_disable(); +# else + GC_dont_gc = 1; +# endif + } + if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) { + GC_print_back_height = TRUE; + } + if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) { + GC_large_alloc_warn_interval = LONG_MAX; + } + { + char * addr_string = GETENV("GC_TRACE"); + if (0 != addr_string) { +# ifndef ENABLE_TRACE + WARN("Tracing not enabled: Ignoring GC_TRACE value\n", 0); +# else + word addr = (word)STRTOULL(addr_string, NULL, 16); + if (addr < 0x1000) + WARN("Unlikely trace address: %p\n", (void *)addr); + GC_trace_addr = (ptr_t)addr; +# 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 +# if !defined(GC_DISABLE_INCREMENTAL) && !defined(NO_CLOCK) + { + char * time_limit_string = GETENV("GC_PAUSE_TIME_TARGET"); + if (0 != time_limit_string) { + long time_limit = atol(time_limit_string); + if (time_limit > 0) { + GC_time_limit = time_limit; + } + } + } +# endif +# ifndef SMALL_CONFIG + { + char * full_freq_string = GETENV("GC_FULL_FREQUENCY"); + if (full_freq_string != NULL) { + int full_freq = atoi(full_freq_string); + if (full_freq > 0) + GC_full_freq = full_freq; + } + } +# endif + { + char * interval_string = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL"); + if (0 != interval_string) { + long interval = atol(interval_string); + if (interval <= 0) { + WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has " + "bad value: Ignoring\n", 0); + } else { + GC_large_alloc_warn_interval = interval; + } + } + } + { + char * space_divisor_string = GETENV("GC_FREE_SPACE_DIVISOR"); + if (space_divisor_string != NULL) { + int space_divisor = atoi(space_divisor_string); + if (space_divisor > 0) + GC_free_space_divisor = (unsigned)space_divisor; + } + } +# ifdef USE_MUNMAP + { + char * string = GETENV("GC_UNMAP_THRESHOLD"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to disable unmapping. */ + GC_unmap_threshold = 0; + } else { + int unmap_threshold = atoi(string); + if (unmap_threshold > 0) + GC_unmap_threshold = unmap_threshold; + } + } + } + { + char * string = GETENV("GC_FORCE_UNMAP_ON_GCOLLECT"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to turn off the mode. */ + GC_force_unmap_on_gcollect = FALSE; + } else { + GC_force_unmap_on_gcollect = TRUE; + } + } + } + { + char * string = GETENV("GC_USE_ENTIRE_HEAP"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to turn off the mode. */ + GC_use_entire_heap = FALSE; + } else { + GC_use_entire_heap = TRUE; + } + } + } +# endif +# if !defined(NO_DEBUGGING) && !defined(NO_CLOCK) + GET_TIME(GC_init_time); +# endif + maybe_install_looping_handler(); +# if ALIGNMENT > GC_DS_TAGS + /* Adjust normal object descriptor for extra allocation. */ + if (EXTRA_BYTES != 0) + GC_obj_kinds[NORMAL].ok_descriptor = + ((~(word)ALIGNMENT) + 1) | GC_DS_LENGTH; +# endif + GC_exclude_static_roots_inner(beginGC_arrays, endGC_arrays); + GC_exclude_static_roots_inner(beginGC_obj_kinds, endGC_obj_kinds); +# ifdef SEPARATE_GLOBALS + GC_exclude_static_roots_inner(beginGC_objfreelist, endGC_objfreelist); + GC_exclude_static_roots_inner(beginGC_aobjfreelist, endGC_aobjfreelist); +# endif +# if defined(USE_PROC_FOR_LIBRARIES) && defined(GC_LINUX_THREADS) + WARN("USE_PROC_FOR_LIBRARIES + GC_LINUX_THREADS performs poorly.\n", 0); + /* If thread stacks are cached, they tend to be scanned in */ + /* entirety as part of the root set. This will grow them to */ + /* maximum size, and is generally not desirable. */ +# endif +# if !defined(THREADS) || defined(GC_PTHREADS) \ + || defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(GC_WIN32_THREADS) || defined(GC_SOLARIS_THREADS) + if (GC_stackbottom == 0) { + GC_stackbottom = GC_get_main_stack_base(); +# if (defined(LINUX) || defined(HPUX)) && defined(IA64) + GC_register_stackbottom = GC_get_register_stack_base(); +# endif + } else { +# if (defined(LINUX) || defined(HPUX)) && defined(IA64) + if (GC_register_stackbottom == 0) { + WARN("GC_register_stackbottom should be set with GC_stackbottom\n", 0); + /* The following may fail, since we may rely on */ + /* alignment properties that may not hold with a user set */ + /* GC_stackbottom. */ + GC_register_stackbottom = GC_get_register_stack_base(); + } +# endif + } +# endif +# if !defined(CPPCHECK) + GC_STATIC_ASSERT(sizeof(ptr_t) == sizeof(word)); + GC_STATIC_ASSERT(sizeof(signed_word) == sizeof(word)); +# if !defined(_AUX_SOURCE) || defined(__GNUC__) + GC_STATIC_ASSERT((word)(-1) > (word)0); + /* word should be unsigned */ +# 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); +# endif + GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE); +# ifndef THREADS + GC_ASSERT(!((word)GC_stackbottom HOTTER_THAN (word)GC_approx_sp())); +# endif + GC_init_headers(); +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) \ + && (defined(SEARCH_FOR_DATA_START) || defined(NETBSD)) + LOCK(); /* just to set GC_lock_holder */ +# endif +# ifdef SEARCH_FOR_DATA_START + /* For MPROTECT_VDB, the temporary fault handler should be */ + /* installed first, before the write fault one in GC_dirty_init. */ + if (GC_REGISTER_MAIN_STATIC_DATA()) GC_init_linux_data_start(); +# elif defined(NETBSD) && defined(__ELF__) + if (GC_REGISTER_MAIN_STATIC_DATA()) GC_init_netbsd_elf(); +# endif +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) \ + && (defined(SEARCH_FOR_DATA_START) || defined(NETBSD)) + UNLOCK(); +# endif +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental || 0 != GETENV("GC_ENABLE_INCREMENTAL")) { +# if defined(BASE_ATOMIC_OPS_EMULATED) || defined(CHECKSUMS) \ + || defined(REDIRECT_MALLOC) || defined(REDIRECT_MALLOC_IN_HEADER) \ + || defined(SMALL_CONFIG) + /* TODO: Implement CHECKSUMS for manual VDB. */ +# else + if (manual_vdb_allowed) { + GC_manual_vdb = TRUE; + GC_incremental = TRUE; + } else +# endif + /* else */ { + /* For GWW_VDB on Win32, this needs to happen before any */ + /* heap memory is allocated. */ + GC_incremental = GC_dirty_init(); + GC_ASSERT(GC_bytes_allocd == 0); + } + } +# endif + + /* Add initial guess of root sets. Do this first, since sbrk(0) */ + /* might be used. */ + if (GC_REGISTER_MAIN_STATIC_DATA()) GC_register_data_segments(); + + GC_bl_init(); + GC_mark_init(); + { + char * sz_str = GETENV("GC_INITIAL_HEAP_SIZE"); + if (sz_str != NULL) { + initial_heap_sz = GC_parse_mem_size_arg(sz_str); + if (initial_heap_sz <= MINHINCR * HBLKSIZE) { + WARN("Bad initial heap size %s - ignoring it.\n", sz_str); + } + } + } + { + char * sz_str = GETENV("GC_MAXIMUM_HEAP_SIZE"); + if (sz_str != NULL) { + word max_heap_sz = GC_parse_mem_size_arg(sz_str); + if (max_heap_sz < initial_heap_sz) { + WARN("Bad maximum heap size %s - ignoring it.\n", sz_str); + } + if (0 == GC_max_retries) GC_max_retries = 2; + GC_set_max_heap_size(max_heap_sz); + } + } +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + LOCK(); /* just to set GC_lock_holder */ +# endif + if (!GC_expand_hp_inner(divHBLKSZ(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(); + GC_register_displacement_inner(0L); +# if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) + if (!GC_all_interior_pointers) { + /* TLS ABI uses pointer-sized offsets for dtv. */ + GC_register_displacement_inner(sizeof(void *)); + } +# endif + GC_init_size_map(); +# ifdef PCR + if (PCR_IL_Lock(PCR_Bool_false, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay) { + ABORT("Can't lock load state"); + } else if (PCR_IL_Unlock() != PCR_ERes_okay) { + ABORT("Can't unlock load state"); + } + PCR_IL_Unlock(); + GC_pcr_install(); +# endif + GC_is_initialized = TRUE; +# if defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) +# if defined(LINT2) \ + && !(defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED)) + LOCK(); + GC_thr_init(); + UNLOCK(); +# else + GC_thr_init(); +# endif +# endif + COND_DUMP; + /* Get black list set up and/or incremental GC started */ + if (!GC_dont_precollect || GC_incremental) { + GC_gcollect_inner(); + } +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + UNLOCK(); +# endif +# if defined(THREADS) && defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + /* Ensure getcontext_works is set to avoid potential data race. */ + if (GC_dont_gc || GC_dont_precollect) + GC_with_callee_saves_pushed(callee_saves_pushed_dummy_fn, NULL); +# endif +# 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 + + /* The rest of this again assumes we don't really hold */ + /* the allocation lock. */ +# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC) \ + || (defined(GC_ALWAYS_MULTITHREADED) && defined(GC_WIN32_THREADS) \ + && !defined(GC_NO_THREADS_DISCOVERY)) + /* Make sure thread local allocation is initialized, in */ + /* case we did not get called from GC_init_parallel(). */ + GC_init_parallel(); +# endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */ + +# if defined(DYNAMIC_LOADING) && defined(DARWIN) + /* This must be called WITHOUT the allocation lock held */ + /* and before any threads are created. */ + GC_init_dyld(); +# endif + RESTORE_CANCEL(cancel_state); +} + +GC_API void GC_CALL GC_enable_incremental(void) +{ +# if !defined(GC_DISABLE_INCREMENTAL) && !defined(KEEP_BACK_PTRS) + DCL_LOCK_STATE; + /* If we are keeping back pointers, the GC itself dirties all */ + /* pages on which objects have been marked, making */ + /* incremental GC pointless. */ + if (!GC_find_leak && 0 == GETENV("GC_DISABLE_INCREMENTAL")) { + LOCK(); + if (!GC_incremental) { + GC_setpagesize(); + /* TODO: Should we skip enabling incremental if win32s? */ + maybe_install_looping_handler(); /* Before write fault handler! */ + if (!GC_is_initialized) { + UNLOCK(); + GC_incremental = TRUE; /* indicate intention to turn it on */ + GC_init(); + LOCK(); + } else { +# if !defined(BASE_ATOMIC_OPS_EMULATED) && !defined(CHECKSUMS) \ + && !defined(REDIRECT_MALLOC) \ + && !defined(REDIRECT_MALLOC_IN_HEADER) && !defined(SMALL_CONFIG) + if (manual_vdb_allowed) { + GC_manual_vdb = TRUE; + GC_incremental = TRUE; + } else +# endif + /* else */ { + GC_incremental = GC_dirty_init(); + } + } + if (GC_incremental && !GC_dont_gc) { + /* Can't easily do it if GC_dont_gc. */ + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + if (GC_bytes_allocd > 0) { + /* There may be unmarked reachable objects. */ + GC_gcollect_inner(); + } + /* else we're OK in assuming everything's */ + /* clean since nothing can point to an */ + /* unmarked object. */ + GC_read_dirty(FALSE); + RESTORE_CANCEL(cancel_state); + } + } + UNLOCK(); + return; + } +# endif + GC_init(); +} + +GC_API void GC_CALL GC_start_mark_threads(void) +{ +# ifdef PARALLEL_MARK + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + DISABLE_CANCEL(cancel_state); + LOCK(); + GC_start_mark_threads_inner(); + UNLOCK(); + RESTORE_CANCEL(cancel_state); +# else + /* No action since parallel markers are disabled (or no POSIX fork). */ + GC_ASSERT(I_DONT_HOLD_LOCK()); +# endif +} + + GC_API void GC_CALL GC_deinit(void) + { + if (GC_is_initialized) { + /* Prevent duplicate resource close. */ + GC_is_initialized = FALSE; +# if defined(GC_WIN32_THREADS) && (defined(MSWIN32) || defined(MSWINCE)) +# if !defined(CONSOLE_LOG) || defined(MSWINCE) + DeleteCriticalSection(&GC_write_cs); +# endif +# ifndef GC_PTHREADS + DeleteCriticalSection(&GC_allocate_ml); +# endif +# endif + } + } + +#if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE) + +# if defined(_MSC_VER) && defined(_DEBUG) && !defined(MSWINCE) +# include +# endif + + STATIC HANDLE GC_log = 0; + +# ifdef THREADS +# if defined(PARALLEL_MARK) && !defined(GC_ALWAYS_MULTITHREADED) +# define IF_NEED_TO_LOCK(x) if (GC_parallel || GC_need_to_lock) x +# else +# define IF_NEED_TO_LOCK(x) if (GC_need_to_lock) x +# endif +# else +# define IF_NEED_TO_LOCK(x) +# endif /* !THREADS */ + +# ifdef MSWINRT_FLAVOR +# include + + /* This API is defined in roapi.h, but we cannot include it here */ + /* since it does not compile in C. */ + DECLSPEC_IMPORT HRESULT WINAPI RoGetActivationFactory( + HSTRING activatableClassId, + REFIID iid, void** factory); + + static GC_bool getWinRTLogPath(wchar_t* buf, size_t bufLen) + { + static const GUID kIID_IApplicationDataStatics = { + 0x5612147B, 0xE843, 0x45E3, + 0x94, 0xD8, 0x06, 0x16, 0x9E, 0x3C, 0x8E, 0x17 + }; + static const GUID kIID_IStorageItem = { + 0x4207A996, 0xCA2F, 0x42F7, + 0xBD, 0xE8, 0x8B, 0x10, 0x45, 0x7A, 0x7F, 0x30 + }; + GC_bool result = FALSE; + HSTRING_HEADER appDataClassNameHeader; + HSTRING appDataClassName; + __x_ABI_CWindows_CStorage_CIApplicationDataStatics* appDataStatics = 0; + + GC_ASSERT(bufLen > 0); + if (SUCCEEDED(WindowsCreateStringReference( + RuntimeClass_Windows_Storage_ApplicationData, + (sizeof(RuntimeClass_Windows_Storage_ApplicationData)-1) + / sizeof(wchar_t), + &appDataClassNameHeader, &appDataClassName)) + && SUCCEEDED(RoGetActivationFactory(appDataClassName, + &kIID_IApplicationDataStatics, + &appDataStatics))) { + __x_ABI_CWindows_CStorage_CIApplicationData* appData = NULL; + __x_ABI_CWindows_CStorage_CIStorageFolder* tempFolder = NULL; + __x_ABI_CWindows_CStorage_CIStorageItem* tempFolderItem = NULL; + HSTRING tempPath = NULL; + + if (SUCCEEDED(appDataStatics->lpVtbl->get_Current(appDataStatics, + &appData)) + && SUCCEEDED(appData->lpVtbl->get_TemporaryFolder(appData, + &tempFolder)) + && SUCCEEDED(tempFolder->lpVtbl->QueryInterface(tempFolder, + &kIID_IStorageItem, + &tempFolderItem)) + && SUCCEEDED(tempFolderItem->lpVtbl->get_Path(tempFolderItem, + &tempPath))) { + UINT32 tempPathLen; + const wchar_t* tempPathBuf = + WindowsGetStringRawBuffer(tempPath, &tempPathLen); + + buf[0] = '\0'; + if (wcsncat_s(buf, bufLen, tempPathBuf, tempPathLen) == 0 + && wcscat_s(buf, bufLen, L"\\") == 0 + && wcscat_s(buf, bufLen, TEXT(GC_LOG_STD_NAME)) == 0) + result = TRUE; + WindowsDeleteString(tempPath); + } + + if (tempFolderItem != NULL) + tempFolderItem->lpVtbl->Release(tempFolderItem); + if (tempFolder != NULL) + tempFolder->lpVtbl->Release(tempFolder); + if (appData != NULL) + appData->lpVtbl->Release(appData); + appDataStatics->lpVtbl->Release(appDataStatics); + } + return result; + } +# endif /* MSWINRT_FLAVOR */ + + STATIC HANDLE GC_CreateLogFile(void) + { + HANDLE hFile; +# ifdef MSWINRT_FLAVOR + TCHAR pathBuf[_MAX_PATH + 0x10]; /* buffer for path + ext */ + + hFile = INVALID_HANDLE_VALUE; + if (getWinRTLogPath(pathBuf, _MAX_PATH + 1)) { + CREATEFILE2_EXTENDED_PARAMETERS extParams; + + BZERO(&extParams, sizeof(extParams)); + extParams.dwSize = sizeof(extParams); + extParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + extParams.dwFileFlags = GC_print_stats == VERBOSE ? 0 + : FILE_FLAG_WRITE_THROUGH; + hFile = CreateFile2(pathBuf, GENERIC_WRITE, FILE_SHARE_READ, + CREATE_ALWAYS, &extParams); + } + +# else + TCHAR *logPath; +# if defined(NO_GETENV_WIN32) && defined(CPPCHECK) +# define appendToFile FALSE +# else + BOOL appendToFile = FALSE; +# endif +# if !defined(NO_GETENV_WIN32) || !defined(OLD_WIN32_LOG_FILE) + 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"), 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 + logPath = TEXT(GC_LOG_STD_NAME); +# else + int len = (int)GetModuleFileName(NULL /* hModule */, pathBuf, + _MAX_PATH + 1); + /* If GetModuleFileName() has failed then len is 0. */ + if (len > 4 && pathBuf[len - 4] == (TCHAR)'.') { + len -= 4; /* strip executable file extension */ + } + BCOPY(TEXT(".") TEXT(GC_LOG_STD_NAME), &pathBuf[len], + sizeof(TEXT(".") TEXT(GC_LOG_STD_NAME))); +# endif + } + + 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 */); + +# 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 +# undef appendToFile +# endif + return hFile; + } + + STATIC int GC_write(const char *buf, size_t len) + { + 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)); +# 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)); +# 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; +# endif + } + res = WriteFile(GC_log, buf, (DWORD)len, &written, NULL); +# if defined(_MSC_VER) && defined(_DEBUG) && !defined(NO_CRT) +# ifdef MSWINCE + /* There is no CrtDbgReport() in WinCE */ + { + WCHAR wbuf[1024]; + /* Always use Unicode variant of OutputDebugString() */ + wbuf[MultiByteToWideChar(CP_ACP, 0 /* dwFlags */, + buf, len, wbuf, + sizeof(wbuf) / sizeof(wbuf[0]) - 1)] = 0; + OutputDebugStringW(wbuf); + } +# else + _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%.*s", len, buf); +# endif +# endif + IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs)); + return res ? (int)written : -1; + } + + /* TODO: This is pretty ugly ... */ +# define WRITE(f, buf, len) GC_write(buf, len) + +#elif defined(OS2) || defined(MACOS) + STATIC FILE * GC_stdout = NULL; + STATIC FILE * GC_stderr = NULL; + STATIC FILE * GC_log = NULL; + + /* Initialize GC_log (and the friends) passed to GC_write(). */ + STATIC void GC_set_files(void) + { + if (GC_stdout == NULL) { + GC_stdout = stdout; + } + if (GC_stderr == NULL) { + GC_stderr = stderr; + } + if (GC_log == NULL) { + GC_log = stderr; + } + } + + GC_INLINE int GC_write(FILE *f, const char *buf, size_t len) + { + int res = fwrite(buf, 1, len, f); + fflush(f); + return res; + } + +# define WRITE(f, buf, len) (GC_set_files(), GC_write(f, buf, len)) + +#elif defined(GC_ANDROID_LOG) + +# include + +# 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) + +#elif defined(NN_PLATFORM_CTR) + int n3ds_log_write(const char* text, int length); +# define WRITE(level, buf, len) n3ds_log_write(buf, len) + +#elif defined(NINTENDO_SWITCH) + int switch_log_write(const char* text, int length); +# define WRITE(level, buf, len) switch_log_write(buf, len) + +#else + +# if !defined(GC_NO_TYPES) && !defined(SN_TARGET_PSP2) +# if !defined(AMIGA) && !defined(MSWIN32) && !defined(MSWIN_XBOX1) \ + && !defined(__CC_ARM) +# include +# endif +# if !defined(ECOS) && !defined(NOSYS) +# include +# endif +# endif /* !GC_NO_TYPES && !SN_TARGET_PSP2 */ + + STATIC int GC_write(int fd, const char *buf, size_t len) + { +# if defined(ECOS) || defined(PLATFORM_WRITE) || defined(SN_TARGET_PSP2) \ + || defined(NOSYS) +# ifdef ECOS + /* FIXME: This seems to be defined nowhere at present. */ + /* _Jv_diag_write(buf, len); */ +# else + /* No writing. */ +# endif + return len; +# else + int bytes_written = 0; + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + while ((unsigned)bytes_written < len) { +# ifdef GC_SOLARIS_THREADS + int result = syscall(SYS_write, fd, buf + bytes_written, + len - bytes_written); +# elif defined(_MSC_VER) + int result = _write(fd, buf + bytes_written, + (unsigned)(len - bytes_written)); +# else + int result = write(fd, buf + bytes_written, len - bytes_written); +# endif + + if (-1 == result) { + if (EAGAIN == errno) /* Resource temporarily unavailable */ + continue; + RESTORE_CANCEL(cancel_state); + return(result); + } + bytes_written += result; + } + RESTORE_CANCEL(cancel_state); + return(bytes_written); +# endif + } + +# define WRITE(f, buf, len) GC_write(f, buf, len) +#endif /* !MSWINCE && !OS2 && !MACOS && !GC_ANDROID_LOG */ + +#define BUFSZ 1024 + +#if defined(DJGPP) || defined(__STRICT_ANSI__) + /* vsnprintf is missing in DJGPP (v2.0.3) */ +# define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args) +#elif defined(_MSC_VER) +# ifdef MSWINCE + /* _vsnprintf is deprecated in WinCE */ +# define GC_VSNPRINTF StringCchVPrintfA +# else +# 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 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, ...) +{ + if (!GC_quiet) { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); +# ifdef NACL + (void)WRITE(GC_stdout, buf, strlen(buf)); + /* Ignore errors silently. */ +# else + if (WRITE(GC_stdout, buf, strlen(buf)) < 0 +# if defined(CYGWIN32) || (defined(CONSOLE_LOG) && defined(MSWIN32)) + && GC_stdout != GC_DEFAULT_STDOUT_FD +# endif + ) { + ABORT("write to stdout failed"); + } +# endif + } +} + +void GC_err_printf(const char *format, ...) +{ + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + GC_err_puts(buf); +} + +void GC_log_printf(const char *format, ...) +{ + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); +# ifdef NACL + (void)WRITE(GC_log, buf, strlen(buf)); +# else + if (WRITE(GC_log, buf, strlen(buf)) < 0 +# if defined(CYGWIN32) || (defined(CONSOLE_LOG) && defined(MSWIN32)) + && GC_log != GC_DEFAULT_STDERR_FD +# endif + ) { + ABORT("write to GC log failed"); + } +# endif +} + +#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) +{ + (void)WRITE(GC_stderr, s, strlen(s)); /* ignore errors */ +} + +STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word 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; + +/* This is recommended for production code (release). */ +GC_API void GC_CALLBACK GC_ignore_warn_proc(char *msg, GC_word arg) +{ + if (GC_print_stats) { + /* Don't ignore warnings if stats printing is on. */ + GC_default_warn_proc(msg, arg); + } +} + +GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc p) +{ + DCL_LOCK_STATE; + GC_ASSERT(NONNULL_ARG_NOT_NULL(p)); +# ifdef GC_WIN32_THREADS +# ifdef CYGWIN32 + /* Need explicit GC_INIT call */ + GC_ASSERT(GC_is_initialized); +# else + if (!GC_is_initialized) GC_init(); +# endif +# endif + LOCK(); + GC_current_warn_proc = p; + UNLOCK(); +} + +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void) +{ + GC_warn_proc result; + DCL_LOCK_STATE; + LOCK(); + result = GC_current_warn_proc; + UNLOCK(); + return(result); +} + +#if !defined(PCR) && !defined(SMALL_CONFIG) + /* 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) + { +# ifndef DONT_USE_ATEXIT + skip_gc_atexit = TRUE; /* disable at-exit GC_gcollect() */ +# endif + + if (msg != NULL) { +# ifdef MSGBOX_ON_ERROR + GC_win32_MessageBoxA(msg, "Fatal error in GC", MB_ICONERROR | MB_OK); + /* 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 defined(GC_WIN32_THREADS) && defined(GC_ASSERTIONS) \ + && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)) + if (!GC_write_disabled) +# endif + { + if (WRITE(GC_stderr, msg, strlen(msg)) >= 0) + (void)WRITE(GC_stderr, "\n", 1); + } +# else + __android_log_assert("*" /* cond */, GC_ANDROID_LOG_TAG, "%s\n", msg); +# endif + } + +# 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 */ + /* about threads. */ + 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(NONNULL_ARG_NOT_NULL(fn)); + 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(); +} + +GC_API void GC_CALL GC_disable(void) +{ + DCL_LOCK_STATE; + LOCK(); + GC_dont_gc++; + 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) +{ + void *result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_INTERNAL_MALLOC((MAXOBJGRANULES+1) * sizeof(ptr_t), PTRFREE); + if (NULL == result) ABORT("Failed to allocate freelist for new kind"); + BZERO(result, (MAXOBJGRANULES+1)*sizeof(ptr_t)); + return (void **)result; +} + +GC_API void ** GC_CALL GC_new_free_list(void) +{ + void ** result; + DCL_LOCK_STATE; + LOCK(); + result = GC_new_free_list_inner(); + UNLOCK(); + return result; +} + +GC_API unsigned GC_CALL GC_new_kind_inner(void **fl, GC_word descr, + int adjust, int clear) +{ + unsigned result = GC_n_kinds; + + GC_ASSERT(NONNULL_ARG_NOT_NULL(fl)); + GC_ASSERT(adjust == FALSE || adjust == TRUE); + /* If an object is not needed to be cleared (when moved to the */ + /* free list) then its descriptor should be zero to denote */ + /* a pointer-free object (and, as a consequence, the size of the */ + /* object should not be added to the descriptor template). */ + GC_ASSERT(clear == TRUE + || (descr == 0 && adjust == FALSE && clear == FALSE)); + if (result < MAXOBJKINDS) { + GC_ASSERT(result > 0); + GC_n_kinds++; + GC_obj_kinds[result].ok_freelist = fl; + GC_obj_kinds[result].ok_reclaim_list = 0; + GC_obj_kinds[result].ok_descriptor = descr; + GC_obj_kinds[result].ok_relocate_descr = adjust; + GC_obj_kinds[result].ok_init = (GC_bool)clear; +# ifdef ENABLE_DISCLAIM + GC_obj_kinds[result].ok_mark_unconditionally = FALSE; + GC_obj_kinds[result].ok_disclaim_proc = 0; +# endif + } else { + ABORT("Too many kinds"); + } + return result; +} + +GC_API unsigned GC_CALL GC_new_kind(void **fl, GC_word descr, int adjust, + int clear) +{ + unsigned result; + DCL_LOCK_STATE; + LOCK(); + result = GC_new_kind_inner(fl, descr, adjust, clear); + UNLOCK(); + return result; +} + +GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc proc) +{ + unsigned result = GC_n_mark_procs; + + if (result < MAX_MARK_PROCS) { + GC_n_mark_procs++; + GC_mark_procs[result] = proc; + } else { + ABORT("Too many mark procedures"); + } + return result; +} + +GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc proc) +{ + unsigned result; + DCL_LOCK_STATE; + LOCK(); + result = GC_new_proc_inner(proc); + UNLOCK(); + return result; +} + +GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type fn, void *client_data) +{ + void * result; + DCL_LOCK_STATE; + + LOCK(); + result = (*fn)(client_data); + UNLOCK(); + return(result); +} + +GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func fn, void *arg) +{ + struct GC_stack_base base; + void *result; + + 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. */ +# elif defined(E2K) + base.reg_base = NULL; /* not used by GC currently */ +# endif + result = fn(&base, arg); + /* Strongly discourage the compiler from treating the above */ + /* as a tail call. */ + GC_noop1(COVERT_DATAFLOW(&base)); + return result; +} + +#ifndef THREADS + +GC_INNER ptr_t GC_blocked_sp = NULL; + /* NULL value means we are not inside GC_do_blocking() call. */ +# ifdef IA64 + STATIC ptr_t GC_blocked_register_sp = NULL; +# endif + +GC_INNER struct GC_traced_stack_sect_s *GC_traced_stack_sect = NULL; + +/* This is nearly the same as in win32_threads.c */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, + void * client_data) +{ + struct GC_traced_stack_sect_s stacksect; + GC_ASSERT(GC_is_initialized); + + /* Adjust our stack bottom pointer (this could happen if */ + /* GC_get_main_stack_base() is unimplemented or broken for */ + /* the platform). */ + if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect)) + GC_stackbottom = (ptr_t)COVERT_DATAFLOW(&stacksect); + + if (GC_blocked_sp == NULL) { + /* We are not inside GC_do_blocking() - do nothing more. */ + client_data = fn(client_data); + /* Prevent treating the above as a tail call. */ + GC_noop1(COVERT_DATAFLOW(&stacksect)); + return client_data; /* result */ + } + + /* Setup new "stack section". */ + stacksect.saved_stack_ptr = GC_blocked_sp; +# ifdef IA64 + /* This is the same as in GC_call_with_stack_base(). */ + stacksect.backing_store_end = GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ + stacksect.saved_backing_store_ptr = GC_blocked_register_sp; +# endif + stacksect.prev = GC_traced_stack_sect; + GC_blocked_sp = NULL; + GC_traced_stack_sect = &stacksect; + + client_data = fn(client_data); + GC_ASSERT(GC_blocked_sp == NULL); + GC_ASSERT(GC_traced_stack_sect == &stacksect); + +# if defined(CPPCHECK) + GC_noop1((word)GC_traced_stack_sect - (word)GC_blocked_sp); +# endif + /* Restore original "stack section". */ + GC_traced_stack_sect = stacksect.prev; +# ifdef IA64 + GC_blocked_register_sp = stacksect.saved_backing_store_ptr; +# endif + GC_blocked_sp = stacksect.saved_stack_ptr; + + return client_data; /* result */ +} + +/* This is nearly the same as in win32_threads.c */ +STATIC void GC_do_blocking_inner(ptr_t data, void * context GC_ATTR_UNUSED) +{ + struct blocking_data * d = (struct blocking_data *) data; + GC_ASSERT(GC_is_initialized); + GC_ASSERT(GC_blocked_sp == NULL); +# ifdef SPARC + GC_blocked_sp = GC_save_regs_in_stack(); +# else + GC_blocked_sp = (ptr_t) &d; /* save approx. sp */ +# ifdef IA64 + GC_blocked_register_sp = GC_save_regs_in_stack(); +# elif defined(E2K) + (void)GC_save_regs_in_stack(); +# endif +# endif + + d -> client_data = (d -> fn)(d -> client_data); + +# ifdef SPARC + GC_ASSERT(GC_blocked_sp != NULL); +# else + GC_ASSERT(GC_blocked_sp == (ptr_t)(&d)); +# endif +# if defined(CPPCHECK) + GC_noop1((word)GC_blocked_sp); +# endif + GC_blocked_sp = NULL; +} + + GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle, + const struct GC_stack_base *sb) + { + GC_ASSERT(sb -> mem_base != NULL); + GC_ASSERT(NULL == gc_thread_handle || &GC_stackbottom == gc_thread_handle); + GC_ASSERT(NULL == GC_blocked_sp + && NULL == GC_traced_stack_sect); /* for now */ + (void)gc_thread_handle; + + GC_stackbottom = (char *)sb->mem_base; +# ifdef IA64 + GC_register_stackbottom = (ptr_t)sb->reg_base; +# endif + } + + GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb) + { + GC_ASSERT(GC_is_initialized); + sb -> mem_base = GC_stackbottom; +# ifdef IA64 + sb -> reg_base = GC_register_stackbottom; +# elif defined(E2K) + sb -> reg_base = NULL; +# endif + return &GC_stackbottom; /* gc_thread_handle */ + } +#endif /* !THREADS */ + +GC_API void * GC_CALL GC_do_blocking(GC_fn_type fn, void * client_data) +{ + struct blocking_data my_data; + + my_data.fn = fn; + my_data.client_data = client_data; + GC_with_callee_saves_pushed(GC_do_blocking_inner, (ptr_t)(&my_data)); + return my_data.client_data; /* result */ +} + +#if !defined(NO_DEBUGGING) + GC_API void GC_CALL GC_dump(void) + { + DCL_LOCK_STATE; + + LOCK(); + GC_dump_named(NULL); + UNLOCK(); + } + + GC_API void GC_CALL GC_dump_named(const char *name) + { +# ifndef NO_CLOCK + CLOCK_TYPE current_time; + + GET_TIME(current_time); +# endif + if (name != NULL) { + GC_printf("***GC Dump %s\n", name); + } else { + GC_printf("***GC Dump collection #%lu\n", (unsigned long)GC_gc_no); + } +# ifndef NO_CLOCK + /* Note that the time is wrapped in ~49 days if sizeof(long)==4. */ + GC_printf("Time since GC init: %lu ms\n", + MS_TIME_DIFF(current_time, GC_init_time)); +# endif + + GC_printf("\n***Static roots:\n"); + GC_print_static_roots(); + GC_printf("\n***Heap sections:\n"); + GC_print_heap_sects(); + GC_printf("\n***Free blocks:\n"); + GC_print_hblkfreelist(); + GC_printf("\n***Blocks in use:\n"); + GC_print_block_list(); + } +#endif /* !NO_DEBUGGING */ + +static void block_add_size(struct hblk *h, word pbytes) +{ + hdr *hhdr = HDR(h); + *(word *)pbytes += (WORDS_TO_BYTES(hhdr->hb_sz) + (HBLKSIZE - 1)) + & ~(word)(HBLKSIZE - 1); +} + +GC_API size_t GC_CALL GC_get_memory_use(void) +{ + word bytes = 0; + DCL_LOCK_STATE; + + LOCK(); + GC_apply_to_all_blocks(block_add_size, (word)(&bytes)); + UNLOCK(); + return (size_t)bytes; +} + +/* Getter functions for the public Read-only variables. */ + +/* GC_get_gc_no() is unsynchronized and should be typically called */ +/* inside the context of GC_call_with_alloc_lock() to prevent data */ +/* races (on multiprocessors). */ +GC_API GC_word GC_CALL GC_get_gc_no(void) +{ + return GC_gc_no; +} + +#ifdef THREADS + GC_API int GC_CALL GC_get_parallel(void) + { + return GC_parallel; + } + + GC_API void GC_CALL GC_alloc_lock(void) + { + DCL_LOCK_STATE; + LOCK(); + } + + GC_API void GC_CALL GC_alloc_unlock(void) + { + /* no DCL_LOCK_STATE */ + UNLOCK(); + } + + GC_INNER GC_on_thread_event_proc GC_on_thread_event = 0; + + GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc fn) + { + /* fn may be 0 (means no event notifier). */ + DCL_LOCK_STATE; + LOCK(); + GC_on_thread_event = fn; + UNLOCK(); + } + + GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void) + { + GC_on_thread_event_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_on_thread_event; + UNLOCK(); + return fn; + } + +# ifdef STACKPTR_CORRECTOR_AVAILABLE + GC_INNER GC_sp_corrector_proc GC_sp_corrector = 0; +# endif + + GC_API void GC_CALL GC_set_sp_corrector( + GC_sp_corrector_proc fn GC_ATTR_UNUSED) + { +# ifdef STACKPTR_CORRECTOR_AVAILABLE + DCL_LOCK_STATE; + + LOCK(); + GC_sp_corrector = fn; + UNLOCK(); +# endif + } + + GC_API GC_sp_corrector_proc GC_CALL GC_get_sp_corrector(void) + { +# ifdef STACKPTR_CORRECTOR_AVAILABLE + GC_sp_corrector_proc fn; + DCL_LOCK_STATE; + + LOCK(); + fn = GC_sp_corrector; + UNLOCK(); + return fn; +# else + return 0; /* unsupported */ +# endif + } +#endif /* THREADS */ + +/* Setter and getter functions for the public R/W function variables. */ +/* These functions are synchronized (like GC_set_warn_proc() and */ +/* GC_get_warn_proc()). */ + +GC_API void GC_CALL GC_set_oom_fn(GC_oom_func fn) +{ + DCL_LOCK_STATE; + + GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); + LOCK(); + GC_oom_fn = fn; + UNLOCK(); +} + +GC_API GC_oom_func GC_CALL GC_get_oom_fn(void) +{ + GC_oom_func fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_oom_fn; + UNLOCK(); + return fn; +} + +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc fn) +{ + /* fn may be 0 (means no event notifier). */ + DCL_LOCK_STATE; + LOCK(); + GC_on_heap_resize = fn; + UNLOCK(); +} + +GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void) +{ + GC_on_heap_resize_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_on_heap_resize; + UNLOCK(); + return fn; +} + +GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn) +{ + /* fn may be 0 (means no finalizer notifier). */ + DCL_LOCK_STATE; + LOCK(); + GC_finalizer_notifier = fn; + UNLOCK(); +} + +GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void) +{ + GC_finalizer_notifier_proc fn; + DCL_LOCK_STATE; + LOCK(); + fn = GC_finalizer_notifier; + UNLOCK(); + return fn; +} + +/* Setter and getter functions for the public numeric R/W variables. */ +/* It is safe to call these functions even before GC_INIT(). */ +/* These functions are unsynchronized and should be typically called */ +/* inside the context of GC_call_with_alloc_lock() (if called after */ +/* GC_INIT()) to prevent data races (unless it is guaranteed the */ +/* collector is not multi-threaded at that execution point). */ + +GC_API void GC_CALL GC_set_find_leak(int value) +{ + /* value is of boolean type. */ + GC_find_leak = value; +} + +GC_API int GC_CALL GC_get_find_leak(void) +{ + return GC_find_leak; +} + +GC_API void GC_CALL GC_set_all_interior_pointers(int value) +{ + DCL_LOCK_STATE; + + GC_all_interior_pointers = value ? 1 : 0; + if (GC_is_initialized) { + /* It is not recommended to change GC_all_interior_pointers value */ + /* after GC is initialized but it seems GC could work correctly */ + /* even after switching the mode. */ + LOCK(); + GC_initialize_offsets(); /* NOTE: this resets manual offsets as well */ + if (!GC_all_interior_pointers) + GC_bl_init_no_interiors(); + UNLOCK(); + } +} + +GC_API int GC_CALL GC_get_all_interior_pointers(void) +{ + return GC_all_interior_pointers; +} + +GC_API void GC_CALL GC_set_finalize_on_demand(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_finalize_on_demand = value; +} + +GC_API int GC_CALL GC_get_finalize_on_demand(void) +{ + return GC_finalize_on_demand; +} + +GC_API void GC_CALL GC_set_java_finalization(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_java_finalization = value; +} + +GC_API int GC_CALL GC_get_java_finalization(void) +{ + return GC_java_finalization; +} + +GC_API void GC_CALL GC_set_dont_expand(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_dont_expand = value; +} + +GC_API int GC_CALL GC_get_dont_expand(void) +{ + return GC_dont_expand; +} + +GC_API void GC_CALL GC_set_no_dls(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_no_dls = value; +} + +GC_API int GC_CALL GC_get_no_dls(void) +{ + return GC_no_dls; +} + +GC_API void GC_CALL GC_set_non_gc_bytes(GC_word value) +{ + GC_non_gc_bytes = value; +} + +GC_API GC_word GC_CALL GC_get_non_gc_bytes(void) +{ + return GC_non_gc_bytes; +} + +GC_API void GC_CALL GC_set_free_space_divisor(GC_word value) +{ + GC_ASSERT(value > 0); + GC_free_space_divisor = value; +} + +GC_API GC_word GC_CALL GC_get_free_space_divisor(void) +{ + return GC_free_space_divisor; +} + +GC_API void GC_CALL GC_set_max_retries(GC_word value) +{ + GC_ASSERT((GC_signed_word)value != -1); + /* -1 was used to retrieve old value in gc-7.2 */ + GC_max_retries = value; +} + +GC_API GC_word GC_CALL GC_get_max_retries(void) +{ + return GC_max_retries; +} + +GC_API void GC_CALL GC_set_dont_precollect(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_dont_precollect = value; +} + +GC_API int GC_CALL GC_get_dont_precollect(void) +{ + return GC_dont_precollect; +} + +GC_API void GC_CALL GC_set_full_freq(int value) +{ + GC_ASSERT(value >= 0); + GC_full_freq = value; +} + +GC_API int GC_CALL GC_get_full_freq(void) +{ + return GC_full_freq; +} + +GC_API void GC_CALL GC_set_time_limit(unsigned long value) +{ + GC_ASSERT((long)value != -1L); + /* -1 was used to retrieve old value in gc-7.2 */ + GC_time_limit = value; +} + +GC_API unsigned long GC_CALL GC_get_time_limit(void) +{ + return GC_time_limit; +} + +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int value) +{ + GC_force_unmap_on_gcollect = (GC_bool)value; +} + +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void) +{ + return (int)GC_force_unmap_on_gcollect; +} + +GC_API void GC_CALL GC_abort_on_oom(void) +{ + GC_err_printf("Insufficient memory for the allocation\n"); + EXIT(); +} + +#ifdef THREADS + GC_API void GC_CALL GC_stop_world_external(void) + { + GC_ASSERT(GC_is_initialized); + LOCK(); +# ifdef THREAD_LOCAL_ALLOC + GC_ASSERT(!GC_world_stopped); +# endif + STOP_WORLD(); +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = TRUE; +# endif + } + + GC_API void GC_CALL GC_start_world_external(void) + { +# ifdef THREAD_LOCAL_ALLOC + GC_ASSERT(GC_world_stopped); + GC_world_stopped = FALSE; +# else + GC_ASSERT(GC_is_initialized); +# endif + START_WORLD(); + UNLOCK(); + } +#endif /* THREADS */ diff --git a/bdwgc/new_hblk.c b/bdwgc/new_hblk.c new file mode 100644 index 000000000..ff8f61801 --- /dev/null +++ b/bdwgc/new_hblk.c @@ -0,0 +1,192 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 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. + * + * 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. + */ + +#include "private/gc_priv.h" + +/* + * This file contains the functions: + * ptr_t GC_build_flXXX(h, old_fl) + * void GC_new_hblk(size, kind) + */ + +#include + +#ifndef SMALL_CONFIG + /* Build a free list for size 2 (words) cleared objects inside */ + /* hblk h. Set the last link to be ofl. Return a pointer to the */ + /* first free list entry. */ + STATIC ptr_t GC_build_fl_clear2(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = 0; + p[2] = (word)p; + p[3] = 0; + p += 4; + for (; (word)p < (word)lim; p += 4) { + p[0] = (word)(p-2); + p[1] = 0; + p[2] = (word)p; + p[3] = 0; + } + return((ptr_t)(p-2)); + } + + /* The same for size 4 cleared objects. */ + STATIC ptr_t GC_build_fl_clear4(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = 0; + p[2] = 0; + p[3] = 0; + p += 4; + for (; (word)p < (word)lim; p += 4) { + GC_PREFETCH_FOR_WRITE((ptr_t)(p + 64)); + p[0] = (word)(p-4); + p[1] = 0; + CLEAR_DOUBLE(p+2); + } + return((ptr_t)(p-4)); + } + + /* The same for size 2 uncleared objects. */ + STATIC ptr_t GC_build_fl2(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[2] = (word)p; + p += 4; + for (; (word)p < (word)lim; p += 4) { + p[0] = (word)(p-2); + p[2] = (word)p; + } + return((ptr_t)(p-2)); + } + + /* The same for size 4 uncleared objects. */ + STATIC ptr_t GC_build_fl4(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[4] = (word)p; + p += 8; + for (; (word)p < (word)lim; p += 8) { + GC_PREFETCH_FOR_WRITE((ptr_t)(p + 64)); + p[0] = (word)(p-4); + p[4] = (word)p; + } + return((ptr_t)(p-4)); + } +#endif /* !SMALL_CONFIG */ + +/* Build a free list for objects of size sz inside heap block h. */ +/* Clear objects inside h if clear is set. Add list to the end of */ +/* the free list we build. Return the new free list. */ +/* This could be called without the main GC lock, if we ensure that */ +/* there is no concurrent collection which might reclaim objects that */ +/* we have not yet allocated. */ +GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t sz, GC_bool clear, + ptr_t list) +{ + word *p, *prev; + word *last_object; /* points to last object in new hblk */ + + /* Do a few prefetches here, just because it's cheap. */ + /* If we were more serious about it, these should go inside */ + /* the loops. But write prefetches usually don't seem to */ + /* matter much. */ + GC_PREFETCH_FOR_WRITE((ptr_t)h); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 128); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 256); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 378); +# ifndef SMALL_CONFIG + /* Handle small objects sizes more efficiently. For larger objects */ + /* the difference is less significant. */ + switch (sz) { + case 2: if (clear) { + return GC_build_fl_clear2(h, list); + } else { + return GC_build_fl2(h, list); + } + case 4: if (clear) { + return GC_build_fl_clear4(h, list); + } else { + return GC_build_fl4(h, list); + } + default: + break; + } +# endif /* !SMALL_CONFIG */ + + /* Clear the page if necessary. */ + if (clear) BZERO(h, HBLKSIZE); + + /* Add objects to free list */ + p = (word *)(h -> hb_body) + sz; /* second object in *h */ + prev = (word *)(h -> hb_body); /* One object behind p */ + last_object = (word *)((char *)h + HBLKSIZE); + last_object -= sz; + /* Last place for last object to start */ + + /* make a list of all objects in *h with head as last object */ + while ((word)p <= (word)last_object) { + /* current object's link points to last object */ + obj_link(p) = (ptr_t)prev; + prev = p; + p += sz; + } + p -= sz; /* p now points to last object */ + + /* Put p (which is now head of list of objects in *h) as first */ + /* pointer in the appropriate free list for this size. */ + *(ptr_t *)h = list; + 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 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 */ + GC_bool clear = GC_obj_kinds[kind].ok_init; + + GC_STATIC_ASSERT((sizeof (struct hblk)) == HBLKSIZE); + GC_ASSERT(I_HOLD_LOCK()); + + if (GC_debugging_started) clear = TRUE; + + /* Allocate a new heap block */ + h = GC_allochblk(GRANULES_TO_BYTES(gran), kind, 0); + if (h == 0) return; + + /* Mark all objects if appropriate. */ + if (IS_UNCOLLECTABLE(kind)) GC_set_hdr_marks(HDR(h)); + + /* Build the free list */ + GC_obj_kinds[kind].ok_freelist[gran] = + GC_build_fl(h, GRANULES_TO_WORDS(gran), clear, + (ptr_t)GC_obj_kinds[kind].ok_freelist[gran]); +} diff --git a/bdwgc/obj_map.c b/bdwgc/obj_map.c new file mode 100644 index 000000000..2468e5bfa --- /dev/null +++ b/bdwgc/obj_map.c @@ -0,0 +1,90 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991, 1992 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2001 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. + * + * 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. + */ + +#include "private/gc_priv.h" + +/* Routines for maintaining maps describing heap block + * layouts for various object sizes. Allows fast pointer validity checks + * and fast location of object start locations on machines (such as SPARC) + * with slow division. + */ + +/* Consider pointers that are offset bytes displaced from the beginning */ +/* of an object to be valid. */ + +GC_API void GC_CALL GC_register_displacement(size_t offset) +{ + DCL_LOCK_STATE; + + LOCK(); + GC_register_displacement_inner(offset); + UNLOCK(); +} + +GC_INNER void GC_register_displacement_inner(size_t offset) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (offset >= VALID_OFFSET_SZ) { + ABORT("Bad argument to GC_register_displacement"); + } + if (!GC_valid_offsets[offset]) { + GC_valid_offsets[offset] = TRUE; + GC_modws_valid_offsets[offset % sizeof(word)] = TRUE; + } +} + +#ifdef MARK_BIT_PER_GRANULE + /* Add a heap block map for objects of size granules to obj_map. */ + /* A size of 0 is used for large objects. Return FALSE on failure. */ + GC_INNER GC_bool GC_add_map_entry(size_t granules) + { + unsigned displ; + unsigned short * new_map; + + if (granules > BYTES_TO_GRANULES(MAXOBJBYTES)) granules = 0; + if (GC_obj_map[granules] != 0) { + return(TRUE); + } + new_map = (unsigned short *)GC_scratch_alloc(MAP_LEN * sizeof(short)); + if (new_map == 0) return(FALSE); + 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. */ + } + } else { + for (displ = 0; displ < BYTES_TO_GRANULES(HBLKSIZE); displ++) { + new_map[displ] = (unsigned short)(displ % granules); + } + } + GC_obj_map[granules] = new_map; + return(TRUE); + } +#endif /* MARK_BIT_PER_GRANULE */ + +GC_INNER void GC_initialize_offsets(void) +{ + unsigned i; + if (GC_all_interior_pointers) { + for (i = 0; i < VALID_OFFSET_SZ; ++i) + GC_valid_offsets[i] = TRUE; + } else { + BZERO(GC_valid_offsets, sizeof(GC_valid_offsets)); + for (i = 0; i < sizeof(word); ++i) + GC_modws_valid_offsets[i] = FALSE; + } +} diff --git a/bdwgc/os_dep.c b/bdwgc/os_dep.c new file mode 100644 index 000000000..a42a3f220 --- /dev/null +++ b/bdwgc/os_dep.c @@ -0,0 +1,5408 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2021 Ivan Maidanski + * + * 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. + */ + +#include "private/gc_priv.h" + +#if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \ + && !defined(MSWINCE) && !defined(SN_TARGET_ORBIS) \ + && !defined(SN_TARGET_PSP2) && !defined(__CC_ARM) +# include +# if !defined(MSWIN32) && !defined(MSWIN_XBOX1) +# include +# endif +#endif + +#include +#if defined(MSWINCE) || defined(SN_TARGET_PS3) +# define SIGSEGV 0 /* value is irrelevant */ +#else +# include +#endif + +#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(NACL) \ + || defined(SYMBIAN) +# include +#endif + +#if defined(LINUX) || defined(LINUX_STACKBOTTOM) +# include +#endif + +/* Blatantly OS dependent routines, except for those that are related */ +/* to dynamic loading. */ + +#ifdef AMIGA +# define GC_AMIGA_DEF +# include "extra/AmigaOS.c" +# undef GC_AMIGA_DEF +#endif + +#ifdef MACOS +# include +#endif + +#ifdef IRIX5 +# include +# include /* for locking */ +#endif + +#if defined(MMAP_SUPPORTED) || defined(ADD_HEAP_GUARD_PAGES) +# if defined(USE_MUNMAP) && !defined(USE_MMAP) && !defined(CPPCHECK) +# error Invalid config: USE_MUNMAP requires USE_MMAP +# endif +# include +# include +# include +#endif + +#if defined(ADD_HEAP_GUARD_PAGES) || defined(LINUX_STACKBOTTOM) \ + || defined(MMAP_SUPPORTED) || defined(NEED_PROC_MAPS) +# include +#endif + +#ifdef DARWIN + /* for get_etext and friends */ +# include +#endif + +#ifdef DJGPP + /* Apparently necessary for djgpp 2.01. May cause problems with */ + /* other versions. */ + typedef long unsigned int caddr_t; +#endif + +#ifdef PCR +# include "il/PCR_IL.h" +# include "th/PCR_ThCtl.h" +# include "mm/PCR_MM.h" +#endif + +#if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB) + /* Declare GC_mprotect_stop and GC_mprotect_resume as extern "C". */ +# include "private/darwin_stop_world.h" +#endif + +#if !defined(NO_EXECUTE_PERMISSION) + STATIC GC_bool GC_pages_executable = TRUE; +#else + STATIC GC_bool GC_pages_executable = FALSE; +#endif +#define IGNORE_PAGES_EXECUTABLE 1 + /* Undefined on GC_pages_executable real use. */ + +#if ((defined(LINUX_STACKBOTTOM) || defined(NEED_PROC_MAPS) \ + || defined(PROC_VDB) || defined(SOFT_VDB)) && !defined(PROC_READ)) \ + || defined(CPPCHECK) +# define PROC_READ read + /* Should probably call the real read, if read is wrapped. */ +#endif + +#if defined(LINUX_STACKBOTTOM) || defined(NEED_PROC_MAPS) + /* Repeatedly perform a read call until the buffer is filled */ + /* up, or we encounter EOF or an error. */ + STATIC ssize_t GC_repeat_read(int fd, char *buf, size_t count) + { + size_t num_read = 0; + + ASSERT_CANCEL_DISABLED(); + while (num_read < count) { + ssize_t result = PROC_READ(fd, buf + num_read, count - num_read); + + if (result < 0) return result; + if (result == 0) break; + num_read += result; + } + return num_read; + } +#endif /* LINUX_STACKBOTTOM || NEED_PROC_MAPS */ + +#ifdef NEED_PROC_MAPS +/* We need to parse /proc/self/maps, either to find dynamic libraries, */ +/* and/or to find the register backing store base (IA64). Do it once */ +/* here. */ + +#ifdef THREADS + /* Determine the length of a file by incrementally reading it into a */ + /* buffer. This would be silly to use it on a file supporting lseek, */ + /* but Linux /proc files usually do not. */ + /* As of Linux 4.15.0, lseek(SEEK_END) fails for /proc/self/maps. */ + STATIC size_t GC_get_file_len(int f) + { + size_t total = 0; + ssize_t result; +# define GET_FILE_LEN_BUF_SZ 500 + char buf[GET_FILE_LEN_BUF_SZ]; + + do { + result = PROC_READ(f, buf, sizeof(buf)); + if (result == -1) return 0; + total += result; + } while (result > 0); + return total; + } + + STATIC size_t GC_get_maps_len(void) + { + int f = open("/proc/self/maps", O_RDONLY); + size_t result; + if (f < 0) return 0; /* treat missing file as empty */ + result = GC_get_file_len(f); + close(f); + return result; + } +#endif /* THREADS */ + +/* Copy the contents of /proc/self/maps to a buffer in our address */ +/* space. Return the address of the buffer. */ +GC_INNER const char * GC_get_maps(void) +{ + ssize_t result; + static char *maps_buf = NULL; + static size_t maps_buf_sz = 1; + size_t maps_size; +# ifdef THREADS + size_t old_maps_size = 0; +# endif + + /* The buffer is essentially static, so there must be a single client. */ + GC_ASSERT(I_HOLD_LOCK()); + + /* Note that in the presence of threads, the maps file can */ + /* essentially shrink asynchronously and unexpectedly as */ + /* threads that we already think of as dead release their */ + /* stacks. And there is no easy way to read the entire */ + /* file atomically. This is arguably a misfeature of the */ + /* /proc/self/maps interface. */ + /* Since we expect the file can grow asynchronously in rare */ + /* cases, it should suffice to first determine */ + /* the size (using read), and then to reread the file. */ + /* If the size is inconsistent we have to retry. */ + /* This only matters with threads enabled, and if we use */ + /* this to locate roots (not the default). */ + +# ifdef THREADS + /* Determine the initial size of /proc/self/maps. */ + maps_size = GC_get_maps_len(); + if (0 == maps_size) + ABORT("Cannot determine length of /proc/self/maps"); +# else + maps_size = 4000; /* Guess */ +# endif + + /* Read /proc/self/maps, growing maps_buf as necessary. */ + /* Note that we may not allocate conventionally, and */ + /* thus can't use stdio. */ + do { + int f; + + while (maps_size >= maps_buf_sz) { +# ifdef LINT2 + /* Workaround passing tainted maps_buf to a tainted sink. */ + GC_noop1((word)maps_buf); +# else + GC_scratch_recycle_no_gww(maps_buf, maps_buf_sz); +# endif + /* Grow only by powers of 2, since we leak "too small" buffers.*/ + while (maps_size >= maps_buf_sz) maps_buf_sz *= 2; + maps_buf = GC_scratch_alloc(maps_buf_sz); + if (NULL == maps_buf) + ABORT_ARG1("Insufficient space for /proc/self/maps buffer", + ", %lu bytes requested", (unsigned long)maps_buf_sz); +# ifdef THREADS + /* Recompute initial length, since we allocated. */ + /* This can only happen a few times per program */ + /* execution. */ + maps_size = GC_get_maps_len(); + if (0 == maps_size) + ABORT("Cannot determine length of /proc/self/maps"); +# endif + } + GC_ASSERT(maps_buf_sz >= maps_size + 1); + f = open("/proc/self/maps", O_RDONLY); + if (-1 == f) + ABORT_ARG1("Cannot open /proc/self/maps", + ": errno= %d", errno); +# ifdef THREADS + old_maps_size = maps_size; +# endif + maps_size = 0; + do { + result = GC_repeat_read(f, maps_buf, maps_buf_sz-1); + if (result < 0) { + ABORT_ARG1("Failed to read /proc/self/maps", + ": errno= %d", errno); + } + maps_size += result; + } while ((size_t)result == maps_buf_sz-1); + close(f); + if (0 == maps_size) + ABORT("Empty /proc/self/maps"); +# ifdef THREADS + if (maps_size > old_maps_size) { + /* This might be caused by e.g. thread creation. */ + WARN("Unexpected asynchronous /proc/self/maps growth" + " (to %" WARN_PRIuPTR " bytes)\n", maps_size); + } +# endif + } while (maps_size >= maps_buf_sz +# ifdef THREADS + || maps_size < old_maps_size +# endif + ); + maps_buf[maps_size] = '\0'; + return maps_buf; +} + +/* + * GC_parse_map_entry parses an entry from /proc/self/maps so we can + * locate all writable data segments that belong to shared libraries. + * The format of one of these entries and the fields we care about + * is as follows: + * XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537 name of mapping...\n + * ^^^^^^^^ ^^^^^^^^ ^^^^ ^^ + * start end prot maj_dev + * + * Note that since about august 2003 kernels, the columns no longer have + * fixed offsets on 64-bit kernels. Hence we no longer rely on fixed offsets + * anywhere, which is safer anyway. + */ + +/* Assign various fields of the first line in maps_ptr to (*start), */ +/* (*end), (*prot), (*maj_dev) and (*mapping_name). mapping_name may */ +/* be NULL. (*prot) and (*mapping_name) are assigned pointers into the */ +/* original buffer. */ +#if (defined(DYNAMIC_LOADING) && defined(USE_PROC_FOR_LIBRARIES)) \ + || defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) \ + || (defined(REDIRECT_MALLOC) && defined(GC_LINUX_THREADS)) + GC_INNER const char *GC_parse_map_entry(const char *maps_ptr, + ptr_t *start, ptr_t *end, + const char **prot, unsigned *maj_dev, + const char **mapping_name) + { + const unsigned char *start_start, *end_start, *maj_dev_start; + const unsigned char *p; /* unsigned for isspace, isxdigit */ + + if (maps_ptr == NULL || *maps_ptr == '\0') { + return NULL; + } + + p = (const unsigned char *)maps_ptr; + while (isspace(*p)) ++p; + start_start = p; + GC_ASSERT(isxdigit(*start_start)); + *start = (ptr_t)strtoul((const char *)start_start, (char **)&p, 16); + GC_ASSERT(*p=='-'); + + ++p; + end_start = p; + GC_ASSERT(isxdigit(*end_start)); + *end = (ptr_t)strtoul((const char *)end_start, (char **)&p, 16); + GC_ASSERT(isspace(*p)); + + while (isspace(*p)) ++p; + GC_ASSERT(*p == 'r' || *p == '-'); + *prot = (const char *)p; + /* Skip past protection field to offset field */ + while (!isspace(*p)) ++p; + while (isspace(*p)) p++; + GC_ASSERT(isxdigit(*p)); + /* Skip past offset field, which we ignore */ + while (!isspace(*p)) ++p; + while (isspace(*p)) p++; + maj_dev_start = p; + GC_ASSERT(isxdigit(*maj_dev_start)); + *maj_dev = strtoul((const char *)maj_dev_start, NULL, 16); + + if (mapping_name != NULL) { + while (*p && *p != '\n' && *p != '/' && *p != '[') p++; + *mapping_name = (const char *)p; + } + while (*p && *p++ != '\n'); + return (const char *)p; + } +#endif /* REDIRECT_MALLOC || DYNAMIC_LOADING || IA64 || ... */ + +#if defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) + /* Try to read the backing store base from /proc/self/maps. */ + /* Return the bounds of the writable mapping with a 0 major device, */ + /* which includes the address passed as data. */ + /* Return FALSE if there is no such mapping. */ + GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, + ptr_t *endp) + { + const char *prot; + ptr_t my_start, my_end; + unsigned int maj_dev; + const char *maps_ptr = GC_get_maps(); + + for (;;) { + maps_ptr = GC_parse_map_entry(maps_ptr, &my_start, &my_end, + &prot, &maj_dev, 0); + if (NULL == maps_ptr) break; + + if (prot[1] == 'w' && maj_dev == 0 + && (word)my_end > (word)addr && (word)my_start <= (word)addr) { + *startp = my_start; + *endp = my_end; + return TRUE; + } + } + return FALSE; + } +#endif /* IA64 || INCLUDE_LINUX_THREAD_DESCR */ + +#if defined(REDIRECT_MALLOC) && defined(GC_LINUX_THREADS) + /* Find the text(code) mapping for the library whose name, after */ + /* stripping the directory part, starts with nm. */ + GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp) + { + size_t nm_len = strlen(nm); + const char *prot, *map_path; + ptr_t my_start, my_end; + unsigned int maj_dev; + const char *maps_ptr = GC_get_maps(); + + for (;;) { + maps_ptr = GC_parse_map_entry(maps_ptr, &my_start, &my_end, + &prot, &maj_dev, &map_path); + if (NULL == maps_ptr) break; + + if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x') { + const 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 != '/' && (word)p >= (word)map_path) --p; + ++p; + if (strncmp(nm, p, nm_len) == 0) { + *startp = my_start; + *endp = my_end; + return TRUE; + } + } + } + return FALSE; + } +#endif /* REDIRECT_MALLOC */ + +#ifdef IA64 + static ptr_t backing_store_base_from_proc(void) + { + ptr_t my_start, my_end; + if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) { + GC_COND_LOG_PRINTF("Failed to find backing store base from /proc\n"); + return 0; + } + return my_start; + } +#endif + +#endif /* NEED_PROC_MAPS */ + +#if defined(SEARCH_FOR_DATA_START) + /* The x86 case can be handled without a search. The Alpha case */ + /* used to be handled differently as well, but the rules changed */ + /* for recent Linux versions. This seems to be the easiest way to */ + /* cover all versions. */ + +# if defined(LINUX) || defined(HURD) + /* Some Linux distributions arrange to define __data_start. Some */ + /* define data_start as a weak symbol. The latter is technically */ + /* broken, since the user program may define data_start, in which */ + /* case we lose. Nonetheless, we try both, preferring __data_start.*/ + /* We assume gcc-compatible pragmas. */ + EXTERN_C_BEGIN +# pragma weak __data_start +# pragma weak data_start + extern int __data_start[], data_start[]; + EXTERN_C_END +# endif /* LINUX */ + + ptr_t GC_data_start = NULL; + + GC_INNER void GC_init_linux_data_start(void) + { + ptr_t data_end = DATAEND; + +# if (defined(LINUX) || defined(HURD)) && defined(USE_PROG_DATA_START) + /* Try the easy approaches first: */ + /* However, this may lead to wrong data start value if libgc */ + /* code is put into a shared library (directly or indirectly) */ + /* which is linked with -Bsymbolic-functions option. Thus, */ + /* the following is not used by default. */ + if (COVERT_DATAFLOW(__data_start) != 0) { + GC_data_start = (ptr_t)(__data_start); + } else { + GC_data_start = (ptr_t)(data_start); + } + if (COVERT_DATAFLOW(GC_data_start) != 0) { + if ((word)GC_data_start > (word)data_end) + ABORT_ARG2("Wrong __data_start/_end pair", + ": %p .. %p", (void *)GC_data_start, (void *)data_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 = data_end; /* set data root size to 0 */ + return; + } + + GC_data_start = (ptr_t)GC_find_limit(data_end, FALSE); + } +#endif /* SEARCH_FOR_DATA_START */ + +#ifdef ECOS + +# ifndef ECOS_GC_MEMORY_SIZE +# define ECOS_GC_MEMORY_SIZE (448 * 1024) +# endif /* ECOS_GC_MEMORY_SIZE */ + + /* TODO: This is a simple way of allocating memory which is */ + /* compatible with ECOS early releases. Later releases use a more */ + /* sophisticated means of allocating memory than this simple static */ + /* allocator, but this method is at least bound to work. */ + static char ecos_gc_memory[ECOS_GC_MEMORY_SIZE]; + static char *ecos_gc_brk = ecos_gc_memory; + + static void *tiny_sbrk(ptrdiff_t increment) + { + void *p = ecos_gc_brk; + ecos_gc_brk += increment; + if ((word)ecos_gc_brk > (word)(ecos_gc_memory + sizeof(ecos_gc_memory))) { + ecos_gc_brk -= increment; + return NULL; + } + return p; + } +# define sbrk tiny_sbrk +#endif /* ECOS */ + +#if defined(NETBSD) && defined(__ELF__) + ptr_t GC_data_start = NULL; + + EXTERN_C_BEGIN + extern char **environ; + EXTERN_C_END + + GC_INNER void GC_init_netbsd_elf(void) + { + /* This may need to be environ, without the underscore, for */ + /* some versions. */ + GC_data_start = (ptr_t)GC_find_limit(&environ, FALSE); + } +#endif /* NETBSD */ + +#if defined(ADDRESS_SANITIZER) && (defined(UNIX_LIKE) \ + || defined(NEED_FIND_LIMIT) || defined(MPROTECT_VDB)) \ + && !defined(CUSTOM_ASAN_DEF_OPTIONS) + EXTERN_C_BEGIN + GC_API const char *__asan_default_options(void); + EXTERN_C_END + + /* To tell ASan to allow GC to use its own SIGBUS/SEGV handlers. */ + /* The function is exported just to be visible to ASan library. */ + GC_API const char *__asan_default_options(void) + { + return "allow_user_segv_handler=1"; + } +#endif + +#ifdef OPENBSD + static struct sigaction old_segv_act; + STATIC JMP_BUF GC_jmp_buf_openbsd; + + STATIC void GC_fault_handler_openbsd(int sig GC_ATTR_UNUSED) + { + LONGJMP(GC_jmp_buf_openbsd, 1); + } + +# ifdef GC_OPENBSD_UTHREADS +# include + EXTERN_C_BEGIN + extern sigset_t __syscall(quad_t, ...); + EXTERN_C_END + + /* Don't use GC_find_limit() because siglongjmp() outside of the */ + /* signal handler by-passes our userland pthreads lib, leaving */ + /* SIGSEGV and SIGPROF masked. Instead, use this custom one that */ + /* works-around the issues. */ + + /* 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) + { + static volatile ptr_t result; + /* Safer if static, since otherwise it may not be */ + /* preserved across the longjmp. Can safely be */ + /* static since it's only called with the */ + /* allocation lock held. */ + + struct sigaction act; + word pgsz = (word)sysconf(_SC_PAGESIZE); + + GC_ASSERT((word)bound >= pgsz); + GC_ASSERT(I_HOLD_LOCK()); + + 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 (SETJMP(GC_jmp_buf_openbsd) == 0) { + result = (ptr_t)((word)p & ~(pgsz-1)); + for (;;) { + if ((word)result >= (word)bound - pgsz) { + result = bound; + break; + } + result += pgsz; /* no overflow expected */ + GC_noop1((word)(*result)); + } + } + +# ifdef THREADS + /* Due to the siglongjump we need to manually unmask SIGPROF. */ + __syscall(SYS_sigprocmask, SIG_UNBLOCK, sigmask(SIGPROF)); +# endif + + sigaction(SIGSEGV, &old_segv_act, 0); + return(result); + } +# endif /* GC_OPENBSD_UTHREADS */ + + static volatile int firstpass; + + /* Return first addressable location > p or bound. */ + /* Requires the allocation lock. */ + STATIC ptr_t GC_skip_hole_openbsd(ptr_t p, ptr_t bound) + { + static volatile ptr_t result; + + struct sigaction act; + word pgsz = (word)sysconf(_SC_PAGESIZE); + + GC_ASSERT((word)bound >= pgsz); + GC_ASSERT(I_HOLD_LOCK()); + + 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; + result = (ptr_t)((word)p & ~(pgsz-1)); + if (SETJMP(GC_jmp_buf_openbsd) != 0 || firstpass) { + firstpass = 0; + if ((word)result >= (word)bound - pgsz) { + result = bound; + } else { + result += pgsz; /* no overflow expected */ + GC_noop1((word)(*result)); + } + } + + sigaction(SIGSEGV, &old_segv_act, 0); + return(result); + } +#endif /* OPENBSD */ + +# ifdef OS2 + +# include + +# if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */ + +struct exe_hdr { + unsigned short magic_number; + unsigned short padding[29]; + long new_exe_offset; +}; + +#define E_MAGIC(x) (x).magic_number +#define EMAGIC 0x5A4D +#define E_LFANEW(x) (x).new_exe_offset + +struct e32_exe { + unsigned char magic_number[2]; + unsigned char byte_order; + unsigned char word_order; + unsigned long exe_format_level; + unsigned short cpu; + unsigned short os; + unsigned long padding1[13]; + unsigned long object_table_offset; + unsigned long object_count; + unsigned long padding2[31]; +}; + +#define E32_MAGIC1(x) (x).magic_number[0] +#define E32MAGIC1 'L' +#define E32_MAGIC2(x) (x).magic_number[1] +#define E32MAGIC2 'X' +#define E32_BORDER(x) (x).byte_order +#define E32LEBO 0 +#define E32_WORDER(x) (x).word_order +#define E32LEWO 0 +#define E32_CPU(x) (x).cpu +#define E32CPU286 1 +#define E32_OBJTAB(x) (x).object_table_offset +#define E32_OBJCNT(x) (x).object_count + +struct o32_obj { + unsigned long size; + unsigned long base; + unsigned long flags; + unsigned long pagemap; + unsigned long mapsize; + unsigned long reserved; +}; + +#define O32_FLAGS(x) (x).flags +#define OBJREAD 0x0001L +#define OBJWRITE 0x0002L +#define OBJINVALID 0x0080L +#define O32_SIZE(x) (x).size +#define O32_BASE(x) (x).base + +# else /* IBM's compiler */ + +/* A kludge to get around what appears to be a header file bug */ +# ifndef WORD +# define WORD unsigned short +# endif +# ifndef DWORD +# define DWORD unsigned long +# endif + +# define EXE386 1 +# include +# include + +# endif /* __IBMC__ */ + +# define INCL_DOSEXCEPTIONS +# define INCL_DOSPROCESS +# define INCL_DOSERRORS +# define INCL_DOSMODULEMGR +# define INCL_DOSMEMMGR +# include + +# endif /* OS/2 */ + +/* Find the page size */ +GC_INNER size_t GC_page_size = 0; + +#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# ifndef VER_PLATFORM_WIN32_CE +# define VER_PLATFORM_WIN32_CE 3 +# endif + +# if defined(MSWINCE) && defined(THREADS) + 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); +# if defined(CYGWIN32) && (defined(MPROTECT_VDB) || defined(USE_MUNMAP)) + /* Allocations made with mmap() are aligned to the allocation */ + /* granularity, which (at least on Win64) is not the */ + /* same as the page size. Probably a separate variable could */ + /* be added to distinguish the allocation granularity from the */ + /* actual page size, but in practice there is no good reason to */ + /* make allocations smaller than dwAllocationGranularity, so we */ + /* just use it instead of the actual page size here (as Cygwin */ + /* itself does in many cases). */ + GC_page_size = (size_t)GC_sysinfo.dwAllocationGranularity; + GC_ASSERT(GC_page_size >= (size_t)GC_sysinfo.dwPageSize); +# else + GC_page_size = (size_t)GC_sysinfo.dwPageSize; +# endif +# if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION) + { + OSVERSIONINFO verInfo; + /* Check the current WinCE version. */ + verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx(&verInfo)) + ABORT("GetVersionEx failed"); + if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_CE && + verInfo.dwMajorVersion < 6) { + /* Only the first 32 MB of address space belongs to the */ + /* current process (unless WinCE 6.0+ or emulation). */ + GC_sysinfo.lpMaximumApplicationAddress = (LPVOID)((word)32 << 20); +# ifdef THREADS + /* On some old WinCE versions, it's observed that */ + /* VirtualQuery calls don't work properly when used to */ + /* get thread current stack committed minimum. */ + if (verInfo.dwMajorVersion < 5) + GC_dont_query_stack_min = TRUE; +# endif + } + } +# endif + } + +# ifndef CYGWIN32 +# define is_writable(prot) ((prot) == PAGE_READWRITE \ + || (prot) == PAGE_WRITECOPY \ + || (prot) == PAGE_EXECUTE_READWRITE \ + || (prot) == PAGE_EXECUTE_WRITECOPY) + /* Return the number of bytes that are writable starting at p. */ + /* The pointer p is assumed to be page aligned. */ + /* If base is not 0, *base becomes the beginning of the */ + /* allocation region containing p. */ + STATIC word GC_get_writable_length(ptr_t p, ptr_t *base) + { + MEMORY_BASIC_INFORMATION buf; + word result; + word protect; + + result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) ABORT("Weird VirtualQuery result"); + if (base != 0) *base = (ptr_t)(buf.AllocationBase); + protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE)); + if (!is_writable(protect)) { + return(0); + } + if (buf.State != MEM_COMMIT) return(0); + return(buf.RegionSize); + } + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + ptr_t trunc_sp; + word size; + + /* Set page size if it is not ready (so client can use this */ + /* function even before GC is initialized). */ + if (!GC_page_size) GC_setpagesize(); + + 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). */ + size = GC_get_writable_length(trunc_sp, 0); + GC_ASSERT(size != 0); + sb -> mem_base = trunc_sp + size; + return GC_SUCCESS; + } +# else /* CYGWIN32 */ + /* An alternate version for Cygwin (adapted from Dave Korn's */ + /* gcc version of boehm-gc). */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { +# ifdef X86_64 + sb -> mem_base = ((NT_TIB*)NtCurrentTeb())->StackBase; +# else + void * _tlsbase; + + __asm__ ("movl %%fs:4, %0" + : "=r" (_tlsbase)); + sb -> mem_base = _tlsbase; +# endif + return GC_SUCCESS; + } +# endif /* CYGWIN32 */ +# define HAVE_GET_STACK_BASE + +#else /* !MSWIN32 */ + GC_INNER void GC_setpagesize(void) + { +# if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(SOFT_VDB) \ + || defined(USE_MMAP) + GC_page_size = (size_t)GETPAGESIZE(); +# if !defined(CPPCHECK) + if (0 == GC_page_size) + ABORT("getpagesize failed"); +# endif +# else + /* It's acceptable to fake it. */ + GC_page_size = HBLKSIZE; +# endif + } +#endif /* !MSWIN32 */ + +#ifdef HAIKU +# include + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + thread_info th; + get_thread_info(find_thread(NULL),&th); + sb->mem_base = th.stack_end; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* HAIKU */ + +#ifdef OS2 + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + PTIB ptib; /* thread information block */ + PPIB ppib; + if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + WARN("DosGetInfoBlocks failed\n", 0); + return GC_UNIMPLEMENTED; + } + sb->mem_base = ptib->tib_pstacklimit; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* OS2 */ + +# ifdef AMIGA +# define GC_AMIGA_SB +# include "extra/AmigaOS.c" +# undef GC_AMIGA_SB +# define GET_MAIN_STACKBASE_SPECIAL +# endif /* AMIGA */ + +# if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE) \ + || (defined(WRAP_MARK_SOME) && defined(__GNUC__)) + + typedef void (*GC_fault_handler_t)(int); + +# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \ + || defined(HAIKU) || defined(HURD) || defined(FREEBSD) \ + || defined(NETBSD) + static struct sigaction old_segv_act; +# if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) \ + || defined(HURD) || defined(NETBSD) || defined(FREEBSD) + static struct sigaction old_bus_act; +# endif +# else + static GC_fault_handler_t old_segv_handler; +# ifdef HAVE_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(HAIKU) || defined(HURD) || defined(FREEBSD) \ + || defined(NETBSD) || defined(OPENBSD) + struct sigaction act; + + act.sa_handler = h; +# ifdef SIGACTION_FLAGS_NODEFER_HACK + /* Was necessary for Solaris 2.3 and very temporary */ + /* NetBSD bugs. */ + act.sa_flags = SA_RESTART | SA_NODEFER; +# else + act.sa_flags = SA_RESTART; +# 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. */ + (void) sigaction(SIGSEGV, 0, &old_segv_act); + (void) sigaction(SIGSEGV, &act, 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) + /* 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 */ +# else + old_segv_handler = signal(SIGSEGV, h); +# ifdef HAVE_SIGBUS + old_bus_handler = signal(SIGBUS, h); +# endif +# endif +# if defined(CPPCHECK) && defined(ADDRESS_SANITIZER) + GC_noop1((word)&__asan_default_options); +# endif + } +# endif /* NEED_FIND_LIMIT || UNIX_LIKE */ + +# if defined(NEED_FIND_LIMIT) \ + || (defined(WRAP_MARK_SOME) && defined(__GNUC__)) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) + /* Some tools to implement HEURISTIC2 */ +# define MIN_PAGE_SIZE 256 /* Smallest conceivable page size, bytes */ + + GC_INNER JMP_BUF GC_jmp_buf; + + STATIC void GC_fault_handler(int sig GC_ATTR_UNUSED) + { + LONGJMP(GC_jmp_buf, 1); + } + + GC_INNER void GC_setup_temporary_fault_handler(void) + { + /* Handler is process-wide, so this should only happen in */ + /* one thread at a time. */ + GC_ASSERT(I_HOLD_LOCK()); + GC_set_and_save_fault_handler(GC_fault_handler); + } + + GC_INNER void GC_reset_fault_handler(void) + { +# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \ + || defined(HAIKU) || defined(HURD) || defined(FREEBSD) \ + || defined(NETBSD) || defined(OPENBSD) + (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) + (void) sigaction(SIGBUS, &old_bus_act, 0); +# endif +# else + (void) signal(SIGSEGV, old_segv_handler); +# ifdef HAVE_SIGBUS + (void) signal(SIGBUS, old_bus_handler); +# endif +# endif + } + + /* Return the first non-addressable location > p (up) or */ + /* the smallest location q s.t. [q,p) is addressable (!up). */ + /* We assume that p (up) or p-1 (!up) is addressable. */ + /* Requires allocation lock. */ + GC_ATTR_NO_SANITIZE_ADDR + STATIC ptr_t GC_find_limit_with_bound(ptr_t p, GC_bool up, ptr_t bound) + { + static volatile ptr_t result; + /* Safer if static, since otherwise it may not be */ + /* preserved across the longjmp. Can safely be */ + /* static since it's only called with the */ + /* allocation lock held. */ + + GC_ASSERT(up ? (word)bound >= MIN_PAGE_SIZE + : (word)bound <= ~(word)MIN_PAGE_SIZE); + GC_ASSERT(I_HOLD_LOCK()); + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + result = (ptr_t)(((word)(p)) + & ~(MIN_PAGE_SIZE-1)); + for (;;) { + if (up) { + if ((word)result >= (word)bound - MIN_PAGE_SIZE) { + result = bound; + break; + } + result += MIN_PAGE_SIZE; /* no overflow expected */ + } else { + if ((word)result <= (word)bound + MIN_PAGE_SIZE) { + result = bound - MIN_PAGE_SIZE; + /* This is to compensate */ + /* further result increment (we */ + /* do not modify "up" variable */ + /* since it might be clobbered */ + /* by setjmp otherwise). */ + break; + } + result -= MIN_PAGE_SIZE; /* no underflow expected */ + } + GC_noop1((word)(*result)); + } + } + GC_reset_fault_handler(); + if (!up) { + result += MIN_PAGE_SIZE; + } + return(result); + } + + void * GC_find_limit(void * p, int up) + { + return GC_find_limit_with_bound((ptr_t)p, (GC_bool)up, + up ? (ptr_t)GC_WORD_MAX : 0); + } +# endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES */ + +#ifdef HPUX_MAIN_STACKBOTTOM +# include +# include + + STATIC ptr_t GC_hpux_main_stack_base(void) + { + struct pst_vm_status vm_status; + int i = 0; + + while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) { + if (vm_status.pst_type == PS_STACK) + return (ptr_t)vm_status.pst_vaddr; + } + + /* Old way to get the stack bottom. */ +# ifdef STACK_GROWS_UP + return (ptr_t)GC_find_limit(GC_approx_sp(), /* up= */ FALSE); +# else /* not HP_PA */ + return (ptr_t)GC_find_limit(GC_approx_sp(), TRUE); +# endif + } +#endif /* HPUX_MAIN_STACKBOTTOM */ + +#ifdef HPUX_STACKBOTTOM + +#include +#include + + GC_INNER ptr_t GC_get_register_stack_base(void) + { + struct pst_vm_status vm_status; + + int i = 0; + while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) { + if (vm_status.pst_type == PS_RSESTACK) { + return (ptr_t) vm_status.pst_vaddr; + } + } + + /* old way to get the register stackbottom */ + return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1) + & ~(BACKING_STORE_ALIGNMENT - 1)); + } + +#endif /* HPUX_STACK_BOTTOM */ + +#ifdef LINUX_STACKBOTTOM + +# include +# include + +# define STAT_SKIP 27 /* Number of fields preceding startstack */ + /* field in /proc/self/stat */ + +# ifdef USE_LIBC_PRIVATES + EXTERN_C_BEGIN +# pragma weak __libc_stack_end + extern ptr_t __libc_stack_end; +# ifdef IA64 +# pragma weak __libc_ia64_register_backing_store_base + extern ptr_t __libc_ia64_register_backing_store_base; +# endif + EXTERN_C_END +# endif + +# ifdef IA64 + GC_INNER ptr_t GC_get_register_stack_base(void) + { + ptr_t result; + +# ifdef USE_LIBC_PRIVATES + if (0 != &__libc_ia64_register_backing_store_base + && 0 != __libc_ia64_register_backing_store_base) { + /* Glibc 2.2.4 has a bug such that for dynamically linked */ + /* executables __libc_ia64_register_backing_store_base is */ + /* defined but uninitialized during constructor calls. */ + /* Hence we check for both nonzero address and value. */ + return __libc_ia64_register_backing_store_base; + } +# endif + result = backing_store_base_from_proc(); + if (0 == result) { + result = (ptr_t)GC_find_limit(GC_save_regs_in_stack(), FALSE); + /* This works better than a constant displacement heuristic. */ + } + return result; + } +# endif /* IA64 */ + + STATIC ptr_t GC_linux_main_stack_base(void) + { + /* We read the stack bottom value from /proc/self/stat. We do this */ + /* using direct I/O system calls in order to avoid calling malloc */ + /* in case REDIRECT_MALLOC is defined. */ +# define STAT_BUF_SIZE 4096 + char stat_buf[STAT_BUF_SIZE]; + int f; + word result; + ssize_t i, buf_offset = 0, len; + + /* First try the easy way. This should work for glibc 2.2 */ + /* This fails in a prelinked ("prelink" command) executable */ + /* since the correct value of __libc_stack_end never */ + /* becomes visible to us. The second test works around */ + /* this. */ +# ifdef USE_LIBC_PRIVATES + if (0 != &__libc_stack_end && 0 != __libc_stack_end ) { +# if defined(IA64) + /* Some versions of glibc set the address 16 bytes too */ + /* low while the initialization code is running. */ + if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) { + return __libc_stack_end + 0x10; + } /* 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. */ + if (__libc_stack_end != (ptr_t) (unsigned long)0x1) + return __libc_stack_end; +# else + return __libc_stack_end; +# endif + } +# endif + + f = open("/proc/self/stat", O_RDONLY); + if (-1 == f) + ABORT_ARG1("Could not open /proc/self/stat", ": errno= %d", errno); + len = GC_repeat_read(f, stat_buf, sizeof(stat_buf)); + if (len < 0) + ABORT_ARG1("Failed to read /proc/self/stat", + ": errno= %d", errno); + close(f); + + /* Skip the required number of fields. This number is hopefully */ + /* constant across all Linux implementations. */ + for (i = 0; i < STAT_SKIP; ++i) { + while (buf_offset < len && isspace(stat_buf[buf_offset++])) { + /* empty */ + } + while (buf_offset < len && !isspace(stat_buf[buf_offset++])) { + /* empty */ + } + } + /* Skip spaces. */ + while (buf_offset < len && isspace(stat_buf[buf_offset])) { + buf_offset++; + } + /* Find the end of the number and cut the buffer there. */ + for (i = 0; buf_offset + i < len; i++) { + if (!isdigit(stat_buf[buf_offset + i])) break; + } + if (buf_offset + i >= len) ABORT("Could not parse /proc/self/stat"); + stat_buf[buf_offset + i] = '\0'; + + result = (word)STRTOULL(&stat_buf[buf_offset], NULL, 10); + if (result < 0x100000 || (result & (sizeof(word) - 1)) != 0) + ABORT_ARG1("Absurd stack bottom value", + ": 0x%lx", (unsigned long)result); + return (ptr_t)result; + } +#endif /* LINUX_STACKBOTTOM */ + +#ifdef FREEBSD_STACKBOTTOM + /* This uses an undocumented sysctl call, but at least one expert */ + /* believes it will stay. */ + +# include +# include +# include + + STATIC ptr_t GC_freebsd_main_stack_base(void) + { + int nm[2] = {CTL_KERN, KERN_USRSTACK}; + ptr_t base; + size_t len = sizeof(ptr_t); + int r = sysctl(nm, 2, &base, &len, NULL, 0); + if (r) ABORT("Error getting main stack base"); + return base; + } +#endif /* FREEBSD_STACKBOTTOM */ + +#if defined(ECOS) || defined(NOSYS) + ptr_t GC_get_main_stack_base(void) + { + return STACKBOTTOM; + } +# define GET_MAIN_STACKBASE_SPECIAL +#elif defined(SYMBIAN) + EXTERN_C_BEGIN + extern int GC_get_main_symbian_stack_base(void); + EXTERN_C_END + + ptr_t GC_get_main_stack_base(void) + { + return (ptr_t)GC_get_main_symbian_stack_base(); + } +# define GET_MAIN_STACKBASE_SPECIAL +#elif defined(EMSCRIPTEN) + +# if defined(USE_EMSCRIPTEN_SCAN_STACK) && defined(EMSCRIPTEN_ASYNCIFY) + /* According to the documentation, emscripten_scan_stack() is only */ + /* guaranteed to be available when building with ASYNCIFY. */ +# include + + static void *emscripten_stack_base; + + static void scan_stack_cb(void *begin, void *end) + { + (void)begin; + emscripten_stack_base = end; + } +# else +# include +# endif + + ptr_t GC_get_main_stack_base(void) + { +# if defined(USE_EMSCRIPTEN_SCAN_STACK) && defined(EMSCRIPTEN_ASYNCIFY) + emscripten_scan_stack(scan_stack_cb); + return (ptr_t)emscripten_stack_base; +# else + return (ptr_t)emscripten_stack_get_base(); +# endif + } +# define GET_MAIN_STACKBASE_SPECIAL +#elif !defined(AMIGA) && !defined(HAIKU) && !defined(OS2) \ + && !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) \ + && !defined(GC_OPENBSD_THREADS) \ + && (!defined(GC_SOLARIS_THREADS) || defined(_STRICT_STDC)) + +# if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && (defined(THREADS) || defined(USE_GET_STACKBASE_FOR_MAIN)) +# include +# ifdef HAVE_PTHREAD_NP_H +# include /* for pthread_attr_get_np() */ +# endif +# elif defined(DARWIN) && !defined(NO_PTHREAD_GET_STACKADDR_NP) + /* We could use pthread_get_stackaddr_np even in case of a */ + /* single-threaded gclib (there is no -lpthread on Darwin). */ +# include +# undef STACKBOTTOM +# define STACKBOTTOM (ptr_t)pthread_get_stackaddr_np(pthread_self()) +# endif + + ptr_t GC_get_main_stack_base(void) + { + ptr_t result; +# if (defined(HAVE_PTHREAD_ATTR_GET_NP) \ + || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && (defined(USE_GET_STACKBASE_FOR_MAIN) \ + || (defined(THREADS) && !defined(REDIRECT_MALLOC))) + pthread_attr_t attr; + void *stackaddr; + size_t size; + +# ifdef HAVE_PTHREAD_ATTR_GET_NP + if (pthread_attr_init(&attr) == 0 + && (pthread_attr_get_np(pthread_self(), &attr) == 0 + ? TRUE : (pthread_attr_destroy(&attr), FALSE))) +# else /* HAVE_PTHREAD_GETATTR_NP */ + if (pthread_getattr_np(pthread_self(), &attr) == 0) +# endif + { + if (pthread_attr_getstack(&attr, &stackaddr, &size) == 0 + && stackaddr != NULL) { + (void)pthread_attr_destroy(&attr); +# ifdef STACK_GROWS_DOWN + stackaddr = (char *)stackaddr + size; +# endif + return (ptr_t)stackaddr; + } + (void)pthread_attr_destroy(&attr); + } + WARN("pthread_getattr_np or pthread_attr_getstack failed" + " for main thread\n", 0); +# endif +# ifdef STACKBOTTOM + result = STACKBOTTOM; +# else +# ifdef HEURISTIC1 +# define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) +# ifdef STACK_GROWS_DOWN + result = (ptr_t)(((word)GC_approx_sp() + STACKBOTTOM_ALIGNMENT_M1) + & ~STACKBOTTOM_ALIGNMENT_M1); +# else + result = (ptr_t)((word)GC_approx_sp() & ~STACKBOTTOM_ALIGNMENT_M1); +# endif +# elif defined(HPUX_MAIN_STACKBOTTOM) + result = GC_hpux_main_stack_base(); +# elif defined(LINUX_STACKBOTTOM) + result = GC_linux_main_stack_base(); +# elif defined(FREEBSD_STACKBOTTOM) + result = GC_freebsd_main_stack_base(); +# elif defined(HEURISTIC2) + { + ptr_t sp = GC_approx_sp(); +# ifdef STACK_GROWS_DOWN + result = (ptr_t)GC_find_limit(sp, TRUE); +# if defined(HEURISTIC2_LIMIT) && !defined(CPPCHECK) + if ((word)result > (word)HEURISTIC2_LIMIT + && (word)sp < (word)HEURISTIC2_LIMIT) { + result = HEURISTIC2_LIMIT; + } +# endif +# else + result = (ptr_t)GC_find_limit(sp, FALSE); +# if defined(HEURISTIC2_LIMIT) && !defined(CPPCHECK) + if ((word)result < (word)HEURISTIC2_LIMIT + && (word)sp > (word)HEURISTIC2_LIMIT) { + result = HEURISTIC2_LIMIT; + } +# endif +# endif + } +# elif defined(STACK_NOT_SCANNED) || defined(CPPCHECK) + result = NULL; +# else +# error None of HEURISTIC* and *STACKBOTTOM defined! +# endif +# if defined(STACK_GROWS_DOWN) && !defined(CPPCHECK) + if (result == 0) + result = (ptr_t)(signed_word)(-sizeof(ptr_t)); +# endif +# endif +# if !defined(CPPCHECK) + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)result); +# endif + return(result); + } +# define GET_MAIN_STACKBASE_SPECIAL +#endif /* !AMIGA, !HAIKU, !OPENBSD, !OS2, !Windows */ + +#if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && defined(THREADS) && !defined(HAVE_GET_STACK_BASE) +# include +# ifdef HAVE_PTHREAD_NP_H +# include +# endif + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + pthread_attr_t attr; + size_t size; +# ifdef IA64 + DCL_LOCK_STATE; +# endif + +# ifdef HAVE_PTHREAD_ATTR_GET_NP + if (pthread_attr_init(&attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_get_np(pthread_self(), &attr) != 0) { + WARN("pthread_attr_get_np failed\n", 0); + (void)pthread_attr_destroy(&attr); + return GC_UNIMPLEMENTED; + } +# else /* HAVE_PTHREAD_GETATTR_NP */ + if (pthread_getattr_np(pthread_self(), &attr) != 0) { + WARN("pthread_getattr_np failed\n", 0); + return GC_UNIMPLEMENTED; + } +# endif + if (pthread_attr_getstack(&attr, &(b -> mem_base), &size) != 0) { + ABORT("pthread_attr_getstack failed"); + } + (void)pthread_attr_destroy(&attr); +# ifdef STACK_GROWS_DOWN + b -> mem_base = (char *)(b -> mem_base) + size; +# endif +# ifdef IA64 + /* We could try backing_store_base_from_proc, but that's safe */ + /* only if no mappings are being asynchronously created. */ + /* Subtracting the size from the stack base doesn't work for at */ + /* least the main thread. */ + LOCK(); + { + IF_CANCEL(int cancel_state;) + ptr_t bsp; + ptr_t next_stack; + + DISABLE_CANCEL(cancel_state); + bsp = GC_save_regs_in_stack(); + next_stack = GC_greatest_stack_base_below(bsp); + if (0 == next_stack) { + b -> reg_base = GC_find_limit(bsp, FALSE); + } else { + /* Avoid walking backwards into preceding memory stack and */ + /* growing it. */ + b -> reg_base = GC_find_limit_with_bound(bsp, FALSE, next_stack); + } + RESTORE_CANCEL(cancel_state); + } + UNLOCK(); +# elif defined(E2K) + b -> reg_base = NULL; +# endif + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* THREADS && (HAVE_PTHREAD_ATTR_GET_NP || HAVE_PTHREAD_GETATTR_NP) */ + +#if defined(GC_DARWIN_THREADS) && !defined(NO_PTHREAD_GET_STACKADDR_NP) +# include + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + /* pthread_get_stackaddr_np() should return stack bottom (highest */ + /* stack address plus 1). */ + b->mem_base = pthread_get_stackaddr_np(pthread_self()); + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)b->mem_base); + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_DARWIN_THREADS */ + +#ifdef GC_OPENBSD_THREADS +# include +# include +# include + + /* Find the stack using pthread_stackseg_np(). */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + stack_t stack; + if (pthread_stackseg_np(pthread_self(), &stack)) + ABORT("pthread_stackseg_np(self) failed"); + sb->mem_base = stack.ss_sp; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_OPENBSD_THREADS */ + +#if defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC) + +# include +# include +# include + + /* These variables are used to cache ss_sp value for the primordial */ + /* thread (it's better not to call thr_stksegment() twice for this */ + /* thread - see JDK bug #4352906). */ + static pthread_t stackbase_main_self = 0; + /* 0 means stackbase_main_ss_sp value is unset. */ + static void *stackbase_main_ss_sp = NULL; + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + 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 */ + /* then just return the cached value. */ + b -> mem_base = stackbase_main_ss_sp; + GC_ASSERT(b -> mem_base != NULL); + return GC_SUCCESS; + } + + if (thr_stksegment(&s)) { + /* According to the manual, the only failure error code returned */ + /* is EAGAIN meaning "the information is not available due to the */ + /* thread is not yet completely initialized or it is an internal */ + /* thread" - this shouldn't happen here. */ + ABORT("thr_stksegment failed"); + } + /* s.ss_sp holds the pointer to the stack bottom. */ + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)s.ss_sp); + + if (!stackbase_main_self && thr_main() != 0) + { + /* Cache the stack bottom pointer for the primordial thread */ + /* (this is done during GC_init, so there is no race). */ + stackbase_main_ss_sp = s.ss_sp; + stackbase_main_self = self; + } + + b -> mem_base = s.ss_sp; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_SOLARIS_THREADS */ + +#ifdef GC_RTEMS_PTHREADS + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + sb->mem_base = rtems_get_stack_bottom(); + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_RTEMS_PTHREADS */ + +#ifndef HAVE_GET_STACK_BASE +# ifdef NEED_FIND_LIMIT + /* Retrieve the stack bottom. */ + /* 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. */ + /* TODO: 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(GC_approx_sp(), TRUE); +# else + b -> mem_base = GC_find_limit(GC_approx_sp(), FALSE); +# endif +# ifdef IA64 + b -> reg_base = GC_find_limit(GC_save_regs_in_stack(), FALSE); +# elif defined(E2K) + b -> reg_base = NULL; +# endif + RESTORE_CANCEL(cancel_state); + UNLOCK(); + return GC_SUCCESS; + } +# 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 + /* This is always called from the main thread. Default implementation. */ + 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((word)GC_approx_sp() HOTTER_THAN (word)sb.mem_base); + return (ptr_t)sb.mem_base; + } +#endif /* !GET_MAIN_STACKBASE_SPECIAL */ + +/* Register static data segment(s) as roots. If more data segments are */ +/* added later then they need to be registered at that point (as we do */ +/* with SunOS dynamic loading), or GC_mark_roots needs to check for */ +/* them (as we do with PCR). Called with allocator lock held. */ +# ifdef OS2 + +void GC_register_data_segments(void) +{ + PTIB ptib; + PPIB ppib; + HMODULE module_handle; +# define PBUFSIZ 512 + UCHAR path[PBUFSIZ]; + FILE * myexefile; + struct exe_hdr hdrdos; /* MSDOS header. */ + struct e32_exe hdr386; /* Real header for my executable */ + struct o32_obj seg; /* Current segment */ + int nsegs; + +# if defined(CPPCHECK) + hdrdos.padding[0] = 0; /* to prevent "field unused" warnings */ + hdr386.exe_format_level = 0; + hdr386.os = 0; + hdr386.padding1[0] = 0; + hdr386.padding2[0] = 0; + seg.pagemap = 0; + seg.mapsize = 0; + seg.reserved = 0; +# endif + if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + ABORT("DosGetInfoBlocks failed"); + } + module_handle = ppib -> pib_hmte; + if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) { + ABORT("DosQueryModuleName failed"); + } + myexefile = fopen(path, "rb"); + if (myexefile == 0) { + ABORT_ARG1("Failed to open executable", ": %s", path); + } + if (fread((char *)(&hdrdos), 1, sizeof(hdrdos), myexefile) + < sizeof(hdrdos)) { + ABORT_ARG1("Could not read MSDOS header", " from: %s", path); + } + if (E_MAGIC(hdrdos) != EMAGIC) { + ABORT_ARG1("Bad DOS magic number", " in file: %s", path); + } + if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) { + ABORT_ARG1("Bad DOS magic number", " in file: %s", path); + } + if (fread((char *)(&hdr386), 1, sizeof(hdr386), myexefile) + < sizeof(hdr386)) { + ABORT_ARG1("Could not read OS/2 header", " from: %s", path); + } + if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) { + ABORT_ARG1("Bad OS/2 magic number", " in file: %s", path); + } + if (E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) { + ABORT_ARG1("Bad byte order in executable", " file: %s", path); + } + if (E32_CPU(hdr386) == E32CPU286) { + ABORT_ARG1("GC cannot handle 80286 executables", ": %s", path); + } + if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386), + SEEK_SET) != 0) { + 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)) { + ABORT_ARG1("Could not read obj table entry", " from file: %s", path); + } + flags = O32_FLAGS(seg); + if (!(flags & OBJWRITE)) continue; + if (!(flags & OBJREAD)) continue; + if (flags & OBJINVALID) { + GC_err_printf("Object with invalid pages?\n"); + continue; + } + GC_add_roots_inner((ptr_t)O32_BASE(seg), + (ptr_t)(O32_BASE(seg)+O32_SIZE(seg)), FALSE); + } + (void)fclose(myexefile); +} + +# else /* !OS2 */ + +# if defined(GWW_VDB) +# ifndef MEM_WRITE_WATCH +# define MEM_WRITE_WATCH 0x200000 +# endif +# ifndef WRITE_WATCH_FLAG_RESET +# define WRITE_WATCH_FLAG_RESET 1 +# endif + + /* Since we can't easily check whether ULONG_PTR and SIZE_T are */ + /* defined in Win32 basetsd.h, we define own ULONG_PTR. */ +# define GC_ULONG_PTR word + + typedef UINT (WINAPI * GetWriteWatch_type)( + DWORD, PVOID, GC_ULONG_PTR /* SIZE_T */, + PVOID *, GC_ULONG_PTR *, PULONG); + static FARPROC GetWriteWatch_func; + static DWORD GetWriteWatch_alloc_flag; + +# define GC_GWW_AVAILABLE() (GetWriteWatch_func != 0) + + static void detect_GetWriteWatch(void) + { + static GC_bool done; + HMODULE hK32; + if (done) + return; + +# if defined(MPROTECT_VDB) + { + char * str = GETENV("GC_USE_GETWRITEWATCH"); +# if defined(GC_PREFER_MPROTECT_VDB) + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) { + /* GC_USE_GETWRITEWATCH is unset or set to "0". */ + done = TRUE; /* falling back to MPROTECT_VDB strategy. */ + /* This should work as if GWW_VDB is undefined. */ + return; + } +# else + if (str != NULL && *str == '0' && *(str + 1) == '\0') { + /* GC_USE_GETWRITEWATCH is set "0". */ + done = TRUE; /* falling back to MPROTECT_VDB strategy. */ + return; + } +# endif + } +# endif + +# ifdef MSWINRT_FLAVOR + { + MEMORY_BASIC_INFORMATION memInfo; + SIZE_T result = VirtualQuery((void*)(word)GetProcAddress, + &memInfo, sizeof(memInfo)); + if (result != sizeof(memInfo)) + ABORT("Weird VirtualQuery result"); + hK32 = (HMODULE)memInfo.AllocationBase; + } +# else + hK32 = GetModuleHandle(TEXT("kernel32.dll")); +# endif + if (hK32 != (HMODULE)0 && + (GetWriteWatch_func = GetProcAddress(hK32, "GetWriteWatch")) != 0) { + /* Also check whether VirtualAlloc accepts MEM_WRITE_WATCH, */ + /* as some versions of kernel32.dll have one but not the */ + /* other, making the feature completely broken. */ + void * page; + + GC_ASSERT(GC_page_size != 0); + page = VirtualAlloc(NULL, GC_page_size, MEM_WRITE_WATCH | MEM_RESERVE, + PAGE_READWRITE); + if (page != NULL) { + PVOID pages[16]; + GC_ULONG_PTR count = 16; + DWORD page_size; + /* Check that it actually works. In spite of some */ + /* documentation it actually seems to exist on Win2K. */ + /* This test may be unnecessary, but ... */ + if ((*(GetWriteWatch_type)(word)GetWriteWatch_func)( + WRITE_WATCH_FLAG_RESET, page, + GC_page_size, pages, &count, + &page_size) != 0) { + /* GetWriteWatch always fails. */ + GetWriteWatch_func = 0; + } else { + GetWriteWatch_alloc_flag = MEM_WRITE_WATCH; + } + VirtualFree(page, 0 /* dwSize */, MEM_RELEASE); + } else { + /* GetWriteWatch will be useless. */ + GetWriteWatch_func = 0; + } + } + done = TRUE; + } + +# else +# define GetWriteWatch_alloc_flag 0 +# endif /* !GWW_VDB */ + +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + +# ifdef MSWIN32 + /* Unfortunately, we have to handle win32s very differently from NT, */ + /* Since VirtualQuery has very different semantics. In particular, */ + /* under win32s a VirtualQuery call on an unmapped page returns an */ + /* invalid result. Under NT, GC_register_data_segments is a no-op */ + /* and all real work is done by GC_register_dynamic_libraries. Under */ + /* win32s, we cannot find the data segments associated with dll's. */ + /* We register the main data segment here. */ + GC_INNER GC_bool GC_no_win32_dlls = FALSE; + /* This used to be set for gcc, to avoid dealing with */ + /* the structured exception handling issues. But we now have */ + /* assembly code to do that right. */ + + GC_INNER GC_bool GC_wnt = FALSE; + /* This is a Windows NT derivative, i.e. NT, Win2K, XP or later. */ + + GC_INNER void GC_init_win32(void) + { +# if defined(_WIN64) || (defined(_MSC_VER) && _MSC_VER >= 1800) + /* MS Visual Studio 2013 deprecates GetVersion, but on the other */ + /* hand it cannot be used to target pre-Win2K. */ + GC_wnt = TRUE; +# else + /* Set GC_wnt. If we're running under win32s, assume that no */ + /* DLLs will be loaded. I doubt anyone still runs win32s, but... */ + DWORD v = GetVersion(); + + GC_wnt = !(v & 0x80000000); + GC_no_win32_dlls |= ((!GC_wnt) && (v & 0xff) <= 3); +# endif +# ifdef USE_MUNMAP + if (GC_no_win32_dlls) { + /* Turn off unmapping for safety (since may not work well with */ + /* GlobalAlloc). */ + GC_unmap_threshold = 0; + } +# endif + } + + /* Return the smallest address a such that VirtualQuery */ + /* returns correct results for all addresses between a and start. */ + /* Assumes VirtualQuery returns correct information for start. */ + STATIC ptr_t GC_least_described_address(ptr_t start) + { + MEMORY_BASIC_INFORMATION buf; + LPVOID limit = GC_sysinfo.lpMinimumApplicationAddress; + ptr_t p = (ptr_t)((word)start & ~(GC_page_size - 1)); + + GC_ASSERT(GC_page_size != 0); + for (;;) { + size_t result; + LPVOID q = (LPVOID)(p - GC_page_size); + + 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); + } + return p; + } +# endif /* MSWIN32 */ + +# if defined(USE_WINALLOC) && !defined(REDIRECT_MALLOC) + /* We maintain a linked list of AllocationBase values that we know */ + /* correspond to malloc heap sections. Currently this is only called */ + /* during a GC. But there is some hope that for long running */ + /* programs we will eventually see most heap sections. */ + + /* In the long run, it would be more reliable to occasionally walk */ + /* the malloc heap with HeapWalk on the default heap. But that */ + /* apparently works only for NT-based Windows. */ + + STATIC size_t GC_max_root_size = 100000; /* Appr. largest root size. */ + + /* In the long run, a better data structure would also be nice ... */ + STATIC struct GC_malloc_heap_list { + void * allocation_base; + struct GC_malloc_heap_list *next; + } *GC_malloc_heap_l = 0; + + /* Is p the base of one of the malloc heap sections we already know */ + /* about? */ + STATIC GC_bool GC_is_malloc_heap_base(void *p) + { + struct GC_malloc_heap_list *q = GC_malloc_heap_l; + + while (0 != q) { + if (q -> allocation_base == p) return TRUE; + q = q -> next; + } + return FALSE; + } + + STATIC void *GC_get_allocation_base(void *p) + { + MEMORY_BASIC_INFORMATION buf; + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + return buf.AllocationBase; + } + + GC_INNER void GC_add_current_malloc_heap(void) + { + struct GC_malloc_heap_list *new_l = (struct GC_malloc_heap_list *) + malloc(sizeof(struct GC_malloc_heap_list)); + void *candidate; + + if (NULL == new_l) return; + new_l -> allocation_base = NULL; + /* to suppress maybe-uninitialized gcc warning */ + + candidate = GC_get_allocation_base(new_l); + if (GC_is_malloc_heap_base(candidate)) { + /* Try a little harder to find malloc heap. */ + size_t req_size = 10000; + do { + void *p = malloc(req_size); + if (0 == p) { + free(new_l); + return; + } + candidate = GC_get_allocation_base(p); + free(p); + req_size *= 2; + } while (GC_is_malloc_heap_base(candidate) + && req_size < GC_max_root_size/10 && req_size < 500000); + if (GC_is_malloc_heap_base(candidate)) { + free(new_l); + return; + } + } + 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; + } + + /* Free all the linked list nodes. Could be invoked at process exit */ + /* to avoid memory leak complains of a dynamic code analysis tool. */ + STATIC void GC_free_malloc_heap_list(void) + { + struct GC_malloc_heap_list *q = GC_malloc_heap_l; + + GC_malloc_heap_l = NULL; + while (q != NULL) { + struct GC_malloc_heap_list *next = q -> next; + free(q); + q = next; + } + } +# endif /* USE_WINALLOC && !REDIRECT_MALLOC */ + + /* Is p the start of either the malloc heap, or of one of our */ + /* heap sections? */ + GC_INNER GC_bool GC_is_heap_base(void *p) + { + int i; + +# if defined(USE_WINALLOC) && !defined(REDIRECT_MALLOC) + if (GC_root_size > GC_max_root_size) + GC_max_root_size = GC_root_size; + if (GC_is_malloc_heap_base(p)) + return TRUE; +# endif + for (i = 0; i < (int)GC_n_heap_bases; i++) { + if (GC_heap_bases[i] == p) return TRUE; + } + return FALSE; + } + +#ifdef MSWIN32 + STATIC void GC_register_root_section(ptr_t static_root) + { + MEMORY_BASIC_INFORMATION buf; + LPVOID p; + char * base; + char * limit; + + if (!GC_no_win32_dlls) return; + p = base = limit = GC_least_described_address(static_root); + while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) { + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + char * new_limit; + DWORD protect; + + if (result != sizeof(buf) || buf.AllocationBase == 0 + || GC_is_heap_base(buf.AllocationBase)) break; + new_limit = (char *)p + buf.RegionSize; + protect = buf.Protect; + if (buf.State == MEM_COMMIT + && is_writable(protect)) { + if ((char *)p == limit) { + limit = new_limit; + } else { + if (base != limit) GC_add_roots_inner(base, limit, FALSE); + base = (char *)p; + limit = new_limit; + } + } + if ((word)p > (word)new_limit /* overflow */) break; + p = (LPVOID)new_limit; + } + if (base != limit) GC_add_roots_inner(base, limit, FALSE); + } +#endif /* MSWIN32 */ + + void GC_register_data_segments(void) + { +# ifdef MSWIN32 + GC_register_root_section((ptr_t)&GC_pages_executable); + /* any other GC global variable would fit too. */ +# endif + } + +# else /* !OS2 && !Windows */ + +# if (defined(SVR4) || defined(AIX) || defined(DGUX) \ + || (defined(LINUX) && defined(SPARC))) && !defined(PCR) + ptr_t GC_SysVGetDataStart(size_t max_page_size, ptr_t etext_addr) + { + word text_end = ((word)(etext_addr) + sizeof(word) - 1) + & ~(word)(sizeof(word) - 1); + /* etext rounded to word boundary */ + 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 ptr_t 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 */ + + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + /* Try writing to the address. */ +# ifdef AO_HAVE_fetch_and_add + volatile AO_t zero = 0; + (void)AO_fetch_and_add((volatile AO_t *)result, zero); +# else + /* Fallback to non-atomic fetch-and-store. */ + char v = *result; +# if defined(CPPCHECK) + GC_noop1((word)&v); +# endif + *result = v; +# endif + GC_reset_fault_handler(); + } else { + GC_reset_fault_handler(); + /* We got here via a longjmp. The address is not readable. */ + /* This is known to happen under Solaris 2.4 + gcc, which place */ + /* string constants in the text segment, but after etext. */ + /* Use plan B. Note that we now know there is a gap between */ + /* text and data segments, so plan A brought us something. */ + result = (char *)GC_find_limit(DATAEND, FALSE); + } + return (/* no volatile */ ptr_t)result; + } +# endif + +#ifdef DATASTART_USES_BSDGETDATASTART +/* It's unclear whether this should be identical to the above, or */ +/* whether it should apply to non-x86 architectures. */ +/* For now we don't assume that there is always an empty page after */ +/* etext. But in some cases there actually seems to be slightly more. */ +/* This also deals with holes between read-only data and writable data. */ + GC_INNER ptr_t GC_FreeBSDGetDataStart(size_t max_page_size, + ptr_t etext_addr) + { + word text_end = ((word)(etext_addr) + sizeof(word) - 1) + & ~(word)(sizeof(word) - 1); + /* etext rounded to word boundary */ + volatile word next_page = (text_end + (word)max_page_size - 1) + & ~((word)max_page_size - 1); + volatile ptr_t result = (ptr_t)text_end; + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + /* Try reading at the address. */ + /* This should happen before there is another thread. */ + for (; next_page < (word)DATAEND; next_page += (word)max_page_size) + *(volatile char *)next_page; + GC_reset_fault_handler(); + } else { + GC_reset_fault_handler(); + /* As above, we go to plan B */ + result = (ptr_t)GC_find_limit(DATAEND, FALSE); + } + return(result); + } +#endif /* DATASTART_USES_BSDGETDATASTART */ + +#ifdef AMIGA + +# define GC_AMIGA_DS +# include "extra/AmigaOS.c" +# undef GC_AMIGA_DS + +#elif defined(OPENBSD) + +/* Depending on arch alignment, there can be multiple holes */ +/* between DATASTART and DATAEND. Scan in DATASTART .. DATAEND */ +/* and register each region. */ +void GC_register_data_segments(void) +{ + ptr_t region_start = DATASTART; + + if ((word)region_start - 1U >= (word)DATAEND) + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)region_start, (void *)DATAEND); + for (;;) { +# ifdef GC_OPENBSD_UTHREADS + ptr_t region_end = GC_find_limit_openbsd(region_start, DATAEND); +# else + ptr_t region_end = GC_find_limit_with_bound(region_start, TRUE, DATAEND); +# endif + + GC_add_roots_inner(region_start, region_end, FALSE); + if ((word)region_end >= (word)DATAEND) + break; + region_start = GC_skip_hole_openbsd(region_end, DATAEND); + } +} + +# else /* !OS2 && !Windows && !AMIGA && !OPENBSD */ + +# if !defined(PCR) && !defined(MACOS) && defined(REDIRECT_MALLOC) \ + && defined(GC_SOLARIS_THREADS) + EXTERN_C_BEGIN + extern caddr_t sbrk(int); + EXTERN_C_END +# endif + + void GC_register_data_segments(void) + { +# if !defined(DYNAMIC_LOADING) && defined(GC_DONT_REGISTER_MAIN_STATIC_DATA) + /* Avoid even referencing DATASTART and DATAEND as they are */ + /* unnecessary and cause linker errors when bitcode is enabled. */ + /* GC_register_data_segments() is not called anyway. */ +# elif !defined(PCR) && !defined(MACOS) +# if defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS) + /* As of Solaris 2.3, the Solaris threads implementation */ + /* allocates the data structure for the initial thread with */ + /* sbrk at process startup. It needs to be scanned, so that */ + /* we don't lose some malloc allocated data structures */ + /* hanging from it. We're on thin ice here ... */ + GC_ASSERT(DATASTART); + { + ptr_t p = (ptr_t)sbrk(0); + if ((word)DATASTART < (word)p) + GC_add_roots_inner(DATASTART, p, FALSE); + } +# else + if ((word)DATASTART - 1U >= (word)DATAEND) { + /* Subtract one to check also for NULL */ + /* without a compiler warning. */ + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)DATASTART, (void *)DATAEND); + } + GC_add_roots_inner(DATASTART, DATAEND, FALSE); +# ifdef GC_HAVE_DATAREGION2 + if ((word)DATASTART2 - 1U >= (word)DATAEND2) + ABORT_ARG2("Wrong DATASTART/END2 pair", + ": %p .. %p", (void *)DATASTART2, (void *)DATAEND2); + GC_add_roots_inner(DATASTART2, DATAEND2, FALSE); +# endif +# endif +# endif +# if defined(MACOS) + { +# if defined(THINK_C) + extern void* GC_MacGetDataStart(void); + /* globals begin above stack and end at a5. */ + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)LMGetCurrentA5(), FALSE); +# else +# if defined(__MWERKS__) +# if !__POWERPC__ + extern void* GC_MacGetDataStart(void); + /* MATTHEW: Function to handle Far Globals (CW Pro 3) */ +# if __option(far_data) + extern void* GC_MacGetDataEnd(void); +# endif + /* globals begin above stack and end at a5. */ + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)LMGetCurrentA5(), FALSE); + /* MATTHEW: Handle Far Globals */ +# if __option(far_data) + /* Far globals follow the QD globals: */ + GC_add_roots_inner((ptr_t)LMGetCurrentA5(), + (ptr_t)GC_MacGetDataEnd(), FALSE); +# endif +# else + extern char __data_start__[], __data_end__[]; + GC_add_roots_inner((ptr_t)&__data_start__, + (ptr_t)&__data_end__, FALSE); +# endif /* __POWERPC__ */ +# endif /* __MWERKS__ */ +# endif /* !THINK_C */ + } +# endif /* MACOS */ + + /* Dynamic libraries are added at every collection, since they may */ + /* change. */ + } + +# endif /* !AMIGA */ +# endif /* !MSWIN32 && !MSWINCE */ +# endif /* !OS2 */ + +/* + * Auxiliary routines for obtaining memory from OS. + */ + +# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \ + && !defined(USE_WINALLOC) && !defined(MACOS) && !defined(DOS4GW) \ + && !defined(NINTENDO_SWITCH) && !defined(NONSTOP) \ + && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PS3) \ + && !defined(SN_TARGET_PSP2) && !defined(RTEMS) && !defined(__CC_ARM) + +# define SBRK_ARG_T ptrdiff_t + +#if defined(MMAP_SUPPORTED) + +#ifdef USE_MMAP_FIXED +# define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE + /* Seems to yield better performance on Solaris 2, but can */ + /* be unreliable if something is already mapped at the address. */ +#else +# define GC_MMAP_FLAGS MAP_PRIVATE +#endif + +#ifdef USE_MMAP_ANON +# define zero_fd -1 +# if defined(MAP_ANONYMOUS) && !defined(CPPCHECK) +# define OPT_MAP_ANON MAP_ANONYMOUS +# else +# define OPT_MAP_ANON MAP_ANON +# endif +#else + static int zero_fd = -1; +# define OPT_MAP_ANON 0 +#endif + +# ifndef MSWIN_XBOX1 +# if defined(SYMBIAN) && !defined(USE_MMAP_ANON) + EXTERN_C_BEGIN + extern char *GC_get_private_path_and_zero_file(void); + EXTERN_C_END +# endif + + STATIC ptr_t GC_unix_mmap_get_mem(size_t bytes) + { + void *result; + static ptr_t last_addr = HEAP_START; + +# ifndef USE_MMAP_ANON + static GC_bool initialized = FALSE; + + if (!EXPECT(initialized, TRUE)) { +# ifdef SYMBIAN + char *path = GC_get_private_path_and_zero_file(); + if (path != NULL) { + zero_fd = open(path, O_RDWR | O_CREAT, 0644); + free(path); + } +# else + zero_fd = open("/dev/zero", O_RDONLY); +# endif + if (zero_fd == -1) + ABORT("Could not open /dev/zero"); + if (fcntl(zero_fd, F_SETFD, FD_CLOEXEC) == -1) + WARN("Could not set FD_CLOEXEC for /dev/zero\n", 0); + + initialized = TRUE; + } +# endif + + GC_ASSERT(GC_page_size != 0); + if (bytes & (GC_page_size - 1)) ABORT("Bad GET_MEM arg"); + result = mmap(last_addr, bytes, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0), + GC_MMAP_FLAGS | OPT_MAP_ANON, zero_fd, 0/* offset */); +# undef IGNORE_PAGES_EXECUTABLE + + if (EXPECT(MAP_FAILED == result, FALSE)) { + if (HEAP_START == last_addr && GC_pages_executable + && (EACCES == errno || EPERM == errno)) + ABORT("Cannot allocate executable pages"); + return NULL; + } + last_addr = (ptr_t)(((word)result + bytes + GC_page_size - 1) + & ~(GC_page_size - 1)); +# if !defined(LINUX) + if (last_addr == 0) { + /* Oops. We got the end of the address space. This isn't */ + /* usable by arbitrary C code, since one-past-end pointers */ + /* don't work, so we discard it and try again. */ + munmap(result, ~GC_page_size - (size_t)result + 1); + /* Leave last page mapped, so we can't repeat. */ + return GC_unix_mmap_get_mem(bytes); + } +# else + GC_ASSERT(last_addr != 0); +# endif + if (((word)result % HBLKSIZE) != 0) + ABORT( + "GC_unix_get_mem: Memory returned by mmap is not aligned to HBLKSIZE."); + return((ptr_t)result); + } +# endif /* !MSWIN_XBOX1 */ + +#endif /* MMAP_SUPPORTED */ + +#if defined(USE_MMAP) + ptr_t GC_unix_get_mem(size_t bytes) + { + return GC_unix_mmap_get_mem(bytes); + } +#else /* !USE_MMAP */ + +STATIC ptr_t GC_unix_sbrk_get_mem(size_t bytes) +{ + ptr_t result; +# ifdef IRIX5 + /* Bare sbrk isn't thread safe. Play by malloc rules. */ + /* The equivalent may be needed on other systems as well. */ + __LOCK_MALLOC(); +# endif + { + ptr_t cur_brk = (ptr_t)sbrk(0); + SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1); + + GC_ASSERT(GC_page_size != 0); + if ((SBRK_ARG_T)bytes < 0) { + result = 0; /* too big */ + goto out; + } + if (lsbs != 0) { + if((ptr_t)sbrk((SBRK_ARG_T)GC_page_size - lsbs) == (ptr_t)(-1)) { + result = 0; + goto out; + } + } +# ifdef ADD_HEAP_GUARD_PAGES + /* This is useful for catching severe memory overwrite problems that */ + /* span heap sections. It shouldn't otherwise be turned on. */ + { + ptr_t guard = (ptr_t)sbrk((SBRK_ARG_T)GC_page_size); + if (mprotect(guard, GC_page_size, PROT_NONE) != 0) + ABORT("ADD_HEAP_GUARD_PAGES: mprotect failed"); + } +# endif /* ADD_HEAP_GUARD_PAGES */ + result = (ptr_t)sbrk((SBRK_ARG_T)bytes); + if (result == (ptr_t)(-1)) result = 0; + } + out: +# ifdef IRIX5 + __UNLOCK_MALLOC(); +# endif + return(result); +} + +ptr_t GC_unix_get_mem(size_t bytes) +{ +# if defined(MMAP_SUPPORTED) + /* By default, we try both sbrk and mmap, in that order. */ + static GC_bool sbrk_failed = FALSE; + ptr_t result = 0; + + if (GC_pages_executable) { + /* If the allocated memory should have the execute permission */ + /* then sbrk() cannot be used. */ + return GC_unix_mmap_get_mem(bytes); + } + if (!sbrk_failed) result = GC_unix_sbrk_get_mem(bytes); + if (0 == result) { + sbrk_failed = TRUE; + result = GC_unix_mmap_get_mem(bytes); + } + if (0 == result) { + /* Try sbrk again, in case sbrk memory became available. */ + result = GC_unix_sbrk_get_mem(bytes); + } + return result; +# else /* !MMAP_SUPPORTED */ + return GC_unix_sbrk_get_mem(bytes); +# endif +} + +#endif /* !USE_MMAP */ + +# endif /* UN*X */ + +# ifdef OS2 + +void * os2_alloc(size_t bytes) +{ + void * result; + + if (DosAllocMem(&result, bytes, (PAG_READ | PAG_WRITE | PAG_COMMIT) + | (GC_pages_executable ? PAG_EXECUTE : 0)) + != NO_ERROR) { + return(0); + } + /* FIXME: What's the purpose of this recursion? (Probably, if */ + /* DosAllocMem returns memory at 0 address then just retry once.) */ + if (result == 0) return(os2_alloc(bytes)); + return(result); +} + +# endif /* OS2 */ + +#ifdef MSWIN_XBOX1 + ptr_t GC_durango_get_mem(size_t bytes) + { + if (0 == bytes) return NULL; + return (ptr_t)VirtualAlloc(NULL, bytes, MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + } +#elif defined(MSWINCE) + ptr_t GC_wince_get_mem(size_t bytes) + { + ptr_t result = 0; /* initialized to prevent warning. */ + word i; + + GC_ASSERT(GC_page_size != 0); + bytes = ROUNDUP_PAGESIZE(bytes); + + /* 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 */ + size_t res_bytes = + SIZET_SAT_ADD(bytes, (size_t)GC_sysinfo.dwAllocationGranularity-1) + & ~((size_t)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 > 64 KB 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) /* && !MSWIN_XBOX1 */ || defined(CYGWIN32) + +# ifdef USE_GLOBAL_ALLOC +# define GLOBAL_ALLOC_TEST 1 +# else +# define GLOBAL_ALLOC_TEST GC_no_win32_dlls +# endif + +# if (defined(GC_USE_MEM_TOP_DOWN) && defined(USE_WINALLOC)) \ + || defined(CPPCHECK) + 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 4 GB, hiding bugs. */ +# else +# define GC_mem_top_down 0 +# endif /* !GC_USE_MEM_TOP_DOWN */ + + ptr_t GC_win32_get_mem(size_t bytes) + { + ptr_t result; + +# ifndef USE_WINALLOC + result = GC_unix_get_mem(bytes); +# else +# if defined(MSWIN32) && !defined(MSWINRT_FLAVOR) + 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, SIZET_SAT_ADD(bytes, HBLKSIZE)); + /* Align it at HBLKSIZE boundary. */ + result = (ptr_t)(((word)result + HBLKSIZE - 1) + & ~(word)(HBLKSIZE - 1)); + } 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 */ + /* in separate regions, and eliminate any temptation */ + /* to call VirtualProtect on a range spanning regions. */ + /* This wastes a small amount of memory, and risks */ + /* increased fragmentation. But better alternatives */ + /* would require effort. */ +# ifdef MPROTECT_VDB + /* We can't check for GC_incremental here (because */ + /* GC_enable_incremental() might be called some time */ + /* later after the GC initialization). */ +# ifdef GWW_VDB +# define VIRTUAL_ALLOC_PAD (GC_GWW_AVAILABLE() ? 0 : 1) +# else +# define VIRTUAL_ALLOC_PAD 1 +# endif +# else +# define VIRTUAL_ALLOC_PAD 0 +# endif + /* Pass the MEM_WRITE_WATCH only if GetWriteWatch-based */ + /* VDBs are enabled and the GetWriteWatch function is */ + /* available. Otherwise we waste resources or possibly */ + /* cause VirtualAlloc to fail (observed in Windows 2000 */ + /* SP2). */ + result = (ptr_t) VirtualAlloc(NULL, + SIZET_SAT_ADD(bytes, VIRTUAL_ALLOC_PAD), + GetWriteWatch_alloc_flag + | (MEM_COMMIT | MEM_RESERVE) + | GC_mem_top_down, + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); +# undef IGNORE_PAGES_EXECUTABLE + } +# endif /* USE_WINALLOC */ + if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); + /* If I read the documentation correctly, this can */ + /* only happen if HBLKSIZE > 64 KB or not a power of 2. */ + if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections"); + if (0 != result) GC_heap_bases[GC_n_heap_bases++] = result; + return(result); + } +#endif /* USE_WINALLOC || CYGWIN32 */ + +#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) \ + || defined(MSWIN_XBOX1) + GC_API void GC_CALL GC_win32_free_heap(void) + { +# if defined(USE_WINALLOC) && !defined(REDIRECT_MALLOC) \ + && !defined(MSWIN_XBOX1) + GC_free_malloc_heap_list(); +# endif +# if (defined(USE_WINALLOC) && !defined(MSWIN_XBOX1) \ + && !defined(MSWINCE)) || defined(CYGWIN32) +# ifndef MSWINRT_FLAVOR +# ifndef CYGWIN32 + if (GLOBAL_ALLOC_TEST) +# endif + { + while (GC_n_heap_bases-- > 0) { +# ifdef CYGWIN32 + /* 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; + } + return; + } +# endif /* !MSWINRT_FLAVOR */ +# ifndef CYGWIN32 + /* 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 /* USE_WINALLOC || CYGWIN32 */ + } +#endif /* Windows */ + +#ifdef AMIGA +# define GC_AMIGA_AM +# include "extra/AmigaOS.c" +# undef GC_AMIGA_AM +#endif + +#if defined(HAIKU) +# include + ptr_t GC_haiku_get_mem(size_t bytes) + { + void* mem; + + GC_ASSERT(GC_page_size != 0); + if (posix_memalign(&mem, GC_page_size, bytes) == 0) + return mem; + return NULL; + } +#endif /* HAIKU */ + +#if (defined(USE_MUNMAP) || defined(MPROTECT_VDB)) && !defined(USE_WINALLOC) +# define ABORT_ON_REMAP_FAIL(C_msg_prefix, start_addr, len) \ + ABORT_ARG3(C_msg_prefix " failed", \ + " at %p (length %lu), errno= %d", \ + (void *)(start_addr), (unsigned long)(len), errno) +#endif + +#ifdef USE_MUNMAP + +/* For now, this only works on Win32/WinCE and some Unix-like */ +/* systems. If you have something else, don't define */ +/* USE_MUNMAP. */ + +#if !defined(NN_PLATFORM_CTR) && !defined(MSWIN32) && !defined(MSWINCE) \ + && !defined(MSWIN_XBOX1) +# include +# ifdef SN_TARGET_PS3 +# include +# else +# include +# endif +# include +# include +#endif + +/* Compute a page aligned starting address for the unmap */ +/* operation on a block of size bytes starting at start. */ +/* Return 0 if the block is too small to make this feasible. */ +STATIC ptr_t GC_unmap_start(ptr_t start, size_t bytes) +{ + ptr_t result = (ptr_t)(((word)start + GC_page_size - 1) + & ~(GC_page_size - 1)); + + GC_ASSERT(GC_page_size != 0); + if ((word)(result + GC_page_size) > (word)(start + bytes)) return 0; + return result; +} + +/* Under Win32/WinCE we commit (map) and decommit (unmap) */ +/* memory using VirtualAlloc and VirtualFree. These functions */ +/* work on individual allocations of virtual memory, made */ +/* previously using VirtualAlloc with the MEM_RESERVE flag. */ +/* The ranges we need to (de)commit may span several of these */ +/* allocations; therefore we use VirtualQuery to check */ +/* allocation lengths, and split up the range as necessary. */ + +/* We assume that GC_remap is called on exactly the same range */ +/* as a previous call to GC_unmap. It is safe to consistently */ +/* round the endpoints in both places. */ + +static void block_unmap_inner(ptr_t start_addr, size_t len) +{ + if (0 == start_addr) return; + +# ifdef USE_WINALLOC + while (len != 0) { + MEMORY_BASIC_INFORMATION mem_info; + word free_len; + + if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) + != sizeof(mem_info)) + ABORT("Weird VirtualQuery result"); + free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; + if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT)) + ABORT("VirtualFree failed"); + GC_unmapped_bytes += free_len; + start_addr += free_len; + len -= free_len; + } +# else + /* We immediately remap it to prevent an intervening mmap from */ + /* accidentally grabbing the same address space. */ + if (len != 0) { +# ifdef SN_TARGET_PS3 + ps3_free_mem(start_addr, len); +# elif defined(AIX) || defined(CYGWIN32) || defined(HAIKU) \ + || (defined(LINUX) && !defined(PREFER_MMAP_PROT_NONE)) \ + || defined(HPUX) + /* On AIX, mmap(PROT_NONE) fails with ENOMEM unless the */ + /* environment variable XPG_SUS_ENV is set to ON. */ + /* On Cygwin, calling mmap() with the new protection flags on */ + /* an existing memory map with MAP_FIXED is broken. */ + /* However, calling mprotect() on the given address range */ + /* with PROT_NONE seems to work fine. */ + /* On Linux, low RLIMIT_AS value may lead to mmap failure. */ +# if defined(LINUX) && !defined(FORCE_MPROTECT_BEFORE_MADVISE) + /* On Linux, at least, madvise() should be sufficient. */ +# else + if (mprotect(start_addr, len, PROT_NONE)) + ABORT_ON_REMAP_FAIL("unmap: mprotect", start_addr, len); +# endif +# if !defined(CYGWIN32) + /* On Linux (and some other platforms probably), */ + /* mprotect(PROT_NONE) is just disabling access to */ + /* the pages but not returning them to OS. */ + if (madvise(start_addr, len, MADV_DONTNEED) == -1) + ABORT_ON_REMAP_FAIL("unmap: madvise", start_addr, len); +# endif +# elif defined(EMSCRIPTEN) + /* Nothing to do, mmap(PROT_NONE) is not supported and */ + /* mprotect() is just a no-op. */ +# else + void * result = mmap(start_addr, len, PROT_NONE, + MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, + zero_fd, 0/* offset */); + + if (EXPECT(MAP_FAILED == result, FALSE)) + ABORT_ON_REMAP_FAIL("unmap: mmap", start_addr, len); + if (result != (void *)start_addr) + ABORT("unmap: mmap() result differs from start_addr"); +# if defined(CPPCHECK) || defined(LINT2) + /* Explicitly store the resource handle to a global variable. */ + GC_noop1((word)result); +# endif +# endif + GC_unmapped_bytes += len; + } +# endif +} + +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); + + block_unmap_inner(start_addr, (size_t)(end_addr - start_addr)); +} + +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) */ +# ifdef USE_WINALLOC + while (len != 0) { + MEMORY_BASIC_INFORMATION mem_info; + word alloc_len; + ptr_t result; + + if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) + != sizeof(mem_info)) + ABORT("Weird VirtualQuery result"); + alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; + result = (ptr_t)VirtualAlloc(start_addr, alloc_len, MEM_COMMIT, + GC_pages_executable + ? PAGE_EXECUTE_READWRITE + : PAGE_READWRITE); + if (result != start_addr) { + if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY || + GetLastError() == ERROR_OUTOFMEMORY) { + ABORT("Not enough memory to process remapping"); + } else { + ABORT("VirtualAlloc remapping failed"); + } + } +# ifdef LINT2 + GC_noop1((word)result); +# endif + GC_unmapped_bytes -= alloc_len; + start_addr += alloc_len; + len -= alloc_len; + } +# undef IGNORE_PAGES_EXECUTABLE +# else + /* It was already remapped with PROT_NONE. */ + { +# if !defined(SN_TARGET_PS3) && !defined(FORCE_MPROTECT_BEFORE_MADVISE) \ + && defined(LINUX) && !defined(PREFER_MMAP_PROT_NONE) + /* Nothing to unprotect as madvise() is just a hint. */ +# elif defined(NACL) || defined(NETBSD) + /* NaCl does not expose mprotect, but mmap should work fine. */ + /* In case of NetBSD, mprotect fails (unlike mmap) even */ + /* without PROT_EXEC if PaX MPROTECT feature is enabled. */ + void *result = mmap(start_addr, len, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0), + MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, + zero_fd, 0 /* offset */); + if (EXPECT(MAP_FAILED == result, FALSE)) + ABORT_ON_REMAP_FAIL("remap: mmap", start_addr, len); + if (result != (void *)start_addr) + ABORT("remap: mmap() result differs from start_addr"); +# if defined(CPPCHECK) || defined(LINT2) + GC_noop1((word)result); +# endif +# undef IGNORE_PAGES_EXECUTABLE +# else + if (mprotect(start_addr, len, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0))) + ABORT_ON_REMAP_FAIL("remap: mprotect", start_addr, len); +# undef IGNORE_PAGES_EXECUTABLE +# endif /* !NACL */ + } + GC_unmapped_bytes -= len; +# endif +} + +/* Two adjacent blocks have already been unmapped and are about to */ +/* be merged. Unmap the whole block. This typically requires */ +/* that we unmap a small section in the middle that was not previously */ +/* unmapped due to alignment constraints. */ +GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, + size_t bytes2) +{ + ptr_t start1_addr = GC_unmap_start(start1, bytes1); + ptr_t end1_addr = GC_unmap_end(start1, bytes1); + ptr_t start2_addr = GC_unmap_start(start2, bytes2); + ptr_t start_addr = end1_addr; + ptr_t end_addr = start2_addr; + + 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); + block_unmap_inner(start_addr, (size_t)(end_addr - start_addr)); +} + +#endif /* USE_MUNMAP */ + +/* Routine for pushing any additional roots. In THREADS */ +/* environment, this is also responsible for marking from */ +/* thread stacks. */ +#ifndef THREADS + +# if defined(EMSCRIPTEN) && defined(EMSCRIPTEN_ASYNCIFY) +# include + + static void scan_regs_cb(void *begin, void *end) + { + GC_push_all_stack((ptr_t)begin, (ptr_t)end); + } + + STATIC void GC_CALLBACK GC_default_push_other_roots(void) + { + /* Note: this needs -sASYNCIFY linker flag. */ + emscripten_scan_registers(scan_regs_cb); + } + +# else +# define GC_default_push_other_roots 0 +# endif + +#else /* THREADS */ + +# ifdef PCR +PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy) +{ + struct PCR_ThCtl_TInfoRep info; + PCR_ERes result; + + info.ti_stkLow = info.ti_stkHi = 0; + result = PCR_ThCtl_GetInfo(t, &info); + GC_push_all_stack((ptr_t)(info.ti_stkLow), (ptr_t)(info.ti_stkHi)); + return(result); +} + +/* Push the contents of an old object. We treat this as stack */ +/* data only because that makes it robust against mark stack */ +/* overflow. */ +PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data) +{ + GC_push_all_stack((ptr_t)p, (ptr_t)p + size); + return(PCR_ERes_okay); +} + +extern struct PCR_MM_ProcsRep * GC_old_allocator; + /* defined in pcr_interface.c. */ + +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, + GC_push_old_obj, 0) + != PCR_ERes_okay) { + ABORT("Old object enumeration failed"); + } + /* Traverse all thread stacks. */ + if (PCR_ERes_IsErr( + PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0)) + || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) { + ABORT("Thread stack marking failed"); + } +} + +# elif defined(SN_TARGET_PS3) + STATIC void GC_CALLBACK GC_default_push_other_roots(void) + { + ABORT("GC_default_push_other_roots is not implemented"); + } + + void GC_push_thread_structures(void) + { + ABORT("GC_push_thread_structures is not implemented"); + } + +# else /* GC_PTHREADS, or GC_WIN32_THREADS, etc. */ + STATIC void GC_CALLBACK GC_default_push_other_roots(void) + { + GC_push_all_stacks(); + } +# endif + +#endif /* THREADS */ + +GC_push_other_roots_proc GC_push_other_roots = GC_default_push_other_roots; + +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; +} + +#if defined(SOFT_VDB) && !defined(NO_SOFT_VDB_LINUX_VER_RUNTIME_CHECK) \ + || (defined(GLIBC_2_19_TSX_BUG) && defined(PARALLEL_MARK)) + GC_INNER int GC_parse_version(int *pminor, const char *pverstr) { + char *endp; + unsigned long value = strtoul(pverstr, &endp, 10); + int major = (int)value; + + if (major < 0 || (char *)pverstr == endp || (unsigned)major != value) { + /* Parse error. */ + return -1; + } + if (*endp != '.') { + /* No minor part. */ + *pminor = -1; + } else { + value = strtoul(endp + 1, &endp, 10); + *pminor = (int)value; + if (*pminor < 0 || (unsigned)(*pminor) != value) { + return -1; + } + } + return major; + } +#endif + +/* + * Routines for accessing dirty bits on virtual pages. + * There are six ways to maintain this information: + * DEFAULT_VDB: A simple dummy implementation that treats every page + * as possibly dirty. This makes incremental collection + * useless, but the implementation is still correct. + * Manual VDB: Stacks and static data are always considered dirty. + * Heap pages are considered dirty if GC_dirty(p) has been + * called on some pointer p pointing to somewhere inside + * an object on that page. A GC_dirty() call on a large + * object directly dirties only a single page, but for the + * manual VDB we are careful to treat an object with a dirty + * page as completely dirty. + * In order to avoid races, an object must be marked dirty + * after it is written, and a reference to the object + * must be kept on a stack or in a register in the interim. + * With threads enabled, an object directly reachable from the + * stack at the time of a collection is treated as dirty. + * In single-threaded mode, it suffices to ensure that no + * collection can take place between the pointer assignment + * and the GC_dirty() call. + * PCR_VDB: Use PPCRs virtual dirty bit facility. + * PROC_VDB: Use the /proc facility for reading dirty bits. Only + * works under some SVR4 variants. Even then, it may be + * too slow to be entirely satisfactory. Requires reading + * dirty bits for entire address space. Implementations tend + * to assume that the client is a (slow) debugger. + * SOFT_VDB: Use the /proc facility for reading soft-dirty PTEs. + * Works on Linux 3.18+ if the kernel is properly configured. + * The proposed implementation iterates over GC_heap_sects and + * GC_static_roots examining the soft-dirty bit of the words + * in /proc/self/pagemap corresponding to the pages of the + * sections; finally all soft-dirty bits of the process are + * cleared (by writing some special value to + * /proc/self/clear_refs file). In case the soft-dirty bit is + * not supported by the kernel, MPROTECT_VDB may be defined as + * a fallback strategy. + * MPROTECT_VDB:Protect pages and then catch the faults to keep track of + * dirtied pages. The implementation (and implementability) + * is highly system dependent. This usually fails when system + * calls write to a protected page. We prevent the read system + * call from doing so. It is the clients responsibility to + * make sure that other system calls are similarly protected + * or write only to the stack. + * GWW_VDB: Use the Win32 GetWriteWatch functions, if available, to + * read dirty bits. In case it is not available (because we + * are running on Windows 95, Windows 2000 or earlier), + * MPROTECT_VDB may be defined as a fallback strategy. + */ + +#if (defined(CHECKSUMS) && (defined(GWW_VDB) || defined(SOFT_VDB))) \ + || defined(PROC_VDB) + /* Add all pages in pht2 to pht1. */ + STATIC void GC_or_pages(page_hash_table pht1, page_hash_table pht2) + { + unsigned i; + for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i]; + } +#endif /* CHECKSUMS && (GWW_VDB || SOFT_VDB) || PROC_VDB */ + +#ifdef GWW_VDB + +# define GC_GWW_BUF_LEN (MAXHINCR * HBLKSIZE / 4096 /* x86 page size */) + /* Still susceptible to overflow, if there are very large allocations, */ + /* and everything is dirty. */ + static PVOID gww_buf[GC_GWW_BUF_LEN]; + +# ifndef MPROTECT_VDB +# define GC_gww_dirty_init GC_dirty_init +# endif + + GC_INNER GC_bool GC_gww_dirty_init(void) + { + detect_GetWriteWatch(); + return GC_GWW_AVAILABLE(); + } + + GC_INLINE void GC_gww_read_dirty(GC_bool output_unneeded) + { + word i; + + if (!output_unneeded) + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); + + for (i = 0; i != GC_n_heap_sects; ++i) { + GC_ULONG_PTR count; + + do { + PVOID * pages = gww_buf; + DWORD page_size; + + count = GC_GWW_BUF_LEN; + /* GetWriteWatch is documented as returning non-zero when it */ + /* fails, but the documentation doesn't explicitly say why it */ + /* would fail or what its behavior will be if it fails. It */ + /* does appear to fail, at least on recent Win2K instances, if */ + /* the underlying memory was not allocated with the appropriate */ + /* flag. This is common if GC_enable_incremental is called */ + /* shortly after GC initialization. To avoid modifying the */ + /* interface, we silently work around such a failure, it only */ + /* affects the initial (small) heap allocation. If there are */ + /* more dirty pages than will fit in the buffer, this is not */ + /* treated as a failure; we must check the page count in the */ + /* loop condition. Since each partial call will reset the */ + /* status of some pages, this should eventually terminate even */ + /* in the overflow case. */ + if ((*(GetWriteWatch_type)(word)GetWriteWatch_func)( + WRITE_WATCH_FLAG_RESET, + GC_heap_sects[i].hs_start, + GC_heap_sects[i].hs_bytes, + pages, &count, &page_size) != 0) { + static int warn_count = 0; + struct hblk * start = (struct hblk *)GC_heap_sects[i].hs_start; + static struct hblk *last_warned = 0; + size_t nblocks = divHBLKSZ(GC_heap_sects[i].hs_bytes); + + if (i != 0 && last_warned != start && warn_count++ < 5) { + last_warned = start; + WARN("GC_gww_read_dirty unexpectedly failed at %p: " + "Falling back to marking all pages dirty\n", start); + } + if (!output_unneeded) { + unsigned j; + + for (j = 0; j < nblocks; ++j) { + word hash = PHT_HASH(start + j); + set_pht_entry_from_index(GC_grungy_pages, hash); + } + } + count = 1; /* Done with this section. */ + } else /* succeeded */ if (!output_unneeded) { + PVOID * pages_end = pages + count; + + while (pages != pages_end) { + struct hblk * h = (struct hblk *) *pages++; + struct hblk * h_end = (struct hblk *) ((char *) h + page_size); + do { + set_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h)); + } while ((word)(++h) < (word)h_end); + } + } + } while (count == GC_GWW_BUF_LEN); + /* FIXME: It's unclear from Microsoft's documentation if this loop */ + /* is useful. We suspect the call just fails if the buffer fills */ + /* up. But that should still be handled correctly. */ + } + +# ifdef CHECKSUMS + GC_ASSERT(!output_unneeded); + GC_or_pages(GC_written_pages, GC_grungy_pages); +# endif + } + +#elif defined(SOFT_VDB) + static int clear_refs_fd = -1; +# define GC_GWW_AVAILABLE() (clear_refs_fd != -1) +#else +# define GC_GWW_AVAILABLE() FALSE +#endif /* !GWW_VDB && !SOFT_VDB */ + +#ifdef DEFAULT_VDB + /* All of the following assume the allocation lock is held. */ + + /* The client asserts that unallocated pages in the heap are never */ + /* written. */ + + /* Initialize virtual dirty bit implementation. */ + GC_INNER GC_bool GC_dirty_init(void) + { + GC_VERBOSE_LOG_PRINTF("Initializing DEFAULT_VDB...\n"); + /* GC_dirty_pages and GC_grungy_pages are already cleared. */ + return TRUE; + } +#endif /* DEFAULT_VDB */ + +#ifndef GC_DISABLE_INCREMENTAL +# if !defined(THREADS) || defined(HAVE_LOCKFREE_AO_OR) +# define async_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index_concurrent(db, index) +# elif defined(AO_HAVE_test_and_set_acquire) + /* We need to lock around the bitmap update (in the write fault */ + /* handler or GC_dirty) in order to avoid the risk of losing a bit. */ + /* We do this with a test-and-set spin lock if possible. */ + GC_INNER volatile AO_TS_t GC_fault_handler_lock = AO_TS_INITIALIZER; + + static void async_set_pht_entry_from_index(volatile page_hash_table db, + size_t index) + { + GC_acquire_dirty_lock(); + set_pht_entry_from_index(db, index); + GC_release_dirty_lock(); + } +# else +# error No test_and_set operation: Introduces a race. +# endif /* THREADS && !AO_HAVE_test_and_set_acquire */ +#endif /* !GC_DISABLE_INCREMENTAL */ + +#ifdef MPROTECT_VDB + /* + * This implementation maintains dirty bits itself by catching write + * faults and keeping track of them. We assume nobody else catches + * 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 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 pointer-free + * objects only if they are the same. + */ +# ifdef DARWIN + /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to + decrease the likelihood of some of the problems described below. */ +# include + STATIC mach_port_t GC_task_self = 0; +# define PROTECT_INNER(addr, len, allow_write, C_msg_prefix) \ + if (vm_protect(GC_task_self, (vm_address_t)(addr), (vm_size_t)(len), \ + FALSE, VM_PROT_READ \ + | ((allow_write) ? VM_PROT_WRITE : 0) \ + | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \ + == KERN_SUCCESS) {} else ABORT(C_msg_prefix \ + "vm_protect() failed") + +# elif !defined(USE_WINALLOC) +# include +# include +# if !defined(CYGWIN32) && !defined(HAIKU) +# include +# endif + +# define PROTECT_INNER(addr, len, allow_write, C_msg_prefix) \ + if (mprotect((caddr_t)(addr), (size_t)(len), \ + PROT_READ | ((allow_write) ? PROT_WRITE : 0) \ + | (GC_pages_executable ? PROT_EXEC : 0)) >= 0) { \ + } else if (GC_pages_executable) { \ + ABORT_ON_REMAP_FAIL(C_msg_prefix \ + "mprotect vdb executable pages", \ + addr, len); \ + } else ABORT_ON_REMAP_FAIL(C_msg_prefix "mprotect vdb", addr, len) +# undef IGNORE_PAGES_EXECUTABLE + +# else /* USE_WINALLOC */ +# ifndef MSWINCE +# include +# endif + + static DWORD protect_junk; +# define PROTECT_INNER(addr, len, allow_write, C_msg_prefix) \ + if (VirtualProtect(addr, len, \ + GC_pages_executable ? \ + ((allow_write) ? PAGE_EXECUTE_READWRITE : \ + PAGE_EXECUTE_READ) : \ + (allow_write) ? PAGE_READWRITE : \ + PAGE_READONLY, \ + &protect_junk)) { \ + } else ABORT_ARG1(C_msg_prefix "VirtualProtect failed", \ + ": errcode= 0x%X", (unsigned)GetLastError()) +# endif /* USE_WINALLOC */ + +# define PROTECT(addr, len) PROTECT_INNER(addr, len, FALSE, "") +# define UNPROTECT(addr, len) PROTECT_INNER(addr, len, TRUE, "un-") + +# if defined(MSWIN32) + typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_HNDLR_PTR; +# undef SIG_DFL +# define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER)((signed_word)-1) +# elif defined(MSWINCE) + typedef LONG (WINAPI *SIG_HNDLR_PTR)(struct _EXCEPTION_POINTERS *); +# undef SIG_DFL +# define SIG_DFL (SIG_HNDLR_PTR) (-1) +# elif defined(DARWIN) + typedef void (* SIG_HNDLR_PTR)(); +# else + typedef void (* SIG_HNDLR_PTR)(int, siginfo_t *, void *); + typedef void (* PLAIN_HNDLR_PTR)(int); +# endif + +#ifndef DARWIN + STATIC SIG_HNDLR_PTR GC_old_segv_handler = 0; + /* Also old MSWIN32 ACCESS_VIOLATION filter */ +# if defined(FREEBSD) || defined(HPUX) || defined(HURD) || defined(LINUX) + STATIC SIG_HNDLR_PTR GC_old_bus_handler = 0; +# ifndef LINUX + STATIC GC_bool GC_old_bus_handler_used_si = FALSE; +# endif +# endif +# if !defined(MSWIN32) && !defined(MSWINCE) + STATIC GC_bool GC_old_segv_handler_used_si = FALSE; +# endif /* !MSWIN32 */ +#endif /* !DARWIN */ + +#ifdef THREADS + /* This function is used only by the fault handler. Potential data */ + /* race between this function and GC_install_header, GC_remove_header */ + /* should not be harmful because the added or removed header should */ + /* be already unprotected. */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_bool is_header_found_async(void *addr) + { +# ifdef HASH_TL + hdr *result; + GET_HDR((ptr_t)addr, result); + return result != NULL; +# else + return HDR_INNER(addr) != NULL; +# endif + } +#else +# define is_header_found_async(addr) (HDR(addr) != NULL) +#endif /* !THREADS */ + +#ifndef DARWIN + +# if !defined(MSWIN32) && !defined(MSWINCE) +# include +# if defined(FREEBSD) || defined(HURD) || defined(HPUX) +# 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 +# if defined(AARCH64) || defined(ARM32) || defined(MIPS) \ + || __FreeBSD__ >= 7 +# define CODE_OK (si -> si_code == SEGV_ACCERR) +# elif defined(POWERPC) +# define AIM /* Pretend that we're AIM. */ +# include +# 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) +# define CODE_OK (si -> si_code == EACCES) +# elif defined(CYGWIN32) || defined(HAIKU) || defined(HURD) +# define CODE_OK TRUE +# elif defined(LINUX) +# define CODE_OK TRUE + /* Empirically c.trapno == 14, on IA32, but is that useful? */ + /* Should probably consider alignment issues on other */ + /* architectures. */ +# elif defined(HPUX) +# define CODE_OK (si -> si_code == SEGV_ACCERR \ + || si -> si_code == BUS_ADRERR \ + || si -> si_code == BUS_UNKNOWN \ + || si -> si_code == SEGV_UNKNOWN \ + || si -> si_code == BUS_OBJERR) +# elif defined(SUNOS5SIGS) +# define CODE_OK (si -> si_code == SEGV_ACCERR) +# endif +# ifndef NO_GETCONTEXT +# include +# endif + STATIC void GC_write_fault_handler(int sig, siginfo_t *si, void *raw_sc) +# else +# define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode \ + == STATUS_ACCESS_VIOLATION) +# define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] \ + == 1) /* Write fault */ + STATIC LONG WINAPI GC_write_fault_handler( + struct _EXCEPTION_POINTERS *exc_info) +# endif /* MSWIN32 || MSWINCE */ + { +# if !defined(MSWIN32) && !defined(MSWINCE) + char *addr = (char *)si->si_addr; +# else + char * addr = (char *) (exc_info -> ExceptionRecord + -> ExceptionInformation[1]); +# endif + + if (SIG_OK && CODE_OK) { + struct hblk * h = (struct hblk *)((word)addr & ~(GC_page_size-1)); + GC_bool in_allocd_block; + size_t i; + + GC_ASSERT(GC_page_size != 0); +# ifdef CHECKSUMS + GC_record_fault(h); +# endif +# ifdef SUNOS5SIGS + /* Address is only within the correct physical page. */ + in_allocd_block = FALSE; + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + if (is_header_found_async(&h[i])) { + in_allocd_block = TRUE; + break; + } + } +# else + in_allocd_block = is_header_found_async(addr); +# endif + if (!in_allocd_block) { + /* FIXME - We should make sure that we invoke the */ + /* old handler with the appropriate calling */ + /* sequence, which often depends on SA_SIGINFO. */ + + /* Heap blocks now begin and end on page boundaries */ + SIG_HNDLR_PTR old_handler; + +# if defined(MSWIN32) || defined(MSWINCE) + old_handler = GC_old_segv_handler; +# else + GC_bool used_si; + +# 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)(signed_word)SIG_DFL) { +# if !defined(MSWIN32) && !defined(MSWINCE) + ABORT_ARG1("Unexpected bus error or segmentation fault", + " at %p", (void *)addr); +# else + return(EXCEPTION_CONTINUE_SEARCH); +# endif + } else { + /* + * FIXME: This code should probably check if the + * old signal handler used the traditional style and + * if so call it using that style. + */ +# if defined(MSWIN32) || defined(MSWINCE) + return((*old_handler)(exc_info)); +# else + if (used_si) + ((SIG_HNDLR_PTR)old_handler) (sig, si, raw_sc); + else + /* FIXME: should pass nonstandard args as well. */ + ((PLAIN_HNDLR_PTR)(signed_word)old_handler)(sig); + return; +# endif + } + } + UNPROTECT(h, GC_page_size); + /* We need to make sure that no collection occurs between */ + /* the UNPROTECT and the setting of the dirty bit. Otherwise */ + /* a write by a third thread might go unnoticed. Reversing */ + /* the order is just as bad, since we would end up unprotecting */ + /* a page in a GC cycle during which it's not marked. */ + /* Currently we do this by disabling the thread stopping */ + /* signals while this handler is running. An alternative might */ + /* be to record the fact that we're about to unprotect, or */ + /* have just unprotected a page in the GC's thread structure, */ + /* and then to have the thread stopping code set the dirty */ + /* flag, if necessary. */ + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + word index = PHT_HASH(h+i); + + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + /* The write may not take place before dirty bits are read. */ + /* But then we'll fault again ... */ +# if defined(MSWIN32) || defined(MSWINCE) + return(EXCEPTION_CONTINUE_EXECUTION); +# else + return; +# endif + } +# if defined(MSWIN32) || defined(MSWINCE) + return EXCEPTION_CONTINUE_SEARCH; +# else + ABORT_ARG1("Unexpected bus error or segmentation fault", + " at %p", (void *)addr); +# endif + } + +# if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) + GC_INNER void GC_set_write_fault_handler(void) + { + SetUnhandledExceptionFilter(GC_write_fault_handler); + } +# endif + +# ifdef SOFT_VDB + static GC_bool soft_dirty_init(void); +# endif + + GC_INNER GC_bool GC_dirty_init(void) + { +# if !defined(MSWIN32) && !defined(MSWINCE) + struct sigaction act, oldact; + act.sa_flags = SA_RESTART | SA_SIGINFO; + act.sa_sigaction = GC_write_fault_handler; + (void)sigemptyset(&act.sa_mask); +# 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, GC_get_suspend_signal()); +# endif +# endif /* !MSWIN32 */ + GC_VERBOSE_LOG_PRINTF( + "Initializing mprotect virtual dirty bit implementation\n"); + if (GC_page_size % HBLKSIZE != 0) { + ABORT("Page size not multiple of HBLKSIZE"); + } +# ifdef GWW_VDB + if (GC_gww_dirty_init()) { + GC_COND_LOG_PRINTF("Using GetWriteWatch()\n"); + return TRUE; + } +# elif defined(SOFT_VDB) + if (soft_dirty_init()) { + GC_COND_LOG_PRINTF("Using soft-dirty bit feature\n"); + return TRUE; + } +# endif +# ifdef MSWIN32 + GC_old_segv_handler = SetUnhandledExceptionFilter( + GC_write_fault_handler); + if (GC_old_segv_handler != NULL) { + GC_COND_LOG_PRINTF("Replaced other UnhandledExceptionFilter\n"); + } else { + GC_old_segv_handler = SIG_DFL; + } +# elif defined(MSWINCE) + /* MPROTECT_VDB is unsupported for WinCE at present. */ + /* FIXME: implement it (if possible). */ +# else + /* act.sa_restorer is deprecated and should not be initialized. */ +# if defined(GC_IRIX_THREADS) + sigaction(SIGSEGV, 0, &oldact); + sigaction(SIGSEGV, &act, 0); +# else + { + int res = sigaction(SIGSEGV, &act, &oldact); + if (res != 0) ABORT("Sigaction failed"); + } +# endif + if (oldact.sa_flags & SA_SIGINFO) { + GC_old_segv_handler = oldact.sa_sigaction; + GC_old_segv_handler_used_si = TRUE; + } else { + GC_old_segv_handler = (SIG_HNDLR_PTR)(signed_word)oldact.sa_handler; + GC_old_segv_handler_used_si = FALSE; + } + if (GC_old_segv_handler == (SIG_HNDLR_PTR)(signed_word)SIG_IGN) { + WARN("Previously ignored segmentation violation!?\n", 0); + GC_old_segv_handler = (SIG_HNDLR_PTR)(signed_word)SIG_DFL; + } + if (GC_old_segv_handler != (SIG_HNDLR_PTR)(signed_word)SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGSEGV handler\n"); + } +# if defined(HPUX) || defined(LINUX) || defined(HURD) \ + || (defined(FREEBSD) && defined(SUNOS5SIGS)) + sigaction(SIGBUS, &act, &oldact); + if ((oldact.sa_flags & SA_SIGINFO) != 0) { + GC_old_bus_handler = oldact.sa_sigaction; +# if !defined(LINUX) + GC_old_bus_handler_used_si = TRUE; +# endif + } else { + GC_old_bus_handler = (SIG_HNDLR_PTR)(signed_word)oldact.sa_handler; + } + if (GC_old_bus_handler == (SIG_HNDLR_PTR)(signed_word)SIG_IGN) { + WARN("Previously ignored bus error!?\n", 0); +# if !defined(LINUX) + GC_old_bus_handler = (SIG_HNDLR_PTR)(signed_word)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)(signed_word)SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n"); + } +# endif /* HPUX || LINUX || HURD || (FREEBSD && SUNOS5SIGS) */ +# endif /* !MSWIN32 && !MSWINCE */ +# if defined(CPPCHECK) && defined(ADDRESS_SANITIZER) + GC_noop1((word)&__asan_default_options); +# endif + return TRUE; + } +#endif /* !DARWIN */ + +GC_API int GC_CALL GC_incremental_protection_needs(void) +{ + GC_ASSERT(GC_is_initialized); +# if defined(GWW_VDB) || defined(SOFT_VDB) + /* Only if the incremental mode is already switched on. */ + if (GC_GWW_AVAILABLE()) + return GC_PROTECTS_NONE; +# endif + if (GC_page_size == HBLKSIZE) { + return GC_PROTECTS_POINTER_HEAP; + } else { + return GC_PROTECTS_POINTER_HEAP | GC_PROTECTS_PTRFREE_HEAP; + } +} +#define HAVE_INCREMENTAL_PROTECTION_NEEDS + +#define IS_PTRFREE(hhdr) ((hhdr)->hb_descr == 0) +#define PAGE_ALIGNED(x) !((word)(x) & (GC_page_size - 1)) + +STATIC void GC_protect_heap(void) +{ + unsigned i; + GC_bool protect_all = + (0 != (GC_incremental_protection_needs() & GC_PROTECTS_PTRFREE_HEAP)); + + GC_ASSERT(GC_page_size != 0); + 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; + + if (protect_all) { + PROTECT(start, len); + } else { + struct hblk * current; + struct hblk * current_start; /* Start of block to be protected. */ + struct hblk * limit; + + GC_ASSERT(PAGE_ALIGNED(len)); + GC_ASSERT(PAGE_ALIGNED(start)); + current_start = current = (struct hblk *)start; + limit = (struct hblk *)(start + len); + while ((word)current < (word)limit) { + hdr * hhdr; + word nhblks; + GC_bool is_ptrfree; + + GC_ASSERT(PAGE_ALIGNED(current)); + GET_HDR(current, hhdr); + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + /* This can happen only if we're at the beginning of a */ + /* heap segment, and a block spans heap segments. */ + /* We will handle that block as part of the preceding */ + /* segment. */ + GC_ASSERT(current_start == current); + current_start = ++current; + continue; + } + if (HBLK_IS_FREE(hhdr)) { + GC_ASSERT(PAGE_ALIGNED(hhdr -> hb_sz)); + nhblks = divHBLKSZ(hhdr -> hb_sz); + is_ptrfree = TRUE; /* dirty on alloc */ + } else { + nhblks = OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + is_ptrfree = IS_PTRFREE(hhdr); + } + if (is_ptrfree) { + if ((word)current_start < (word)current) { + PROTECT(current_start, (ptr_t)current - (ptr_t)current_start); + } + current_start = (current += nhblks); + } else { + current += nhblks; + } + } + if ((word)current_start < (word)current) { + PROTECT(current_start, (ptr_t)current - (ptr_t)current_start); + } + } + } +} + +/* + * Acquiring the allocation lock here is dangerous, since this + * can be called from within GC_call_with_alloc_lock, and the cord + * package does so. On systems that allow nested lock acquisition, this + * happens to work. + */ + +# ifdef THREAD_SANITIZER + /* Used by GC_remove_protection only. Potential data race between */ + /* this function and GC_write_fault_handler should not be harmful */ + /* because it would only result in a double call of UNPROTECT() for */ + /* a region. */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_bool get_pht_entry_from_index_async(volatile page_hash_table db, + size_t index) + { + return (GC_bool)get_pht_entry_from_index(db, index); + } +# else +# define get_pht_entry_from_index_async(bl, index) \ + get_pht_entry_from_index(bl, index) +# endif + +/* We no longer wrap read by default, since that was causing too many */ +/* problems. It is preferred that the client instead avoids writing */ +/* to the write-protected heap with a system call. */ +#endif /* MPROTECT_VDB */ + +#if !defined(THREADS) && (defined(PROC_VDB) || defined(SOFT_VDB)) + static pid_t saved_proc_pid; /* pid used to compose /proc file names */ +#endif + +#ifdef PROC_VDB +/* This implementation assumes a Solaris 2.X like /proc */ +/* pseudo-file-system from which we can read page modified bits. This */ +/* facility is far from optimal (e.g. we would like to get the info for */ +/* only some of the address space), but it avoids intercepting system */ +/* calls. */ + +# include +# include +# include +# include +# include + +# ifdef GC_NO_SYS_FAULT_H + /* This exists only to check PROC_VDB code compilation (on Linux). */ +# define PG_MODIFIED 1 + struct prpageheader { + int dummy[2]; /* pr_tstamp */ + unsigned long pr_nmap; + unsigned long pr_npage; + }; + struct prasmap { + char *pr_vaddr; + size_t pr_npage; + char dummy1[64+8]; /* pr_mapname, pr_offset */ + unsigned pr_mflags; + unsigned pr_pagesize; + int dummy2[2]; + }; +# else +# include +# include +# endif + +# define INITIAL_BUF_SZ 16384 + STATIC size_t GC_proc_buf_size = INITIAL_BUF_SZ; + STATIC char *GC_proc_buf = NULL; + STATIC int GC_proc_fd = -1; + + static GC_bool proc_dirty_open_files(void) + { + char buf[40]; + pid_t pid = getpid(); + + (void)snprintf(buf, sizeof(buf), "/proc/%ld/pagedata", (long)pid); + buf[sizeof(buf) - 1] = '\0'; + GC_proc_fd = open(buf, O_RDONLY); + if (-1 == GC_proc_fd) { + WARN("/proc open failed; cannot enable GC incremental mode\n", 0); + return FALSE; + } + if (syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC) == -1) + WARN("Could not set FD_CLOEXEC for /proc\n", 0); +# ifndef THREADS + saved_proc_pid = pid; /* updated on success only */ +# endif + return TRUE; + } + +# ifdef CAN_HANDLE_FORK + GC_INNER void GC_dirty_update_child(void) + { + if (-1 == GC_proc_fd) + return; /* GC incremental mode is off */ + + close(GC_proc_fd); + if (!proc_dirty_open_files()) + GC_incremental = FALSE; /* should be safe to turn it off */ + } +# endif /* CAN_HANDLE_FORK */ + +GC_INNER GC_bool GC_dirty_init(void) +{ + if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) { + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + GC_VERBOSE_LOG_PRINTF( + "Allocated %lu bytes: all pages may have been written\n", + (unsigned long)(GC_bytes_allocd + GC_bytes_allocd_before_gc)); + } + if (!proc_dirty_open_files()) + return FALSE; + GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size); + if (GC_proc_buf == NULL) + ABORT("Insufficient space for /proc read"); + return TRUE; +} + +GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded) +{ + int nmaps; + char * bufp = GC_proc_buf; + int i; + +# ifndef THREADS + /* If the current pid differs from the saved one, then we are in */ + /* the forked (child) process, the current /proc file should be */ + /* closed, the new one should be opened with the updated path. */ + /* Note, this is not needed for multi-threaded case because */ + /* fork_child_proc() reopens the file right after fork. */ + if (getpid() != saved_proc_pid + && (-1 == GC_proc_fd /* no need to retry */ + || (close(GC_proc_fd), !proc_dirty_open_files()))) { + /* Failed to reopen the file. Punt! */ + if (!output_unneeded) + memset(GC_grungy_pages, 0xff, sizeof(page_hash_table)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + return; + } +# endif + + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); + if (PROC_READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + /* Retry with larger buffer. */ + size_t new_size = 2 * GC_proc_buf_size; + char *new_buf; + + WARN("/proc read failed: GC_proc_buf_size= %" WARN_PRIuPTR "\n", + GC_proc_buf_size); + new_buf = GC_scratch_alloc(new_size); + if (new_buf != 0) { + GC_scratch_recycle_no_gww(bufp, GC_proc_buf_size); + GC_proc_buf = bufp = new_buf; + GC_proc_buf_size = new_size; + } + if (PROC_READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + WARN("Insufficient space for /proc read\n", 0); + /* Punt: */ + if (!output_unneeded) + memset(GC_grungy_pages, 0xff, sizeof (page_hash_table)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + return; + } + } + + /* Copy dirty bits into GC_grungy_pages */ + nmaps = ((struct prpageheader *)bufp) -> pr_nmap; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("Proc VDB read: pr_nmap= %u, pr_npage= %lu\n", + nmaps, ((struct prpageheader *)bufp)->pr_npage); +# endif +# if defined(GC_NO_SYS_FAULT_H) && defined(CPPCHECK) + GC_noop1(((struct prpageheader *)bufp)->dummy[0]); +# endif + bufp += sizeof(struct prpageheader); + for (i = 0; i < nmaps; i++) { + struct prasmap * map = (struct prasmap *)bufp; + ptr_t vaddr = (ptr_t)(map -> pr_vaddr); + unsigned long npages = map -> pr_npage; + unsigned pagesize = map -> pr_pagesize; + ptr_t limit; + +# if defined(GC_NO_SYS_FAULT_H) && defined(CPPCHECK) + GC_noop1(map->dummy1[0] + map->dummy2[0]); +# endif +# ifdef DEBUG_DIRTY_BITS + GC_log_printf( + "pr_vaddr= %p, npage= %lu, mflags= 0x%x, pagesize= 0x%x\n", + (void *)vaddr, npages, map->pr_mflags, pagesize); +# endif + + bufp += sizeof(struct prasmap); + limit = vaddr + pagesize * npages; + for (; (word)vaddr < (word)limit; vaddr += pagesize) { + if ((*bufp++) & PG_MODIFIED) { + struct hblk * h; + ptr_t next_vaddr = vaddr + pagesize; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("dirty page at: %p\n", (void *)vaddr); +# endif + for (h = (struct hblk *)vaddr; + (word)h < (word)next_vaddr; h++) { + word index = PHT_HASH(h); + + set_pht_entry_from_index(GC_grungy_pages, index); + } + } + } + bufp = (char *)(((word)bufp + (sizeof(long)-1)) + & ~(word)(sizeof(long)-1)); + } +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("Proc VDB read done\n"); +# endif + + /* Update GC_written_pages (even if output_unneeded). */ + GC_or_pages(GC_written_pages, GC_grungy_pages); +} + +#endif /* PROC_VDB */ + +#ifdef SOFT_VDB +# ifndef VDB_BUF_SZ +# define VDB_BUF_SZ 16384 +# endif + + static int open_proc_fd(pid_t pid, const char *proc_filename, int mode) + { + int f; + char buf[40]; + + (void)snprintf(buf, sizeof(buf), "/proc/%ld/%s", (long)pid, + proc_filename); + buf[sizeof(buf) - 1] = '\0'; + f = open(buf, mode); + if (-1 == f) { + WARN("/proc/self/%s open failed; cannot enable GC incremental mode\n", + proc_filename); + } else if (fcntl(f, F_SETFD, FD_CLOEXEC) == -1) { + WARN("Could not set FD_CLOEXEC for /proc\n", 0); + } + return f; + } + +# include /* for uint64_t */ + + typedef uint64_t pagemap_elem_t; + + static pagemap_elem_t *soft_vdb_buf; + static int pagemap_fd; + + static GC_bool soft_dirty_open_files(void) + { + pid_t pid = getpid(); + + clear_refs_fd = open_proc_fd(pid, "clear_refs", O_WRONLY); + if (-1 == clear_refs_fd) + return FALSE; + pagemap_fd = open_proc_fd(pid, "pagemap", O_RDONLY); + if (-1 == pagemap_fd) { + close(clear_refs_fd); + clear_refs_fd = -1; + return FALSE; + } +# ifndef THREADS + saved_proc_pid = pid; /* updated on success only */ +# endif + return TRUE; + } + +# ifdef CAN_HANDLE_FORK + GC_INNER void GC_dirty_update_child(void) + { + if (-1 == clear_refs_fd) + return; /* GC incremental mode is off */ + + close(clear_refs_fd); + close(pagemap_fd); + if (!soft_dirty_open_files()) + GC_incremental = FALSE; + } +# endif /* CAN_HANDLE_FORK */ + + /* Clear soft-dirty bits from the task's PTEs. */ + static void clear_soft_dirty_bits(void) + { + ssize_t res = write(clear_refs_fd, "4\n", 2); + + if (res != 2) + ABORT_ARG1("Failed to write to /proc/self/clear_refs", + ": errno= %d", res < 0 ? errno : 0); + } + + /* The bit 55 of the 64-bit qword of pagemap file is the soft-dirty one. */ +# define PM_SOFTDIRTY_MASK ((pagemap_elem_t)1 << 55) + + static GC_bool detect_soft_dirty_supported(ptr_t vaddr) + { + off_t fpos; + pagemap_elem_t buf[1]; + + GC_ASSERT(GC_page_size != 0); + *vaddr = 1; /* make it dirty */ + fpos = (off_t)((word)vaddr / GC_page_size * sizeof(pagemap_elem_t)); + + for (;;) { + /* Read the relevant PTE from the pagemap file. */ + if (lseek(pagemap_fd, fpos, SEEK_SET) == (off_t)(-1)) + return FALSE; + if (PROC_READ(pagemap_fd, buf, sizeof(buf)) != (int)sizeof(buf)) + return FALSE; + + /* Is the soft-dirty bit unset? */ + if ((buf[0] & PM_SOFTDIRTY_MASK) == 0) return FALSE; + + if (0 == *vaddr) break; + /* Retry to check that writing to clear_refs works as expected. */ + /* This malfunction of the soft-dirty bits implementation is */ + /* observed on some Linux kernels on Power9 (e.g. in Fedora 36). */ + clear_soft_dirty_bits(); + *vaddr = 0; + } + return TRUE; /* success */ + } + +# ifndef NO_SOFT_VDB_LINUX_VER_RUNTIME_CHECK +# include +# include /* for strcmp() */ + + /* Ensure the linux (kernel) major/minor version is as given or higher. */ + static GC_bool ensure_min_linux_ver(int major, int minor) { + struct utsname info; + int actual_major; + int actual_minor = -1; + + if (uname(&info) == -1) { + return FALSE; /* uname() failed, should not happen actually. */ + } + if (strcmp(info.sysname, "Linux")) { + WARN("Cannot ensure Linux version as running on other OS: %s\n", + info.sysname); + return FALSE; + } + actual_major = GC_parse_version(&actual_minor, info.release); + return actual_major > major + || (actual_major == major && actual_minor >= minor); + } +# endif + +# ifdef MPROTECT_VDB + static GC_bool soft_dirty_init(void) +# else + GC_INNER GC_bool GC_dirty_init(void) +# endif + { +# ifdef MPROTECT_VDB + char * str = GETENV("GC_USE_GETWRITEWATCH"); + +# ifdef GC_PREFER_MPROTECT_VDB + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) + return FALSE; /* the environment variable is unset or set to "0" */ +# else + if (str != NULL && *str == '0' && *(str + 1) == '\0') + return FALSE; /* the environment variable is set "0" */ +# endif +# endif + GC_ASSERT(NULL == soft_vdb_buf); +# ifndef NO_SOFT_VDB_LINUX_VER_RUNTIME_CHECK + if (!ensure_min_linux_ver(3, 18)) { + GC_COND_LOG_PRINTF( + "Running on old kernel lacking correct soft-dirty bit support\n"); + return FALSE; + } +# endif + if (!soft_dirty_open_files()) + return FALSE; + soft_vdb_buf = (pagemap_elem_t *)GC_scratch_alloc(VDB_BUF_SZ); + if (NULL == soft_vdb_buf) + ABORT("Insufficient space for /proc pagemap buffer"); + if (!detect_soft_dirty_supported((ptr_t)soft_vdb_buf)) { + GC_COND_LOG_PRINTF("Soft-dirty bit is not supported by kernel\n"); + /* Release the resources. */ + GC_scratch_recycle_no_gww(soft_vdb_buf, VDB_BUF_SZ); + soft_vdb_buf = NULL; + close(clear_refs_fd); + clear_refs_fd = -1; + close(pagemap_fd); + return FALSE; + } + return TRUE; + } + + static off_t pagemap_buf_fpos; /* valid only if pagemap_buf_len > 0 */ + static size_t pagemap_buf_len; + + /* Read bytes from /proc/self/pagemap at given file position. */ + /* len - the maximum number of bytes to read; (*pres) - amount of */ + /* bytes actually read, always bigger than 0 but never exceeds len; */ + /* next_fpos_hint - the file position of the next bytes block to read */ + /* ahead if possible (0 means no information provided). */ + static const pagemap_elem_t *pagemap_buffered_read(size_t *pres, + off_t fpos, size_t len, + off_t next_fpos_hint) + { + ssize_t res; + size_t ofs; + + GC_ASSERT(len > 0); + if (pagemap_buf_fpos <= fpos + && fpos < pagemap_buf_fpos + (off_t)pagemap_buf_len) { + /* The requested data is already in the buffer. */ + ofs = (size_t)(fpos - pagemap_buf_fpos); + res = (ssize_t)(pagemap_buf_fpos + pagemap_buf_len - fpos); + } else { + off_t aligned_pos = fpos & ~(GC_page_size < VDB_BUF_SZ + ? GC_page_size-1 : VDB_BUF_SZ-1); + + for (;;) { + size_t count; + + if ((0 == pagemap_buf_len + || pagemap_buf_fpos + (off_t)pagemap_buf_len != aligned_pos) + && lseek(pagemap_fd, aligned_pos, SEEK_SET) == (off_t)(-1)) + ABORT_ARG2("Failed to lseek /proc/self/pagemap", + ": offset= %lu, errno= %d", (unsigned long)fpos, errno); + + /* How much to read at once? */ + ofs = (size_t)(fpos - aligned_pos); + GC_ASSERT(ofs < VDB_BUF_SZ); + if (next_fpos_hint > aligned_pos + && next_fpos_hint - aligned_pos < VDB_BUF_SZ) { + count = VDB_BUF_SZ; + } else { + count = len + ofs; + if (count > VDB_BUF_SZ) + count = VDB_BUF_SZ; + } + + GC_ASSERT(count % sizeof(pagemap_elem_t) == 0); + res = PROC_READ(pagemap_fd, soft_vdb_buf, count); + if (res > (ssize_t)ofs) + break; + if (res <= 0) + ABORT_ARG1("Failed to read /proc/self/pagemap", + ": errno= %d", res < 0 ? errno : 0); + /* Retry (once) w/o page-alignment. */ + aligned_pos = fpos; + } + + /* Save the buffer (file window) position and size. */ + pagemap_buf_fpos = aligned_pos; + pagemap_buf_len = (size_t)res; + res -= (ssize_t)ofs; + } + + GC_ASSERT(ofs % sizeof(pagemap_elem_t) == 0); + *pres = (size_t)res < len ? (size_t)res : len; + return &soft_vdb_buf[ofs / sizeof(pagemap_elem_t)]; + } + + static void soft_set_grungy_pages(ptr_t vaddr /* start */, ptr_t limit, + ptr_t next_start_hint) + { + GC_ASSERT(GC_page_size != 0); + while ((word)vaddr < (word)limit) { + size_t res; + word limit_buf; + const pagemap_elem_t *bufp = pagemap_buffered_read(&res, + (off_t)((word)vaddr / GC_page_size * sizeof(pagemap_elem_t)), + (size_t)((((word)limit - (word)vaddr + GC_page_size-1) + / GC_page_size) * sizeof(pagemap_elem_t)), + (off_t)((word)next_start_hint / GC_page_size + * sizeof(pagemap_elem_t))); + + if (res % sizeof(pagemap_elem_t) != 0) { + /* Punt: */ + memset(GC_grungy_pages, 0xff, sizeof(page_hash_table)); + WARN("Incomplete read of pagemap, not multiple of entry size\n", 0); + break; + } + + limit_buf = ((word)vaddr & ~(GC_page_size-1)) + + (res / sizeof(pagemap_elem_t)) * GC_page_size; + for (; (word)vaddr < limit_buf; vaddr += GC_page_size, bufp++) + if ((*bufp & PM_SOFTDIRTY_MASK) != 0) { + struct hblk * h; + ptr_t next_vaddr = vaddr + GC_page_size; + + /* If the bit is set, the respective PTE was written to */ + /* since clearing the soft-dirty bits. */ +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("dirty page at: %p\n", (void *)vaddr); +# endif + for (h = (struct hblk *)vaddr; (word)h < (word)next_vaddr; h++) { + word index = PHT_HASH(h); + set_pht_entry_from_index(GC_grungy_pages, index); + } + } + /* Read the next portion of pagemap file if incomplete. */ + } + } + + GC_INLINE void GC_soft_read_dirty(GC_bool output_unneeded) + { +# ifndef THREADS + /* Similar as for GC_proc_read_dirty. */ + if (getpid() != saved_proc_pid + && (-1 == clear_refs_fd /* no need to retry */ + || (close(clear_refs_fd), close(pagemap_fd), + !soft_dirty_open_files()))) { + /* Failed to reopen the files. */ + if (!output_unneeded) { + /* Punt: */ + memset(GC_grungy_pages, 0xff, sizeof(page_hash_table)); +# ifdef CHECKSUMS + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); +# endif + } + return; + } +# endif + + if (!output_unneeded) { + word i; + + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); + pagemap_buf_len = 0; /* invalidate soft_vdb_buf */ + + for (i = 0; i != GC_n_heap_sects; ++i) { + ptr_t vaddr = GC_heap_sects[i].hs_start; + + soft_set_grungy_pages(vaddr, vaddr + GC_heap_sects[i].hs_bytes, + i < GC_n_heap_sects-1 ? + GC_heap_sects[i+1].hs_start : NULL); + } +# ifdef CHECKSUMS + GC_or_pages(GC_written_pages, GC_grungy_pages); +# endif + +# ifndef NO_VDB_FOR_STATIC_ROOTS + for (i = 0; (int)i < n_root_sets; ++i) { + soft_set_grungy_pages(GC_static_roots[i].r_start, + GC_static_roots[i].r_end, + (int)i < n_root_sets-1 ? + GC_static_roots[i+1].r_start : NULL); + } +# endif + } + + clear_soft_dirty_bits(); + } +#endif /* SOFT_VDB */ + +#ifdef PCR_VDB + +# include "vd/PCR_VD.h" + +# define NPAGES (32*1024) /* 128 MB */ + +PCR_VD_DB GC_grungy_bits[NPAGES]; + +STATIC ptr_t GC_vd_base = NULL; + /* Address corresponding to GC_grungy_bits[0] */ + /* HBLKSIZE aligned. */ + +GC_INNER GC_bool GC_dirty_init(void) +{ + /* For the time being, we assume the heap generally grows up */ + GC_vd_base = GC_heap_sects[0].hs_start; + if (GC_vd_base == 0) { + ABORT("Bad initial heap segment"); + } + if (PCR_VD_Start(HBLKSIZE, GC_vd_base, NPAGES*HBLKSIZE) + != PCR_ERes_okay) { + ABORT("Dirty bit initialization failed"); + } + return TRUE; +} +#endif /* PCR_VDB */ + +#ifndef GC_DISABLE_INCREMENTAL + GC_INNER GC_bool GC_manual_vdb = FALSE; + + /* Manually mark the page containing p as dirty. Logically, this */ + /* dirties the entire object. */ + GC_INNER void GC_dirty_inner(const void *p) + { + word index = PHT_HASH(p); + +# if defined(MPROTECT_VDB) + /* Do not update GC_dirty_pages if it should be followed by the */ + /* page unprotection. */ + GC_ASSERT(GC_manual_vdb); +# endif + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + + /* Retrieve system dirty bits for the heap to a local buffer (unless */ + /* output_unneeded). Restore the systems notion of which pages are */ + /* dirty. We assume that either the world is stopped or it is OK to */ + /* lose dirty bits while it's happening (as in GC_enable_incremental).*/ + GC_INNER void GC_read_dirty(GC_bool output_unneeded) + { + if (GC_manual_vdb +# if defined(MPROTECT_VDB) + || !GC_GWW_AVAILABLE() +# endif + ) { + if (!output_unneeded) + BCOPY((/* no volatile */ void *)GC_dirty_pages, GC_grungy_pages, + sizeof(GC_dirty_pages)); + BZERO((/* no volatile */ void *)GC_dirty_pages, + sizeof(GC_dirty_pages)); +# ifdef MPROTECT_VDB + if (!GC_manual_vdb) + GC_protect_heap(); +# endif + return; + } + +# ifdef GWW_VDB + GC_gww_read_dirty(output_unneeded); +# elif defined(PROC_VDB) + GC_proc_read_dirty(output_unneeded); +# elif defined(SOFT_VDB) + GC_soft_read_dirty(output_unneeded); +# elif defined(PCR_VDB) + /* lazily enable dirty bits on newly added heap sects */ + { + static int onhs = 0; + int nhs = GC_n_heap_sects; + for (; onhs < nhs; onhs++) { + PCR_VD_WriteProtectEnable( + GC_heap_sects[onhs].hs_start, + GC_heap_sects[onhs].hs_bytes); + } + } + if (PCR_VD_Clear(GC_vd_base, NPAGES*HBLKSIZE, GC_grungy_bits) + != PCR_ERes_okay) { + ABORT("Dirty bit read failed"); + } +# endif + } + +# if !defined(NO_VDB_FOR_STATIC_ROOTS) && !defined(PROC_VDB) + GC_INNER GC_bool GC_is_vdb_for_static_roots(void) + { + if (GC_manual_vdb) return FALSE; +# if defined(MPROTECT_VDB) + /* Currently used only in conjunction with SOFT_VDB. */ + return GC_GWW_AVAILABLE(); +# else + GC_ASSERT(GC_incremental); + return TRUE; +# endif + } +# endif + + /* Is the HBLKSIZE sized page at h marked dirty in the local buffer? */ + /* If the actual page size is different, this returns TRUE if any */ + /* of the pages overlapping h are dirty. This routine may err on the */ + /* side of labeling pages as dirty (and this implementation does). */ + GC_INNER GC_bool GC_page_was_dirty(struct hblk *h) + { + word index; + +# ifdef PCR_VDB + if (!GC_manual_vdb) { + 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; + } +# elif defined(DEFAULT_VDB) + if (!GC_manual_vdb) + return TRUE; +# elif defined(PROC_VDB) + /* Unless manual VDB is on, the bitmap covers all process memory. */ + if (GC_manual_vdb) +# endif + { + if (NULL == HDR(h)) + return TRUE; + } + index = PHT_HASH(h); + return get_pht_entry_from_index(GC_grungy_pages, index); + } + +# if defined(CHECKSUMS) || defined(PROC_VDB) + /* Could any valid GC heap pointer ever have been written to this page? */ + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk *h) + { +# if defined(GWW_VDB) || defined(PROC_VDB) || defined(SOFT_VDB) + word index; + +# ifdef MPROTECT_VDB + if (!GC_GWW_AVAILABLE()) + return TRUE; +# endif +# if defined(PROC_VDB) + if (GC_manual_vdb) +# endif + { + if (NULL == HDR(h)) + return TRUE; + } + index = PHT_HASH(h); + return get_pht_entry_from_index(GC_written_pages, index); +# else + /* TODO: implement me for MANUAL_VDB. */ + (void)h; + return TRUE; +# endif + } +# endif /* CHECKSUMS || PROC_VDB */ + + /* We expect block h to be written shortly. Ensure that all pages */ + /* containing any part of the n hblks starting at h are no longer */ + /* protected. If is_ptrfree is false, also ensure that they will */ + /* subsequently appear to be dirty. Not allowed to call GC_printf */ + /* (and the friends) here, see Win32 GC_stop_world for the details. */ + GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, + GC_bool is_ptrfree) + { +# ifdef PCR_VDB + (void)is_ptrfree; + if (!GC_auto_incremental) + return; + PCR_VD_WriteProtectDisable(h, nblocks*HBLKSIZE); + PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE); +# elif defined(MPROTECT_VDB) + struct hblk * h_trunc; /* Truncated to page boundary */ + struct hblk * h_end; /* Page boundary following block end */ + struct hblk * current; + + if (!GC_auto_incremental || GC_GWW_AVAILABLE()) + return; + GC_ASSERT(GC_page_size != 0); + h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1)); + h_end = (struct hblk *)(((word)(h + nblocks) + GC_page_size - 1) + & ~(GC_page_size - 1)); + if (h_end == h_trunc + 1 && + get_pht_entry_from_index_async(GC_dirty_pages, PHT_HASH(h_trunc))) { + /* already marked dirty, and hence unprotected. */ + return; + } + for (current = h_trunc; (word)current < (word)h_end; ++current) { + word index = PHT_HASH(current); + + if (!is_ptrfree || (word)current < (word)h + || (word)current >= (word)(h + nblocks)) { + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + } + UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc); +# else + /* Ignore write hints. They don't help us here. */ + (void)h; (void)nblocks; (void)is_ptrfree; +# endif + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +#if defined(MPROTECT_VDB) && defined(DARWIN) +/* The following sources were used as a "reference" for this exception + handling code: + 1. Apple's mach/xnu documentation + 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the + omnigroup's macosx-dev list. + www.omnigroup.com/mailman/archive/macosx-dev/2000-June/014178.html + 3. macosx-nat.c from Apple's GDB source code. +*/ + +/* The bug that caused all this trouble should now be fixed. This should + eventually be removed if all goes well. */ + +/* #define BROKEN_EXCEPTION_HANDLING */ + +#include +#include +#include +#include +#include + +EXTERN_C_BEGIN + +/* Some of the following prototypes are missing in any header, although */ +/* they are documented. Some are in mach/exc.h file. */ +extern boolean_t +exc_server(mach_msg_header_t *, mach_msg_header_t *); + +extern kern_return_t +exception_raise(mach_port_t, mach_port_t, mach_port_t, exception_type_t, + exception_data_t, mach_msg_type_number_t); + +extern kern_return_t +exception_raise_state(mach_port_t, mach_port_t, mach_port_t, exception_type_t, + exception_data_t, mach_msg_type_number_t, + thread_state_flavor_t*, thread_state_t, + mach_msg_type_number_t, thread_state_t, + mach_msg_type_number_t*); + +extern kern_return_t +exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, + exception_type_t, exception_data_t, + mach_msg_type_number_t, thread_state_flavor_t*, + thread_state_t, mach_msg_type_number_t, + thread_state_t, mach_msg_type_number_t*); + +GC_API_OSCALL kern_return_t +catch_exception_raise(mach_port_t exception_port, mach_port_t thread, + mach_port_t task, exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count); + +GC_API_OSCALL kern_return_t +catch_exception_raise_state(mach_port_name_t exception_port, + int exception, exception_data_t code, + mach_msg_type_number_t codeCnt, int flavor, + thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt); + +GC_API_OSCALL kern_return_t +catch_exception_raise_state_identity(mach_port_name_t exception_port, + mach_port_t thread, mach_port_t task, int exception, + exception_data_t code, mach_msg_type_number_t codeCnt, + int flavor, thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt); + +EXTERN_C_END + +/* These should never be called, but just in case... */ +GC_API_OSCALL kern_return_t +catch_exception_raise_state(mach_port_name_t exception_port GC_ATTR_UNUSED, + int exception GC_ATTR_UNUSED, exception_data_t code GC_ATTR_UNUSED, + mach_msg_type_number_t codeCnt GC_ATTR_UNUSED, int flavor GC_ATTR_UNUSED, + thread_state_t old_state GC_ATTR_UNUSED, int old_stateCnt GC_ATTR_UNUSED, + thread_state_t new_state GC_ATTR_UNUSED, int new_stateCnt GC_ATTR_UNUSED) +{ + ABORT_RET("Unexpected catch_exception_raise_state invocation"); + return(KERN_INVALID_ARGUMENT); +} + +GC_API_OSCALL kern_return_t +catch_exception_raise_state_identity( + mach_port_name_t exception_port GC_ATTR_UNUSED, + mach_port_t thread GC_ATTR_UNUSED, mach_port_t task GC_ATTR_UNUSED, + int exception GC_ATTR_UNUSED, exception_data_t code GC_ATTR_UNUSED, + mach_msg_type_number_t codeCnt GC_ATTR_UNUSED, int flavor GC_ATTR_UNUSED, + thread_state_t old_state GC_ATTR_UNUSED, int old_stateCnt GC_ATTR_UNUSED, + thread_state_t new_state GC_ATTR_UNUSED, int new_stateCnt GC_ATTR_UNUSED) +{ + ABORT_RET("Unexpected catch_exception_raise_state_identity invocation"); + return(KERN_INVALID_ARGUMENT); +} + +#define MAX_EXCEPTION_PORTS 16 + +static struct { + mach_msg_type_number_t count; + exception_mask_t masks[MAX_EXCEPTION_PORTS]; + exception_handler_t ports[MAX_EXCEPTION_PORTS]; + exception_behavior_t behaviors[MAX_EXCEPTION_PORTS]; + thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS]; +} GC_old_exc_ports; + +STATIC struct ports_s { + void (*volatile os_callback[3])(void); + mach_port_t exception; +# if defined(THREADS) + mach_port_t reply; +# endif +} GC_ports = { + { + /* This is to prevent stripping these routines as dead. */ + (void (*)(void))catch_exception_raise, + (void (*)(void))catch_exception_raise_state, + (void (*)(void))catch_exception_raise_state_identity + }, +# ifdef THREADS + 0, /* for 'exception' */ +# endif + 0 +}; + +typedef struct { + mach_msg_header_t head; +} GC_msg_t; + +typedef enum { + GC_MP_NORMAL, + GC_MP_DISCARDING, + GC_MP_STOPPED +} GC_mprotect_state_t; + +#ifdef THREADS + /* 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 + + /* This value is only used on the reply port. */ +# define ID_ACK 3 + + STATIC GC_mprotect_state_t GC_mprotect_state = GC_MP_NORMAL; + + /* The following should ONLY be called when the world is stopped. */ + STATIC void GC_mprotect_thread_notify(mach_msg_id_t id) + { + struct buf_s { + GC_msg_t msg; + mach_msg_trailer_t trailer; + } buf; + mach_msg_return_t r; + + /* remote, local */ + buf.msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + buf.msg.head.msgh_size = sizeof(buf.msg); + buf.msg.head.msgh_remote_port = GC_ports.exception; + buf.msg.head.msgh_local_port = MACH_PORT_NULL; + buf.msg.head.msgh_id = id; + + r = mach_msg(&buf.msg.head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_LARGE, + sizeof(buf.msg), sizeof(buf), GC_ports.reply, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) + ABORT("mach_msg failed in GC_mprotect_thread_notify"); + if (buf.msg.head.msgh_id != ID_ACK) + ABORT("Invalid ack in GC_mprotect_thread_notify"); + } + + /* Should only be called by the mprotect thread */ + STATIC void GC_mprotect_thread_reply(void) + { + GC_msg_t msg; + mach_msg_return_t r; + /* remote, local */ + + msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + msg.head.msgh_size = sizeof(msg); + msg.head.msgh_remote_port = GC_ports.reply; + msg.head.msgh_local_port = MACH_PORT_NULL; + msg.head.msgh_id = ID_ACK; + + r = mach_msg(&msg.head, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) + ABORT("mach_msg failed in GC_mprotect_thread_reply"); + } + + GC_INNER void GC_mprotect_stop(void) + { + GC_mprotect_thread_notify(ID_STOP); + } + + GC_INNER void GC_mprotect_resume(void) + { + GC_mprotect_thread_notify(ID_RESUME); + } + +#else + /* The compiler should optimize away any GC_mprotect_state computations */ +# define GC_mprotect_state GC_MP_NORMAL +#endif /* !THREADS */ + +struct mp_reply_s { + mach_msg_header_t head; + char data[256]; +}; + +struct mp_msg_s { + mach_msg_header_t head; + mach_msg_body_t msgh_body; + char data[1024]; +}; + +STATIC void *GC_mprotect_thread(void *arg) +{ + mach_msg_return_t r; + /* These two structures contain some private kernel data. We don't */ + /* need to access any of it so we don't bother defining a proper */ + /* struct. The correct definitions are in the xnu source code. */ + struct mp_reply_s reply; + struct mp_msg_s msg; + mach_msg_id_t id; + + if ((word)arg == GC_WORD_MAX) return 0; /* to prevent a compiler warning */ +# if defined(CPPCHECK) + reply.data[0] = 0; /* to prevent "field unused" warnings */ + msg.data[0] = 0; +# endif + +# if defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) + (void)pthread_setname_np("GC-mprotect"); +# endif +# if defined(THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + GC_darwin_register_mach_handler_thread(mach_thread_self()); +# endif + + for(;;) { + r = mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE | + (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0), + 0, sizeof(msg), GC_ports.exception, + GC_mprotect_state == GC_MP_DISCARDING ? 0 + : MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1; + +# if defined(THREADS) + if(GC_mprotect_state == GC_MP_DISCARDING) { + if(r == MACH_RCV_TIMED_OUT) { + GC_mprotect_state = GC_MP_STOPPED; + GC_mprotect_thread_reply(); + continue; + } + if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME)) + ABORT("Out of order mprotect thread request"); + } +# endif /* THREADS */ + + if (r != MACH_MSG_SUCCESS) { + ABORT_ARG2("mach_msg failed", + ": errcode= %d (%s)", (int)r, mach_error_string(r)); + } + + switch(id) { +# if defined(THREADS) + case ID_STOP: + if(GC_mprotect_state != GC_MP_NORMAL) + ABORT("Called mprotect_stop when state wasn't normal"); + GC_mprotect_state = GC_MP_DISCARDING; + break; + case ID_RESUME: + if(GC_mprotect_state != GC_MP_STOPPED) + ABORT("Called mprotect_resume when state wasn't stopped"); + GC_mprotect_state = GC_MP_NORMAL; + GC_mprotect_thread_reply(); + break; +# endif /* THREADS */ + default: + /* Handle the message (calls catch_exception_raise) */ + if(!exc_server(&msg.head, &reply.head)) + ABORT("exc_server failed"); + /* Send the reply */ + r = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0, + MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if(r != MACH_MSG_SUCCESS) { + /* This will fail if the thread dies, but the thread */ + /* shouldn't die... */ +# ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf("mach_msg failed with %d %s while sending " + "exc reply\n", (int)r, mach_error_string(r)); +# else + ABORT("mach_msg failed while sending exception reply"); +# endif + } + } /* switch */ + } /* for(;;) */ +} + +/* All this SIGBUS code shouldn't be necessary. All protection faults should + be going through the mach exception handler. However, it seems a SIGBUS is + occasionally sent for some unknown reason. Even more odd, it seems to be + meaningless and safe to ignore. */ +#ifdef BROKEN_EXCEPTION_HANDLING + + /* Updates to this aren't atomic, but the SIGBUS'es seem pretty rare. */ + /* Even if this doesn't get updated property, it isn't really a problem. */ + STATIC int GC_sigbus_count = 0; + + STATIC void GC_darwin_sigbus(int num, siginfo_t *sip, void *context) + { + if (num != SIGBUS) + ABORT("Got a non-sigbus signal in the sigbus handler"); + + /* Ugh... some seem safe to ignore, but too many in a row probably means + trouble. GC_sigbus_count is reset for each mach exception that is + handled */ + if (GC_sigbus_count >= 8) { + ABORT("Got more than 8 SIGBUSs in a row!"); + } else { + GC_sigbus_count++; + WARN("Ignoring SIGBUS\n", 0); + } + } +#endif /* BROKEN_EXCEPTION_HANDLING */ + +GC_INNER GC_bool GC_dirty_init(void) +{ + kern_return_t r; + mach_port_t me; + pthread_t thread; + pthread_attr_t attr; + exception_mask_t mask; + +# 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_incremental 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. */ + WARN("Can't turn on GC incremental mode as fork()" + " handling requested\n", 0); + return FALSE; + } +# 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); +# endif + if (GC_page_size % HBLKSIZE != 0) { + ABORT("Page size not multiple of HBLKSIZE"); + } + + GC_task_self = me = mach_task_self(); + + r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.exception); + /* TODO: WARN and return FALSE in case of a failure. */ + if (r != KERN_SUCCESS) + ABORT("mach_port_allocate failed (exception port)"); + + r = mach_port_insert_right(me, GC_ports.exception, GC_ports.exception, + MACH_MSG_TYPE_MAKE_SEND); + if (r != KERN_SUCCESS) + ABORT("mach_port_insert_right failed (exception port)"); + +# if defined(THREADS) + r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.reply); + if(r != KERN_SUCCESS) + ABORT("mach_port_allocate failed (reply port)"); +# endif + + /* The exceptions we want to catch */ + mask = EXC_MASK_BAD_ACCESS; + + r = task_get_exception_ports(me, mask, GC_old_exc_ports.masks, + &GC_old_exc_ports.count, GC_old_exc_ports.ports, + GC_old_exc_ports.behaviors, + GC_old_exc_ports.flavors); + if (r != KERN_SUCCESS) + ABORT("task_get_exception_ports failed"); + + r = task_set_exception_ports(me, mask, GC_ports.exception, EXCEPTION_DEFAULT, + GC_MACH_THREAD_STATE); + if (r != KERN_SUCCESS) + ABORT("task_set_exception_ports failed"); + if (pthread_attr_init(&attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) + ABORT("pthread_attr_setdetachedstate failed"); + +# undef pthread_create + /* This will call the real pthread function, not our wrapper */ + if (pthread_create(&thread, &attr, GC_mprotect_thread, NULL) != 0) + ABORT("pthread_create failed"); + (void)pthread_attr_destroy(&attr); + + /* Setup the sigbus handler for ignoring the meaningless SIGBUSs */ +# ifdef BROKEN_EXCEPTION_HANDLING + { + struct sigaction sa, oldsa; + sa.sa_handler = (SIG_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 (oldsa.sa_handler != (SIG_HNDLR_PTR)(signed_word)SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n"); + } + } +# endif /* BROKEN_EXCEPTION_HANDLING */ +# if defined(CPPCHECK) + GC_noop1((word)GC_ports.os_callback[0]); +# endif + return TRUE; +} + +/* The source code for Apple's GDB was used as a reference for the */ +/* exception forwarding code. This code is similar to be GDB code only */ +/* because there is only one way to do it. */ +STATIC kern_return_t GC_forward_exception(mach_port_t thread, mach_port_t task, + exception_type_t exception, + exception_data_t data, + mach_msg_type_number_t data_count) +{ + unsigned int i; + kern_return_t r; + mach_port_t port; + exception_behavior_t behavior; + thread_state_flavor_t flavor; + + thread_state_data_t thread_state; + mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; + + for (i=0; i < GC_old_exc_ports.count; i++) + if (GC_old_exc_ports.masks[i] & (1 << exception)) + break; + if (i == GC_old_exc_ports.count) + ABORT("No handler for exception!"); + + port = GC_old_exc_ports.ports[i]; + behavior = GC_old_exc_ports.behaviors[i]; + flavor = GC_old_exc_ports.flavors[i]; + + if (behavior == EXCEPTION_STATE || behavior == EXCEPTION_STATE_IDENTITY) { + r = thread_get_state(thread, flavor, thread_state, &thread_state_count); + if(r != KERN_SUCCESS) + ABORT("thread_get_state failed in forward_exception"); + } + + switch(behavior) { + case EXCEPTION_STATE: + r = exception_raise_state(port, thread, task, exception, data, data_count, + &flavor, thread_state, thread_state_count, + thread_state, &thread_state_count); + break; + case EXCEPTION_STATE_IDENTITY: + r = exception_raise_state_identity(port, thread, task, exception, data, + data_count, &flavor, thread_state, + thread_state_count, thread_state, + &thread_state_count); + break; + /* case EXCEPTION_DEFAULT: */ /* default signal handlers */ + default: /* user-supplied signal handlers */ + r = exception_raise(port, thread, task, exception, data, data_count); + } + + if (behavior == EXCEPTION_STATE || behavior == EXCEPTION_STATE_IDENTITY) { + r = thread_set_state(thread, flavor, thread_state, thread_state_count); + if (r != KERN_SUCCESS) + ABORT("thread_set_state failed in forward_exception"); + } + return r; +} + +#define FWD() GC_forward_exception(thread, task, exception, code, code_count) + +#ifdef ARM32 +# define DARWIN_EXC_STATE ARM_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT ARM_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T arm_exception_state_t +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(far) +#elif defined(AARCH64) +# define DARWIN_EXC_STATE ARM_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT ARM_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T arm_exception_state64_t +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(far) +#elif defined(POWERPC) +# if CPP_WORDSZ == 32 +# define DARWIN_EXC_STATE PPC_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT PPC_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T ppc_exception_state_t +# else +# define DARWIN_EXC_STATE PPC_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT PPC_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T ppc_exception_state64_t +# endif +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(dar) +#elif defined(I386) || defined(X86_64) +# if CPP_WORDSZ == 32 +# if defined(i386_EXCEPTION_STATE_COUNT) \ + && !defined(x86_EXCEPTION_STATE32_COUNT) + /* Use old naming convention for 32-bit x86. */ +# define DARWIN_EXC_STATE i386_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT i386_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T i386_exception_state_t +# else +# define DARWIN_EXC_STATE x86_EXCEPTION_STATE32 +# define DARWIN_EXC_STATE_COUNT x86_EXCEPTION_STATE32_COUNT +# define DARWIN_EXC_STATE_T x86_exception_state32_t +# endif +# else +# define DARWIN_EXC_STATE x86_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT x86_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T x86_exception_state64_t +# endif +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(faultvaddr) +#elif !defined(CPPCHECK) +# error FIXME for non-arm/ppc/x86 darwin +#endif + +/* This violates the namespace rules but there isn't anything that can */ +/* be done about it. The exception handling stuff is hard coded to */ +/* call this. catch_exception_raise, catch_exception_raise_state and */ +/* and catch_exception_raise_state_identity are called from OS. */ +GC_API_OSCALL kern_return_t +catch_exception_raise(mach_port_t exception_port GC_ATTR_UNUSED, + mach_port_t thread, mach_port_t task GC_ATTR_UNUSED, + exception_type_t exception, exception_data_t code, + mach_msg_type_number_t code_count GC_ATTR_UNUSED) +{ + kern_return_t r; + char *addr; + thread_state_flavor_t flavor = DARWIN_EXC_STATE; + mach_msg_type_number_t exc_state_count = DARWIN_EXC_STATE_COUNT; + DARWIN_EXC_STATE_T exc_state; + + if (exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) { +# ifdef DEBUG_EXCEPTION_HANDLING + /* We aren't interested, pass it on to the old handler */ + GC_log_printf("Exception: 0x%x Code: 0x%x 0x%x in catch...\n", + exception, code_count > 0 ? code[0] : -1, + code_count > 1 ? code[1] : -1); +# endif + return FWD(); + } + + r = thread_get_state(thread, flavor, (natural_t*)&exc_state, + &exc_state_count); + if(r != KERN_SUCCESS) { + /* The thread is supposed to be suspended while the exception */ + /* handler is called. This shouldn't fail. */ +# ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf("thread_get_state failed in catch_exception_raise\n"); + return KERN_SUCCESS; +# else + ABORT("thread_get_state failed in catch_exception_raise"); +# endif + } + + /* This is the address that caused the fault */ + addr = (char*) exc_state.DARWIN_EXC_STATE_DAR; + if (!is_header_found_async(addr)) { + /* Ugh... just like the SIGBUS problem above, it seems we get */ + /* a bogus KERN_PROTECTION_FAILURE every once and a while. We wait */ + /* till we get a bunch in a row before doing anything about it. */ + /* If a "real" fault ever occurs it'll just keep faulting over and */ + /* over and we'll hit the limit pretty quickly. */ +# ifdef BROKEN_EXCEPTION_HANDLING + static char *last_fault; + static int last_fault_count; + + if(addr != last_fault) { + last_fault = addr; + last_fault_count = 0; + } + if(++last_fault_count < 32) { + if(last_fault_count == 1) + WARN("Ignoring KERN_PROTECTION_FAILURE at %p\n", addr); + return KERN_SUCCESS; + } + + GC_err_printf("Unexpected KERN_PROTECTION_FAILURE at %p; aborting...\n", + (void *)addr); + /* Can't pass it along to the signal handler because that is */ + /* ignoring SIGBUS signals. We also shouldn't call ABORT here as */ + /* signals don't always work too well from the exception handler. */ + EXIT(); +# else /* BROKEN_EXCEPTION_HANDLING */ + /* Pass it along to the next exception handler + (which should call SIGBUS/SIGSEGV) */ + return FWD(); +# endif /* !BROKEN_EXCEPTION_HANDLING */ + } + +# ifdef BROKEN_EXCEPTION_HANDLING + /* Reset the number of consecutive SIGBUSs */ + GC_sigbus_count = 0; +# endif + + GC_ASSERT(GC_page_size != 0); + if (GC_mprotect_state == GC_MP_NORMAL) { /* common case */ + struct hblk * h = (struct hblk*)((word)addr & ~(GC_page_size-1)); + size_t i; + + UNPROTECT(h, GC_page_size); + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + word index = PHT_HASH(h+i); + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + } else if (GC_mprotect_state == GC_MP_DISCARDING) { + /* Lie to the thread for now. No sense UNPROTECT()ing the memory + when we're just going to PROTECT() it again later. The thread + will just fault again once it resumes */ + } else { + /* Shouldn't happen, i don't think */ + GC_err_printf("KERN_PROTECTION_FAILURE while world is stopped\n"); + return FWD(); + } + return KERN_SUCCESS; +} +#undef FWD + +#ifndef NO_DESC_CATCH_EXCEPTION_RAISE + /* These symbols should have REFERENCED_DYNAMICALLY (0x10) bit set to */ + /* let strip know they are not to be stripped. */ + __asm__(".desc _catch_exception_raise, 0x10"); + __asm__(".desc _catch_exception_raise_state, 0x10"); + __asm__(".desc _catch_exception_raise_state_identity, 0x10"); +#endif + +#endif /* DARWIN && MPROTECT_VDB */ + +#ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS + GC_API int GC_CALL GC_incremental_protection_needs(void) + { + GC_ASSERT(GC_is_initialized); + return GC_PROTECTS_NONE; + } +#endif /* !HAVE_INCREMENTAL_PROTECTION_NEEDS */ + +#ifdef ECOS + /* Undo sbrk() redirection. */ +# undef sbrk +#endif + +/* If value is non-zero then allocate executable memory. */ +GC_API void GC_CALL GC_set_pages_executable(int value) +{ + GC_ASSERT(!GC_is_initialized); + /* Even if IGNORE_PAGES_EXECUTABLE is defined, GC_pages_executable is */ + /* touched here to prevent a compiler warning. */ + GC_pages_executable = (GC_bool)(value != 0); +} + +/* Returns non-zero if the GC-allocated memory is executable. */ +/* GC_get_pages_executable is defined after all the places */ +/* where GC_get_pages_executable is undefined. */ +GC_API int GC_CALL GC_get_pages_executable(void) +{ +# ifdef IGNORE_PAGES_EXECUTABLE + return 1; /* Always allocate executable memory. */ +# else + return (int)GC_pages_executable; +# endif +} + +/* Call stack save code for debugging. Should probably be in */ +/* mach_dep.c, but that requires reorganization. */ + +/* I suspect the following works for most *nix x86 variants, so */ +/* long as the frame pointer is explicitly stored. In the case of gcc, */ +/* compiler flags (e.g. -fomit-frame-pointer) determine whether it is. */ +#if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN) +# include + + struct frame { + struct frame *fr_savfp; + long fr_savpc; +# if NARGS > 0 + long fr_arg[NARGS]; /* All the arguments go here. */ +# endif + }; +#endif + +#if defined(SPARC) +# if defined(LINUX) +# include + +# if defined(SAVE_CALL_CHAIN) + struct frame { + long fr_local[8]; + long fr_arg[6]; + struct frame *fr_savfp; + long fr_savpc; +# ifndef __arch64__ + char *fr_stret; +# endif + long fr_argd[6]; + long fr_argx[0]; + }; +# endif +# elif defined (DRSNX) +# include +# elif defined(OPENBSD) +# include +# elif defined(FREEBSD) || defined(NETBSD) +# include +# else +# include +# endif +# if NARGS > 6 +# error We only know how to get the first 6 arguments +# endif +#endif /* SPARC */ + +#ifdef NEED_CALLINFO +/* Fill in the pc and argument information for up to NFRAMES of my */ +/* callers. Ignore my frame and my callers frame. */ + +#ifdef LINUX +# include +#endif + +#endif /* NEED_CALLINFO */ + +#if defined(GC_HAVE_BUILTIN_BACKTRACE) +# ifdef _MSC_VER +# include "private/msvc_dbg.h" +# else +# include +# endif +#endif + +#ifdef SAVE_CALL_CHAIN + +#if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \ + && defined(GC_HAVE_BUILTIN_BACKTRACE) + +#ifdef REDIRECT_MALLOC + /* Deal with possible malloc calls in backtrace by omitting */ + /* the infinitely recursing backtrace. */ +# ifdef THREADS + __thread /* If your compiler doesn't understand this */ + /* you could use something like pthread_getspecific. */ +# endif + GC_bool GC_in_save_callers = FALSE; +#endif + +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) +{ + void * tmp_info[NFRAMES + 1]; + int npcs, i; +# define IGNORE_FRAMES 1 + + /* We retrieve NFRAMES+1 pc values, but discard the first, since it */ + /* points to our own frame. */ +# ifdef REDIRECT_MALLOC + if (GC_in_save_callers) { + info[0].ci_pc = (word)(&GC_save_callers); + for (i = 1; i < NFRAMES; ++i) info[i].ci_pc = 0; + return; + } + GC_in_save_callers = TRUE; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + /* backtrace may call dl_iterate_phdr which is also */ + /* used by GC_register_dynamic_libraries, and */ + /* dl_iterate_phdr is not guaranteed to be reentrant. */ + + GC_STATIC_ASSERT(sizeof(struct callinfo) == sizeof(void *)); + npcs = backtrace((void **)tmp_info, NFRAMES + IGNORE_FRAMES); + if (npcs > IGNORE_FRAMES) + BCOPY(&tmp_info[IGNORE_FRAMES], info, + (npcs - IGNORE_FRAMES) * sizeof(void *)); + for (i = npcs - IGNORE_FRAMES; i < NFRAMES; ++i) info[i].ci_pc = 0; +# ifdef REDIRECT_MALLOC + GC_in_save_callers = FALSE; +# endif +} + +#else /* No builtin backtrace; do it ourselves */ + +#if (defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD)) && defined(SPARC) +# define FR_SAVFP fr_fp +# define FR_SAVPC fr_pc +#else +# define FR_SAVFP fr_savfp +# define FR_SAVPC fr_savpc +#endif + +#if defined(SPARC) && (defined(__arch64__) || defined(__sparcv9)) +# define BIAS 2047 +#else +# define BIAS 0 +#endif + +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) +{ + struct frame *frame; + struct frame *fp; + int nframes = 0; +# ifdef I386 + /* We assume this is turned on only with gcc as the compiler. */ + asm("movl %%ebp,%0" : "=r"(frame)); + fp = frame; +# else + frame = (struct frame *)GC_save_regs_in_stack(); + fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS); +#endif + + for (; !((word)fp HOTTER_THAN (word)frame) +# ifndef THREADS + && !((word)GC_stackbottom HOTTER_THAN (word)fp) +# elif defined(STACK_GROWS_UP) + && fp != NULL +# endif + && nframes < NFRAMES; + fp = (struct frame *)((long) fp -> FR_SAVFP + BIAS), nframes++) { +# if NARGS > 0 + int i; +# endif + + info[nframes].ci_pc = fp->FR_SAVPC; +# if NARGS > 0 + for (i = 0; i < NARGS; i++) { + info[nframes].ci_arg[i] = ~(fp->fr_arg[i]); + } +# endif /* NARGS > 0 */ + } + if (nframes < NFRAMES) info[nframes].ci_pc = 0; +} + +#endif /* No builtin backtrace */ + +#endif /* SAVE_CALL_CHAIN */ + +#ifdef NEED_CALLINFO + +/* Print info to stderr. We do NOT hold the allocation lock */ +GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]) +{ + int i; + static int reentry_count = 0; + DCL_LOCK_STATE; + + /* FIXME: This should probably use a different lock, so that we */ + /* become callable with or without the allocation lock. */ + LOCK(); + ++reentry_count; + UNLOCK(); + +# if NFRAMES == 1 + GC_err_printf("\tCaller at allocation:\n"); +# else + GC_err_printf("\tCall chain at allocation:\n"); +# endif + for (i = 0; i < NFRAMES; i++) { +# if defined(LINUX) && !defined(SMALL_CONFIG) + GC_bool stop = FALSE; +# endif + + if (0 == info[i].ci_pc) + break; +# if NARGS > 0 + { + int j; + + GC_err_printf("\t\targs: "); + for (j = 0; j < NARGS; j++) { + if (j != 0) GC_err_printf(", "); + GC_err_printf("%d (0x%X)", ~(info[i].ci_arg[j]), + ~(info[i].ci_arg[j])); + } + GC_err_printf("\n"); + } +# endif + if (reentry_count > 1) { + /* We were called during an allocation during */ + /* a previous GC_print_callers call; punt. */ + GC_err_printf("\t\t##PC##= 0x%lx\n", + (unsigned long)info[i].ci_pc); + continue; + } + { + char buf[40]; + char *name; +# if defined(GC_HAVE_BUILTIN_BACKTRACE) \ + && !defined(GC_BACKTRACE_SYMBOLS_BROKEN) + char **sym_name = + backtrace_symbols((void **)(&(info[i].ci_pc)), 1); + if (sym_name != NULL) { + name = sym_name[0]; + } else +# endif + /* else */ { + (void)snprintf(buf, sizeof(buf), "##PC##= 0x%lx", + (unsigned long)info[i].ci_pc); + buf[sizeof(buf) - 1] = '\0'; + name = buf; + } +# if defined(LINUX) && !defined(SMALL_CONFIG) + /* Try for a line number. */ + do { + FILE *pipe; +# define EXE_SZ 100 + static char exe_name[EXE_SZ]; +# define CMD_SZ 200 + char cmd_buf[CMD_SZ]; +# define RESULT_SZ 200 + static char result_buf[RESULT_SZ]; + size_t result_len; + char *old_preload; +# define PRELOAD_SZ 200 + char preload_buf[PRELOAD_SZ]; + static GC_bool found_exe_name = FALSE; + static GC_bool will_fail = FALSE; + + /* Try to get it via a hairy and expensive scheme. */ + /* First we get the name of the executable: */ + if (will_fail) + break; + if (!found_exe_name) { + int ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ); + + if (ret_code < 0 || ret_code >= EXE_SZ + || exe_name[0] != '/') { + will_fail = TRUE; /* Don't try again. */ + break; + } + exe_name[ret_code] = '\0'; + found_exe_name = TRUE; + } + /* Then we use popen to start addr2line -e */ + /* There are faster ways to do this, but hopefully this */ + /* isn't time critical. */ + (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) { + size_t old_len = strlen(old_preload); + if (old_len >= PRELOAD_SZ) { + will_fail = TRUE; + break; + } + BCOPY(old_preload, preload_buf, old_len + 1); + unsetenv ("LD_PRELOAD"); + } + pipe = popen(cmd_buf, "r"); + if (0 != old_preload + && 0 != setenv ("LD_PRELOAD", preload_buf, 0)) { + WARN("Failed to reset LD_PRELOAD\n", 0); + } + if (NULL == pipe) { + will_fail = TRUE; + break; + } + result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe); + (void)pclose(pipe); + if (0 == result_len) { + will_fail = TRUE; + break; + } + if (result_buf[result_len - 1] == '\n') --result_len; + result_buf[result_len] = 0; + if (result_buf[0] == '?' + || (result_buf[result_len-2] == ':' + && result_buf[result_len-1] == '0')) + break; + /* Get rid of embedded newline, if any. Test for "main" */ + { + char * nl = strchr(result_buf, '\n'); + if (nl != NULL + && (word)nl < (word)(result_buf + result_len)) { + *nl = ':'; + } + if (strncmp(result_buf, "main", + nl != NULL + ? (size_t)((word)nl /* a cppcheck workaround */ + - COVERT_DATAFLOW(result_buf)) + : result_len) == 0) { + stop = TRUE; + } + } + if (result_len < RESULT_SZ - 25) { + /* Add in hex address */ + (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'; + } +# if defined(CPPCHECK) + GC_noop1((unsigned char)name[0]); + /* name computed previously is discarded */ +# endif + name = result_buf; + } while (0); +# endif /* LINUX */ + GC_err_printf("\t\t%s\n", name); +# if defined(GC_HAVE_BUILTIN_BACKTRACE) \ + && !defined(GC_BACKTRACE_SYMBOLS_BROKEN) + if (sym_name != NULL) + free(sym_name); /* May call GC_[debug_]free; that's OK */ +# endif + } +# if defined(LINUX) && !defined(SMALL_CONFIG) + if (stop) + break; +# endif + } + LOCK(); + --reentry_count; + UNLOCK(); +} + +#endif /* NEED_CALLINFO */ + +#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + /* Dump /proc/self/maps to GC_stderr, to enable looking up names for */ + /* addresses in FIND_LEAK output. */ + void GC_print_address_map(void) + { + const char *maps = GC_get_maps(); + + GC_err_printf("---------- Begin address map ----------\n"); + GC_err_puts(maps); + GC_err_printf("---------- End address map ----------\n"); + } +#endif /* LINUX && ELF */ diff --git a/bdwgc/pthread_start.c b/bdwgc/pthread_start.c new file mode 100644 index 000000000..202dfeea8 --- /dev/null +++ b/bdwgc/pthread_start.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +/* We want to make sure that GC_thread_exit_proc() is unconditionally */ +/* invoked, even if the client is not compiled with -fexceptions, but */ +/* the GC is. The workaround is to put GC_inner_start_routine() in its */ +/* own file (pthread_start.c), and undefine __EXCEPTIONS in the GCC */ +/* case at the top of the file. FIXME: it's still unclear whether this */ +/* will actually cause the exit handler to be invoked last when */ +/* thread_exit is called (and if -fexceptions is used). */ +#if !defined(DONT_UNDEF_EXCEPTIONS) && defined(__GNUC__) && defined(__linux__) + /* We undefine __EXCEPTIONS to avoid using GCC __cleanup__ attribute. */ + /* The current NPTL implementation of pthread_cleanup_push uses */ + /* __cleanup__ attribute when __EXCEPTIONS is defined (-fexceptions). */ + /* The stack unwinding and cleanup with __cleanup__ attributes work */ + /* correctly when everything is compiled with -fexceptions, but it is */ + /* not the requirement for this library clients to use -fexceptions */ + /* everywhere. With __EXCEPTIONS undefined, the cleanup routines are */ + /* registered with __pthread_register_cancel thus should work anyway. */ +# undef __EXCEPTIONS +#endif + +#include "private/pthread_support.h" + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + +#include +#include + +/* Invoked from GC_start_routine(). */ +GC_INNER_PTHRSTART void * GC_CALLBACK GC_inner_start_routine( + struct GC_stack_base *sb, void *arg) +{ + void * (*start)(void *); + void * start_arg; + void * result; + volatile GC_thread me = + GC_start_rtn_prepare_thread(&start, &start_arg, sb, arg); + +# ifndef NACL + pthread_cleanup_push(GC_thread_exit_proc, me); +# endif + result = (*start)(start_arg); +# if defined(DEBUG_THREADS) && !defined(GC_PTHREAD_START_STANDALONE) + GC_log_printf("Finishing thread %p\n", (void *)pthread_self()); +# endif + me -> status = result; + GC_end_stubborn_change(me); /* cannot use GC_dirty */ + /* Cleanup acquires lock, ensuring that we can't exit while */ + /* a collection that thinks we're alive is trying to stop us. */ +# ifdef NACL + GC_thread_exit_proc((void *)me); +# else + pthread_cleanup_pop(1); +# endif + return result; +} + +#endif /* GC_PTHREADS */ diff --git a/bdwgc/pthread_stop_world.c b/bdwgc/pthread_stop_world.c new file mode 100644 index 000000000..2b4548905 --- /dev/null +++ b/bdwgc/pthread_stop_world.c @@ -0,0 +1,1449 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2008-2020 Ivan Maidanski + * + * 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. + */ + +#include "private/pthread_support.h" + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) && \ + !defined(GC_DARWIN_THREADS) && !defined(PLATFORM_STOP_WORLD) \ + && !defined(SN_TARGET_PSP2) + +#ifdef NACL +# include +# include +#elif defined(GC_OPENBSD_UTHREADS) +# include +#else +# include +# include +# include +# include /* for nanosleep() */ +# include +#endif /* !GC_OPENBSD_UTHREADS && !NACL */ + +#ifndef GC_OPENBSD_UTHREADS + GC_INLINE void GC_usleep(unsigned us) + { +# if defined(LINT2) || defined(THREAD_SANITIZER) + /* Workaround "waiting while holding a lock" static analyzer warning. */ + /* Workaround a rare hang in usleep() trying to acquire TSan Lock. */ + while (us-- > 0) + sched_yield(); /* pretending it takes 1us */ +# elif defined(CPPCHECK) /* || _POSIX_C_SOURCE >= 199309L */ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = us * 1000; + /* This requires _POSIX_TIMERS feature. */ + (void)nanosleep(&ts, NULL); +# else + usleep(us); +# endif + } +#endif /* !GC_OPENBSD_UTHREADS */ + +#ifdef NACL + + STATIC int GC_nacl_num_gc_threads = 0; + STATIC __thread int GC_nacl_thread_idx = -1; + STATIC volatile int GC_nacl_park_threads_now = 0; + STATIC volatile pthread_t GC_nacl_thread_parker = -1; + + GC_INNER __thread GC_thread GC_nacl_gc_thread_self = NULL; + + volatile int GC_nacl_thread_parked[MAX_NACL_GC_THREADS]; + int GC_nacl_thread_used[MAX_NACL_GC_THREADS]; + +#elif !defined(GC_OPENBSD_UTHREADS) + +#if (!defined(AO_HAVE_load_acquire) || !defined(AO_HAVE_store_release)) \ + && !defined(CPPCHECK) +# error AO_load_acquire and/or AO_store_release are missing; +# error please define AO_REQUIRE_CAS manually +#endif + +/* It's safe to call original pthread_sigmask() here. */ +#undef pthread_sigmask + +#ifdef DEBUG_THREADS +# ifndef NSIG +# ifdef CPPCHECK +# define NSIG 32 +# elif defined(MAXSIG) +# define NSIG (MAXSIG+1) +# elif defined(_NSIG) +# define NSIG _NSIG +# elif defined(__SIGRTMAX) +# define NSIG (__SIGRTMAX+1) +# else +# error define NSIG +# endif +# endif /* !NSIG */ + + void GC_print_sig_mask(void) + { + sigset_t blocked; + int i; + + if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) + ABORT("pthread_sigmask failed"); + for (i = 1; i < NSIG; i++) { + if (sigismember(&blocked, i)) + GC_printf("Signal blocked: %d\n", i); + } + } +#endif /* DEBUG_THREADS */ + +/* Remove the signals that we want to allow in thread stopping */ +/* handler from a set. */ +STATIC void GC_remove_allowed_signals(sigset_t *set) +{ + if (sigdelset(set, SIGINT) != 0 + || sigdelset(set, SIGQUIT) != 0 + || sigdelset(set, SIGABRT) != 0 + || sigdelset(set, SIGTERM) != 0) { + ABORT("sigdelset failed"); + } + +# ifdef MPROTECT_VDB + /* Handlers write to the thread structure, which is in the heap, */ + /* and hence can trigger a protection fault. */ + if (sigdelset(set, SIGSEGV) != 0 +# ifdef HAVE_SIGBUS + || sigdelset(set, SIGBUS) != 0 +# endif + ) { + ABORT("sigdelset failed"); + } +# endif +} + +static sigset_t suspend_handler_mask; + +#define THREAD_RESTARTED 0x1 + +STATIC volatile AO_t GC_stop_count = 0; + /* Incremented by two (not to alter */ + /* THREAD_RESTARTED bit) at the beginning of */ + /* GC_stop_world. */ + +STATIC volatile AO_t GC_world_is_stopped = FALSE; + /* FALSE ==> it is safe for threads to restart, */ + /* i.e. they will see another suspend signal */ + /* before they are expected to stop (unless */ + /* they have stopped voluntarily). */ + +#ifndef NO_RETRY_SIGNALS + /* Any platform could lose signals, so let's be conservative and */ + /* always enable signals retry logic. */ + STATIC GC_bool GC_retry_signals = TRUE; +#else + STATIC GC_bool GC_retry_signals = FALSE; +#endif + +/* + * We use signals to stop threads during GC. + * + * Suspended threads wait in signal handler for SIG_THR_RESTART. + * That's more portable than semaphores or condition variables. + * (We do use sem_post from a signal handler, but that should be portable.) + * + * The thread suspension signal SIG_SUSPEND is now defined in gc_priv.h. + * Note that we can't just stop a thread; we need it to save its stack + * pointer(s) and acknowledge. + */ +#ifndef SIG_THR_RESTART +# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) \ + || defined(GC_NETBSD_THREADS) || defined(GC_USESIGRT_SIGNALS) +# if defined(_SIGRTMIN) && !defined(CPPCHECK) +# define SIG_THR_RESTART _SIGRTMIN + 5 +# else +# define SIG_THR_RESTART SIGRTMIN + 5 +# endif +# elif defined(GC_FREEBSD_THREADS) && defined(__GLIBC__) +# define SIG_THR_RESTART (32+5) +# elif defined(GC_FREEBSD_THREADS) || defined(HURD) || defined(RTEMS) +# define SIG_THR_RESTART SIGUSR2 +# else +# define SIG_THR_RESTART SIGXCPU +# endif +#endif + +#define SIGNAL_UNSET (-1) + /* Since SIG_SUSPEND and/or SIG_THR_RESTART could represent */ + /* a non-constant expression (e.g., in case of SIGRTMIN), */ + /* actual signal numbers are determined by GC_stop_init() */ + /* unless manually set (before GC initialization). */ +STATIC int GC_sig_suspend = SIGNAL_UNSET; +STATIC int GC_sig_thr_restart = SIGNAL_UNSET; + +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 != SIGNAL_UNSET ? GC_sig_suspend : SIG_SUSPEND; +} + +GC_API int GC_CALL GC_get_thr_restart_signal(void) +{ + return GC_sig_thr_restart != SIGNAL_UNSET + ? GC_sig_thr_restart : SIG_THR_RESTART; +} + +#if defined(GC_EXPLICIT_SIGNALS_UNBLOCK) \ + || !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) + /* Some targets (e.g., Solaris) might require this to be called when */ + /* doing thread registering from the thread destructor. */ + GC_INNER void GC_unblock_gc_signals(void) + { + sigset_t set; + sigemptyset(&set); + GC_ASSERT(GC_sig_suspend != SIGNAL_UNSET); + GC_ASSERT(GC_sig_thr_restart != SIGNAL_UNSET); + sigaddset(&set, GC_sig_suspend); + sigaddset(&set, GC_sig_thr_restart); + if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) + ABORT("pthread_sigmask failed"); + } +#endif /* GC_EXPLICIT_SIGNALS_UNBLOCK */ + +STATIC sem_t GC_suspend_ack_sem; /* also used to acknowledge restart */ + +STATIC void GC_suspend_handler_inner(ptr_t dummy, void *context); + +#ifndef NO_SA_SIGACTION + STATIC void GC_suspend_handler(int sig, siginfo_t * info GC_ATTR_UNUSED, + void * context GC_ATTR_UNUSED) +#else + STATIC void GC_suspend_handler(int sig) +#endif +{ + int old_errno = errno; + + if (sig != GC_sig_suspend) { +# if defined(GC_FREEBSD_THREADS) + /* Workaround "deferred signal handling" bug in FreeBSD 9.2. */ + if (0 == sig) return; +# endif + ABORT("Bad signal in suspend_handler"); + } + +# if defined(E2K) || defined(HP_PA) || defined(IA64) || defined(M68K) \ + || defined(NO_SA_SIGACTION) + GC_with_callee_saves_pushed(GC_suspend_handler_inner, NULL); +# else + /* We believe that in this case the full context is already */ + /* in the signal handler frame. */ + GC_suspend_handler_inner(NULL, context); +# endif + errno = old_errno; +} + +#ifdef BASE_ATOMIC_OPS_EMULATED + /* The AO primitives emulated with locks cannot be used inside signal */ + /* handlers as this could cause a deadlock or a double lock. */ + /* The following "async" macro definitions are correct only for */ + /* an uniprocessor case and are provided for a test purpose. */ +# define ao_load_acquire_async(p) (*(p)) +# define ao_load_async(p) ao_load_acquire_async(p) +# define ao_store_release_async(p, v) (void)(*(p) = (v)) +# define ao_store_async(p, v) ao_store_release_async(p, v) +#else +# define ao_load_acquire_async(p) AO_load_acquire(p) +# define ao_load_async(p) AO_load(p) +# define ao_store_release_async(p, v) AO_store_release(p, v) +# define ao_store_async(p, v) AO_store(p, v) +#endif /* !BASE_ATOMIC_OPS_EMULATED */ + +/* The lookup here is safe, since this is done on behalf */ +/* of a thread which holds the allocation lock in order */ +/* to stop the world. Thus concurrent modification of the */ +/* data structure is impossible. Unfortunately, we have to */ +/* instruct TSan that the lookup is safe. */ +#ifdef THREAD_SANITIZER + /* The implementation of the function is the same as that of */ + /* GC_lookup_thread except for the attribute added here. */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_thread GC_lookup_thread_async(pthread_t id) + { + GC_thread p = GC_threads[THREAD_TABLE_INDEX(id)]; + + while (p != NULL && !THREAD_EQUAL(p->id, id)) + p = p->next; + return p; + } +#else +# define GC_lookup_thread_async GC_lookup_thread +#endif + +GC_INLINE void GC_store_stack_ptr(GC_thread me) +{ + /* There is no data race between the suspend handler (storing */ + /* stack_ptr) and GC_push_all_stacks (fetching stack_ptr) because */ + /* GC_push_all_stacks is executed after GC_stop_world exits and the */ + /* latter runs sem_wait repeatedly waiting for all the suspended */ + /* threads to call sem_post. Nonetheless, stack_ptr is stored (here) */ + /* and fetched (by GC_push_all_stacks) using the atomic primitives to */ + /* avoid the related TSan warning. */ +# ifdef SPARC + ao_store_async((volatile AO_t *)&me->stop_info.stack_ptr, + (AO_t)GC_save_regs_in_stack()); + /* TODO: regs saving already done by GC_with_callee_saves_pushed */ +# else +# ifdef IA64 + me -> backing_store_ptr = GC_save_regs_in_stack(); +# endif + ao_store_async((volatile AO_t *)&me->stop_info.stack_ptr, + (AO_t)GC_approx_sp()); +# endif +} + +STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED, + void * context GC_ATTR_UNUSED) +{ + pthread_t self = pthread_self(); + GC_thread me; +# ifdef E2K + ptr_t bs_lo; + size_t stack_size; +# endif + IF_CANCEL(int cancel_state;) +# ifdef GC_ENABLE_SUSPEND_THREAD + word suspend_cnt; +# endif + AO_t my_stop_count = ao_load_acquire_async(&GC_stop_count); + /* After the barrier, this thread should see */ + /* the actual content of GC_threads. */ + + DISABLE_CANCEL(cancel_state); + /* pthread_setcancelstate is not defined to be async-signal-safe. */ + /* But the glibc version appears to be in the absence of */ + /* asynchronous cancellation. And since this signal handler */ + /* to block on sigsuspend, which is both async-signal-safe */ + /* and a cancellation point, there seems to be no obvious way */ + /* out of it. In fact, it looks to me like an async-signal-safe */ + /* cancellation point is inherently a problem, unless there is */ + /* some way to disable cancellation in the handler. */ +# ifdef DEBUG_THREADS + GC_log_printf("Suspending %p\n", (void *)self); +# endif + GC_ASSERT(((word)my_stop_count & THREAD_RESTARTED) == 0); + + me = GC_lookup_thread_async(self); + +# ifdef GC_ENABLE_SUSPEND_THREAD +# if defined(__GNUC__) && !defined(__clang__) + /* Workaround "writing 8 bytes into a region of size 0" bogus */ + /* gcc warning (produced by gcc-12.2.0/aarch64, at least). */ + if (NULL == me) ABORT("Lookup self failed"); +# endif + suspend_cnt = (word)ao_load_async(&(me -> stop_info.ext_suspend_cnt)); +# endif + if (((word)me->stop_info.last_stop_count & ~(word)THREAD_RESTARTED) + == (word)my_stop_count +# ifdef GC_ENABLE_SUSPEND_THREAD + && (suspend_cnt & 1) == 0 +# endif + ) { + /* Duplicate signal. OK if we are retrying. */ + if (!GC_retry_signals) { + WARN("Duplicate suspend signal in thread %p\n", self); + } + RESTORE_CANCEL(cancel_state); + return; + } + GC_store_stack_ptr(me); +# ifdef E2K + GC_ASSERT(NULL == me -> backing_store_end); + GET_PROCEDURE_STACK_LOCAL(&bs_lo, &stack_size); + me -> backing_store_end = bs_lo; + me -> backing_store_ptr = bs_lo + stack_size; +# endif + + /* Tell the thread that wants to stop the world that this */ + /* thread has been stopped. Note that sem_post() is */ + /* the only async-signal-safe primitive in LinuxThreads. */ + sem_post(&GC_suspend_ack_sem); + ao_store_release_async(&me->stop_info.last_stop_count, my_stop_count); + + /* Wait until that thread tells us to restart by sending */ + /* 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 */ + /* primitive and expensive mechanism to wait until it's */ + /* really safe to proceed. Under normal circumstances, */ + /* this code should not be executed. */ + do { + sigsuspend(&suspend_handler_mask); + /* Iterate while not restarting the world or thread is suspended. */ + } while ((ao_load_acquire_async(&GC_world_is_stopped) + && ao_load_async(&GC_stop_count) == my_stop_count) +# ifdef GC_ENABLE_SUSPEND_THREAD + || ((suspend_cnt & 1) != 0 + && (word)ao_load_async(&(me -> stop_info.ext_suspend_cnt)) + == suspend_cnt) +# endif + ); + +# ifdef DEBUG_THREADS + GC_log_printf("Continuing %p\n", (void *)self); +# endif +# ifdef E2K + GC_ASSERT(me -> backing_store_end == bs_lo); + FREE_PROCEDURE_STACK_LOCAL(bs_lo, stack_size); + me -> backing_store_ptr = NULL; + me -> backing_store_end = NULL; +# endif +# ifndef GC_NETBSD_THREADS_WORKAROUND + if (GC_retry_signals) +# endif + { + /* If the RESTART signal loss is possible (though it should be */ + /* less likely than losing the SUSPEND signal as we do not do */ + /* much between the first sem_post and sigsuspend calls), more */ + /* handshaking is provided to work around it. */ + sem_post(&GC_suspend_ack_sem); +# ifdef GC_NETBSD_THREADS_WORKAROUND + if (GC_retry_signals) +# endif + { + /* Set the flag that the thread has been restarted. */ + ao_store_release_async(&me->stop_info.last_stop_count, + (AO_t)((word)my_stop_count | THREAD_RESTARTED)); + } + } + RESTORE_CANCEL(cancel_state); +} + +static void suspend_restart_barrier(int n_live_threads) +{ + int i; + + for (i = 0; i < n_live_threads; i++) { + while (0 != sem_wait(&GC_suspend_ack_sem)) { + /* On Linux, sem_wait is documented to always return zero. */ + /* But the documentation appears to be incorrect. */ + /* EINTR seems to happen with some versions of gdb. */ + if (errno != EINTR) + ABORT("sem_wait failed"); + } + } +# ifdef GC_ASSERTIONS + sem_getvalue(&GC_suspend_ack_sem, &i); + GC_ASSERT(0 == i); +# endif +} + +# define WAIT_UNIT 3000 /* us */ + +static int resend_lost_signals(int n_live_threads, + int (*suspend_restart_all)(void)) +{ +# define RETRY_INTERVAL 100000 /* us */ +# define RESEND_SIGNALS_LIMIT 150 + + if (n_live_threads > 0) { + unsigned long wait_usecs = 0; /* Total wait since retry. */ + int retry = 0; + int prev_sent = 0; + + for (;;) { + int ack_count; + + sem_getvalue(&GC_suspend_ack_sem, &ack_count); + if (ack_count == n_live_threads) + break; + if (wait_usecs > RETRY_INTERVAL) { + int newly_sent = suspend_restart_all(); + + if (newly_sent != prev_sent) { + retry = 0; /* restart the counter */ + } else if (++retry >= RESEND_SIGNALS_LIMIT) /* no progress */ + ABORT_ARG1("Signals delivery fails constantly", + " at GC #%lu", (unsigned long)GC_gc_no); + + GC_COND_LOG_PRINTF("Resent %d signals after timeout, retry: %d\n", + newly_sent, retry); + sem_getvalue(&GC_suspend_ack_sem, &ack_count); + if (newly_sent < n_live_threads - ack_count) { + WARN("Lost some threads while stopping or starting world?!\n", 0); + n_live_threads = ack_count + newly_sent; + } + prev_sent = newly_sent; + wait_usecs = 0; + } + GC_usleep(WAIT_UNIT); + wait_usecs += WAIT_UNIT; + } + } + return n_live_threads; +} + +#ifdef HAVE_CLOCK_GETTIME +# define TS_NSEC_ADD(ts, ns) \ + (ts.tv_nsec += (ns), \ + (void)(ts.tv_nsec >= 1000000L*1000 ? \ + (ts.tv_nsec -= 1000000L*1000, ts.tv_sec++, 0) : 0)) +#endif + +static void resend_lost_signals_retry(int n_live_threads, + int (*suspend_restart_all)(void)) +{ +# if defined(HAVE_CLOCK_GETTIME) && !defined(DONT_TIMEDWAIT_ACK_SEM) +# define TIMEOUT_BEFORE_RESEND 10000 /* us */ + struct timespec ts; + + if (n_live_threads > 0 && clock_gettime(CLOCK_REALTIME, &ts) == 0) { + int i; + + TS_NSEC_ADD(ts, TIMEOUT_BEFORE_RESEND * 1000); + /* First, try to wait for the semaphore with some timeout. */ + /* On failure, fallback to WAIT_UNIT pause and resend of the signal. */ + for (i = 0; i < n_live_threads; i++) { + if (0 != sem_timedwait(&GC_suspend_ack_sem, &ts)) + break; /* Wait timed out or any other error. */ + } + /* Update the count of threads to wait the ack from. */ + n_live_threads -= i; + } +# endif + n_live_threads = resend_lost_signals(n_live_threads, suspend_restart_all); + suspend_restart_barrier(n_live_threads); +} + +STATIC void GC_restart_handler(int sig) +{ +# if defined(DEBUG_THREADS) + int old_errno = errno; /* Preserve errno value. */ +# endif + + if (sig != GC_sig_thr_restart) + ABORT("Bad signal in restart handler"); + + /* + ** Note: even if we don't do anything useful here, + ** it would still be necessary to have a signal handler, + ** rather than ignoring the signals, otherwise + ** the signals will not be delivered at all, and + ** will thus not interrupt the sigsuspend() above. + */ +# ifdef DEBUG_THREADS + GC_log_printf("In GC_restart_handler for %p\n", (void *)pthread_self()); + errno = old_errno; +# endif +} + +# ifdef USE_TKILL_ON_ANDROID + EXTERN_C_BEGIN + extern int tkill(pid_t tid, int sig); /* from sys/linux-unistd.h */ + EXTERN_C_END +# define THREAD_SYSTEM_ID(t) (t)->kernel_id +# else +# define THREAD_SYSTEM_ID(t) (t)->id +# endif + +# ifndef RETRY_TKILL_EAGAIN_LIMIT +# define RETRY_TKILL_EAGAIN_LIMIT 16 +# endif + + static int raise_signal(GC_thread p, int sig) + { + int res; +# ifdef RETRY_TKILL_ON_EAGAIN + int retry; + + for (retry = 0; ; retry++) +# endif + { +# ifdef USE_TKILL_ON_ANDROID + int old_errno = errno; + + res = tkill(THREAD_SYSTEM_ID(p), sig); + if (res < 0) { + res = errno; + errno = old_errno; + } +# else + res = pthread_kill(THREAD_SYSTEM_ID(p), sig); +# endif +# ifdef RETRY_TKILL_ON_EAGAIN + if (res != EAGAIN || retry >= RETRY_TKILL_EAGAIN_LIMIT) break; + /* A temporal overflow of the real-time signal queue. */ + GC_usleep(WAIT_UNIT); +# endif + } + return res; + } + +# ifdef GC_ENABLE_SUSPEND_THREAD +# include +# include "javaxfc.h" /* to get the prototypes as extern "C" */ + + STATIC void GC_brief_async_signal_safe_sleep(void) + { + struct timeval tv; + tv.tv_sec = 0; +# if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + tv.tv_usec = 1000 * GC_TIME_LIMIT / 2; +# else + tv.tv_usec = 1000 * 50 / 2; +# endif + (void)select(0, 0, 0, 0, &tv); + } + + GC_INNER void GC_suspend_self_inner(GC_thread me, word suspend_cnt) { + IF_CANCEL(int cancel_state;) + + GC_ASSERT((suspend_cnt & 1) != 0); + DISABLE_CANCEL(cancel_state); +# ifdef DEBUG_THREADS + GC_log_printf("Suspend self: %p\n", (void *)(me -> id)); +# endif + while ((word)ao_load_acquire_async(&(me -> stop_info.ext_suspend_cnt)) + == suspend_cnt) { + /* TODO: Use sigsuspend() even for self-suspended threads. */ + GC_brief_async_signal_safe_sleep(); + } +# ifdef DEBUG_THREADS + GC_log_printf("Resume self: %p\n", (void *)(me -> id)); +# endif + RESTORE_CANCEL(cancel_state); + } + + GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + word suspend_cnt; + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + LOCK(); + t = GC_lookup_thread((pthread_t)thread); + if (NULL == t) { + UNLOCK(); + return; + } + suspend_cnt = (word)(t -> stop_info.ext_suspend_cnt); + if ((suspend_cnt & 1) != 0) /* already suspended? */ { + GC_ASSERT(!THREAD_EQUAL((pthread_t)thread, pthread_self())); + UNLOCK(); + return; + } + if ((t -> flags & FINISHED) != 0 || t -> thread_blocked) { + t -> stop_info.ext_suspend_cnt = (AO_t)(suspend_cnt | 1); /* suspend */ + /* Terminated but not joined yet, or in do-blocking state. */ + UNLOCK(); + return; + } + + if (THREAD_EQUAL((pthread_t)thread, pthread_self())) { + t -> stop_info.ext_suspend_cnt = (AO_t)(suspend_cnt | 1); + GC_with_callee_saves_pushed(GC_suspend_self_blocked, (ptr_t)t); + UNLOCK(); + return; + } + + DISABLE_CANCEL(cancel_state); + /* GC_suspend_thread is not a cancellation point. */ +# ifdef PARALLEL_MARK + /* Ensure we do not suspend a thread while it is rebuilding */ + /* a free list, otherwise such a dead-lock is possible: */ + /* thread 1 is blocked in GC_wait_for_reclaim holding */ + /* the allocation lock, thread 2 is suspended in */ + /* GC_reclaim_generic invoked from GC_generic_malloc_many */ + /* (with GC_fl_builder_count > 0), and thread 3 is blocked */ + /* acquiring the allocation lock in GC_resume_thread. */ + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + + if (GC_manual_vdb) { + /* See the relevant comment in GC_stop_world. */ + GC_acquire_dirty_lock(); + } + /* Else do not acquire the lock as the write fault handler might */ + /* be trying to acquire this lock too, and the suspend handler */ + /* execution is deferred until the write fault handler completes. */ + + /* Set the flag making the change visible to the signal handler. */ + AO_store_release(&(t -> stop_info.ext_suspend_cnt), + (AO_t)(suspend_cnt | 1)); + + /* TODO: Support GC_retry_signals (not needed for TSan) */ + switch (raise_signal(t, GC_sig_suspend)) { + /* ESRCH cannot happen as terminated threads are handled above. */ + case 0: + break; + default: + ABORT("pthread_kill failed"); + } + + /* Wait for the thread to complete threads table lookup and */ + /* stack_ptr assignment. */ + GC_ASSERT(GC_thr_initialized); + suspend_restart_barrier(1); + if (GC_manual_vdb) + GC_release_dirty_lock(); + RESTORE_CANCEL(cancel_state); + UNLOCK(); + } + + GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + DCL_LOCK_STATE; + + LOCK(); + t = GC_lookup_thread((pthread_t)thread); + if (t != NULL) { + word suspend_cnt = (word)(t -> stop_info.ext_suspend_cnt); + + if ((suspend_cnt & 1) != 0) /* is suspended? */ { + /* Mark the thread as not suspended - it will be resumed shortly. */ + AO_store(&(t -> stop_info.ext_suspend_cnt), (AO_t)(suspend_cnt + 1)); + + if ((t -> flags & FINISHED) == 0 && !(t -> thread_blocked)) { + int result = raise_signal(t, GC_sig_thr_restart); + + /* TODO: Support signal resending on GC_retry_signals */ + if (result != 0) + ABORT_ARG1("pthread_kill failed in GC_resume_thread", + ": errcode= %d", result); +# ifndef GC_NETBSD_THREADS_WORKAROUND + if (GC_retry_signals) +# endif + { + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + suspend_restart_barrier(1); + RESTORE_CANCEL(cancel_state); + } + } + } + } + UNLOCK(); + } + + GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + int is_suspended = 0; + DCL_LOCK_STATE; + + LOCK(); + t = GC_lookup_thread((pthread_t)thread); + if (t != NULL && (t -> stop_info.ext_suspend_cnt & 1) != 0) + is_suspended = (int)TRUE; + UNLOCK(); + return is_suspended; + } +# endif /* GC_ENABLE_SUSPEND_THREAD */ + +# undef ao_load_acquire_async +# undef ao_load_async +# undef ao_store_async +# undef ao_store_release_async +#endif /* !GC_OPENBSD_UTHREADS && !NACL */ + +/* We hold allocation lock. Should do exactly the right thing if the */ +/* world is stopped. Should not fail if it isn't. */ +GC_INNER void GC_push_all_stacks(void) +{ + GC_bool found_me = FALSE; + size_t nthreads = 0; + int i; + GC_thread p; + ptr_t lo; /* stack top (sp) */ + ptr_t hi; /* bottom */ +# if defined(E2K) || defined(IA64) + /* We also need to scan the register backing store. */ + ptr_t bs_lo, bs_hi; +# endif + struct GC_traced_stack_sect_s *traced_stack_sect; + pthread_t self = pthread_self(); + word total_size = 0; +# ifdef E2K + GC_bool is_stopped = (GC_bool)GC_world_is_stopped; +# endif + + if (!EXPECT(GC_thr_initialized, TRUE)) + GC_thr_init(); +# ifdef DEBUG_THREADS + GC_log_printf("Pushing stacks from thread %p\n", (void *)self); +# endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + 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 + lo = GC_save_regs_in_stack(); +# else + lo = GC_approx_sp(); +# ifdef IA64 + bs_hi = GC_save_regs_in_stack(); +# elif defined(E2K) + GC_ASSERT(NULL == p -> backing_store_end); + (void)GC_save_regs_in_stack(); + { + size_t stack_size; + GET_PROCEDURE_STACK_LOCAL(&bs_lo, &stack_size); + bs_hi = bs_lo + stack_size; + } +# endif +# endif + found_me = TRUE; + } else { + lo = (ptr_t)AO_load((volatile AO_t *)&p->stop_info.stack_ptr); +# ifdef IA64 + bs_hi = p -> backing_store_ptr; +# elif defined(E2K) + bs_lo = p -> backing_store_end; + bs_hi = p -> backing_store_ptr; +# endif + 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; +# ifdef IA64 + bs_lo = p -> backing_store_end; +# endif + } else { + /* The original stack. */ + hi = GC_stackbottom; +# ifdef IA64 + bs_lo = BACKING_STORE_BASE; +# endif + } +# ifdef DEBUG_THREADS +# ifdef STACK_GROWS_UP + GC_log_printf("Stack for thread %p is (%p,%p]\n", + (void *)(p -> id), (void *)hi, (void *)lo); +# else + GC_log_printf("Stack for thread %p is [%p,%p)\n", + (void *)(p -> id), (void *)lo, (void *)hi); +# endif +# endif + if (0 == lo) ABORT("GC_push_all_stacks: sp not set!"); + if (p->altstack != NULL && (word)p->altstack <= (word)lo + && (word)lo <= (word)p->altstack + p->altstack_size) { +# ifdef STACK_GROWS_UP + hi = p->altstack; +# else + hi = p->altstack + p->altstack_size; +# endif + /* FIXME: Need to scan the normal stack too, but how ? */ + } +# ifdef STACKPTR_CORRECTOR_AVAILABLE + if (GC_sp_corrector != 0) + GC_sp_corrector((void **)&lo, (void *)(p -> id)); +# endif + GC_push_all_stack_sections(lo, hi, traced_stack_sect); +# ifdef STACK_GROWS_UP + total_size += lo - hi; +# else + total_size += hi - lo; /* lo <= hi */ +# endif +# ifdef NACL + /* Push reg_storage as roots, this will cover the reg context. */ + GC_push_all_stack((ptr_t)p -> stop_info.reg_storage, + (ptr_t)(p -> stop_info.reg_storage + NACL_GC_REG_STORAGE_SIZE)); + total_size += NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t); +# endif +# ifdef E2K + if (!is_stopped && !p->thread_blocked +# ifdef GC_ENABLE_SUSPEND_THREAD + && (p -> stop_info.ext_suspend_cnt & 1) == 0 +# endif + && !THREAD_EQUAL(p -> id, self)) + continue; /* procedure stack buffer has already been freed */ +# endif +# if defined(E2K) || defined(IA64) +# ifdef DEBUG_THREADS + GC_log_printf("Reg stack for thread %p is [%p,%p)\n", + (void *)(p -> id), (void *)bs_lo, (void *)bs_hi); +# endif + GC_ASSERT(bs_lo != NULL && bs_hi != NULL); + /* FIXME: This (if p->id==self) may add an unbounded number of */ + /* entries, and hence overflow the mark stack, which is bad. */ + GC_push_all_register_sections(bs_lo, bs_hi, + THREAD_EQUAL(p -> id, self), + traced_stack_sect); + total_size += bs_hi - bs_lo; /* bs_lo <= bs_hi */ +# endif +# ifdef E2K + if (THREAD_EQUAL(p -> id, self)) + FREE_PROCEDURE_STACK_LOCAL(bs_lo, (size_t)(bs_hi - bs_lo)); +# endif + } + } + 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; +} + +#ifdef DEBUG_THREADS + /* There seems to be a very rare thread stopping problem. To help us */ + /* debug that, we save the ids of the stopping thread. */ + pthread_t GC_stopping_thread; + int GC_stopping_pid = 0; +#endif + +/* We hold the allocation lock. Suspend all threads that might */ +/* still be running. Return the number of suspend signals that */ +/* were sent. */ +STATIC int GC_suspend_all(void) +{ + int n_live_threads = 0; + int i; +# ifndef NACL + GC_thread p; +# ifndef GC_OPENBSD_UTHREADS + int result; +# endif + pthread_t self = pthread_self(); + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (!THREAD_EQUAL(p -> id, self)) { + if ((p -> flags & FINISHED) != 0) continue; + if (p -> thread_blocked) /* Will wait */ continue; +# ifndef GC_OPENBSD_UTHREADS +# ifdef GC_ENABLE_SUSPEND_THREAD + if ((p -> stop_info.ext_suspend_cnt & 1) != 0) continue; +# endif + if (AO_load(&p->stop_info.last_stop_count) == GC_stop_count) + continue; /* matters only if GC_retry_signals */ + n_live_threads++; +# endif +# ifdef DEBUG_THREADS + GC_log_printf("Sending suspend signal to %p\n", (void *)p->id); +# endif + +# ifdef GC_OPENBSD_UTHREADS + { + stack_t stack; + + GC_acquire_dirty_lock(); + if (pthread_suspend_np(p -> id) != 0) + ABORT("pthread_suspend_np failed"); + GC_release_dirty_lock(); + 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; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, + (void *)p->id); + } +# else + /* The synchronization between GC_dirty (based on */ + /* test-and-set) and the signal-based thread suspension */ + /* is performed in GC_stop_world because */ + /* GC_release_dirty_lock cannot be called before */ + /* acknowledging the thread is really suspended. */ + result = raise_signal(p, GC_sig_suspend); + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, + (void *)(word)THREAD_SYSTEM_ID(p)); + /* Note: thread id might be truncated. */ + break; + default: + ABORT_ARG1("pthread_kill failed at suspend", + ": errcode= %d", result); + } +# endif + } + } + } + +# else /* NACL */ +# ifndef NACL_PARK_WAIT_USEC +# define NACL_PARK_WAIT_USEC 100 /* us */ +# endif + unsigned long num_sleeps = 0; + +# ifdef DEBUG_THREADS + GC_log_printf("pthread_stop_world: number of threads: %d\n", + GC_nacl_num_gc_threads - 1); +# endif + GC_nacl_thread_parker = pthread_self(); + GC_nacl_park_threads_now = 1; + + if (GC_manual_vdb) + GC_acquire_dirty_lock(); + for (;;) { + int num_threads_parked = 0; + int num_used = 0; + + /* Check the 'parked' flag for each thread the GC knows about. */ + for (i = 0; i < MAX_NACL_GC_THREADS + && num_used < GC_nacl_num_gc_threads; i++) { + if (GC_nacl_thread_used[i] == 1) { + num_used++; + if (GC_nacl_thread_parked[i] == 1) { + num_threads_parked++; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, (void *)(word)i); + } + } + } + /* -1 for the current thread. */ + if (num_threads_parked >= GC_nacl_num_gc_threads - 1) + break; +# ifdef DEBUG_THREADS + GC_log_printf("Sleep waiting for %d threads to park...\n", + GC_nacl_num_gc_threads - num_threads_parked - 1); +# endif + GC_usleep(NACL_PARK_WAIT_USEC); + if (++num_sleeps > (1000 * 1000) / NACL_PARK_WAIT_USEC) { + WARN("GC appears stalled waiting for %" WARN_PRIdPTR + " threads to park...\n", + GC_nacl_num_gc_threads - num_threads_parked - 1); + num_sleeps = 0; + } + } + if (GC_manual_vdb) + GC_release_dirty_lock(); +# endif /* NACL */ + return n_live_threads; +} + +GC_INNER void GC_stop_world(void) +{ +# if !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) + int n_live_threads; +# endif + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_stopping_thread = pthread_self(); + GC_stopping_pid = getpid(); + GC_log_printf("Stopping the world from %p\n", (void *)GC_stopping_thread); +# endif + + /* Make sure all free list construction has stopped before we start. */ + /* No new construction can start, since free list construction is */ + /* required to acquire and release the GC lock before it starts, */ + /* and we have the lock. */ +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + +# if defined(GC_OPENBSD_UTHREADS) || defined(NACL) + (void)GC_suspend_all(); +# else + AO_store(&GC_stop_count, + (AO_t)((word)GC_stop_count + (THREAD_RESTARTED+1))); + /* Only concurrent reads are possible. */ + if (GC_manual_vdb) { + GC_acquire_dirty_lock(); + /* The write fault handler cannot be called if GC_manual_vdb */ + /* (thus double-locking should not occur in */ + /* async_set_pht_entry_from_index based on test-and-set). */ + } + AO_store_release(&GC_world_is_stopped, TRUE); + n_live_threads = GC_suspend_all(); + if (GC_retry_signals) { + resend_lost_signals_retry(n_live_threads, GC_suspend_all); + } else { + suspend_restart_barrier(n_live_threads); + } + if (GC_manual_vdb) + GC_release_dirty_lock(); /* cannot be done in GC_suspend_all */ +# endif + +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("World stopped from %p\n", (void *)pthread_self()); + GC_stopping_thread = 0; +# endif +} + +#ifdef NACL +# if defined(__x86_64__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push %rbx"); \ + __asm__ __volatile__ ("push %rbp"); \ + __asm__ __volatile__ ("push %r12"); \ + __asm__ __volatile__ ("push %r13"); \ + __asm__ __volatile__ ("push %r14"); \ + __asm__ __volatile__ ("push %r15"); \ + __asm__ __volatile__ ("mov %%esp, %0" \ + : "=m" (GC_nacl_gc_thread_self->stop_info.stack_ptr)); \ + BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr, \ + GC_nacl_gc_thread_self->stop_info.reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); \ + __asm__ __volatile__ ("naclasp $48, %r15"); \ + } while (0) +# elif defined(__i386__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push %ebx"); \ + __asm__ __volatile__ ("push %ebp"); \ + __asm__ __volatile__ ("push %esi"); \ + __asm__ __volatile__ ("push %edi"); \ + __asm__ __volatile__ ("mov %%esp, %0" \ + : "=m" (GC_nacl_gc_thread_self->stop_info.stack_ptr)); \ + BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr, \ + GC_nacl_gc_thread_self->stop_info.reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t));\ + __asm__ __volatile__ ("add $16, %esp"); \ + } while (0) +# elif defined(__arm__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push {r4-r8,r10-r12,lr}"); \ + __asm__ __volatile__ ("mov r0, %0" \ + : : "r" (&GC_nacl_gc_thread_self->stop_info.stack_ptr)); \ + __asm__ __volatile__ ("bic r0, r0, #0xc0000000"); \ + __asm__ __volatile__ ("str sp, [r0]"); \ + BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr, \ + GC_nacl_gc_thread_self->stop_info.reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); \ + __asm__ __volatile__ ("add sp, sp, #40"); \ + __asm__ __volatile__ ("bic sp, sp, #0xc0000000"); \ + } while (0) +# else +# error TODO Please port NACL_STORE_REGS +# endif + + GC_API_OSCALL void nacl_pre_syscall_hook(void) + { + if (GC_nacl_thread_idx != -1) { + NACL_STORE_REGS(); + GC_nacl_gc_thread_self->stop_info.stack_ptr = GC_approx_sp(); + GC_nacl_thread_parked[GC_nacl_thread_idx] = 1; + } + } + + GC_API_OSCALL void __nacl_suspend_thread_if_needed(void) + { + if (GC_nacl_park_threads_now) { + pthread_t self = pthread_self(); + + /* Don't try to park the thread parker. */ + if (GC_nacl_thread_parker == self) + return; + + /* This can happen when a thread is created outside of the GC */ + /* system (wthread mostly). */ + if (GC_nacl_thread_idx < 0) + return; + + /* If it was already 'parked', we're returning from a syscall, */ + /* 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 = GC_approx_sp(); + } + GC_nacl_thread_parked[GC_nacl_thread_idx] = 1; + while (GC_nacl_park_threads_now) { + /* Just spin. */ + } + GC_nacl_thread_parked[GC_nacl_thread_idx] = 0; + + /* Clear out the reg storage for next suspend. */ + BZERO(GC_nacl_gc_thread_self->stop_info.reg_storage, + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); + } + } + + GC_API_OSCALL void nacl_post_syscall_hook(void) + { + /* Calling __nacl_suspend_thread_if_needed right away should */ + /* guarantee we don't mutate the GC set. */ + __nacl_suspend_thread_if_needed(); + if (GC_nacl_thread_idx != -1) { + GC_nacl_thread_parked[GC_nacl_thread_idx] = 0; + } + } + + STATIC GC_bool GC_nacl_thread_parking_inited = FALSE; + STATIC pthread_mutex_t GC_nacl_thread_alloc_lock = PTHREAD_MUTEX_INITIALIZER; + + struct nacl_irt_blockhook { + int (*register_block_hooks)(void (*pre)(void), void (*post)(void)); + }; + + EXTERN_C_BEGIN + extern size_t nacl_interface_query(const char *interface_ident, + void *table, size_t tablesize); + EXTERN_C_END + + GC_INNER void GC_nacl_initialize_gc_thread(void) + { + int i; + static struct nacl_irt_blockhook gc_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)); + BZERO(GC_nacl_thread_used, sizeof(GC_nacl_thread_used)); + /* TODO: replace with public 'register hook' function when */ + /* available from glibc. */ + nacl_interface_query("nacl-irt-blockhook-0.1", + &gc_hook, sizeof(gc_hook)); + gc_hook.register_block_hooks(nacl_pre_syscall_hook, + nacl_post_syscall_hook); + GC_nacl_thread_parking_inited = TRUE; + } + GC_ASSERT(GC_nacl_num_gc_threads <= MAX_NACL_GC_THREADS); + for (i = 0; i < MAX_NACL_GC_THREADS; i++) { + if (GC_nacl_thread_used[i] == 0) { + GC_nacl_thread_used[i] = 1; + GC_nacl_thread_idx = i; + GC_nacl_num_gc_threads++; + break; + } + } + pthread_mutex_unlock(&GC_nacl_thread_alloc_lock); + } + + GC_INNER void GC_nacl_shutdown_gc_thread(void) + { + pthread_mutex_lock(&GC_nacl_thread_alloc_lock); + GC_ASSERT(GC_nacl_thread_idx >= 0); + GC_ASSERT(GC_nacl_thread_idx < MAX_NACL_GC_THREADS); + GC_ASSERT(GC_nacl_thread_used[GC_nacl_thread_idx] != 0); + GC_nacl_thread_used[GC_nacl_thread_idx] = 0; + GC_nacl_thread_idx = -1; + GC_nacl_num_gc_threads--; + pthread_mutex_unlock(&GC_nacl_thread_alloc_lock); + } + +#else /* !NACL */ + + /* Restart all threads that were suspended by the collector. */ + /* Return the number of restart signals that were sent. */ + STATIC int GC_restart_all(void) + { + int n_live_threads = 0; + int i; + pthread_t self = pthread_self(); + GC_thread p; +# ifndef GC_OPENBSD_UTHREADS + int result; +# endif + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> next) { + if (!THREAD_EQUAL(p -> id, self)) { + if ((p -> flags & FINISHED) != 0) continue; + if (p -> thread_blocked) continue; +# ifndef GC_OPENBSD_UTHREADS +# ifdef GC_ENABLE_SUSPEND_THREAD + if ((p -> stop_info.ext_suspend_cnt & 1) != 0) continue; +# endif + if (GC_retry_signals + && AO_load(&p->stop_info.last_stop_count) + == (AO_t)((word)GC_stop_count | THREAD_RESTARTED)) + continue; /* The thread has been restarted. */ + n_live_threads++; +# endif +# ifdef DEBUG_THREADS + GC_log_printf("Sending restart signal to %p\n", (void *)p->id); +# endif +# ifdef GC_OPENBSD_UTHREADS + if (pthread_resume_np(p -> id) != 0) + ABORT("pthread_resume_np failed"); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)p->id); +# else + result = raise_signal(p, GC_sig_thr_restart); + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, + (void *)(word)THREAD_SYSTEM_ID(p)); + break; + default: + ABORT_ARG1("pthread_kill failed at resume", + ": errcode= %d", result); + } +# endif + } + } + } + return n_live_threads; + } +#endif /* !NACL */ + +/* Caller holds allocation lock, and has held it continuously since */ +/* the world stopped. */ +GC_INNER void GC_start_world(void) +{ +# ifndef NACL + int n_live_threads; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_log_printf("World starting\n"); +# endif +# ifndef GC_OPENBSD_UTHREADS + AO_store_release(&GC_world_is_stopped, FALSE); + /* The updated value should now be visible to the */ + /* signal handler (note that pthread_kill is not on */ + /* the list of functions which synchronize memory). */ +# endif + n_live_threads = GC_restart_all(); +# ifdef GC_OPENBSD_UTHREADS + (void)n_live_threads; +# else + if (GC_retry_signals) { + resend_lost_signals_retry(n_live_threads, GC_restart_all); + } /* else */ +# ifdef GC_NETBSD_THREADS_WORKAROUND + else { + suspend_restart_barrier(n_live_threads); + } +# endif +# endif +# ifdef DEBUG_THREADS + GC_log_printf("World started\n"); +# endif +# else /* NACL */ +# ifdef DEBUG_THREADS + GC_log_printf("World starting...\n"); +# endif + GC_nacl_park_threads_now = 0; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, NULL); + /* TODO: Send event for every unsuspended thread. */ +# endif +} + +GC_INNER void GC_stop_init(void) +{ +# if !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) + struct sigaction act; + char *str; + + if (SIGNAL_UNSET == GC_sig_suspend) + GC_sig_suspend = SIG_SUSPEND; + if (SIGNAL_UNSET == GC_sig_thr_restart) + GC_sig_thr_restart = SIG_THR_RESTART; + if (GC_sig_suspend == GC_sig_thr_restart) + ABORT("Cannot use same signal for thread suspend and resume"); + + if (sem_init(&GC_suspend_ack_sem, GC_SEM_INIT_PSHARED, 0) != 0) + ABORT("sem_init failed"); + +# ifdef SA_RESTART + act.sa_flags = SA_RESTART +# else + act.sa_flags = 0 +# endif +# ifndef NO_SA_SIGACTION + | SA_SIGINFO +# endif + ; + if (sigfillset(&act.sa_mask) != 0) { + ABORT("sigfillset failed"); + } +# ifdef GC_RTEMS_PTHREADS + if(sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL) != 0) { + ABORT("sigprocmask failed"); + } +# endif + GC_remove_allowed_signals(&act.sa_mask); + /* GC_sig_thr_restart is set in the resulting mask. */ + /* It is unmasked by the handler when necessary. */ +# ifndef NO_SA_SIGACTION + act.sa_sigaction = GC_suspend_handler; +# else + act.sa_handler = GC_suspend_handler; +# endif + /* act.sa_restorer is deprecated and should not be initialized. */ + if (sigaction(GC_sig_suspend, &act, NULL) != 0) { + ABORT("Cannot set SIG_SUSPEND handler"); + } + +# ifndef NO_SA_SIGACTION + act.sa_flags &= ~SA_SIGINFO; +# endif + act.sa_handler = GC_restart_handler; + if (sigaction(GC_sig_thr_restart, &act, NULL) != 0) { + ABORT("Cannot set SIG_THR_RESTART handler"); + } + + /* 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, GC_sig_thr_restart) != 0) + ABORT("sigdelset failed"); + + /* Override the default value of GC_retry_signals. */ + str = GETENV("GC_RETRY_SIGNALS"); + if (str != NULL) { + if (*str == '0' && *(str + 1) == '\0') { + /* Do not retry if the environment variable is set to "0". */ + GC_retry_signals = FALSE; + } else { + GC_retry_signals = TRUE; + } + } + if (GC_retry_signals) { + GC_COND_LOG_PRINTF( + "Will retry suspend and restart signals if necessary\n"); + } +# ifndef NO_SIGNALS_UNBLOCK_IN_MAIN + /* Explicitly unblock the signals once before new threads creation. */ + GC_unblock_gc_signals(); +# endif +# endif /* !GC_OPENBSD_UTHREADS && !NACL */ +} + +#endif /* GC_PTHREADS && !GC_DARWIN_THREADS && !GC_WIN32_THREADS */ diff --git a/bdwgc/pthread_support.c b/bdwgc/pthread_support.c new file mode 100644 index 000000000..48cd28044 --- /dev/null +++ b/bdwgc/pthread_support.c @@ -0,0 +1,2646 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2021 Ivan Maidanski + * + * 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. + */ + +#include "private/pthread_support.h" + +/* + * Support code originally for LinuxThreads, the clone()-based kernel + * thread package for Linux which is included in libc6. + * + * This code no doubt makes some assumptions beyond what is + * guaranteed by the pthread standard, though it now does + * very little of that. It now also supports NPTL, and many + * other Posix thread implementations. We are trying to merge + * all flavors of pthread support code into this file. + */ + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + +# include +# include +# include +# include +# include +# include +# if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) +# if !defined(GC_RTEMS_PTHREADS) +# include +# endif +# include +# include +# include +# include +# endif +# include + +# include "gc_inline.h" + +#if defined(GC_DARWIN_THREADS) +# include "private/darwin_semaphore.h" +#else +# include +#endif /* !GC_DARWIN_THREADS */ + +#if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) +# include +#endif /* GC_DARWIN_THREADS */ + +#if defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) +# include +# include +#endif /* GC_NETBSD_THREADS */ + +/* Allocator lock definitions. */ +#if !defined(USE_SPIN_LOCK) + GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; +#endif + +#ifdef GC_ASSERTIONS + GC_INNER unsigned long GC_lock_holder = NO_THREAD; + /* Used only for assertions. */ +#endif + +#if defined(GC_DGUX386_THREADS) +# include +# include + /* sem_t is an uint in DG/UX */ + typedef unsigned int sem_t; +#endif /* GC_DGUX386_THREADS */ + +/* Undefine macros used to redirect pthread primitives. */ +# undef pthread_create +# ifndef GC_NO_PTHREAD_SIGMASK +# undef pthread_sigmask +# endif +# ifndef GC_NO_PTHREAD_CANCEL +# undef pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# undef pthread_exit +# endif +# undef pthread_join +# undef pthread_detach +# if defined(GC_OSF1_THREADS) && defined(_PTHREAD_USE_MANGLED_NAMES_) \ + && !defined(_PTHREAD_USE_PTDNAM_) + /* Restore the original mangled names on Tru64 UNIX. */ +# define pthread_create __pthread_create +# define pthread_join __pthread_join +# define pthread_detach __pthread_detach +# ifndef GC_NO_PTHREAD_CANCEL +# define pthread_cancel __pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# define pthread_exit __pthread_exit +# endif +# endif + +#ifdef GC_USE_LD_WRAP +# define WRAP_FUNC(f) __wrap_##f +# define REAL_FUNC(f) __real_##f + int REAL_FUNC(pthread_create)(pthread_t *, + GC_PTHREAD_CREATE_CONST pthread_attr_t *, + void *(*start_routine)(void *), void *); + int REAL_FUNC(pthread_join)(pthread_t, void **); + int REAL_FUNC(pthread_detach)(pthread_t); +# ifndef GC_NO_PTHREAD_SIGMASK + int REAL_FUNC(pthread_sigmask)(int, const sigset_t *, sigset_t *); +# endif +# ifndef GC_NO_PTHREAD_CANCEL + int REAL_FUNC(pthread_cancel)(pthread_t); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + void REAL_FUNC(pthread_exit)(void *) GC_PTHREAD_EXIT_ATTRIBUTE; +# endif +#else +# ifdef GC_USE_DLOPEN_WRAP +# include +# define WRAP_FUNC(f) f +# 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, 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 *, + void * (*)(void *), void *); + static GC_pthread_create_t REAL_FUNC(pthread_create); +# ifndef GC_NO_PTHREAD_SIGMASK + typedef int (* GC_pthread_sigmask_t)(int, const sigset_t *, + sigset_t *); + static GC_pthread_sigmask_t REAL_FUNC(pthread_sigmask); +# endif + typedef int (* GC_pthread_join_t)(pthread_t, void **); + static GC_pthread_join_t REAL_FUNC(pthread_join); + typedef int (* GC_pthread_detach_t)(pthread_t); + static GC_pthread_detach_t REAL_FUNC(pthread_detach); +# ifndef GC_NO_PTHREAD_CANCEL + typedef int (* GC_pthread_cancel_t)(pthread_t); + static GC_pthread_cancel_t REAL_FUNC(pthread_cancel); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + typedef void (* GC_pthread_exit_t)(void *) GC_PTHREAD_EXIT_ATTRIBUTE; + static GC_pthread_exit_t REAL_FUNC(pthread_exit); +# endif +# else +# define WRAP_FUNC(f) GC_##f +# if !defined(GC_DGUX386_THREADS) +# define REAL_FUNC(f) f +# else /* GC_DGUX386_THREADS */ +# define REAL_FUNC(f) __d10_##f +# endif /* GC_DGUX386_THREADS */ +# endif +#endif + +#if defined(GC_USE_LD_WRAP) || defined(GC_USE_DLOPEN_WRAP) + /* Define GC_ functions as aliases for the plain ones, which will */ + /* be intercepted. This allows files which include gc.h, and hence */ + /* generate references to the GC_ symbols, to see the right symbols. */ + GC_API int GC_pthread_create(pthread_t * t, + GC_PTHREAD_CREATE_CONST pthread_attr_t *a, + void * (* fn)(void *), void * arg) + { + return pthread_create(t, a, fn, arg); + } + +# ifndef GC_NO_PTHREAD_SIGMASK + GC_API int GC_pthread_sigmask(int how, const sigset_t *mask, + sigset_t *old) + { + return pthread_sigmask(how, mask, old); + } +# endif /* !GC_NO_PTHREAD_SIGMASK */ + + GC_API int GC_pthread_join(pthread_t t, void **res) + { + return pthread_join(t, res); + } + + GC_API int GC_pthread_detach(pthread_t t) + { + return pthread_detach(t); + } + +# ifndef GC_NO_PTHREAD_CANCEL + GC_API int GC_pthread_cancel(pthread_t t) + { + return pthread_cancel(t); + } +# endif /* !GC_NO_PTHREAD_CANCEL */ + +# ifdef GC_HAVE_PTHREAD_EXIT + GC_API GC_PTHREAD_EXIT_ATTRIBUTE void GC_pthread_exit(void *retval) + { + pthread_exit(retval); + } +# endif +#endif /* Linker-based interception. */ + +#ifdef GC_USE_DLOPEN_WRAP + STATIC GC_bool GC_syms_initialized = FALSE; + + STATIC void GC_init_real_syms(void) + { + void *dl_handle; + + if (GC_syms_initialized) return; +# ifdef RTLD_NEXT + dl_handle = RTLD_NEXT; +# else + dl_handle = dlopen("libpthread.so.0", RTLD_LAZY); + if (NULL == dl_handle) { + dl_handle = dlopen("libpthread.so", RTLD_LAZY); /* without ".0" */ + } + if (NULL == dl_handle) ABORT("Couldn't open libpthread"); +# endif + REAL_FUNC(pthread_create) = (GC_pthread_create_t)(word) + dlsym(dl_handle, "pthread_create"); +# ifdef RTLD_NEXT + if (REAL_FUNC(pthread_create) == 0) + ABORT("pthread_create not found" + " (probably -lgc is specified after -lpthread)"); +# endif +# ifndef GC_NO_PTHREAD_SIGMASK + REAL_FUNC(pthread_sigmask) = (GC_pthread_sigmask_t)(word) + dlsym(dl_handle, "pthread_sigmask"); +# endif + REAL_FUNC(pthread_join) = (GC_pthread_join_t)(word) + dlsym(dl_handle, "pthread_join"); + REAL_FUNC(pthread_detach) = (GC_pthread_detach_t)(word) + dlsym(dl_handle, "pthread_detach"); +# ifndef GC_NO_PTHREAD_CANCEL + REAL_FUNC(pthread_cancel) = (GC_pthread_cancel_t)(word) + dlsym(dl_handle, "pthread_cancel"); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + REAL_FUNC(pthread_exit) = (GC_pthread_exit_t)(word) + dlsym(dl_handle, "pthread_exit"); +# endif + GC_syms_initialized = TRUE; + } + +# define INIT_REAL_SYMS() if (EXPECT(GC_syms_initialized, TRUE)) {} \ + else GC_init_real_syms() +#else +# define INIT_REAL_SYMS() (void)0 +#endif + +static GC_bool parallel_initialized = FALSE; + +#ifndef GC_ALWAYS_MULTITHREADED + GC_INNER GC_bool GC_need_to_lock = FALSE; +#endif + +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 ... */ + +#ifdef THREAD_LOCAL_ALLOC + /* We must explicitly mark ptrfree and gcj free lists, since the free */ + /* list links wouldn't otherwise be found. We also set them in the */ + /* normal free lists, since that involves touching less memory than */ + /* if we scanned them normally. */ + GC_INNER void GC_mark_thread_local_free_lists(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; 0 != p; p = p -> next) { + if (!(p -> flags & FINISHED)) + GC_mark_thread_local_fls_for(&(p->tlfs)); + } + } + } + +# if defined(GC_ASSERTIONS) + /* Check that all thread-local free-lists are completely marked. */ + /* Also check that thread-specific-data structures are marked. */ + void GC_check_tls(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; 0 != p; p = p -> next) { + if (!(p -> flags & FINISHED)) + GC_check_tls_for(&(p->tlfs)); + } + } +# if defined(USE_CUSTOM_SPECIFIC) + if (GC_thread_key != 0) + GC_check_tsd_marks(GC_thread_key); +# endif + } +# endif /* GC_ASSERTIONS */ + +#endif /* THREAD_LOCAL_ALLOC */ + +# ifndef MAX_MARKERS +# define MAX_MARKERS 16 +# endif + +#ifdef PARALLEL_MARK + +static ptr_t marker_sp[MAX_MARKERS - 1] = {0}; +#ifdef IA64 + static ptr_t marker_bsp[MAX_MARKERS - 1] = {0}; +#endif + +#if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + static mach_port_t marker_mach_threads[MAX_MARKERS - 1] = {0}; + + /* Used only by GC_suspend_thread_list(). */ + GC_INNER GC_bool GC_is_mach_marker(thread_act_t thread) + { + int i; + for (i = 0; i < GC_markers_m1; i++) { + if (marker_mach_threads[i] == thread) + return TRUE; + } + return FALSE; + } +#endif /* GC_DARWIN_THREADS */ + +#ifdef HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG /* NetBSD */ + static void set_marker_thread_name(unsigned id) + { + int err = pthread_setname_np(pthread_self(), "GC-marker-%zu", + (void*)(size_t)id); + if (err != 0) + WARN("pthread_setname_np failed, errno= %" WARN_PRIdPTR "\n", + (signed_word)err); + } +#elif defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) \ + || defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) + static void set_marker_thread_name(unsigned id) + { + char name_buf[16]; /* pthread_setname_np may fail for longer names */ + int len = sizeof("GC-marker-") - 1; + + /* Compose the name manually as snprintf may be unavailable or */ + /* "%u directive output may be truncated" warning may occur. */ + BCOPY("GC-marker-", name_buf, len); + if (id >= 10) + name_buf[len++] = (char)('0' + (id / 10) % 10); + name_buf[len] = (char)('0' + id % 10); + name_buf[len + 1] = '\0'; + +# ifdef HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID /* iOS, OS X */ + (void)pthread_setname_np(name_buf); +# else /* Linux, Solaris, etc. */ + if (pthread_setname_np(pthread_self(), name_buf) != 0) + WARN("pthread_setname_np failed\n", 0); +# endif + } +#else +# define set_marker_thread_name(id) (void)(id) +#endif + +STATIC void * GC_mark_thread(void * id) +{ + word my_mark_no = 0; + IF_CANCEL(int cancel_state;) + + if ((word)id == GC_WORD_MAX) return 0; /* to prevent a compiler warning */ + DISABLE_CANCEL(cancel_state); + /* Mark threads are not cancellable; they */ + /* should be invisible to client. */ + set_marker_thread_name((unsigned)(word)id); + marker_sp[(word)id] = GC_approx_sp(); +# ifdef IA64 + marker_bsp[(word)id] = GC_save_regs_in_stack(); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + marker_mach_threads[(word)id] = mach_thread_self(); +# endif + + /* Inform GC_start_mark_threads about completion of marker data init. */ + GC_acquire_mark_lock(); + if (0 == --GC_fl_builder_count) /* count may have a negative value */ + GC_notify_all_builder(); + + for (;; ++my_mark_no) { + /* GC_mark_no is passed only to allow GC_help_marker to terminate */ + /* promptly. This is important if it were called from the signal */ + /* handler or from the GC lock acquisition code. Under Linux, it's */ + /* not safe to call it from a signal handler, since it uses mutexes */ + /* and condition variables. Since it is called only here, the */ + /* argument is unnecessary. */ + if (my_mark_no < GC_mark_no || my_mark_no > GC_mark_no + 2) { + /* resynchronize if we get far off, e.g. because GC_mark_no */ + /* wrapped. */ + my_mark_no = GC_mark_no; + } +# ifdef DEBUG_THREADS + GC_log_printf("Starting mark helper for mark number %lu\n", + (unsigned long)my_mark_no); +# endif + GC_help_marker(my_mark_no); + } +} + +STATIC pthread_t GC_mark_threads[MAX_MARKERS]; + +#ifdef GLIBC_2_1_MUTEX_HACK + /* Ugly workaround for a linux threads bug in the final versions */ + /* of glibc2.1. Pthread_mutex_trylock sets the mutex owner */ + /* field even when it fails to acquire the mutex. This causes */ + /* pthread_cond_wait to die. Remove for glibc2.2. */ + /* According to the man page, we should use */ + /* PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, but that isn't actually */ + /* defined. */ + static pthread_mutex_t mark_mutex = + {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, {0, 0}}; +#else + static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +static int available_markers_m1 = 0; + +#ifdef CAN_HANDLE_FORK + static pthread_cond_t mark_cv; + /* initialized by GC_start_mark_threads_inner */ +#else + static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER; +#endif + +STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all); + +GC_INNER void GC_start_mark_threads_inner(void) +{ + int i; + pthread_attr_t attr; +# ifndef NO_MARKER_SPECIAL_SIGMASK + sigset_t set, oldset; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + ASSERT_CANCEL_DISABLED(); + if (available_markers_m1 <= 0 || GC_parallel) return; + /* Skip if parallel markers disabled or already started. */ + GC_wait_for_gc_completion(TRUE); + +# ifdef CAN_HANDLE_FORK + /* Initialize mark_cv (for the first time), or cleanup its value */ + /* after forking in the child process. All the marker threads in */ + /* the parent process were blocked on this variable at fork, so */ + /* pthread_cond_wait() malfunction (hang) is possible in the */ + /* child process without such a cleanup. */ + /* TODO: This is not portable, it is better to shortly unblock */ + /* all marker threads in the parent process at fork. */ + { + pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER; + BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv)); + } +# endif + + GC_ASSERT(GC_fl_builder_count == 0); + 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"); + +# ifdef DEFAULT_STACK_MAYBE_SMALL + /* Default stack size is usually too small: increase it. */ + /* Otherwise marker threads or GC may run out of space. */ + { + size_t old_size; + + if (pthread_attr_getstacksize(&attr, &old_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + if (old_size < MIN_STACK_SIZE + && old_size != 0 /* stack size is known */) { + if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0) + ABORT("pthread_attr_setstacksize failed"); + } + } +# endif /* DEFAULT_STACK_MAYBE_SMALL */ + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Apply special signal mask to GC marker threads, and don't drop */ + /* user defined signals by GC marker threads. */ + if (sigfillset(&set) != 0) + ABORT("sigfillset failed"); + +# if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_UTHREADS) \ + && !defined(NACL) + /* These are used by GC to stop and restart the world. */ + if (sigdelset(&set, GC_get_suspend_signal()) != 0 + || sigdelset(&set, GC_get_thr_restart_signal()) != 0) + ABORT("sigdelset failed"); +# endif + + if (REAL_FUNC(pthread_sigmask)(SIG_BLOCK, &set, &oldset) < 0) { + WARN("pthread_sigmask set failed, no markers started\n", 0); + GC_markers_m1 = 0; + (void)pthread_attr_destroy(&attr); + return; + } +# endif /* !NO_MARKER_SPECIAL_SIGMASK */ + + /* To have proper GC_parallel value in GC_help_marker. */ + GC_markers_m1 = available_markers_m1; + + 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\n", 0); + /* Don't try to create other marker threads. */ + GC_markers_m1 = i; + break; + } + } + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Restore previous signal mask. */ + if (REAL_FUNC(pthread_sigmask)(SIG_SETMASK, &oldset, NULL) < 0) { + WARN("pthread_sigmask restore failed\n", 0); + } +# endif + + (void)pthread_attr_destroy(&attr); + GC_wait_for_markers_init(); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); +} + +#endif /* PARALLEL_MARK */ + +GC_INNER GC_bool GC_thr_initialized = FALSE; + +GC_INNER volatile GC_thread GC_threads[THREAD_TABLE_SZ] = {0}; + +/* It may not be safe to allocate when we register the first thread. */ +/* As "next" and "status" fields are unused, no need to push this */ +/* (but "backing_store_end" field should be pushed on E2K). */ +static struct GC_Thread_Rep first_thread; + +void GC_push_thread_structures(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_PUSH_ALL_SYM(GC_threads); +# ifdef E2K + GC_PUSH_ALL_SYM(first_thread.backing_store_end); +# endif +# if defined(THREAD_LOCAL_ALLOC) && defined(USE_CUSTOM_SPECIFIC) + GC_PUSH_ALL_SYM(GC_thread_key); +# endif +} + +#ifdef DEBUG_THREADS + STATIC int GC_count_threads(void) + { + int i; + int count = 0; + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + GC_thread th = GC_threads[i]; + while (th) { + if (!(th->flags & FINISHED)) + ++count; + th = th->next; + } + } + return count; + } +#endif /* DEBUG_THREADS */ + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* Caller holds allocation lock. */ +STATIC GC_thread GC_new_thread(pthread_t id) +{ + int hv = THREAD_TABLE_INDEX(id); + GC_thread result; + static GC_bool first_thread_used = FALSE; + +# ifdef DEBUG_THREADS + GC_log_printf("Creating thread %p\n", (void *)id); + for (result = GC_threads[hv]; result != NULL; result = result->next) + if (!THREAD_EQUAL(result->id, id)) { + GC_log_printf("Hash collision at GC_threads[%d]\n", hv); + break; + } +# endif + GC_ASSERT(I_HOLD_LOCK()); + if (!EXPECT(first_thread_used, TRUE)) { + result = &first_thread; + first_thread_used = TRUE; + GC_ASSERT(NULL == GC_threads[hv]); +# if defined(THREAD_SANITIZER) && defined(CPPCHECK) + GC_noop1((unsigned char)result->dummy[0]); +# endif + } else { + result = (struct GC_Thread_Rep *) + GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL); + if (result == 0) return(0); + } + result -> id = id; +# ifdef USE_TKILL_ON_ANDROID + result -> kernel_id = gettid(); +# endif + result -> next = GC_threads[hv]; + GC_threads[hv] = result; +# ifdef NACL + GC_nacl_gc_thread_self = result; + GC_nacl_initialize_gc_thread(); +# endif + GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0); + if (EXPECT(result != &first_thread, TRUE)) + GC_dirty(result); + return(result); +} + +/* Delete a thread from GC_threads. We assume it is there. */ +/* (The code intentionally traps if it wasn't.) */ +/* It is safe to delete the main thread. */ +STATIC void GC_delete_thread(pthread_t id) +{ + int hv = THREAD_TABLE_INDEX(id); + GC_thread p = GC_threads[hv]; + GC_thread prev = NULL; + +# ifdef DEBUG_THREADS + GC_log_printf("Deleting thread %p, n_threads= %d\n", + (void *)id, GC_count_threads()); +# endif + GC_ASSERT(I_HOLD_LOCK()); + while (!THREAD_EQUAL(p -> id, id)) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> next = p -> next; + GC_dirty(prev); + } + if (p != &first_thread) { +# ifdef GC_DARWIN_THREADS + mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread); +# endif + GC_INTERNAL_FREE(p); + } +} + +/* If a thread has been joined, but we have not yet */ +/* been notified, then there may be more than one thread */ +/* in the table with the same pthread id. */ +/* This is OK, but we need a way to delete a specific one. */ +STATIC void GC_delete_gc_thread(GC_thread t) +{ + pthread_t id = t -> id; + int hv = THREAD_TABLE_INDEX(id); + GC_thread p = GC_threads[hv]; + GC_thread prev = NULL; + + GC_ASSERT(I_HOLD_LOCK()); + while (p != t) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> next = p -> next; + GC_dirty(prev); + } +# ifdef GC_DARWIN_THREADS + mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread); +# endif + GC_INTERNAL_FREE(p); + +# ifdef DEBUG_THREADS + GC_log_printf("Deleted thread %p, n_threads= %d\n", + (void *)id, GC_count_threads()); +# endif +} + +/* Return a GC_thread corresponding to a given pthread_t. */ +/* Returns 0 if it's not there. */ +/* Caller holds allocation lock or otherwise inhibits */ +/* updates. */ +/* If there is more than one thread with the given id we */ +/* return the most recent one. */ +GC_INNER GC_thread GC_lookup_thread(pthread_t id) +{ + GC_thread p = GC_threads[THREAD_TABLE_INDEX(id)]; + + while (p != 0 && !THREAD_EQUAL(p -> id, id)) p = p -> next; + return(p); +} + +#ifndef GC_NO_FINALIZATION + /* Called by GC_finalize() (in case of an allocation failure observed). */ + GC_INNER void GC_reset_finalizer_nested(void) + { + GC_thread me = GC_lookup_thread(pthread_self()); + + me->finalizer_nested = 0; + } + + /* Checks and updates the thread-local level of finalizers recursion. */ + /* Returns NULL if GC_invoke_finalizers() should not be called by the */ + /* collector (to minimize the risk of a deep finalizers recursion), */ + /* otherwise returns a pointer to the thread-local finalizer_nested. */ + /* Called by GC_notify_or_invoke_finalizers() only (the GC lock is */ + /* held). */ + GC_INNER unsigned char *GC_check_finalizer_nested(void) + { + GC_thread me = GC_lookup_thread(pthread_self()); + unsigned nesting_level = me->finalizer_nested; + + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++me->finalizer_skipped < (1U << nesting_level)) return NULL; + me->finalizer_skipped = 0; + } + me->finalizer_nested = (unsigned char)(nesting_level + 1); + return &me->finalizer_nested; + } +#endif /* !GC_NO_FINALIZATION */ + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + /* This is called from thread-local GC_malloc(). */ + GC_bool GC_is_thread_tsd_valid(void *tsd) + { + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(pthread_self()); + UNLOCK(); + return (word)tsd >= (word)(&me->tlfs) + && (word)tsd < (word)(&me->tlfs) + sizeof(me->tlfs); + } +#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */ + +GC_API int GC_CALL GC_thread_is_registered(void) +{ + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(self); + UNLOCK(); + return me != NULL && !(me -> flags & FINISHED); +} + +static pthread_t main_pthread_id; +static void *main_stack, *main_altstack; +static word main_stack_size, main_altstack_size; + +GC_API void GC_CALL GC_register_altstack(void *stack, GC_word stack_size, + void *altstack, + GC_word altstack_size) +{ + GC_thread me; + pthread_t self = pthread_self(); + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(self); + if (me != NULL) { + me->stack = (ptr_t)stack; + me->stack_size = stack_size; + me->altstack = (ptr_t)altstack; + me->altstack_size = altstack_size; + } else { + /* This happens if we are called before GC_thr_init. */ + main_pthread_id = self; + main_stack = stack; + main_stack_size = stack_size; + main_altstack = altstack; + main_altstack_size = altstack_size; + } + UNLOCK(); +} + +#ifdef CAN_HANDLE_FORK + + /* Prevent TSan false positive about the race during items removal */ + /* from GC_threads. (The race cannot happen since only one thread */ + /* survives in the child.) */ +# ifdef CAN_CALL_ATFORK + GC_ATTR_NO_SANITIZE_THREAD +# endif + static void store_to_threads_table(int hv, GC_thread me) + { + GC_threads[hv] = me; + } + +/* 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 */ +/* survives in the child. */ +STATIC void GC_remove_all_threads_but_me(void) +{ + pthread_t self = pthread_self(); + int hv; + + for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { + GC_thread p, next; + GC_thread me = NULL; + + for (p = GC_threads[hv]; 0 != p; p = next) { + next = p -> next; + if (THREAD_EQUAL(p -> id, self) + && me == NULL) { /* ignore dead threads with the same id */ + 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_inner */ + /* before update). */ + me -> stop_info.mach_thread = mach_thread_self(); +# endif +# ifdef USE_TKILL_ON_ANDROID + me -> kernel_id = gettid(); +# endif +# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC) + { + int res; + + /* 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_inner before). */ + res = GC_setspecific(GC_thread_key, &me->tlfs); + if (COVERT_DATAFLOW(res) != 0) + ABORT("GC_setspecific failed (in child)"); + } +# endif + } else { +# ifdef THREAD_LOCAL_ALLOC + if (!(p -> flags & FINISHED)) { + /* Cannot call GC_destroy_thread_local here. The free */ + /* lists may be in an inconsistent state (as thread p may */ + /* be updating one of the lists by GC_generic_malloc_many */ + /* or GC_FAST_MALLOC_GRANS when fork is invoked). */ + /* This should not be a problem because the lost elements */ + /* of the free lists will be collected during GC. */ + GC_remove_specific_after_fork(GC_thread_key, p -> id); + } +# endif + /* TODO: To avoid TSan hang (when updating GC_bytes_freed), */ + /* we just skip explicit freeing of GC_threads entries. */ +# if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK) + if (p != &first_thread) GC_INTERNAL_FREE(p); +# endif + } + } + store_to_threads_table(hv, me); + } +} +#endif /* CAN_HANDLE_FORK */ + +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi) + { + int i; + GC_thread p; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef PARALLEL_MARK + 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 ((word)marker_bsp[i] > (word)lo + && (word)marker_bsp[i] < (word)hi) + return TRUE; +# endif + } +# endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (0 != p -> stack_end) { +# ifdef STACK_GROWS_UP + if ((word)p->stack_end >= (word)lo + && (word)p->stack_end < (word)hi) + return TRUE; +# else /* STACK_GROWS_DOWN */ + if ((word)p->stack_end > (word)lo + && (word)p->stack_end <= (word)hi) + return TRUE; +# endif + } + } + } + return FALSE; + } +#endif /* USE_PROC_FOR_LIBRARIES */ + +#if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && defined(IA64) + /* Find the largest stack_base smaller than bound. May be used */ + /* to find the boundary between a register stack and adjacent */ + /* immediately preceding memory stack. */ + GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound) + { + int i; + GC_thread p; + ptr_t result = 0; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef PARALLEL_MARK + 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 ((word)p->stack_end > (word)result + && (word)p->stack_end < (word)bound) { + result = p -> stack_end; + } + } + } + return result; + } +#endif /* IA64 */ + +#ifndef STAT_READ +# define STAT_READ read + /* If read is wrapped, this may need to be redefined to call */ + /* the real one. */ +#endif + +#ifdef GC_HPUX_THREADS +# define GC_get_nprocs() pthread_num_processors_np() + +#elif defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \ + || defined(GC_HAIKU_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(HURD) || defined(HOST_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) /* && !HOST_ANDROID && !NACL */ + /* Return the number of processors. */ + STATIC int GC_get_nprocs(void) + { + /* Should be "return sysconf(_SC_NPROCESSORS_ONLN);" but that */ + /* appears to be buggy in many cases. */ + /* We look for lines "cpu" in /proc/stat. */ +# define PROC_STAT_BUF_SZ ((1 + MAX_MARKERS) * 100) /* should be enough */ + /* No need to read the entire /proc/stat to get maximum cpu as */ + /* - the requested lines are located at the beginning of the file; */ + /* - the lines with cpu where N > MAX_MARKERS are not needed. */ + char stat_buf[PROC_STAT_BUF_SZ+1]; + int f; + int result, i, len; + + f = open("/proc/stat", O_RDONLY); + if (f < 0) { + WARN("Could not open /proc/stat\n", 0); + return 1; /* assume an uniprocessor */ + } + len = STAT_READ(f, stat_buf, sizeof(stat_buf)-1); + /* Unlikely that we need to retry because of an incomplete read here. */ + if (len < 0) { + WARN("Failed to read /proc/stat, errno= %" WARN_PRIdPTR "\n", + (signed_word)errno); + close(f); + return 1; + } + stat_buf[len] = '\0'; /* to avoid potential buffer overrun by atoi() */ + close(f); + + result = 1; + /* Some old kernels only have a single "cpu nnnn ..." */ + /* entry in /proc/stat. We identify those as */ + /* uniprocessors. */ + + for (i = 0; i < len - 4; ++i) { + if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c' + && stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') { + int cpu_no = atoi(&stat_buf[i + 4]); + if (cpu_no >= result) + result = cpu_no + 1; + } + } + return result; + } + +#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 */ + /* /proc/stat (and /proc/cpuinfo), so another data system source is */ + /* tried first. Result <= 0 on error. */ + STATIC int GC_get_nprocs_present(void) + { + char stat_buf[16]; + int f; + int len; + + f = open("/sys/devices/system/cpu/present", O_RDONLY); + if (f < 0) + return -1; /* cannot open the file */ + + len = STAT_READ(f, stat_buf, sizeof(stat_buf)); + close(f); + + /* Recognized file format: "0\n" or "0-\n" */ + /* The file might probably contain a comma-separated list */ + /* but we do not need to handle it (just silently ignore). */ + if (len < 2 || stat_buf[0] != '0' || stat_buf[len - 1] != '\n') { + return 0; /* read error or unrecognized content */ + } else if (len == 2) { + return 1; /* an uniprocessor */ + } else if (stat_buf[1] != '-') { + return 0; /* unrecognized content */ + } + + stat_buf[len - 1] = '\0'; /* terminate the string */ + return atoi(&stat_buf[2]) + 1; /* skip "0-" and parse max_cpu_num */ + } +#endif /* ARM32 && GC_LINUX_THREADS && !NACL */ + +#if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER) +# include "private/gc_pmark.h" /* for MS_NONE */ + + /* Workaround for TSan which does not notice that the GC lock */ + /* is acquired in fork_prepare_proc(). */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_bool collection_in_progress(void) + { + return GC_mark_state != MS_NONE; + } +#else +# define collection_in_progress() GC_collection_in_progress() +#endif + +/* We hold the GC lock. Wait until an in-progress GC has finished. */ +/* Repeatedly RELEASES GC LOCK in order to wait. */ +/* If wait_for_all is true, then we exit with the GC lock held and no */ +/* collection in progress; otherwise we just wait for the current GC */ +/* to finish. */ +STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all) +{ + DCL_LOCK_STATE; +# if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK) + /* GC_lock_holder is accessed with the lock held, so there is no */ + /* data race actually (unlike what is reported by TSan). */ + GC_ASSERT(I_HOLD_LOCK()); +# endif + ASSERT_CANCEL_DISABLED(); + if (GC_incremental && 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. */ + while (GC_incremental && collection_in_progress() + && (wait_for_all || old_gc_no == GC_gc_no)) { + ENTER_GC(); + GC_in_thread_creation = TRUE; + GC_collect_a_little_inner(1); + GC_in_thread_creation = FALSE; + EXIT_GC(); + UNLOCK(); + sched_yield(); + LOCK(); + } + } +} + +#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 */ +/* should only call async-signal-safe functions, and we probably can't */ +/* quite guarantee that. But we give it our best shot. (That same */ +/* spec also implies that it's not safe to call the system malloc */ +/* between fork() and exec(). Thus we're doing no worse than it.) */ + +IF_CANCEL(static int fork_cancel_state;) + /* protected by allocation lock. */ + +# ifdef PARALLEL_MARK +# ifdef THREAD_SANITIZER +# if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + STATIC void GC_generic_lock(pthread_mutex_t *); +# endif + GC_ATTR_NO_SANITIZE_THREAD + static void wait_for_reclaim_atfork(void); +# else +# define wait_for_reclaim_atfork() GC_wait_for_reclaim() +# endif +# endif /* PARALLEL_MARK */ + +/* Called before a fork() */ +#if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + /* GC_lock_holder is updated safely (no data race actually). */ + GC_ATTR_NO_SANITIZE_THREAD +#endif +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 */ + /* invariants hold. Unfortunately, we can't acquire libc locks */ + /* we might need, and there seems to be no guarantee that libc */ + /* must install a suitable fork handler. */ + /* Wait for an ongoing GC to finish, since we can't finish it in */ + /* the (one remaining thread in) the child. */ + LOCK(); + DISABLE_CANCEL(fork_cancel_state); + /* Following waits may include cancellation points. */ +# if defined(PARALLEL_MARK) + if (GC_parallel) + wait_for_reclaim_atfork(); +# endif + GC_wait_for_gc_completion(TRUE); +# if defined(PARALLEL_MARK) + if (GC_parallel) { +# if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \ + && defined(CAN_CALL_ATFORK) + /* Prevent TSan false positive about the data race */ + /* when updating GC_mark_lock_holder. */ + GC_generic_lock(&mark_mutex); +# else + GC_acquire_mark_lock(); +# endif + } +# endif + GC_acquire_dirty_lock(); +} + +/* Called in parent after a fork() (even if the latter failed). */ +#if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + GC_ATTR_NO_SANITIZE_THREAD +#endif +static void fork_parent_proc(void) +{ + GC_release_dirty_lock(); +# if defined(PARALLEL_MARK) + if (GC_parallel) { +# if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \ + && defined(CAN_CALL_ATFORK) + /* To match that in fork_prepare_proc. */ + (void)pthread_mutex_unlock(&mark_mutex); +# else + GC_release_mark_lock(); +# endif + } +# endif + RESTORE_CANCEL(fork_cancel_state); + UNLOCK(); +} + +/* Called in child after a fork() */ +#if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + GC_ATTR_NO_SANITIZE_THREAD +#endif +static void fork_child_proc(void) +{ + GC_release_dirty_lock(); +# ifdef PARALLEL_MARK + if (GC_parallel) { +# if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \ + && defined(CAN_CALL_ATFORK) + (void)pthread_mutex_unlock(&mark_mutex); +# else + GC_release_mark_lock(); +# endif + /* Turn off parallel marking in the child, since we are probably */ + /* just going to exec, and we would have to restart mark threads. */ + GC_parallel = FALSE; + } +# ifdef THREAD_SANITIZER + /* TSan does not support threads creation in the child process. */ + available_markers_m1 = 0; +# endif +# endif + /* Clean up the thread table, so that just our thread is left. */ + GC_remove_all_threads_but_me(); +# ifndef GC_DISABLE_INCREMENTAL + GC_dirty_update_child(); +# endif + RESTORE_CANCEL(fork_cancel_state); + UNLOCK(); + /* Even though after a fork the child only inherits the single */ + /* thread that called the fork(), if another thread in the parent */ + /* was attempting to lock the mutex while being held in */ + /* fork_child_prepare(), the mutex will be left in an inconsistent */ + /* state in the child after the UNLOCK. This is the case, at */ + /* least, in Mac OS X and leads to an unusable GC in the child */ + /* which will block when attempting to perform any GC operation */ + /* that acquires the allocation mutex. */ +# ifdef USE_PTHREAD_LOCKS + GC_ASSERT(I_DONT_HOLD_LOCK()); + /* Reinitialize the mutex. It should be safe since we are */ + /* running this in the child which only inherits a single thread. */ + /* mutex_destroy() may return EBUSY, which makes no sense, but */ + /* that is the reason for the need of the reinitialization. */ + (void)pthread_mutex_destroy(&GC_allocate_ml); + /* TODO: Probably some targets might need the default mutex */ + /* attribute to be passed instead of NULL. */ + if (0 != pthread_mutex_init(&GC_allocate_ml, NULL)) + ABORT("pthread_mutex_init failed (in child)"); +# endif +} + + /* Routines for fork handling by client (no-op if pthread_atfork works). */ + GC_API void GC_CALL GC_atfork_prepare(void) + { + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); +# if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB) + if (GC_auto_incremental) { + GC_ASSERT(0 == GC_handle_fork); + ABORT("Unable to fork while mprotect_thread is running"); + } +# endif + 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 */ + +#ifdef INCLUDE_LINUX_THREAD_DESCR + __thread int GC_dummy_thread_local; +#endif + +#ifdef PARALLEL_MARK + static void setup_mark_lock(void); + + static unsigned required_markers_cnt = 0; + /* The default value (0) means the number of */ + /* markers should be selected automatically. */ +#endif /* PARALLEL_MARK */ + +GC_API void GC_CALL GC_set_markers_count(unsigned markers GC_ATTR_UNUSED) +{ +# ifdef PARALLEL_MARK + required_markers_cnt = markers < MAX_MARKERS ? markers : MAX_MARKERS; +# endif +} + +#ifndef DONT_USE_ATEXIT + STATIC pthread_t GC_main_thread_id; + + GC_INNER GC_bool GC_is_main_thread(void) + { + GC_ASSERT(GC_thr_initialized); + return THREAD_EQUAL(GC_main_thread_id, pthread_self()); + } +#endif /* !DONT_USE_ATEXIT */ + +GC_INNER void GC_thr_init(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (GC_thr_initialized) return; + GC_thr_initialized = TRUE; + + 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 */ + /* of a thread local variable. This should include thread */ + /* locals for the main thread, except for those allocated */ + /* in response to dlopen calls. */ + { + ptr_t thread_local_addr = (ptr_t)(&GC_dummy_thread_local); + ptr_t main_thread_start, main_thread_end; + if (!GC_enclosing_mapping(thread_local_addr, &main_thread_start, + &main_thread_end)) { + ABORT("Failed to find mapping for main thread thread locals"); + } else { + /* main_thread_start and main_thread_end are initialized. */ + GC_add_roots_inner(main_thread_start, main_thread_end, FALSE); + } + } +# endif + /* Add the initial thread, so we can stop it. */ + { + pthread_t self = pthread_self(); + GC_thread t = GC_new_thread(self); + + if (t == NULL) + ABORT("Failed to allocate memory for the initial thread"); +# ifdef GC_DARWIN_THREADS + t -> stop_info.mach_thread = mach_thread_self(); +# else + t -> stop_info.stack_ptr = GC_approx_sp(); +# endif +# ifndef DONT_USE_ATEXIT + GC_main_thread_id = self; +# endif + t -> flags = DETACHED | MAIN_THREAD; + if (THREAD_EQUAL(self, main_pthread_id)) { + t -> stack = (ptr_t)main_stack; + t -> stack_size = main_stack_size; + t -> altstack = (ptr_t)main_altstack; + t -> altstack_size = main_altstack_size; + } + } + + /* Set GC_nprocs and available_markers_m1. */ + { + char * nprocs_string = GETENV("GC_NPROCS"); + GC_nprocs = -1; + if (nprocs_string != NULL) GC_nprocs = atoi(nprocs_string); + } + if (GC_nprocs <= 0 +# if defined(ARM32) && defined(GC_LINUX_THREADS) && !defined(NACL) + && (GC_nprocs = GC_get_nprocs_present()) <= 1 + /* Workaround for some Linux/arm kernels */ +# endif + ) + { + GC_nprocs = GC_get_nprocs(); + } + if (GC_nprocs <= 0) { + WARN("GC_get_nprocs() returned %" WARN_PRIdPTR "\n", + (signed_word)GC_nprocs); + GC_nprocs = 2; /* assume dual-core */ +# ifdef PARALLEL_MARK + available_markers_m1 = 0; /* but use only one marker */ +# endif + } else { +# ifdef PARALLEL_MARK + { + char * markers_string = GETENV("GC_MARKERS"); + int markers = required_markers_cnt; + + if (markers_string != NULL) { + markers = atoi(markers_string); + if (markers <= 0 || markers > MAX_MARKERS) { + WARN("Too big or invalid number of mark threads: %" WARN_PRIdPTR + "; using maximum threads\n", (signed_word)markers); + markers = MAX_MARKERS; + } + } else if (0 == markers) { + /* Unless the client sets the desired number of */ + /* parallel markers, it is determined based on the */ + /* number of CPU cores. */ + markers = GC_nprocs; +# if defined(GC_MIN_MARKERS) && !defined(CPPCHECK) + /* This is primarily for targets without getenv(). */ + if (markers < GC_MIN_MARKERS) + markers = GC_MIN_MARKERS; +# endif + if (markers > MAX_MARKERS) + markers = MAX_MARKERS; /* silently limit the value */ + } + available_markers_m1 = markers - 1; + } +# endif + } + GC_COND_LOG_PRINTF("Number of processors: %d\n", GC_nprocs); + +# if defined(BASE_ATOMIC_OPS_EMULATED) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) \ + && !defined(PLATFORM_STOP_WORLD) && !defined(SN_TARGET_PSP2) + /* Ensure the process is running on just one CPU core. */ + /* This is needed because the AO primitives emulated with */ + /* locks cannot be used inside signal handlers. */ + { + cpu_set_t mask; + int cpu_set_cnt = 0; + int cpu_lowest_set = 0; + int i = GC_nprocs > 1 ? GC_nprocs : 2; /* check at least 2 cores */ + + if (sched_getaffinity(0 /* current process */, + sizeof(mask), &mask) == -1) + ABORT_ARG1("sched_getaffinity failed", ": errno= %d", errno); + while (i-- > 0) + if (CPU_ISSET(i, &mask)) { + cpu_lowest_set = i; + cpu_set_cnt++; + } + if (0 == cpu_set_cnt) + ABORT("sched_getaffinity returned empty mask"); + if (cpu_set_cnt > 1) { + CPU_ZERO(&mask); + CPU_SET(cpu_lowest_set, &mask); /* select just one CPU */ + if (sched_setaffinity(0, sizeof(mask), &mask) == -1) + ABORT_ARG1("sched_setaffinity failed", ": errno= %d", errno); + WARN("CPU affinity mask is set to %p\n", (word)1 << cpu_lowest_set); + } + } +# endif /* BASE_ATOMIC_OPS_EMULATED */ + +# ifndef GC_DARWIN_THREADS + GC_stop_init(); +# endif + +# ifdef PARALLEL_MARK + if (available_markers_m1 <= 0) { + /* Disable parallel marking. */ + GC_parallel = FALSE; + GC_COND_LOG_PRINTF( + "Single marker thread, turning off parallel marking\n"); + } else { + setup_mark_lock(); + } +# endif +} + +/* Perform all initializations, including those that */ +/* may require allocation. */ +/* Called without allocation lock. */ +/* Must be called before a second thread is created. */ +/* Did we say it's called without the allocation lock? */ +GC_INNER void GC_init_parallel(void) +{ +# if defined(THREAD_LOCAL_ALLOC) + DCL_LOCK_STATE; +# endif + if (parallel_initialized) return; + parallel_initialized = TRUE; + + /* GC_init() calls us back, so set flag first. */ + if (!GC_is_initialized) GC_init(); + /* Initialize thread local free lists if used. */ +# if defined(THREAD_LOCAL_ALLOC) + LOCK(); + GC_init_thread_local(&(GC_lookup_thread(pthread_self())->tlfs)); + UNLOCK(); +# endif +} + +#ifndef GC_NO_PTHREAD_SIGMASK + GC_API int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, + sigset_t *oset) + { + sigset_t fudged_set; + + INIT_REAL_SYMS(); + if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) { + int sig_suspend = GC_get_suspend_signal(); + + fudged_set = *set; + GC_ASSERT(sig_suspend >= 0); + if (sigdelset(&fudged_set, sig_suspend) != 0) + ABORT("sigdelset failed"); + set = &fudged_set; + } + return(REAL_FUNC(pthread_sigmask)(how, set, oset)); + } +#endif /* !GC_NO_PTHREAD_SIGMASK */ + +static GC_bool do_blocking_enter(GC_thread me) +{ +# if defined(SPARC) || defined(IA64) + ptr_t stack_ptr = GC_save_regs_in_stack(); + /* TODO: regs saving already done by GC_with_callee_saves_pushed */ +# elif defined(E2K) + size_t stack_size; +# endif + GC_bool topOfStackUnset = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(!(me -> thread_blocked)); +# ifdef SPARC + me -> stop_info.stack_ptr = stack_ptr; +# else + me -> stop_info.stack_ptr = GC_approx_sp(); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + if (me -> topOfStack == NULL) { + /* GC_do_blocking_inner is not called recursively, */ + /* so topOfStack should be computed now. */ + topOfStackUnset = TRUE; + me -> topOfStack = GC_FindTopOfStack(0); + } +# endif +# ifdef IA64 + me -> backing_store_ptr = stack_ptr; +# elif defined(E2K) + GC_ASSERT(NULL == me -> backing_store_end); + stack_size = GC_alloc_and_get_procedure_stack(&me->backing_store_end); + me->backing_store_ptr = me->backing_store_end + stack_size; +# endif + me -> thread_blocked = (unsigned char)TRUE; + /* Save context here if we want to support precise stack marking */ + return topOfStackUnset; +} + +static void do_blocking_leave(GC_thread me, GC_bool topOfStackUnset) +{ + GC_ASSERT(I_HOLD_LOCK()); +# if defined(CPPCHECK) + GC_noop1((word)&me->thread_blocked); +# endif + me -> thread_blocked = FALSE; +# ifdef E2K + GC_ASSERT(me -> backing_store_end != NULL); + /* Note that me->backing_store_end value here may differ from */ + /* the one stored in this function previously. */ + GC_INTERNAL_FREE(me -> backing_store_end); + me -> backing_store_ptr = NULL; + me -> backing_store_end = NULL; +# endif +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + if (topOfStackUnset) + me -> topOfStack = NULL; /* make topOfStack unset again */ +# else + (void)topOfStackUnset; +# endif +} + +/* Wrapper for functions that are likely to block for an appreciable */ +/* length of time. */ +GC_INNER void GC_do_blocking_inner(ptr_t data, void * context GC_ATTR_UNUSED) +{ + struct blocking_data *d = (struct blocking_data *)data; + GC_thread me; + GC_bool topOfStackUnset; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(pthread_self()); + topOfStackUnset = do_blocking_enter(me); + UNLOCK(); + + d -> client_data = (d -> fn)(d -> client_data); + + LOCK(); /* This will block if the world is stopped. */ +# ifdef LINT2 + { +# ifdef GC_ASSERTIONS + GC_thread saved_me = me; +# endif + + /* The pointer to the GC thread descriptor should not be */ + /* changed while the thread is registered but a static */ + /* analysis tool might complain that this pointer value */ + /* (obtained in the first locked section) is unreliable in */ + /* the second locked section. */ + me = GC_lookup_thread(pthread_self()); + GC_ASSERT(me == saved_me); + } +# endif +# if defined(GC_ENABLE_SUSPEND_THREAD) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) \ + && !defined(PLATFORM_STOP_WORLD) && !defined(SN_TARGET_PSP2) + /* Note: this code cannot be moved into do_blocking_leave() */ + /* otherwise there could be a static analysis tool warning */ + /* (false positive) about unlock without a matching lock. */ + while (EXPECT((me -> stop_info.ext_suspend_cnt & 1) != 0, FALSE)) { + word suspend_cnt = (word)(me -> stop_info.ext_suspend_cnt); + /* read suspend counter (number) before unlocking */ + + UNLOCK(); + GC_suspend_self_inner(me, suspend_cnt); + LOCK(); + } +# endif + do_blocking_leave(me, topOfStackUnset); + UNLOCK(); +} + +#if defined(GC_ENABLE_SUSPEND_THREAD) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) \ + && !defined(PLATFORM_STOP_WORLD) && !defined(SN_TARGET_PSP2) + /* Similar to GC_do_blocking_inner() but assuming the GC lock is held */ + /* and fn is GC_suspend_self_inner. */ + GC_INNER void GC_suspend_self_blocked(ptr_t thread_me, + void * context GC_ATTR_UNUSED) + { + GC_thread me = (GC_thread)thread_me; + GC_bool topOfStackUnset; + DCL_LOCK_STATE; + + GC_ASSERT(I_HOLD_LOCK()); + topOfStackUnset = do_blocking_enter(me); + while ((me -> stop_info.ext_suspend_cnt & 1) != 0) { + word suspend_cnt = (word)(me -> stop_info.ext_suspend_cnt); + + UNLOCK(); + GC_suspend_self_inner(me, suspend_cnt); + LOCK(); + } + do_blocking_leave(me, topOfStackUnset); + } +#endif + +GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle, + const struct GC_stack_base *sb) +{ + GC_thread t = (GC_thread)gc_thread_handle; + + GC_ASSERT(sb -> mem_base != NULL); + if (!EXPECT(GC_is_initialized, TRUE)) { + GC_ASSERT(NULL == t); + } else { + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == t) /* current thread? */ + t = GC_lookup_thread(pthread_self()); + GC_ASSERT((t -> flags & FINISHED) == 0); + GC_ASSERT(!(t -> thread_blocked) + && NULL == t -> traced_stack_sect); /* for now */ + + if ((t -> flags & MAIN_THREAD) == 0) { + t -> stack_end = (ptr_t)sb->mem_base; +# ifdef IA64 + t -> backing_store_end = (ptr_t)sb->reg_base; +# endif + return; + } + /* Otherwise alter the stack bottom of the primordial thread. */ + } + + GC_stackbottom = (char*)sb->mem_base; +# ifdef IA64 + GC_register_stackbottom = (ptr_t)sb->reg_base; +# endif +} + +GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb) +{ + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread(self); + /* The thread is assumed to be registered. */ + if ((me -> flags & MAIN_THREAD) == 0) { + sb -> mem_base = me -> stack_end; +# ifdef IA64 + sb -> reg_base = me -> backing_store_end; +# elif defined(E2K) + sb -> reg_base = NULL; +# endif + } else { + sb -> mem_base = GC_stackbottom; +# ifdef IA64 + sb -> reg_base = GC_register_stackbottom; +# elif defined(E2K) + sb -> reg_base = NULL; +# endif + } + UNLOCK(); + return (void *)me; /* gc_thread_handle */ +} + +/* GC_call_with_gc_active() has the opposite to GC_do_blocking() */ +/* functionality. It might be called from a user function invoked by */ +/* GC_do_blocking() to temporarily back allow calling any GC function */ +/* and/or manipulating pointers to the garbage collected heap. */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, + void * client_data) +{ + struct GC_traced_stack_sect_s stacksect; + pthread_t self = pthread_self(); + GC_thread me; +# ifdef E2K + size_t stack_size; +# endif + DCL_LOCK_STATE; + + LOCK(); /* This will block if the world is stopped. */ + me = GC_lookup_thread(self); + + /* Adjust our stack bottom value (this could happen unless */ + /* GC_get_stack_base() was used which returned GC_SUCCESS). */ + if ((me -> flags & MAIN_THREAD) == 0) { + GC_ASSERT(me -> stack_end != NULL); + if ((word)me->stack_end HOTTER_THAN (word)(&stacksect)) + me -> stack_end = (ptr_t)(&stacksect); + } else { + /* The original stack. */ + if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect)) + GC_stackbottom = (ptr_t)COVERT_DATAFLOW(&stacksect); + } + + if (!me->thread_blocked) { + /* We are not inside GC_do_blocking() - do nothing more. */ + UNLOCK(); + client_data = fn(client_data); + /* Prevent treating the above as a tail call. */ + GC_noop1(COVERT_DATAFLOW(&stacksect)); + return client_data; /* result */ + } + +# if defined(GC_ENABLE_SUSPEND_THREAD) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) \ + && !defined(PLATFORM_STOP_WORLD) && !defined(SN_TARGET_PSP2) + while (EXPECT((me -> stop_info.ext_suspend_cnt & 1) != 0, FALSE)) { + word suspend_cnt = (word)(me -> stop_info.ext_suspend_cnt); + UNLOCK(); + GC_suspend_self_inner(me, suspend_cnt); + LOCK(); + } +# endif + + /* Setup new "stack section". */ + stacksect.saved_stack_ptr = me -> stop_info.stack_ptr; +# ifdef IA64 + /* This is the same as in GC_call_with_stack_base(). */ + stacksect.backing_store_end = GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ + stacksect.saved_backing_store_ptr = me -> backing_store_ptr; +# elif defined(E2K) + GC_ASSERT(me -> backing_store_end != NULL); + GC_INTERNAL_FREE(me -> backing_store_end); + me -> backing_store_ptr = NULL; + me -> backing_store_end = NULL; +# endif + stacksect.prev = me -> traced_stack_sect; + me -> thread_blocked = FALSE; + me -> traced_stack_sect = &stacksect; + + UNLOCK(); + client_data = fn(client_data); + GC_ASSERT(me -> thread_blocked == FALSE); + GC_ASSERT(me -> traced_stack_sect == &stacksect); + + /* Restore original "stack section". */ +# if defined(CPPCHECK) + GC_noop1((word)me->traced_stack_sect); +# endif +# ifdef E2K + (void)GC_save_regs_in_stack(); +# endif + LOCK(); + me -> traced_stack_sect = stacksect.prev; +# ifdef IA64 + me -> backing_store_ptr = stacksect.saved_backing_store_ptr; +# elif defined(E2K) + GC_ASSERT(NULL == me -> backing_store_end); + stack_size = GC_alloc_and_get_procedure_stack(&me->backing_store_end); + me->backing_store_ptr = me->backing_store_end + stack_size; +# endif + me -> thread_blocked = (unsigned char)TRUE; + me -> stop_info.stack_ptr = stacksect.saved_stack_ptr; + UNLOCK(); + + return client_data; /* result */ +} + +STATIC void GC_unregister_my_thread_inner(GC_thread me) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_log_printf( + "Unregistering thread %p, gc_thread= %p, n_threads= %d\n", + (void *)me->id, (void *)me, GC_count_threads()); +# 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 +# ifdef NACL + GC_nacl_shutdown_gc_thread(); + GC_nacl_gc_thread_self = NULL; +# endif +# if defined(GC_HAVE_PTHREAD_EXIT) || !defined(GC_NO_PTHREAD_CANCEL) + /* Handle DISABLED_GC flag which is set by the */ + /* intercepted pthread_cancel or pthread_exit. */ + if ((me -> flags & DISABLED_GC) != 0) { + GC_dont_gc--; + } +# endif + if (me -> flags & DETACHED) { + GC_delete_thread(pthread_self()); + } else { + 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 +} + +GC_API int GC_CALL GC_unregister_my_thread(void) +{ + pthread_t self = pthread_self(); + GC_thread me; + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + LOCK(); + DISABLE_CANCEL(cancel_state); + /* Wait for any GC that may be marking from our stack to */ + /* complete before we remove this thread. */ + GC_wait_for_gc_completion(FALSE); + me = GC_lookup_thread(self); +# ifdef DEBUG_THREADS + GC_log_printf( + "Called GC_unregister_my_thread on %p, gc_thread= %p\n", + (void *)self, (void *)me); +# endif + GC_ASSERT(THREAD_EQUAL(me->id, self)); + GC_unregister_my_thread_inner(me); + RESTORE_CANCEL(cancel_state); + UNLOCK(); + return GC_SUCCESS; +} + +/* Called at thread exit. */ +/* Never called for main thread. That's OK, since it */ +/* results in at most a tiny one-time leak. And */ +/* linuxthreads doesn't reclaim the main threads */ +/* resources or id anyway. */ +GC_INNER_PTHRSTART void GC_thread_exit_proc(void *arg) +{ + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + +# ifdef DEBUG_THREADS + GC_log_printf("Called GC_thread_exit_proc on %p, gc_thread= %p\n", + (void *)((GC_thread)arg)->id, arg); +# endif + LOCK(); + DISABLE_CANCEL(cancel_state); + GC_wait_for_gc_completion(FALSE); + GC_unregister_my_thread_inner((GC_thread)arg); + RESTORE_CANCEL(cancel_state); + UNLOCK(); +} + +#if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + GC_API int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval) + { + int result; + GC_thread t; + DCL_LOCK_STATE; + + INIT_REAL_SYMS(); + LOCK(); + t = (GC_thread)COVERT_DATAFLOW(GC_lookup_thread(thread)); + /* This is guaranteed to be the intended one, since the thread id */ + /* can't have been recycled by pthreads. */ + UNLOCK(); + result = REAL_FUNC(pthread_join)(thread, retval); +# if defined(GC_FREEBSD_THREADS) + /* On FreeBSD, the wrapped pthread_join() sometimes returns (what + appears to be) a spurious EINTR which caused the test and real code + to gratuitously fail. Having looked at system pthread library source + code, I see how this return code may be generated. In one path of + code, pthread_join() just returns the errno setting of the thread + being joined. This does not match the POSIX specification or the + local man pages thus I have taken the liberty to catch this one + spurious return value properly conditionalized on GC_FREEBSD_THREADS. */ + if (result == EINTR) result = 0; +# endif + if (result == 0) { + LOCK(); + /* Here the pthread thread id may have been recycled. */ + /* Delete the thread from GC_threads (unless it has been */ + /* registered again from the client thread key destructor). */ + if ((t -> flags & FINISHED) != 0) + GC_delete_gc_thread(t); + UNLOCK(); + } + return result; + } + + GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread) + { + int result; + GC_thread t; + DCL_LOCK_STATE; + + INIT_REAL_SYMS(); + LOCK(); + t = (GC_thread)COVERT_DATAFLOW(GC_lookup_thread(thread)); + UNLOCK(); + result = REAL_FUNC(pthread_detach)(thread); + if (result == 0) { + LOCK(); + t -> flags |= DETACHED; + /* Here the pthread thread id may have been recycled. */ + if ((t -> flags & FINISHED) != 0) { + GC_delete_gc_thread(t); + } + UNLOCK(); + } + return result; + } +#endif /* !SN_TARGET_ORBIS && !SN_TARGET_PSP2 */ + +#ifndef GC_NO_PTHREAD_CANCEL + /* We should deal with the fact that apparently on Solaris and, */ + /* probably, on some Linux we can't collect while a thread is */ + /* exiting, since signals aren't handled properly. This currently */ + /* gives rise to deadlocks. The only workaround seen is to intercept */ + /* pthread_cancel() and pthread_exit(), and disable the collections */ + /* until the thread exit handler is called. That's ugly, because we */ + /* risk growing the heap unnecessarily. But it seems that we don't */ + /* really have an option in that the process is not in a fully */ + /* functional state while a thread is exiting. */ + GC_API int WRAP_FUNC(pthread_cancel)(pthread_t thread) + { +# ifdef CANCEL_SAFE + GC_thread t; + DCL_LOCK_STATE; +# endif + + INIT_REAL_SYMS(); +# ifdef CANCEL_SAFE + LOCK(); + t = GC_lookup_thread(thread); + /* We test DISABLED_GC because pthread_exit could be called at */ + /* the same time. (If t is NULL then pthread_cancel should */ + /* return ESRCH.) */ + if (t != NULL && (t -> flags & DISABLED_GC) == 0) { + t -> flags |= DISABLED_GC; + GC_dont_gc++; + } + UNLOCK(); +# endif + return REAL_FUNC(pthread_cancel)(thread); + } +#endif /* !GC_NO_PTHREAD_CANCEL */ + +#ifdef GC_HAVE_PTHREAD_EXIT + GC_API GC_PTHREAD_EXIT_ATTRIBUTE void WRAP_FUNC(pthread_exit)(void *retval) + { + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + + INIT_REAL_SYMS(); + LOCK(); + me = GC_lookup_thread(self); + /* We test DISABLED_GC because someone else could call */ + /* pthread_cancel at the same time. */ + if (me != 0 && (me -> flags & DISABLED_GC) == 0) { + me -> flags |= DISABLED_GC; + GC_dont_gc++; + } + UNLOCK(); + + REAL_FUNC(pthread_exit)(retval); + } +#endif /* GC_HAVE_PTHREAD_EXIT */ + +GC_INNER GC_bool GC_in_thread_creation = FALSE; + /* Protected by allocation lock. */ + +GC_INLINE void GC_record_stack_base(GC_thread me, + const struct GC_stack_base *sb) +{ +# ifndef GC_DARWIN_THREADS + me -> stop_info.stack_ptr = (ptr_t)sb->mem_base; +# endif + me -> stack_end = (ptr_t)sb->mem_base; + if (me -> stack_end == NULL) + ABORT("Bad stack base in GC_register_my_thread"); +# ifdef IA64 + me -> backing_store_end = (ptr_t)sb->reg_base; +# endif +} + +STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, + pthread_t my_pthread) +{ + GC_thread me; + + GC_in_thread_creation = TRUE; /* OK to collect from unknown thread. */ + me = GC_new_thread(my_pthread); + GC_in_thread_creation = FALSE; + if (me == 0) + ABORT("Failed to allocate memory for thread registering"); +# ifdef GC_DARWIN_THREADS + me -> stop_info.mach_thread = mach_thread_self(); +# endif + GC_record_stack_base(me, sb); +# ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + /* Since this could be executed from a detached thread */ + /* destructor, our signals might already be blocked. */ + GC_unblock_gc_signals(); +# endif + return me; +} + +GC_API void GC_CALL GC_allow_register_threads(void) +{ +# ifdef GC_ASSERTIONS + DCL_LOCK_STATE; + + /* Check GC is initialized and the current thread is registered. */ + LOCK(); /* just to match that in win32_threads.c */ + GC_ASSERT(GC_lookup_thread(pthread_self()) != 0); + UNLOCK(); +# endif + INIT_REAL_SYMS(); /* to initialize symbols while single-threaded */ + GC_start_mark_threads(); + set_need_to_lock(); +} + +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) +{ + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + + if (GC_need_to_lock == FALSE) + ABORT("Threads explicit registering is not previously enabled"); + + LOCK(); + me = GC_lookup_thread(self); + if (0 == me) { + me = GC_register_my_thread_inner(sb, self); +# if defined(CPPCHECK) + GC_noop1(me->flags); +# endif + me -> flags |= DETACHED; + /* Treat as detached, since we do not need to worry about */ + /* pointer results. */ +# if defined(THREAD_LOCAL_ALLOC) + GC_init_thread_local(&(me->tlfs)); +# endif + UNLOCK(); + return GC_SUCCESS; + } else if ((me -> flags & FINISHED) != 0) { + /* This code is executed when a thread is registered from the */ + /* client thread key destructor. */ +# ifdef NACL + GC_nacl_gc_thread_self = me; + GC_nacl_initialize_gc_thread(); +# endif +# ifdef GC_DARWIN_THREADS + /* Reinitialize mach_thread to avoid thread_suspend fail */ + /* with MACH_SEND_INVALID_DEST error. */ + me -> stop_info.mach_thread = mach_thread_self(); +# endif + GC_record_stack_base(me, sb); + me -> flags &= ~FINISHED; /* but not DETACHED */ +# ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + /* Since this could be executed from a thread destructor, */ + /* our signals might be blocked. */ + GC_unblock_gc_signals(); +# endif +# if defined(THREAD_LOCAL_ALLOC) + GC_init_thread_local(&(me->tlfs)); +# endif +# if defined(GC_ENABLE_SUSPEND_THREAD) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(NACL) \ + && !defined(PLATFORM_STOP_WORLD) && !defined(SN_TARGET_PSP2) + if ((me -> stop_info.ext_suspend_cnt & 1) != 0) { + GC_with_callee_saves_pushed(GC_suspend_self_blocked, (ptr_t)me); + } +# endif + UNLOCK(); + return GC_SUCCESS; + } else { + UNLOCK(); + return GC_DUPLICATE; + } +} + +struct start_info { + void *(*start_routine)(void *); + void *arg; + word flags; + sem_t registered; /* 1 ==> in our thread table, but */ + /* parent hasn't yet noticed. */ +}; + +/* 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 in that file directly). */ +GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread( + void *(**pstart)(void *), + void **pstart_arg, + struct GC_stack_base *sb, void *arg) +{ + struct start_info * si = (struct start_info *)arg; + pthread_t self = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + +# ifdef DEBUG_THREADS + GC_log_printf("Starting thread %p, pid= %ld, sp= %p\n", + (void *)self, (long)getpid(), (void *)&arg); +# endif + LOCK(); + me = GC_register_my_thread_inner(sb, self); + me -> flags = si -> flags; +# if defined(THREAD_LOCAL_ALLOC) + GC_init_thread_local(&(me->tlfs)); +# endif + UNLOCK(); + *pstart = si -> start_routine; +# ifdef DEBUG_THREADS + GC_log_printf("start_routine= %p\n", (void *)(signed_word)(*pstart)); +# endif + *pstart_arg = si -> arg; + sem_post(&(si -> registered)); /* Last action on si. */ + /* OK to deallocate. */ + return me; +} + +#if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + STATIC void * GC_start_routine(void * arg) + { +# ifdef INCLUDE_LINUX_THREAD_DESCR + struct GC_stack_base sb; + +# ifdef REDIRECT_MALLOC + /* GC_get_stack_base may call pthread_getattr_np, which can */ + /* unfortunately call realloc, which may allocate from an */ + /* unregistered thread. This is unpleasant, since it might */ + /* force heap growth (or, even, heap overflow). */ + GC_disable(); +# endif + if (GC_get_stack_base(&sb) != GC_SUCCESS) + ABORT("Failed to get thread stack base"); +# ifdef REDIRECT_MALLOC + GC_enable(); +# endif + return GC_inner_start_routine(&sb, arg); +# else + return GC_call_with_stack_base(GC_inner_start_routine, arg); +# endif + } + + GC_API int WRAP_FUNC(pthread_create)(pthread_t *new_thread, + GC_PTHREAD_CREATE_CONST pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) + { + int result; + int detachstate; + word my_flags = 0; + struct start_info si; + DCL_LOCK_STATE; + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ + + /* We resist the temptation to muck with the stack size here, */ + /* even if the default is unreasonably small. That's the client's */ + /* responsibility. */ + + INIT_REAL_SYMS(); + if (!EXPECT(parallel_initialized, TRUE)) + GC_init_parallel(); + if (sem_init(&si.registered, GC_SEM_INIT_PSHARED, 0) != 0) + ABORT("sem_init failed"); + + si.start_routine = start_routine; + si.arg = arg; + LOCK(); + if (!EXPECT(GC_thr_initialized, TRUE)) + GC_thr_init(); +# ifdef GC_ASSERTIONS + { + size_t stack_size = 0; + if (NULL != attr) { + if (pthread_attr_getstacksize(attr, &stack_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + } + if (0 == stack_size) { + pthread_attr_t my_attr; + + if (pthread_attr_init(&my_attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_getstacksize(&my_attr, &stack_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + (void)pthread_attr_destroy(&my_attr); + } + /* On Solaris 10, with default attr initialization, */ + /* stack_size remains 0. Fudge it. */ + if (0 == stack_size) { +# ifndef SOLARIS + WARN("Failed to get stack size for assertion checking\n", 0); +# endif + stack_size = 1000000; + } + GC_ASSERT(stack_size >= 65536); + /* Our threads may need to do some work for the GC. */ + /* Ridiculously small threads won't work, and they */ + /* probably wouldn't work anyway. */ + } +# endif + if (NULL == attr) { + detachstate = PTHREAD_CREATE_JOINABLE; + } else { + if (pthread_attr_getdetachstate(attr, &detachstate) != 0) + ABORT("pthread_attr_getdetachstate failed"); + } + if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; + si.flags = my_flags; + UNLOCK(); +# ifdef DEBUG_THREADS + GC_log_printf("About to start new thread from thread %p\n", + (void *)pthread_self()); +# endif +# ifdef PARALLEL_MARK + if (EXPECT(!GC_parallel && available_markers_m1 > 0, FALSE)) + GC_start_mark_threads(); +# endif + set_need_to_lock(); + result = REAL_FUNC(pthread_create)(new_thread, attr, GC_start_routine, + &si); + + /* Wait until child has been added to the thread table. */ + /* This also ensures that we hold onto the stack-allocated si until */ + /* the child is done with it. */ + if (0 == result) { + IF_CANCEL(int cancel_state;) + +# ifdef DEBUG_THREADS + /* new_thread is non-NULL because pthread_create requires it. */ + GC_log_printf("Started thread %p\n", (void *)(*new_thread)); +# endif + DISABLE_CANCEL(cancel_state); + /* pthread_create is not a cancellation point. */ + while (0 != sem_wait(&si.registered)) { +# if defined(GC_HAIKU_THREADS) + /* To workaround some bug in Haiku semaphores. */ + if (EACCES == errno) continue; +# endif + if (EINTR != errno) ABORT("sem_wait failed"); + } + RESTORE_CANCEL(cancel_state); + } + sem_destroy(&si.registered); + return(result); + } +#endif /* !SN_TARGET_ORBIS && !SN_TARGET_PSP2 */ + +#if defined(USE_SPIN_LOCK) || !defined(NO_PTHREAD_TRYLOCK) +/* Spend a few cycles in a way that can't introduce contention with */ +/* other threads. */ +#define GC_PAUSE_SPIN_CYCLES 10 +STATIC void GC_pause(void) +{ + int i; + + for (i = 0; i < GC_PAUSE_SPIN_CYCLES; ++i) { + /* Something that's unlikely to be optimized away. */ +# if defined(AO_HAVE_compiler_barrier) \ + && !defined(BASE_ATOMIC_OPS_EMULATED) + AO_compiler_barrier(); +# else + GC_noop1(i); +# endif + } +} +#endif + +#ifndef SPIN_MAX +# define SPIN_MAX 128 /* Maximum number of calls to GC_pause before */ + /* give up. */ +#endif + +GC_INNER volatile GC_bool GC_collecting = FALSE; + /* A hint that we're in the collector and */ + /* holding the allocation lock for an */ + /* extended period. */ + +#if (!defined(USE_SPIN_LOCK) && !defined(NO_PTHREAD_TRYLOCK)) \ + || defined(PARALLEL_MARK) +/* If we don't want to use the below spinlock implementation, either */ +/* because we don't have a GC_test_and_set implementation, or because */ +/* we don't want to risk sleeping, we can still try spinning on */ +/* pthread_mutex_trylock for a while. This appears to be very */ +/* beneficial in many cases. */ +/* I suspect that under high contention this is nearly always better */ +/* than the spin lock. But it's a bit slower on a uniprocessor. */ +/* Hence we still default to the spin lock. */ +/* This is also used to acquire the mark lock for the parallel */ +/* marker. */ + +/* Here we use a strict exponential backoff scheme. I don't know */ +/* whether that's better or worse than the above. We eventually */ +/* yield by calling pthread_mutex_lock(); it never makes sense to */ +/* explicitly sleep. */ + +/* #define LOCK_STATS */ +/* Note that LOCK_STATS requires AO_HAVE_test_and_set. */ +#ifdef LOCK_STATS + 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) +{ +#ifndef NO_PTHREAD_TRYLOCK + unsigned pause_length = 1; + unsigned i; + + if (0 == pthread_mutex_trylock(lock)) { +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_unlocked_count); +# endif + return; + } + for (; pause_length <= SPIN_MAX; pause_length <<= 1) { + for (i = 0; i < pause_length; ++i) { + GC_pause(); + } + switch(pthread_mutex_trylock(lock)) { + case 0: +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_spin_count); +# endif + return; + case EBUSY: + break; + default: + ABORT("Unexpected error from pthread_mutex_trylock"); + } + } +#endif /* !NO_PTHREAD_TRYLOCK */ +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_block_count); +# endif + pthread_mutex_lock(lock); +} + +#endif /* !USE_SPIN_LOCK || ... */ + +#if defined(AO_HAVE_char_load) && !defined(BASE_ATOMIC_OPS_EMULATED) +# define is_collecting() \ + ((GC_bool)AO_char_load((unsigned char *)&GC_collecting)) +#else + /* GC_collecting is a hint, a potential data race between */ + /* GC_lock() and ENTER/EXIT_GC() is OK to ignore. */ +# define is_collecting() GC_collecting +#endif + +#if defined(USE_SPIN_LOCK) + +/* Reasonably fast spin locks. Basically the same implementation */ +/* as STL alloc.h. This isn't really the right way to do this. */ +/* but until the POSIX scheduling mess gets straightened out ... */ + +GC_INNER volatile AO_TS_t GC_allocate_lock = AO_TS_INITIALIZER; + +# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ +# define high_spin_max SPIN_MAX /* spin cycles for multiprocessor */ + + static volatile AO_t spin_max = low_spin_max; + static volatile AO_t last_spins = 0; + /* A potential data race between */ + /* threads invoking GC_lock which reads */ + /* and updates spin_max and last_spins */ + /* could be ignored because these */ + /* variables are hints only. */ + +GC_INNER void GC_lock(void) +{ + unsigned my_spin_max; + unsigned my_last_spins; + unsigned i; + + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) { + return; + } + my_spin_max = (unsigned)AO_load(&spin_max); + my_last_spins = (unsigned)AO_load(&last_spins); + for (i = 0; i < my_spin_max; i++) { + if (is_collecting() || GC_nprocs == 1) + goto yield; + if (i < my_last_spins/2) { + GC_pause(); + continue; + } + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) { + /* + * got it! + * Spinning worked. Thus we're probably not being scheduled + * against the other process with which we were contending. + * Thus it makes sense to spin longer the next time. + */ + AO_store(&last_spins, (AO_t)i); + AO_store(&spin_max, (AO_t)high_spin_max); + return; + } + } + /* We are probably being scheduled against the other process. Sleep. */ + AO_store(&spin_max, (AO_t)low_spin_max); +yield: + for (i = 0;; ++i) { + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) { + return; + } +# define SLEEP_THRESHOLD 12 + /* Under Linux very short sleeps tend to wait until */ + /* the current time quantum expires. On old Linux */ + /* kernels nanosleep (<= 2 ms) just spins. */ + /* (Under 2.4, this happens only for real-time */ + /* processes.) We want to minimize both behaviors */ + /* here. */ + if (i < SLEEP_THRESHOLD) { + sched_yield(); + } else { + struct timespec ts; + + if (i > 24) i = 24; + /* Don't wait for more than about 15 ms, */ + /* even under extreme contention. */ + ts.tv_sec = 0; + ts.tv_nsec = 1 << i; + nanosleep(&ts, 0); + } + } +} + +#elif defined(USE_PTHREAD_LOCKS) + +# ifndef NO_PTHREAD_TRYLOCK + GC_INNER void GC_lock(void) + { + if (1 == GC_nprocs || is_collecting()) { + pthread_mutex_lock(&GC_allocate_ml); + } else { + GC_generic_lock(&GC_allocate_ml); + } + } +# elif defined(GC_ASSERTIONS) + GC_INNER void GC_lock(void) + { + pthread_mutex_lock(&GC_allocate_ml); + } +# endif + +#endif /* !USE_SPIN_LOCK && USE_PTHREAD_LOCKS */ + +#ifdef PARALLEL_MARK + +# 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 */ + +static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER; + +static void setup_mark_lock(void) +{ +# ifdef GLIBC_2_19_TSX_BUG + pthread_mutexattr_t mattr; + int glibc_minor = -1; + int glibc_major = GC_parse_version(&glibc_minor, gnu_get_libc_version()); + + if (glibc_major > 2 || (glibc_major == 2 && glibc_minor >= 19)) { + /* TODO: disable this workaround for glibc with fixed TSX */ + /* This disables lock elision to workaround a bug in glibc 2.19+ */ + if (0 != pthread_mutexattr_init(&mattr)) { + ABORT("pthread_mutexattr_init failed"); + } + if (0 != pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL)) { + ABORT("pthread_mutexattr_settype failed"); + } + if (0 != pthread_mutex_init(&mark_mutex, &mattr)) { + ABORT("pthread_mutex_init failed"); + } + (void)pthread_mutexattr_destroy(&mattr); + } +# endif +} + +GC_INNER void GC_acquire_mark_lock(void) +{ +# if defined(NUMERIC_THREAD_ID_UNIQUE) && !defined(THREAD_SANITIZER) + GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self())); +# endif + GC_generic_lock(&mark_mutex); + SET_MARK_LOCK_HOLDER; +} + +GC_INNER void GC_release_mark_lock(void) +{ + UNSET_MARK_LOCK_HOLDER; + if (pthread_mutex_unlock(&mark_mutex) != 0) { + ABORT("pthread_mutex_unlock failed"); + } +} + +/* Collector must wait for a freelist builders for 2 reasons: */ +/* 1) Mark bits may still be getting examined without lock. */ +/* 2) Partial free lists referenced only by locals may not be scanned */ +/* correctly, e.g. if they contain "pointer-free" objects, since the */ +/* free-list link may be ignored. */ +STATIC void GC_wait_builder(void) +{ + ASSERT_CANCEL_DISABLED(); + 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); + SET_MARK_LOCK_HOLDER; +} + +GC_INNER void GC_wait_for_reclaim(void) +{ + GC_acquire_mark_lock(); + while (GC_fl_builder_count > 0) { + GC_wait_builder(); + } + GC_release_mark_lock(); +} + +# if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER) + /* Identical to GC_wait_for_reclaim() but with the no_sanitize */ + /* attribute as a workaround for TSan which does not notice that */ + /* the GC lock is acquired in fork_prepare_proc(). */ + GC_ATTR_NO_SANITIZE_THREAD + static void wait_for_reclaim_atfork(void) + { + GC_acquire_mark_lock(); + while (GC_fl_builder_count > 0) + GC_wait_builder(); + GC_release_mark_lock(); + } +# endif /* CAN_HANDLE_FORK && THREAD_SANITIZER */ + +GC_INNER void GC_notify_all_builder(void) +{ + GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self())); + if (pthread_cond_broadcast(&builder_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } +} + +GC_INNER void GC_wait_marker(void) +{ + ASSERT_CANCEL_DISABLED(); + GC_ASSERT(GC_parallel); + 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); + SET_MARK_LOCK_HOLDER; +} + +GC_INNER void GC_notify_all_marker(void) +{ + GC_ASSERT(GC_parallel); + if (pthread_cond_broadcast(&mark_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } +} + +#endif /* PARALLEL_MARK */ + +#ifdef PTHREAD_REGISTER_CANCEL_WEAK_STUBS + /* Workaround "undefined reference" linkage errors on some targets. */ + EXTERN_C_BEGIN + extern void __pthread_register_cancel(void) __attribute__((__weak__)); + extern void __pthread_unregister_cancel(void) __attribute__((__weak__)); + EXTERN_C_END + + void __pthread_register_cancel(void) {} + void __pthread_unregister_cancel(void) {} +#endif + +#endif /* GC_PTHREADS */ diff --git a/bdwgc/ptr_chck.c b/bdwgc/ptr_chck.c new file mode 100644 index 000000000..84b5743aa --- /dev/null +++ b/bdwgc/ptr_chck.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +#include "private/gc_pmark.h" + +/* + * These are checking routines calls to which could be inserted by a + * preprocessor to validate C pointer arithmetic. + */ + +STATIC void GC_CALLBACK GC_default_same_obj_print_proc(void * p, void * q) +{ + 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 *) + = GC_default_same_obj_print_proc; + +/* 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...) */ +/* 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 */ +/* debugging intolerably slow.) */ +GC_API void * GC_CALL GC_same_obj(void *p, void *q) +{ + struct hblk *h; + hdr *hhdr; + ptr_t base, limit; + word sz; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + hhdr = HDR((word)p); + if (hhdr == 0) { + if (divHBLKSZ((word)p) != divHBLKSZ((word)q) + && HDR((word)q) != 0) { + goto fail; + } + return(p); + } + /* If it's a pointer to the middle of a large object, move it */ + /* to the beginning. */ + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = HBLKPTR(p) - (word)hhdr; + hhdr = HDR(h); + while (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = FORWARDED_ADDR(h, hhdr); + hhdr = HDR(h); + } + limit = (ptr_t)h + hhdr -> hb_sz; + if ((word)p >= (word)limit || (word)q >= (word)limit + || (word)q < (word)h) { + goto fail; + } + return(p); + } + sz = hhdr -> hb_sz; + if (sz > MAXOBJBYTES) { + base = (ptr_t)HBLKPTR(p); + limit = base + sz; + if ((word)p >= (word)limit) { + goto fail; + } + } else { + size_t offset; + size_t pdispl = HBLKDISPL(p); + + offset = pdispl % sz; + if (HBLKPTR(p) != HBLKPTR(q)) goto fail; + /* W/o this check, we might miss an error if */ + /* q points to the first object on a page, and */ + /* points just before the page. */ + base = (ptr_t)p - offset; + limit = base + sz; + } + /* [base, limit) delimits the object containing p, if any. */ + /* If p is not inside a valid object, then either q is */ + /* also outside any valid object, or it is outside */ + /* [base, limit). */ + if ((word)q >= (word)limit || (word)q < (word)base) { + goto fail; + } + return(p); +fail: + (*GC_same_obj_print_proc)((ptr_t)p, (ptr_t)q); + return(p); +} + +STATIC void GC_CALLBACK GC_default_is_valid_displacement_print_proc (void *p) +{ + ABORT_ARG1("GC_is_valid_displacement test failed", ": %p not valid", p); +} + +void (GC_CALLBACK *GC_is_valid_displacement_print_proc)(void *) = + GC_default_is_valid_displacement_print_proc; + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. */ +/* Uninteresting with GC_all_interior_pointers. */ +/* Always returns its argument. */ +/* Note that we don't lock, since nothing relevant about the header */ +/* should change while we have a valid object pointer to the block. */ +GC_API void * GC_CALL GC_is_valid_displacement(void *p) +{ + hdr *hhdr; + word pdispl; + word offset; + struct hblk *h; + word sz; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + if (NULL == p) return NULL; + hhdr = HDR((word)p); + if (hhdr == 0) return(p); + h = HBLKPTR(p); + if (GC_all_interior_pointers) { + while (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = FORWARDED_ADDR(h, hhdr); + hhdr = HDR(h); + } + } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + goto fail; + } + sz = hhdr -> hb_sz; + pdispl = HBLKDISPL(p); + offset = pdispl % sz; + if ((sz > MAXOBJBYTES && (word)p >= (word)h + sz) + || !GC_valid_offsets[offset] + || ((word)p + (sz - offset) > (word)(h + 1) + && !IS_FORWARDING_ADDR_OR_NIL(HDR(h + 1)))) { + goto fail; + } + return(p); +fail: + (*GC_is_valid_displacement_print_proc)((ptr_t)p); + return(p); +} + +STATIC void GC_CALLBACK GC_default_is_visible_print_proc(void * p) +{ + ABORT_ARG1("GC_is_visible test failed", ": %p not GC-visible", p); +} + +void (GC_CALLBACK *GC_is_visible_print_proc)(void * p) = + GC_default_is_visible_print_proc; + +#ifndef THREADS +/* Could p be a stack address? */ + STATIC GC_bool GC_on_stack(void *p) + { +# 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 + +/* Check that p is visible */ +/* to the collector as a possibly pointer containing location. */ +/* If it isn't, invoke *GC_is_visible_print_proc. */ +/* Returns the argument in all cases. May erroneously succeed */ +/* 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 the multi-threaded worlds. */ +GC_API void * GC_CALL GC_is_visible(void *p) +{ + hdr *hhdr; + + if ((word)p & (ALIGNMENT - 1)) goto fail; + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); +# ifdef THREADS + hhdr = HDR((word)p); + if (hhdr != 0 && GC_base(p) == 0) { + goto fail; + } else { + /* May be inside thread stack. We can't do much. */ + return(p); + } +# else + /* Check stack first: */ + if (GC_on_stack(p)) return(p); + hhdr = HDR((word)p); + if (hhdr == 0) { + if (GC_is_static_root(p)) return(p); + /* Else do it again correctly: */ +# if defined(DYNAMIC_LOADING) || defined(MSWIN32) \ + || defined(MSWINCE) || defined(CYGWIN32) || defined(PCR) + if (!GC_no_dls) { + GC_register_dynamic_libraries(); + if (GC_is_static_root(p)) return p; + } +# endif + goto fail; + } else { + /* p points to the heap. */ + word descr; + ptr_t base = (ptr_t)GC_base(p); + /* TODO: should GC_base be manually inlined? */ + + if (NULL == base) goto fail; + if (HBLKPTR(base) != HBLKPTR(p)) + hhdr = HDR(base); + descr = hhdr -> hb_descr; + retry: + switch(descr & GC_DS_TAGS) { + case GC_DS_LENGTH: + if ((word)p - (word)base > descr) goto fail; + break; + case GC_DS_BITMAP: + 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; + case GC_DS_PROC: + /* We could try to decipher this partially. */ + /* For now we just punt. */ + break; + case GC_DS_PER_OBJECT: + if ((signed_word)descr >= 0) { + descr = *(word *)((ptr_t)base + (descr & ~GC_DS_TAGS)); + } else { + ptr_t type_descr = *(ptr_t *)base; + descr = *(word *)(type_descr + - (descr - (word)(GC_DS_PER_OBJECT + - GC_INDIR_PER_OBJ_BIAS))); + } + goto retry; + } + return(p); + } +# endif +fail: + (*GC_is_visible_print_proc)((ptr_t)p); + return(p); +} + +GC_API void * GC_CALL GC_pre_incr (void **p, ptrdiff_t how_much) +{ + void * initial = *p; + void * result = GC_same_obj((void *)((ptr_t)initial + how_much), initial); + + if (!GC_all_interior_pointers) { + (void) GC_is_valid_displacement(result); + } + return (*p = result); +} + +GC_API void * GC_CALL GC_post_incr (void **p, ptrdiff_t how_much) +{ + void * initial = *p; + void * result = GC_same_obj((void *)((ptr_t)initial + how_much), initial); + + if (!GC_all_interior_pointers) { + (void) GC_is_valid_displacement(result); + } + *p = result; + return(initial); +} diff --git a/bdwgc/reclaim.c b/bdwgc/reclaim.c new file mode 100644 index 000000000..18e2b2353 --- /dev/null +++ b/bdwgc/reclaim.c @@ -0,0 +1,853 @@ +/* + * Copyright 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-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * 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. + */ + +#include "private/gc_priv.h" + +#ifdef ENABLE_DISCLAIM +# include "gc_disclaim.h" +#endif + +#include + +GC_INNER signed_word GC_bytes_found = 0; + /* Number of bytes of memory reclaimed */ + /* minus the number of bytes originally */ + /* on free lists which we had to drop. */ + +#if defined(PARALLEL_MARK) + GC_INNER signed_word GC_fl_builder_count = 0; + /* Number of threads currently building free lists without */ + /* holding GC lock. It is not safe to collect if this is */ + /* nonzero. Also, together with the mark lock, it is used as */ + /* a semaphore during marker threads startup. */ +#endif /* PARALLEL_MARK */ + +/* We defer printing of leaked objects until we're done with the GC */ +/* cycle, since the routine for printing objects needs to run outside */ +/* the collector, e.g. without the allocation lock. */ +#ifndef MAX_LEAKED +# define MAX_LEAKED 40 +#endif +STATIC ptr_t GC_leaked[MAX_LEAKED] = { NULL }; +STATIC unsigned GC_n_leaked = 0; + +#ifdef AO_HAVE_store + GC_INNER volatile AO_t GC_have_errors = 0; +#else + GC_INNER GC_bool GC_have_errors = FALSE; +#endif + +#if !defined(EAGER_SWEEP) && defined(ENABLE_DISCLAIM) + STATIC void GC_reclaim_unconditionally_marked(void); +#endif + +GC_INLINE void GC_add_leaked(ptr_t leaked) +{ +# ifndef SHORT_DBG_HDRS + if (GC_findleak_delay_free && !GC_check_leaked(leaked)) + return; +# endif + + GC_SET_HAVE_ERRORS(); + if (GC_n_leaked < MAX_LEAKED) { + GC_leaked[GC_n_leaked++] = leaked; + /* Make sure it's not reclaimed this cycle */ + GC_set_mark_bit(leaked); + } +} + +/* Print all objects on the list after printing any smashed objects. */ +/* Clear both lists. Called without the allocation lock held. */ +GC_INNER void GC_print_all_errors(void) +{ + static GC_bool printing_errors = FALSE; + GC_bool have_errors; + unsigned i, n_leaked; + ptr_t leaked[MAX_LEAKED]; + DCL_LOCK_STATE; + + LOCK(); + if (printing_errors) { + UNLOCK(); + return; + } + have_errors = get_have_errors(); + printing_errors = TRUE; + n_leaked = GC_n_leaked; + if (n_leaked > 0) { + GC_ASSERT(n_leaked <= MAX_LEAKED); + BCOPY(GC_leaked, leaked, n_leaked * sizeof(ptr_t)); + GC_n_leaked = 0; + BZERO(GC_leaked, n_leaked * sizeof(ptr_t)); + } + UNLOCK(); + + if (GC_debugging_started) { + GC_print_all_smashed(); + } else { + have_errors = FALSE; + } + + if (n_leaked > 0) { + GC_err_printf("Found %u leaked objects:\n", n_leaked); + have_errors = TRUE; + } + for (i = 0; i < n_leaked; i++) { + ptr_t p = leaked[i]; +# ifndef SKIP_LEAKED_OBJECTS_PRINTING + GC_print_heap_obj(p); +# endif + GC_free(p); + } + + if (have_errors +# ifndef GC_ABORT_ON_LEAK + && GETENV("GC_ABORT_ON_LEAK") != NULL +# endif + ) { + ABORT("Leaked or smashed objects encountered"); + } + + LOCK(); + printing_errors = FALSE; + UNLOCK(); +} + + +/* + * reclaim phase + * + */ + +/* Test whether a block is completely empty, i.e. contains no marked */ +/* objects. This does not require the block to be in physical memory. */ +GC_INNER GC_bool GC_block_empty(hdr *hhdr) +{ + return (hhdr -> hb_n_marks == 0); +} + +STATIC GC_bool GC_block_nearly_full(hdr *hhdr, word sz) +{ + return hhdr -> hb_n_marks > HBLK_OBJS(sz) * 7 / 8; +} + +/* TODO: This should perhaps again be specialized for USE_MARK_BYTES */ +/* and USE_MARK_BITS cases. */ + +GC_INLINE word *GC_clear_block(word *p, word sz, signed_word *count) +{ + word *q = (word *)((ptr_t)p + sz); + + /* Clear object, advance p to next object in the process. */ +# ifdef USE_MARK_BYTES + GC_ASSERT((sz & 1) == 0); + GC_ASSERT(((word)p & (2 * sizeof(word) - 1)) == 0); + p[1] = 0; + p += 2; + while ((word)p < (word)q) { + CLEAR_DOUBLE(p); + p += 2; + } +# else + p++; /* Skip link field */ + while ((word)p < (word)q) { + *p++ = 0; + } +# endif + *count += sz; + return p; +} + +/* + * Restore unmarked small objects in h of size sz to the object + * free list. Returns the new list. + * Clears unmarked objects. Sz is in bytes. + */ +STATIC ptr_t GC_reclaim_clear(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, signed_word *count) +{ + word bit_no = 0; + ptr_t p, plim; + + GC_ASSERT(hhdr == GC_find_header((ptr_t)hbp)); +# ifndef THREADS + GC_ASSERT(sz == hhdr -> hb_sz); +# else + /* Skip the assertion because of a potential race with GC_realloc. */ +# endif + GC_ASSERT((sz & (BYTES_PER_WORD-1)) == 0); + p = hbp->hb_body; + plim = p + HBLKSIZE - sz; + + /* go through all words in block */ + while ((word)p <= (word)plim) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + p += sz; + } else { + /* Object is available - put it on list. */ + obj_link(p) = list; + list = p; + + p = (ptr_t)GC_clear_block((word *)p, sz, count); + } + bit_no += MARK_BIT_OFFSET(sz); + } + return list; +} + +/* The same thing, but don't clear objects: */ +STATIC ptr_t GC_reclaim_uninit(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, signed_word *count) +{ + word bit_no = 0; + word *p, *plim; + signed_word n_bytes_found = 0; + +# ifndef THREADS + GC_ASSERT(sz == hhdr -> hb_sz); +# endif + p = (word *)(hbp->hb_body); + plim = (word *)((ptr_t)hbp + HBLKSIZE - sz); + + /* go through all words in block */ + 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; + list = ((ptr_t)p); + } + p = (word *)((ptr_t)p + sz); + bit_no += MARK_BIT_OFFSET(sz); + } + *count += n_bytes_found; + return(list); +} + +#ifdef ENABLE_DISCLAIM + /* Call reclaim notifier for block's kind on each unmarked object in */ + /* block, all within a pair of corresponding enter/leave callbacks. */ + STATIC ptr_t GC_disclaim_and_reclaim(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, signed_word *count) + { + word bit_no = 0; + ptr_t p, plim; + struct obj_kind *ok = &GC_obj_kinds[hhdr->hb_obj_kind]; + int (GC_CALLBACK *disclaim)(void *) = ok->ok_disclaim_proc; + + GC_ASSERT(disclaim != 0); +# ifndef THREADS + GC_ASSERT(sz == hhdr -> hb_sz); +# endif + p = hbp->hb_body; + plim = p + HBLKSIZE - sz; + + for (; (word)p <= (word)plim; bit_no += MARK_BIT_OFFSET(sz)) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + p += sz; + } else if ((*disclaim)(p)) { + set_mark_bit_from_hdr(hhdr, bit_no); + hhdr -> hb_n_marks++; + p += sz; + } else { + obj_link(p) = list; + list = p; + p = (ptr_t)GC_clear_block((word *)p, sz, count); + } + } + return list; + } +#endif /* ENABLE_DISCLAIM */ + +/* Don't really reclaim objects, just check for unmarked ones: */ +STATIC void GC_reclaim_check(struct hblk *hbp, hdr *hhdr, word sz) +{ + word bit_no; + ptr_t p, plim; + +# ifndef THREADS + GC_ASSERT(sz == hhdr -> hb_sz); +# endif + /* go through all words in block */ + p = hbp->hb_body; + plim = p + HBLKSIZE - 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); + } + } +} + +/* Is a pointer-free block? Same as IS_PTRFREE macro (in os_dep.c) but */ +/* uses unordered atomic access to avoid racing with GC_realloc. */ +#ifdef AO_HAVE_load +# define IS_PTRFREE_SAFE(hhdr) \ + (AO_load((volatile AO_t *)&(hhdr)->hb_descr) == 0) +#else + /* No race as GC_realloc holds the lock while updating hb_descr. */ +# define IS_PTRFREE_SAFE(hhdr) ((hhdr)->hb_descr == 0) +#endif + +/* + * Generic procedure to rebuild a free list in hbp. + * Also called directly from GC_malloc_many. + * Sz is now in bytes. + */ +GC_INNER ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz, + GC_bool init, ptr_t list, + signed_word *count) +{ + ptr_t result; + + GC_ASSERT(GC_find_header((ptr_t)hbp) == hhdr); +# ifndef GC_DISABLE_INCREMENTAL + GC_remove_protection(hbp, 1, IS_PTRFREE_SAFE(hhdr)); +# endif +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & HAS_DISCLAIM) != 0) { + result = GC_disclaim_and_reclaim(hbp, hhdr, sz, list, count); + } else +# endif + /* else */ if (init || GC_debugging_started) { + result = GC_reclaim_clear(hbp, hhdr, sz, list, count); + } else { + GC_ASSERT(IS_PTRFREE_SAFE(hhdr)); + result = GC_reclaim_uninit(hbp, hhdr, sz, list, count); + } + if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) GC_set_hdr_marks(hhdr); + return result; +} + +/* + * Restore unmarked small objects in the block pointed to by hbp + * to the appropriate object free list. + * If entirely empty blocks are to be completely deallocated, then + * caller should perform that check. + */ +STATIC void GC_reclaim_small_nonempty_block(struct hblk *hbp, word sz, + GC_bool report_if_found) +{ + hdr *hhdr = HDR(hbp); + struct obj_kind * ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + void **flh = &(ok -> ok_freelist[BYTES_TO_GRANULES(sz)]); + + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; + + if (report_if_found) { + GC_reclaim_check(hbp, hhdr, sz); + } else { + *flh = GC_reclaim_generic(hbp, hhdr, sz, ok -> ok_init, + (ptr_t)(*flh), &GC_bytes_found); + } +} + +#ifdef ENABLE_DISCLAIM + STATIC void GC_disclaim_and_reclaim_or_free_small_block(struct hblk *hbp) + { + hdr *hhdr = HDR(hbp); + word sz = hhdr -> hb_sz; + struct obj_kind * ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + void **flh = &(ok -> ok_freelist[BYTES_TO_GRANULES(sz)]); + void *flh_next; + + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; + flh_next = GC_reclaim_generic(hbp, hhdr, sz, ok -> ok_init, + (ptr_t)(*flh), &GC_bytes_found); + if (hhdr -> hb_n_marks) + *flh = flh_next; + else { + GC_bytes_found += HBLKSIZE; + GC_freehblk(hbp); + } + } +#endif /* ENABLE_DISCLAIM */ + +/* + * Restore an unmarked large object or an entirely empty blocks of small objects + * to the heap block free list. + * Otherwise enqueue the block for later processing + * by GC_reclaim_small_nonempty_block. + * If report_if_found is TRUE, then process any block immediately, and + * simply report free objects; do not actually reclaim them. + */ +STATIC void GC_reclaim_block(struct hblk *hbp, word report_if_found) +{ + hdr * hhdr = HDR(hbp); + word sz; /* size of objects in current block */ + struct obj_kind * ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + +# ifdef AO_HAVE_load + /* Atomic access is used to avoid racing with GC_realloc. */ + sz = (word)AO_load((volatile AO_t *)&hhdr->hb_sz); +# else + /* No race as GC_realloc holds the lock while updating hb_sz. */ + sz = hhdr -> hb_sz; +# endif + if( sz > MAXOBJBYTES ) { /* 1 big object */ + if( !mark_bit_from_hdr(hhdr, 0) ) { + if (report_if_found) { + GC_add_leaked((ptr_t)hbp); + } else { + word blocks; + +# ifdef ENABLE_DISCLAIM + if (EXPECT(hhdr->hb_flags & HAS_DISCLAIM, 0)) { + if ((*ok->ok_disclaim_proc)(hbp)) { + /* Not disclaimed => resurrect the object. */ + set_mark_bit_from_hdr(hhdr, 0); + goto in_use; + } + } +# endif + blocks = OBJ_SZ_TO_BLOCKS(sz); +# if defined(CPPCHECK) + GC_noop1((word)&blocks); +# endif + if (blocks > 1) { + GC_large_allocd_bytes -= blocks * HBLKSIZE; + } + GC_bytes_found += sz; + GC_freehblk(hbp); + } + } else { +# ifdef ENABLE_DISCLAIM + in_use: +# endif + if (IS_PTRFREE_SAFE(hhdr)) { + GC_atomic_in_use += sz; + } else { + GC_composite_in_use += sz; + } + } + } else { + GC_bool empty = GC_block_empty(hhdr); +# ifdef PARALLEL_MARK + /* Count can be low or one too high because we sometimes */ + /* have to ignore decrements. Objects can also potentially */ + /* be repeatedly marked by each marker. */ + /* Here we assume 3 markers at most, but this is extremely */ + /* unlikely to fail spuriously with more. And if it does, it */ + /* should be looked at. */ + GC_ASSERT(sz != 0 && (GC_markers_m1 > 1 ? 3 : GC_markers_m1 + 1) + * (HBLKSIZE/sz + 1) + 16 >= hhdr->hb_n_marks); +# else + GC_ASSERT(sz * hhdr -> hb_n_marks <= HBLKSIZE); +# endif + if (report_if_found) { + GC_reclaim_small_nonempty_block(hbp, sz, + TRUE /* report_if_found */); + } else if (empty) { +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & HAS_DISCLAIM) != 0) { + GC_disclaim_and_reclaim_or_free_small_block(hbp); + } else +# endif + /* else */ { + GC_bytes_found += HBLKSIZE; + GC_freehblk(hbp); + } + } else if (GC_find_leak || !GC_block_nearly_full(hhdr, sz)) { + /* group of smaller objects, enqueue the real work */ + struct hblk **rlh = ok -> ok_reclaim_list; + + if (rlh != NULL) { + rlh += BYTES_TO_GRANULES(sz); + hhdr -> hb_next = *rlh; + *rlh = hbp; + } + } /* else not worth salvaging. */ + /* We used to do the nearly_full check later, but we */ + /* already have the right cache context here. Also */ + /* doing it here avoids some silly lock contention in */ + /* GC_malloc_many. */ + if (IS_PTRFREE_SAFE(hhdr)) { + GC_atomic_in_use += sz * hhdr -> hb_n_marks; + } else { + GC_composite_in_use += sz * hhdr -> hb_n_marks; + } + } +} + +#if !defined(NO_DEBUGGING) +/* Routines to gather and print heap block info */ +/* intended for debugging. Otherwise should be called */ +/* with lock. */ + +struct Print_stats +{ + size_t number_of_blocks; + size_t total_bytes; +}; + +#ifdef USE_MARK_BYTES + +/* Return the number of set mark bits in the given header. */ +/* Remains externally visible as used by GNU GCJ currently. */ +unsigned GC_n_set_marks(hdr *hhdr) +{ + unsigned result = 0; + word i; + word sz = hhdr -> hb_sz; + word offset = MARK_BIT_OFFSET(sz); + word limit = FINAL_MARK_BIT(sz); + + for (i = 0; i < limit; i += offset) { + result += hhdr -> hb_marks[i]; + } + GC_ASSERT(hhdr -> hb_marks[limit]); + return(result); +} + +#else + +/* Number of set bits in a word. Not performance critical. */ +static unsigned set_bits(word n) +{ + word m = n; + unsigned result = 0; + + while (m > 0) { + if (m & 1) result++; + m >>= 1; + } + return(result); +} + +unsigned GC_n_set_marks(hdr *hhdr) +{ + unsigned result = 0; + word i; + word n_mark_words; +# ifdef MARK_BIT_PER_OBJ + word n_objs = HBLK_OBJS(hhdr -> hb_sz); + + if (0 == n_objs) n_objs = 1; + n_mark_words = divWORDSZ(n_objs + WORDSZ - 1); +# else /* MARK_BIT_PER_GRANULE */ + n_mark_words = MARK_BITS_SZ; +# endif + for (i = 0; i < n_mark_words - 1; i++) { + result += set_bits(hhdr -> hb_marks[i]); + } +# ifdef MARK_BIT_PER_OBJ + result += set_bits((hhdr -> hb_marks[n_mark_words - 1]) + << (n_mark_words * WORDSZ - n_objs)); +# else + result += set_bits(hhdr -> hb_marks[n_mark_words - 1]); +# endif + return result; /* the number of set bits excluding the one past the end */ +} + +#endif /* !USE_MARK_BYTES */ + +STATIC void GC_print_block_descr(struct hblk *h, + word /* struct PrintStats */ raw_ps) +{ + hdr * hhdr = HDR(h); + size_t bytes = hhdr -> hb_sz; + struct Print_stats *ps; + unsigned n_marks = GC_n_set_marks(hhdr); + unsigned n_objs = (unsigned)HBLK_OBJS(bytes); + + if (0 == n_objs) n_objs = 1; + if (hhdr -> hb_n_marks != n_marks) { + GC_printf("%u,%u,%u!=%u,%u\n", hhdr->hb_obj_kind, (unsigned)bytes, + (unsigned)hhdr->hb_n_marks, n_marks, n_objs); + } else { + GC_printf("%u,%u,%u,%u\n", hhdr->hb_obj_kind, (unsigned)bytes, + n_marks, n_objs); + } + + ps = (struct Print_stats *)raw_ps; + ps->total_bytes += (bytes + (HBLKSIZE-1)) & ~(HBLKSIZE-1); /* round up */ + ps->number_of_blocks++; +} + +void GC_print_block_list(void) +{ + struct Print_stats pstats; + + GC_printf("kind(0=ptrfree,1=normal,2=unc.)," + "size_in_bytes,#_marks_set,#objs\n"); + pstats.number_of_blocks = 0; + pstats.total_bytes = 0; + GC_apply_to_all_blocks(GC_print_block_descr, (word)&pstats); + GC_printf("blocks= %lu, bytes= %lu\n", + (unsigned long)pstats.number_of_blocks, + (unsigned long)pstats.total_bytes); +} + +#include "gc_inline.h" /* for GC_print_free_list prototype */ + +/* Currently for debugger use only: */ +GC_API void GC_CALL GC_print_free_list(int kind, size_t sz_in_granules) +{ + void *flh_next; + int n; + + GC_ASSERT(kind < MAXOBJKINDS); + GC_ASSERT(sz_in_granules <= MAXOBJGRANULES); + flh_next = GC_obj_kinds[kind].ok_freelist[sz_in_granules]; + for (n = 0; flh_next; n++) { + GC_printf("Free object in heap block %p [%d]: %p\n", + (void *)HBLKPTR(flh_next), n, flh_next); + flh_next = obj_link(flh_next); + } +} + +#endif /* !NO_DEBUGGING */ + +/* + * Clear all obj_link pointers in the list of free objects *flp. + * Clear *flp. + * This must be done before dropping a list of free gcj-style objects, + * since may otherwise end up with dangling "descriptor" pointers. + * It may help for other pointer-containing objects. + */ +STATIC void GC_clear_fl_links(void **flp) +{ + void *next = *flp; + + while (0 != next) { + *flp = 0; + flp = &(obj_link(next)); + next = *flp; + } +} + +/* + * Perform GC_reclaim_block on the entire heap, after first clearing + * small object free lists (if we are not just looking for leaks). + */ +GC_INNER void GC_start_reclaim(GC_bool report_if_found) +{ + unsigned kind; + +# if defined(PARALLEL_MARK) + GC_ASSERT(0 == GC_fl_builder_count); +# endif + /* Reset in use counters. GC_reclaim_block recomputes them. */ + GC_composite_in_use = 0; + GC_atomic_in_use = 0; + /* Clear reclaim- and free-lists */ + for (kind = 0; kind < GC_n_kinds; kind++) { + struct hblk ** rlist = GC_obj_kinds[kind].ok_reclaim_list; + GC_bool should_clobber = (GC_obj_kinds[kind].ok_descriptor != 0); + + if (rlist == 0) continue; /* This kind not used. */ + if (!report_if_found) { + void **fop; + void **lim = &(GC_obj_kinds[kind].ok_freelist[MAXOBJGRANULES+1]); + + for (fop = GC_obj_kinds[kind].ok_freelist; + (word)fop < (word)lim; (*(word **)&fop)++) { + if (*fop != 0) { + if (should_clobber) { + GC_clear_fl_links(fop); + } else { + *fop = 0; + } + } + } + } /* otherwise free list objects are marked, */ + /* and it's safe to leave them. */ + BZERO(rlist, (MAXOBJGRANULES + 1) * sizeof(void *)); + } + + + /* Go through all heap blocks (in hblklist) and reclaim unmarked objects */ + /* or enqueue the block for later processing. */ + GC_apply_to_all_blocks(GC_reclaim_block, (word)report_if_found); + +# ifdef EAGER_SWEEP + /* This is a very stupid thing to do. We make it possible anyway, */ + /* so that you can convince yourself that it really is very stupid. */ + GC_reclaim_all((GC_stop_func)0, FALSE); +# elif defined(ENABLE_DISCLAIM) + /* However, make sure to clear reclaimable objects of kinds with */ + /* unconditional marking enabled before we do any significant */ + /* marking work. */ + GC_reclaim_unconditionally_marked(); +# endif +# if defined(PARALLEL_MARK) + GC_ASSERT(0 == GC_fl_builder_count); +# endif + +} + +/* + * Sweep blocks of the indicated object size and kind until either the + * appropriate free list is nonempty, or there are no more blocks to + * sweep. + */ +GC_INNER void GC_continue_reclaim(word sz /* granules */, int kind) +{ + hdr * hhdr; + struct hblk * hbp; + struct obj_kind * ok = &(GC_obj_kinds[kind]); + struct hblk ** rlh = ok -> ok_reclaim_list; + void **flh = &(ok -> ok_freelist[sz]); + + if (NULL == rlh) + return; /* No blocks of this kind. */ + + for (rlh += sz; (hbp = *rlh) != NULL; ) { + hhdr = HDR(hbp); + *rlh = hhdr -> hb_next; + GC_reclaim_small_nonempty_block(hbp, hhdr -> hb_sz, FALSE); + if (*flh != 0) + break; + } +} + +/* + * Reclaim all small blocks waiting to be reclaimed. + * Abort and return FALSE when/if (*stop_func)() returns TRUE. + * If this returns TRUE, then it's safe to restart the world + * with incorrectly cleared mark bits. + * If ignore_old is TRUE, then reclaim only blocks that have been + * recently reclaimed, and discard the rest. + * Stop_func may be 0. + */ +GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old) +{ + word sz; + unsigned kind; + hdr * hhdr; + struct hblk * hbp; + struct obj_kind * ok; + struct hblk ** rlp; + struct hblk ** rlh; +# ifndef NO_CLOCK + CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER; + + if (GC_print_stats == VERBOSE) + GET_TIME(start_time); +# endif + + for (kind = 0; kind < GC_n_kinds; kind++) { + ok = &(GC_obj_kinds[kind]); + rlp = ok -> ok_reclaim_list; + if (rlp == 0) continue; + for (sz = 1; sz <= MAXOBJGRANULES; sz++) { + for (rlh = rlp + sz; (hbp = *rlh) != NULL; ) { + if (stop_func != (GC_stop_func)0 && (*stop_func)()) { + return(FALSE); + } + hhdr = HDR(hbp); + *rlh = hhdr -> hb_next; + if (!ignore_old + || (word)hhdr->hb_last_reclaimed == GC_gc_no - 1) { + /* It's likely we'll need it this time, too */ + /* It's been touched recently, so this */ + /* shouldn't trigger paging. */ + GC_reclaim_small_nonempty_block(hbp, hhdr->hb_sz, FALSE); + } + } + } + } +# ifndef NO_CLOCK + if (GC_print_stats == VERBOSE) { + CLOCK_TYPE done_time; + + GET_TIME(done_time); + GC_verbose_log_printf( + "Disposing of reclaim lists took %lu ms %lu ns\n", + MS_TIME_DIFF(done_time, start_time), + NS_FRAC_TIME_DIFF(done_time, start_time)); + } +# endif + return(TRUE); +} + +#if !defined(EAGER_SWEEP) && defined(ENABLE_DISCLAIM) +/* We do an eager sweep on heap blocks where unconditional marking has */ +/* been enabled, so that any reclaimable objects have been reclaimed */ +/* before we start marking. This is a simplified GC_reclaim_all */ +/* restricted to kinds where ok_mark_unconditionally is true. */ + STATIC void GC_reclaim_unconditionally_marked(void) + { + word sz; + unsigned kind; + hdr * hhdr; + struct hblk * hbp; + struct obj_kind * ok; + struct hblk ** rlp; + struct hblk ** rlh; + + for (kind = 0; kind < GC_n_kinds; kind++) { + ok = &(GC_obj_kinds[kind]); + if (!ok->ok_mark_unconditionally) + continue; + rlp = ok->ok_reclaim_list; + if (rlp == 0) + continue; + for (sz = 1; sz <= MAXOBJGRANULES; sz++) { + rlh = rlp + sz; + while ((hbp = *rlh) != 0) { + hhdr = HDR(hbp); + *rlh = hhdr->hb_next; + GC_reclaim_small_nonempty_block(hbp, hhdr->hb_sz, FALSE); + } + } + } + } +#endif /* !EAGER_SWEEP && ENABLE_DISCLAIM */ + +struct enumerate_reachable_s { + GC_reachable_object_proc proc; + void *client_data; +}; + +STATIC void GC_do_enumerate_reachable_objects(struct hblk *hbp, word ped) +{ + struct hblkhdr *hhdr = HDR(hbp); + size_t sz = (size_t)hhdr->hb_sz; + size_t bit_no; + char *p, *plim; + + if (GC_block_empty(hhdr)) { + return; + } + + p = hbp->hb_body; + if (sz > MAXOBJBYTES) { /* one big object */ + plim = p; + } else { + 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) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + ((struct enumerate_reachable_s *)ped)->proc(p, sz, + ((struct enumerate_reachable_s *)ped)->client_data); + } + } +} + +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( + GC_reachable_object_proc proc, + void *client_data) +{ + struct enumerate_reachable_s ed; + + GC_ASSERT(I_HOLD_LOCK()); + ed.proc = proc; + ed.client_data = client_data; + GC_apply_to_all_blocks(GC_do_enumerate_reachable_objects, (word)&ed); +} diff --git a/bdwgc/sparc_mach_dep.S b/bdwgc/sparc_mach_dep.S new file mode 100644 index 000000000..d204dc436 --- /dev/null +++ b/bdwgc/sparc_mach_dep.S @@ -0,0 +1,61 @@ +! SPARCompiler 3.0 and later apparently no longer handles +! asm outside functions. So we need a separate .s file +! This is only set up for SunOS 5, not SunOS 4. +! Assumes this is called before the stack contents are +! examined. + + .seg "text" + .globl GC_save_regs_in_stack +GC_save_regs_in_stack: +#if defined(__arch64__) || defined(__sparcv9) + save %sp,-128,%sp + flushw + ret + restore %sp,2047+128,%o0 +#else /* 32 bit SPARC */ + ta 0x3 ! ST_FLUSH_WINDOWS + mov %sp,%o0 + retl + nop +#endif /* 32 bit SPARC */ +.GC_save_regs_in_stack_end: + .size GC_save_regs_in_stack,.GC_save_regs_in_stack_end-GC_save_regs_in_stack + +! GC_clear_stack_inner(arg, limit) clears stack area up to limit and +! returns arg. Stack clearing is crucial on SPARC, so we supply +! an assembly version that s more careful. Assumes limit is hotter +! than sp, and limit is 8 byte aligned. + .globl GC_clear_stack_inner +GC_clear_stack_inner: +#if defined(__arch64__) || defined(__sparcv9) + mov %sp,%o2 ! Save sp + add %sp,2047-8,%o3 ! p = sp+bias-8 + add %o1,-2047-192,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + stx %g0,[%o3] ! *(long *)p = 0 + cmp %o3,%o1 + bgu,pt %xcc, loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot +#else /* 32 bit SPARC */ + mov %sp,%o2 ! Save sp + add %sp,-8,%o3 ! p = sp-8 + clr %g1 ! [g0,g1] = 0 + add %o1,-0x60,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + std %g0,[%o3] ! *(long long *)p = 0 + cmp %o3,%o1 + bgu loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot +#endif /* 32 bit SPARC */ +.GC_clear_stack_inner_end: + .size GC_clear_stack_inner,.GC_clear_stack_inner_end-GC_clear_stack_inner diff --git a/bdwgc/sparc_netbsd_mach_dep.s b/bdwgc/sparc_netbsd_mach_dep.s new file mode 100644 index 000000000..14feb15ee --- /dev/null +++ b/bdwgc/sparc_netbsd_mach_dep.s @@ -0,0 +1,34 @@ +! SPARCompiler 3.0 and later apparently no longer handles +! asm outside functions. So we need a separate .s file +! This is only set up for SunOS 4. +! Assumes this is called before the stack contents are +! examined. + +#include "machine/asm.h" + + .seg "text" + .globl _C_LABEL(GC_save_regs_in_stack) + .globl _C_LABEL(GC_push_regs) +_C_LABEL(GC_save_regs_in_stack): +_C_LABEL(GC_push_regs): + ta 0x3 ! ST_FLUSH_WINDOWS + mov %sp,%o0 + retl + nop + + .globl _C_LABEL(GC_clear_stack_inner) +_C_LABEL(GC_clear_stack_inner): + mov %sp,%o2 ! Save sp + add %sp,-8,%o3 ! p = sp-8 + clr %g1 ! [g0,g1] = 0 + add %o1,-0x60,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + std %g0,[%o3] ! *(long long *)p = 0 + cmp %o3,%o1 + bgu loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot diff --git a/bdwgc/specific.c b/bdwgc/specific.c new file mode 100644 index 000000000..88988b397 --- /dev/null +++ b/bdwgc/specific.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +#include "private/thread_local_alloc.h" + /* To determine type of tsd impl. */ + /* Includes private/specific.h */ + /* if needed. */ + +#if defined(USE_CUSTOM_SPECIFIC) + +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. */ + +GC_INNER int GC_key_create_inner(tsd ** key_ptr) +{ + int i; + int ret; + tsd * result; + + GC_ASSERT(I_HOLD_LOCK()); + /* A quick alignment check, since we need atomic stores */ + GC_ASSERT((word)(&invalid_tse.next) % sizeof(tse *) == 0); + result = (tsd *)MALLOC_CLEAR(sizeof(tsd)); + if (NULL == result) return ENOMEM; + ret = pthread_mutex_init(&result->lock, NULL); + if (ret != 0) return ret; + for (i = 0; i < TS_CACHE_SIZE; ++i) { + result -> cache[i] = (/* no const */ tse *)&invalid_tse; + } +# ifdef GC_ASSERTIONS + for (i = 0; i < TS_HASH_SIZE; ++i) { + GC_ASSERT(result -> hash[i].p == 0); + } +# endif + *key_ptr = result; + return 0; +} + +GC_INNER int GC_setspecific(tsd * key, void * value) +{ + pthread_t self = pthread_self(); + unsigned hash_val = HASH(self); + volatile tse * entry; + + GC_ASSERT(I_HOLD_LOCK()); + 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].p; + entry -> thread = self; + entry -> value = TS_HIDE_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(&key->hash[hash_val].ao, (AO_t)entry); + GC_dirty((/* no volatile */ void *)entry); + GC_dirty(key->hash + hash_val); + if (pthread_mutex_unlock(&key->lock) != 0) + ABORT("pthread_mutex_unlock failed (setspecific)"); + return 0; +} + +/* Remove thread-specific data for a given thread. This function is */ +/* called at fork from the child process for all threads except for the */ +/* survived one. GC_remove_specific() should be called on thread exit. */ +GC_INNER void GC_remove_specific_after_fork(tsd * key, pthread_t t) +{ + unsigned hash_val = HASH(t); + tse *entry; + tse *prev = NULL; + +# ifdef CAN_HANDLE_FORK + /* Both GC_setspecific and GC_remove_specific should be called */ + /* with the allocation lock held to ensure the consistency of */ + /* the hash table in the forked child. */ + GC_ASSERT(I_HOLD_LOCK()); +# endif + pthread_mutex_lock(&(key -> lock)); + entry = key->hash[hash_val].p; + while (entry != NULL && !THREAD_EQUAL(entry->thread, t)) { + prev = entry; + entry = entry->next; + } + /* Invalidate qtid field, since qtids may be reused, and a later */ + /* cache lookup could otherwise find this entry. */ + if (entry != NULL) { + entry -> qtid = INVALID_QTID; + if (NULL == prev) { + key->hash[hash_val].p = entry->next; + GC_dirty(key->hash + hash_val); + } else { + prev->next = entry->next; + GC_dirty(prev); + } + /* Atomic! concurrent accesses still work. */ + /* They must, since readers don't lock. */ + /* We shouldn't need a volatile access here, */ + /* since both this and the preceding write */ + /* should become visible no later than */ + /* the pthread_mutex_unlock() call. */ + } + /* If we wanted to deallocate the entry, we'd first have to clear */ + /* any cache entries pointing to it. That probably requires */ + /* additional synchronization, since we can't prevent a concurrent */ + /* cache lookup, which should still be examining deallocated memory.*/ + /* This can only happen if the concurrent access is from another */ + /* thread, and hence has missed the cache, but still... */ +# ifdef LINT2 + GC_noop1((word)entry); +# endif + + /* With GC, we're done, since the pointers from the cache will */ + /* be overwritten, all local pointers to the entries will be */ + /* dropped, and the entry will then be reclaimed. */ + if (pthread_mutex_unlock(&key->lock) != 0) + ABORT("pthread_mutex_unlock failed (remove_specific after fork)"); +} + +/* Note that even the slow path doesn't lock. */ +GC_INNER void * GC_slow_getspecific(tsd * key, word qtid, + tse * volatile * cache_ptr) +{ + pthread_t self = pthread_self(); + tse *entry = key->hash[HASH(self)].p; + + GC_ASSERT(qtid != INVALID_QTID); + while (entry != NULL && !THREAD_EQUAL(entry->thread, self)) { + entry = entry -> next; + } + if (entry == NULL) return NULL; + /* Set cache_entry. */ + entry -> qtid = (AO_t)qtid; + /* It's safe to do this asynchronously. Either value */ + /* is safe, though may produce spurious misses. */ + /* We're replacing one qtid with another one for the */ + /* same thread. */ + *cache_ptr = entry; + /* Again this is safe since pointer assignments are */ + /* presumed atomic, and either pointer is valid. */ + return TS_REVEAL_PTR(entry -> value); +} + +#ifdef GC_ASSERTIONS + /* Check that that all elements of the data structure associated */ + /* with key are marked. */ + void GC_check_tsd_marks(tsd *key) + { + int i; + tse *p; + + if (!GC_is_marked(GC_base(key))) { + ABORT("Unmarked thread-specific-data table"); + } + for (i = 0; i < TS_HASH_SIZE; ++i) { + for (p = key->hash[i].p; p != 0; p = p -> next) { + if (!GC_is_marked(GC_base(p))) { + ABORT_ARG1("Unmarked thread-specific-data entry", + " at %p", (void *)p); + } + } + } + for (i = 0; i < TS_CACHE_SIZE; ++i) { + p = key -> cache[i]; + if (p != &invalid_tse && !GC_is_marked(GC_base(p))) { + ABORT_ARG1("Unmarked cached thread-specific-data entry", + " at %p", (void *)p); + } + } + } +#endif /* GC_ASSERTIONS */ + +#endif /* USE_CUSTOM_SPECIFIC */ diff --git a/bdwgc/tests/disclaim_bench.c b/bdwgc/tests/disclaim_bench.c new file mode 100644 index 000000000..2b11d560f --- /dev/null +++ b/bdwgc/tests/disclaim_bench.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2011 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. + * + * 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. + * + */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gc_disclaim.h" + +#define NOT_GCBUILD +#include "private/gc_priv.h" /* for CLOCK_TYPE, COVERT_DATAFLOW, GC_random */ + +#ifdef LINT2 +# undef rand +# define rand() (int)GC_random() +#endif + +#define my_assert(e) \ + if (!(e)) { \ + fprintf(stderr, "Assertion failure, line %d: " #e "\n", __LINE__); \ + exit(-1); \ + } + +static int free_count = 0; + +struct testobj_s { + struct testobj_s *keep_link; + int i; +}; + +typedef struct testobj_s *testobj_t; + +void GC_CALLBACK testobj_finalize(void *obj, void *carg) +{ + ++*(int *)carg; + my_assert(((testobj_t)obj)->i == 109); + ((testobj_t)obj)->i = 110; +} + +static const struct GC_finalizer_closure fclos = { + testobj_finalize, + &free_count +}; + +testobj_t testobj_new(int model) +{ + testobj_t obj; + switch (model) { +# ifndef GC_NO_FINALIZATION + case 0: + obj = (struct testobj_s *)GC_malloc(sizeof(struct testobj_s)); + if (obj != NULL) + GC_register_finalizer_no_order(obj, testobj_finalize, + &free_count, NULL, NULL); + break; +# endif + case 1: + obj = (testobj_t)GC_finalized_malloc(sizeof(struct testobj_s), + &fclos); + break; + case 2: + obj = (struct testobj_s *)GC_malloc(sizeof(struct testobj_s)); + break; + default: + exit(-1); + } + if (obj == NULL) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + my_assert(obj->i == 0 && obj->keep_link == NULL); + obj->i = 109; + return obj; +} + +#define ALLOC_CNT (4*1024*1024) +#define KEEP_CNT (32*1024) + +static char const *model_str[3] = { + "regular finalization", + "finalize on reclaim", + "no finalization" +}; + +int main(int argc, char **argv) +{ + int i; + int model, model_min, model_max; + testobj_t *keep_arr; + + GC_INIT(); + GC_init_finalized_malloc(); + if (argc == 2 && strcmp(argv[1], "--help") == 0) { + fprintf(stderr, + "Usage: %s [FINALIZATION_MODEL]\n" + "\t0 -- original finalization\n" + "\t1 -- finalization on reclaim\n" + "\t2 -- no finalization\n", argv[0]); + return 1; + } + if (argc == 2) { + model_min = model_max = (int)COVERT_DATAFLOW(atoi(argv[1])); + if (model_min < 0 || model_max > 2) + exit(2); + } else { +# ifndef GC_NO_FINALIZATION + model_min = 0; +# else + model_min = 1; +# endif + model_max = 2; + } + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); + + keep_arr = (testobj_t *)GC_malloc(sizeof(void *) * KEEP_CNT); + if (NULL == keep_arr) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + + printf("\t\t\tfin. ratio time/s time/fin.\n"); + for (model = model_min; model <= model_max; ++model) { + double t = 0.0; +# ifndef NO_CLOCK + CLOCK_TYPE tI, tF; + + GET_TIME(tI); +# endif + free_count = 0; + for (i = 0; i < ALLOC_CNT; ++i) { + int k = rand() % KEEP_CNT; + keep_arr[k] = testobj_new(model); + } + GC_gcollect(); +# ifndef NO_CLOCK + GET_TIME(tF); + t = MS_TIME_DIFF(tF, tI)*1e-3; +# endif + + if (model < 2 && free_count > 0) + printf("%20s: %12.4f %12g %12g\n", model_str[model], + free_count/(double)ALLOC_CNT, t, t/free_count); + else + printf("%20s: %12.4f %12g %12s\n", + model_str[model], 0.0, t, "N/A"); + } + return 0; +} diff --git a/bdwgc/tests/disclaim_test.c b/bdwgc/tests/disclaim_test.c new file mode 100644 index 000000000..4f4faf0e0 --- /dev/null +++ b/bdwgc/tests/disclaim_test.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2011 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. + * + * 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. + * + */ + +/* Test that objects reachable from an object allocated with */ +/* GC_malloc_with_finalizer is not reclaimable before the finalizer */ +/* is called. */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H + /* For GC_[P]THREADS */ +# include "config.h" +#endif + +#undef GC_NO_THREAD_REDIRECTS +#include "gc_disclaim.h" + +#if defined(GC_PTHREADS) || defined(LINT2) +# define NOT_GCBUILD +# include "private/gc_priv.h" + + GC_ATTR_NO_SANITIZE_THREAD + static int GC_rand(void) /* nearly identical to GC_random */ + { + static unsigned seed; /* concurrent update does not hurt the test */ + + seed = (seed * 1103515245U + 12345) & (~0U >> 1); + return (int)seed; + } + + /* Redefine the standard rand() with a trivial (yet sufficient for */ + /* the test purpose) implementation to avoid crashes inside rand() */ + /* on some targets (e.g. FreeBSD 13.0) when used concurrently. */ + /* The standard specifies rand() as not a thread-safe API function. */ +# undef rand +# define rand() GC_rand() +#endif /* GC_PTHREADS || LINT2 */ + +#define my_assert(e) \ + if (!(e)) { \ + fflush(stdout); \ + fprintf(stderr, "Assertion failure, line %d: " #e "\n", __LINE__); \ + exit(-1); \ + } + +int memeq(void *s, int c, size_t len) +{ + while (len--) { + if (*(char *)s != c) + return 0; + s = (char *)s + 1; + } + return 1; +} + +void GC_CALLBACK misc_sizes_dct(void *obj, void *cd) +{ + 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)); +} + +void test_misc_sizes(void) +{ + static const struct GC_finalizer_closure fc = { misc_sizes_dct, NULL }; + int i; + for (i = 1; i <= 20; ++i) { /* Up to 1 MiB. */ + void *p = GC_finalized_malloc((size_t)1 << i, &fc); + if (p == NULL) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + my_assert(memeq(p, 0, (size_t)1 << i)); + memset(p, 0x56, (size_t)1 << i); + *(unsigned char *)p = (unsigned char)i; + } +} + +typedef struct pair_s *pair_t; + +struct pair_s { + char magic[16]; + int checksum; + pair_t car; + pair_t cdr; +}; + +static const char * const pair_magic = "PAIR_MAGIC_BYTES"; + +int is_pair(pair_t p) +{ + return memcmp(p->magic, pair_magic, sizeof(p->magic)) == 0; +} + +#define PTR_HASH(p) (GC_HIDE_POINTER(p) >> 4) + +void GC_CALLBACK pair_dct(void *obj, void *cd) +{ + pair_t p = (pair_t)obj; + int checksum; + + my_assert(cd == (void *)PTR_HASH(p)); + /* Check that obj and its car and cdr are not trashed. */ +# ifdef DEBUG_DISCLAIM_DESTRUCT + printf("Destruct %p: (car= %p, cdr= %p)\n", + (void *)p, (void *)p->car, (void *)p->cdr); +# endif + my_assert(GC_base(obj)); + my_assert(is_pair(p)); + my_assert(!p->car || is_pair(p->car)); + my_assert(!p->cdr || is_pair(p->cdr)); + checksum = 782; + if (p->car) checksum += p->car->checksum; + if (p->cdr) checksum += p->cdr->checksum; + my_assert(p->checksum == checksum); + + /* Invalidate it. */ + memset(p->magic, '*', sizeof(p->magic)); + p->checksum = 0; + p->car = NULL; + p->cdr = NULL; +} + +pair_t +pair_new(pair_t car, pair_t cdr) +{ + pair_t p; + struct GC_finalizer_closure *pfc = + GC_NEW_ATOMIC(struct GC_finalizer_closure); + + if (NULL == pfc) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + pfc->proc = pair_dct; + p = (pair_t)GC_finalized_malloc(sizeof(struct pair_s), pfc); + if (p == NULL) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + pfc->cd = (void *)PTR_HASH(p); + my_assert(!is_pair(p)); + my_assert(memeq(p, 0, sizeof(struct pair_s))); + memcpy(p->magic, pair_magic, sizeof(p->magic)); + p->checksum = 782 + (car? car->checksum : 0) + (cdr? cdr->checksum : 0); + p->car = car; + GC_ptr_store_and_dirty(&p->cdr, cdr); + GC_reachable_here(car); +# ifdef DEBUG_DISCLAIM_DESTRUCT + printf("Construct %p: (car= %p, cdr= %p)\n", + (void *)p, (void *)p->car, (void *)p->cdr); +# endif + return p; +} + +void +pair_check_rec(pair_t p) +{ + while (p) { + int checksum = 782; + if (p->car) checksum += p->car->checksum; + if (p->cdr) checksum += p->cdr->checksum; + my_assert(p->checksum == checksum); + if (rand() % 2) + p = p->car; + else + p = p->cdr; + } +} + +#ifdef GC_PTHREADS +# ifndef NTHREADS +# define NTHREADS 6 +# endif +# include /* for EAGAIN */ +# include +#else +# undef NTHREADS +# define NTHREADS 1 +#endif + +#define POP_SIZE 1000 +#if NTHREADS > 1 +# define MUTATE_CNT (2000000/NTHREADS) +#else +# define MUTATE_CNT 10000000 +#endif +#define GROW_LIMIT (MUTATE_CNT/10) + +void *test(void *data) +{ + int i; + pair_t pop[POP_SIZE]; + memset(pop, 0, sizeof(pop)); + for (i = 0; i < MUTATE_CNT; ++i) { + int t = rand() % POP_SIZE; + switch (rand() % (i > GROW_LIMIT? 5 : 3)) { + case 0: case 3: + if (pop[t]) + pop[t] = pop[t]->car; + break; + case 1: case 4: + if (pop[t]) + pop[t] = pop[t]->cdr; + break; + case 2: + pop[t] = pair_new(pop[rand() % POP_SIZE], + pop[rand() % POP_SIZE]); + break; + } + if (rand() % 8 == 1) + pair_check_rec(pop[rand() % POP_SIZE]); + } + return data; +} + +int main(void) +{ +# if NTHREADS > 1 + pthread_t th[NTHREADS]; + int i, n; +# endif + + GC_set_all_interior_pointers(0); /* for a stricter test */ +# ifdef TEST_MANUAL_VDB + GC_set_manual_vdb_allowed(1); +# endif + GC_INIT(); + GC_init_finalized_malloc(); +# ifndef NO_INCREMENTAL + GC_enable_incremental(); +# endif + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); + + test_misc_sizes(); + +# if NTHREADS > 1 + printf("Threaded disclaim test.\n"); + for (i = 0; i < NTHREADS; ++i) { + int err = pthread_create(&th[i], NULL, test, NULL); + if (err) { + fprintf(stderr, "Failed to create thread #%d: %s\n", i, + strerror(err)); + if (i > 1 && EAGAIN == err) break; + exit(1); + } + } + n = i; + for (i = 0; i < n; ++i) { + int err = pthread_join(th[i], NULL); + if (err) { + fprintf(stderr, "Failed to join thread #%d: %s\n", i, + strerror(err)); + exit(69); + } + } +# else + printf("Unthreaded disclaim test.\n"); + test(NULL); +# endif + return 0; +} diff --git a/bdwgc/tests/disclaim_weakmap_test.c b/bdwgc/tests/disclaim_weakmap_test.c new file mode 100644 index 000000000..849c4095f --- /dev/null +++ b/bdwgc/tests/disclaim_weakmap_test.c @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2018 Petter A. Urkedal + * + * 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. + */ + +/* This tests a case where disclaim notifiers sometimes return non-zero */ +/* in order to protect objects from collection. */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H + /* For GC_[P]THREADS */ +# include "config.h" +#endif + +#include "gc_disclaim.h" /* includes gc.h */ + +#if defined(GC_PTHREADS) || defined(LINT2) +# define NOT_GCBUILD +# include "private/gc_priv.h" + + GC_ATTR_NO_SANITIZE_THREAD + static int GC_rand(void) /* same as in disclaim_test.c */ + { + static unsigned seed; /* concurrent update does not hurt the test */ + + seed = (seed * 1103515245U + 12345) & (~0U >> 1); + return (int)seed; + } + +# undef rand +# define rand() GC_rand() +#endif /* GC_PTHREADS || LINT2 */ + +#include "gc_mark.h" /* should not precede include gc_priv.h */ + +#ifdef GC_PTHREADS +# ifndef NTHREADS +# define NTHREADS 8 +# endif +# include /* for EAGAIN, EBUSY */ +# include +# include "private/gc_atomic_ops.h" /* for AO_t and AO_fetch_and_add1 */ +#else +# undef NTHREADS +# define NTHREADS 1 +# define AO_t GC_word +#endif + +#define POP_SIZE 200 +#define MUTATE_CNT (5000000 / NTHREADS) +#define GROW_LIMIT (MUTATE_CNT / 10) + +#define WEAKMAP_CAPACITY 256 +#define WEAKMAP_MUTEX_COUNT 32 + +/* FINALIZER_CLOSURE_FLAG definition matches the one in fnlz_mlc.c. */ +#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) +# define FINALIZER_CLOSURE_FLAG 0x2 +# define INVALIDATE_FLAG 0x1 +#else +# define FINALIZER_CLOSURE_FLAG 0x1 +# define INVALIDATE_FLAG 0x2 +#endif + +#define my_assert(e) \ + if (!(e)) { \ + fflush(stdout); \ + fprintf(stderr, "Assertion failure, line %d: %s\n", __LINE__, #e); \ + exit(70); \ + } + +#define CHECK_OOM(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) + +#ifndef AO_HAVE_fetch_and_add1 +# define AO_fetch_and_add1(p) ((*(p))++) + /* This is used only to update counters. */ +#endif + +unsigned memhash(void *src, size_t len) +{ + unsigned acc = 0; + size_t i; + + my_assert(len % sizeof(GC_word) == 0); + for (i = 0; i < len / sizeof(GC_word); ++i) { + acc = (unsigned)((2003 * (GC_word)acc + ((GC_word *)src)[i]) / 3); + } + return acc; +} + +static volatile AO_t stat_added; +static volatile AO_t stat_found; +static volatile AO_t stat_removed; +static volatile AO_t stat_skip_locked; +static volatile AO_t stat_skip_marked; + +struct weakmap_link { + GC_hidden_pointer obj; + struct weakmap_link *next; +}; + +struct weakmap { +# ifdef GC_PTHREADS + pthread_mutex_t mutex[WEAKMAP_MUTEX_COUNT]; +# endif + size_t key_size; + size_t obj_size; + size_t capacity; + unsigned weakobj_kind; + struct weakmap_link **links; /* NULL means weakmap is destroyed */ +}; + +void weakmap_lock(struct weakmap *wm, unsigned h) +{ +# ifdef GC_PTHREADS + int err = pthread_mutex_lock(&wm->mutex[h % WEAKMAP_MUTEX_COUNT]); + my_assert(0 == err); +# else + (void)wm; (void)h; +# endif +} + +int weakmap_trylock(struct weakmap *wm, unsigned h) +{ +# ifdef GC_PTHREADS + int err = pthread_mutex_trylock(&wm->mutex[h % WEAKMAP_MUTEX_COUNT]); + if (err != 0 && err != EBUSY) { + fprintf(stderr, "pthread_mutex_trylock: %s\n", strerror(err)); + exit(69); + } + return err; +# else + (void)wm; (void)h; + return 0; +# endif +} + +void weakmap_unlock(struct weakmap *wm, unsigned h) +{ +# ifdef GC_PTHREADS + int err = pthread_mutex_unlock(&wm->mutex[h % WEAKMAP_MUTEX_COUNT]); + my_assert(0 == err); +# else + (void)wm; (void)h; +# endif +} + +void *GC_CALLBACK set_mark_bit(void *obj) +{ + GC_set_mark_bit(obj); + return NULL; +} + +void *weakmap_add(struct weakmap *wm, void *obj, size_t obj_size) +{ + struct weakmap_link *link, *new_link, **first; + GC_word *new_base; + void *new_obj; + unsigned h; + size_t key_size = wm->key_size; + + /* Lock and look for an existing entry. */ + my_assert(key_size <= obj_size); + h = memhash(obj, key_size); + first = &wm->links[h % wm->capacity]; + weakmap_lock(wm, h); + + for (link = *first; link != NULL; link = link->next) { + void *old_obj = GC_get_find_leak() ? (void *)link->obj + : GC_REVEAL_POINTER(link->obj); + + if (memcmp(old_obj, obj, key_size) == 0) { + GC_call_with_alloc_lock(set_mark_bit, (GC_word *)old_obj - 1); + /* Pointers in the key part may have been freed and reused, */ + /* changing the keys without memcmp noticing. This is okay */ + /* as long as we update the mapped value. */ + if (memcmp((char *)old_obj + key_size, (char *)obj + key_size, + wm->obj_size - key_size) != 0) { + memcpy((char *)old_obj + key_size, (char *)obj + key_size, + wm->obj_size - key_size); + GC_end_stubborn_change((char *)old_obj + key_size); + } + weakmap_unlock(wm, h); + AO_fetch_and_add1(&stat_found); +# ifdef DEBUG_DISCLAIM_WEAKMAP + printf("Found %p, hash= %p\n", old_obj, (void *)(GC_word)h); +# endif + return old_obj; + } + } + + /* Create new object. */ + new_base = (GC_word *)GC_generic_malloc(sizeof(GC_word) + wm->obj_size, + wm->weakobj_kind); + CHECK_OOM(new_base); + *new_base = (GC_word)wm | FINALIZER_CLOSURE_FLAG; + new_obj = (void *)(new_base + 1); + memcpy(new_obj, obj, wm->obj_size); + GC_end_stubborn_change(new_base); + + /* Add the object to the map. */ + new_link = (struct weakmap_link *)GC_malloc(sizeof(struct weakmap_link)); + CHECK_OOM(new_link); + new_link->obj = GC_get_find_leak() ? (GC_word)new_obj + : GC_HIDE_POINTER(new_obj); + new_link->next = *first; + GC_END_STUBBORN_CHANGE(new_link); + GC_ptr_store_and_dirty(first, new_link); + weakmap_unlock(wm, h); + AO_fetch_and_add1(&stat_added); +# ifdef DEBUG_DISCLAIM_WEAKMAP + printf("Added %p, hash= %p\n", new_obj, (void *)(GC_word)h); +# endif + return new_obj; +} + +int GC_CALLBACK weakmap_disclaim(void *obj_base) +{ + struct weakmap *wm; + struct weakmap_link **link; + GC_word hdr; + void *obj; + unsigned h; + + /* Decode header word. */ + hdr = *(GC_word *)obj_base; + if ((hdr & FINALIZER_CLOSURE_FLAG) == 0) + return 0; /* on GC free list, ignore it. */ + + my_assert((hdr & INVALIDATE_FLAG) == 0); + wm = (struct weakmap *)(hdr & ~(GC_word)FINALIZER_CLOSURE_FLAG); + if (NULL == wm->links) + return 0; /* weakmap has been already destroyed */ + obj = (GC_word *)obj_base + 1; + + /* Lock and check for mark. */ + h = memhash(obj, wm->key_size); + if (weakmap_trylock(wm, h) != 0) { + AO_fetch_and_add1(&stat_skip_locked); +# ifdef DEBUG_DISCLAIM_WEAKMAP + printf("Skipping locked %p, hash= %p\n", obj, (void *)(GC_word)h); +# endif + return 1; + } + if (GC_is_marked(obj_base)) { + weakmap_unlock(wm, h); + AO_fetch_and_add1(&stat_skip_marked); +# ifdef DEBUG_DISCLAIM_WEAKMAP + printf("Skipping marked %p, hash= %p\n", obj, (void *)(GC_word)h); +# endif + return 1; + } + + /* Remove obj from wm. */ + AO_fetch_and_add1(&stat_removed); +# ifdef DEBUG_DISCLAIM_WEAKMAP + printf("Removing %p, hash= %p\n", obj, (void *)(GC_word)h); +# endif + *(GC_word *)obj_base |= INVALIDATE_FLAG; + for (link = &wm->links[h % wm->capacity];; link = &(*link)->next) { + void *old_obj; + + if (NULL == *link) { + fprintf(stderr, "Did not find %p\n", obj); + exit(70); + } + old_obj = GC_get_find_leak() ? (void *)(*link)->obj + : GC_REVEAL_POINTER((*link)->obj); + if (old_obj == obj) + break; + my_assert(memcmp(old_obj, obj, wm->key_size) != 0); + } + GC_ptr_store_and_dirty(link, (*link)->next); + weakmap_unlock(wm, h); + return 0; +} + +struct weakmap *weakmap_new(size_t capacity, size_t key_size, size_t obj_size, + unsigned weakobj_kind) +{ + struct weakmap *wm = (struct weakmap *)GC_malloc(sizeof(struct weakmap)); + + CHECK_OOM(wm); +# ifdef GC_PTHREADS + { + int i; + for (i = 0; i < WEAKMAP_MUTEX_COUNT; ++i) { + int err = pthread_mutex_init(&wm->mutex[i], NULL); + my_assert(err == 0); + } + } +# endif + wm->key_size = key_size; + wm->obj_size = obj_size; + wm->capacity = capacity; + wm->weakobj_kind = weakobj_kind; + GC_ptr_store_and_dirty(&wm->links, + GC_malloc(sizeof(struct weakmap_link *) * capacity)); + CHECK_OOM(wm->links); + return wm; +} + +void weakmap_destroy(struct weakmap *wm) +{ +# ifdef GC_PTHREADS + int i; + + for (i = 0; i < WEAKMAP_MUTEX_COUNT; ++i) { + (void)pthread_mutex_destroy(&wm->mutex[i]); + } +# endif + wm->links = NULL; /* weakmap is destroyed */ +} + +struct weakmap *pair_hcset; + +#define PAIR_MAGIC_SIZE 16 /* should not exceed sizeof(pair_magic) */ + +struct pair_key { + struct pair *car, *cdr; +}; + +struct pair { + struct pair *car; + struct pair *cdr; + char magic[PAIR_MAGIC_SIZE]; + int checksum; +}; + +static const char * const pair_magic = "PAIR_MAGIC_BYTES"; + +struct pair *pair_new(struct pair *car, struct pair *cdr) +{ + struct pair tmpl; + + memset(&tmpl, 0, sizeof(tmpl)); /* To clear the paddings (to avoid */ + /* a compiler warning). */ + tmpl.car = car; + tmpl.cdr = cdr; + memcpy(tmpl.magic, pair_magic, PAIR_MAGIC_SIZE); + tmpl.checksum = 782 + (car? car->checksum : 0) + (cdr? cdr->checksum : 0); + return (struct pair *)weakmap_add(pair_hcset, &tmpl, sizeof(tmpl)); +} + +void pair_check_rec(struct pair *p, int line) +{ + while (p != NULL) { + int checksum = 782; + + if (memcmp(p->magic, pair_magic, PAIR_MAGIC_SIZE) != 0) { + fprintf(stderr, "Magic bytes wrong for %p at %d\n", (void *)p, line); + exit(70); + } + if (p->car != NULL) + checksum += p->car->checksum; + if (p->cdr != NULL) + checksum += p->cdr->checksum; + if (p->checksum != checksum) { + fprintf(stderr, "Checksum failure for %p: (car= %p, cdr= %p) at %d\n", + (void *)p, (void *)p->car, (void *)p->cdr, line); + exit(70); + } + p = (rand() & 1) != 0 ? p->cdr : p->car; + } +} + +void *test(void *data) +{ + int i; + struct pair *p0, *p1; + struct pair *pop[POP_SIZE]; + + memset(pop, 0, sizeof(pop)); + for (i = 0; i < MUTATE_CNT; ++i) { + int bits = rand(); + int t = (bits >> 3) % POP_SIZE; + + switch (bits % (i > GROW_LIMIT ? 5 : 3)) { + case 0: + case 3: + if (pop[t] != NULL) + pop[t] = pop[t]->car; + break; + case 1: + case 4: + if (pop[t] != NULL) + pop[t] = pop[t]->cdr; + break; + case 2: + p0 = pop[rand() % POP_SIZE]; + p1 = pop[rand() % POP_SIZE]; + pop[t] = pair_new(p0, p1); + my_assert(pair_new(p0, p1) == pop[t]); + my_assert(pop[t]->car == p0); + my_assert(pop[t]->cdr == p1); + break; + } + pair_check_rec(pop[rand() % POP_SIZE], __LINE__); + } + return data; +} + +int main(void) +{ + unsigned weakobj_kind; +# ifdef GC_PTHREADS + int i, n; + pthread_t th[NTHREADS]; +# endif + + GC_set_all_interior_pointers(0); /* for a stricter test */ +# ifdef TEST_MANUAL_VDB + GC_set_manual_vdb_allowed(1); +# endif + GC_INIT(); + GC_init_finalized_malloc(); /* to register the displacements */ +# ifndef NO_INCREMENTAL + GC_enable_incremental(); +# endif + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); + weakobj_kind = GC_new_kind(GC_new_free_list(), /* 0 | */ GC_DS_LENGTH, + 1 /* adjust */, 1 /* clear */); + GC_register_disclaim_proc(weakobj_kind, weakmap_disclaim, + 1 /* mark_unconditionally */); + pair_hcset = weakmap_new(WEAKMAP_CAPACITY, sizeof(struct pair_key), + sizeof(struct pair), weakobj_kind); + +# ifdef GC_PTHREADS + for (i = 0; i < NTHREADS; ++i) { + int err = pthread_create(&th[i], NULL, test, NULL); + if (err != 0) { + fprintf(stderr, "Failed to create thread #%d: %s\n", + i, strerror(err)); + if (i > 1 && EAGAIN == err) break; + exit(1); + } + } + n = i; + for (i = 0; i < n; ++i) { + int err = pthread_join(th[i], NULL); + if (err != 0) { + fprintf(stderr, "Failed to join thread #%d: %s\n", i, strerror(err)); + exit(69); + } + } +# else + (void)test(NULL); +# endif + weakmap_destroy(pair_hcset); + printf("%u added, %u found; %u removed, %u locked, %u marked; %u remains\n", + (unsigned)stat_added, (unsigned)stat_found, (unsigned)stat_removed, + (unsigned)stat_skip_locked, (unsigned)stat_skip_marked, + (unsigned)stat_added - (unsigned)stat_removed); + return 0; +} diff --git a/bdwgc/tests/huge_test.c b/bdwgc/tests/huge_test.c new file mode 100644 index 000000000..b9493f7a3 --- /dev/null +++ b/bdwgc/tests/huge_test.c @@ -0,0 +1,67 @@ + +#include +#include +#include + +#ifndef GC_IGNORE_WARN + /* Ignore misleading "Out of Memory!" warning (which is printed on */ + /* every GC_MALLOC call below) by defining this macro before "gc.h" */ + /* inclusion. */ +# define GC_IGNORE_WARN +#endif + +#ifndef GC_MAXIMUM_HEAP_SIZE +# define GC_MAXIMUM_HEAP_SIZE 100 * 1024 * 1024 +# define GC_INITIAL_HEAP_SIZE GC_MAXIMUM_HEAP_SIZE / 20 + /* Otherwise heap expansion aborts when deallocating large block. */ + /* That's OK. We test this corner case mostly to make sure that */ + /* it fails predictably. */ +#endif + +#ifndef GC_ATTR_ALLOC_SIZE + /* Omit alloc_size attribute to avoid compiler warnings about */ + /* exceeding maximum object size when values close to GC_SWORD_MAX */ + /* are passed to GC_MALLOC. */ +# define GC_ATTR_ALLOC_SIZE(argnum) /* empty */ +#endif + +#include "gc.h" + +/* + * Check that very large allocation requests fail. "Success" would usually + * indicate that the size was somehow converted to a negative + * number. Clients shouldn't do this, but we should fail in the + * expected manner. + */ + +#define CHECK_ALLOC_FAILED(r, sz_str) \ + do { \ + if (NULL != (r)) { \ + fprintf(stderr, \ + "Size " sz_str " allocation unexpectedly succeeded\n"); \ + exit(1); \ + } \ + } while (0) + +#define GC_WORD_MAX ((GC_word)-1) +#define GC_SWORD_MAX ((GC_signed_word)(GC_WORD_MAX >> 1)) + +int main(void) +{ + GC_INIT(); + + CHECK_ALLOC_FAILED(GC_MALLOC(GC_SWORD_MAX - 1024), "SWORD_MAX-1024"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_SWORD_MAX), "SWORD_MAX"); + /* Skip other checks to avoid "exceeds maximum object size" gcc warning. */ +# if !defined(_FORTIFY_SOURCE) + CHECK_ALLOC_FAILED(GC_MALLOC((GC_word)GC_SWORD_MAX + 1), "SWORD_MAX+1"); + CHECK_ALLOC_FAILED(GC_MALLOC((GC_word)GC_SWORD_MAX + 1024), + "SWORD_MAX+1024"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX - 1024), "WORD_MAX-1024"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX - 16), "WORD_MAX-16"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX - 8), "WORD_MAX-8"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX - 4), "WORD_MAX-4"); + CHECK_ALLOC_FAILED(GC_MALLOC(GC_WORD_MAX), "WORD_MAX"); +# endif + return 0; +} diff --git a/bdwgc/tests/initsecondarythread.c b/bdwgc/tests/initsecondarythread.c new file mode 100644 index 000000000..1ec9c58ea --- /dev/null +++ b/bdwgc/tests/initsecondarythread.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2011 Ludovic Courtes + * + * 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. + */ + +/* Make sure 'GC_INIT' can be called from threads other than the initial + * thread. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_THREADS +# define GC_THREADS +#endif + +#define GC_NO_THREAD_REDIRECTS 1 + /* Do not redirect thread creation and join calls. */ + +#include "gc.h" + +#ifdef GC_PTHREADS +# include +#else +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +#endif /* !GC_PTHREADS */ + +#include +#include + +#ifdef GC_PTHREADS + static void *thread(void *arg) +#else + static DWORD WINAPI thread(LPVOID arg) +#endif +{ + GC_INIT(); + (void)GC_MALLOC(123); + (void)GC_MALLOC(12345); +# ifdef GC_PTHREADS + return arg; +# else + return (DWORD)(GC_word)arg; +# endif +} + +#include "private/gcconfig.h" + +int main(void) +{ +# ifdef GC_PTHREADS + int code; + pthread_t t; + +# ifdef LINT2 + t = pthread_self(); /* explicitly initialize to some value */ +# endif +# else + HANDLE t; + DWORD thread_id; +# endif +# if !(defined(BEOS) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(CYGWIN32) || defined(GC_OPENBSD_UTHREADS) \ + || (defined(DARWIN) && !defined(NO_PTHREAD_GET_STACKADDR_NP)) \ + || ((defined(FREEBSD) || defined(LINUX) || defined(NETBSD) \ + || defined(HOST_ANDROID)) && !defined(NO_PTHREAD_GETATTR_NP) \ + && !defined(NO_PTHREAD_ATTR_GET_NP)) \ + || (defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC)) \ + || (!defined(STACKBOTTOM) && (defined(HEURISTIC1) \ + || (!defined(LINUX_STACKBOTTOM) && !defined(FREEBSD_STACKBOTTOM))))) + /* GC_INIT() must be called from main thread only. */ + GC_INIT(); +# endif + (void)GC_get_parallel(); /* linking fails if no threads support */ + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); +# ifdef GC_PTHREADS + if ((code = pthread_create (&t, NULL, thread, NULL)) != 0) { + fprintf(stderr, "Thread creation failed, errno= %d\n", code); + return 1; + } + if ((code = pthread_join (t, NULL)) != 0) { + fprintf(stderr, "Thread join failed, errno= %d\n", code); + return 1; + } +# else + t = CreateThread(NULL, 0, thread, 0, 0, &thread_id); + if (t == NULL) { + fprintf(stderr, "Thread creation failed, errcode= %d\n", + (int)GetLastError()); + return 1; + } + if (WaitForSingleObject(t, INFINITE) != WAIT_OBJECT_0) { + fprintf(stderr, "Thread join failed, errcode= %d\n", + (int)GetLastError()); + CloseHandle(t); + return 1; + } + CloseHandle(t); +# endif + return 0; +} diff --git a/bdwgc/tests/leak_test.c b/bdwgc/tests/leak_test.c new file mode 100644 index 000000000..b20e3a93b --- /dev/null +++ b/bdwgc/tests/leak_test.c @@ -0,0 +1,25 @@ +#include "leak_detector.h" + +int main(void) { + char *p[10]; + int i; + GC_set_find_leak(1); /* for new collect versions not compiled */ + /* with -DFIND_LEAK. */ + + GC_INIT(); /* Needed if thread-local allocation is enabled. */ + /* FIXME: This is not ideal. */ + for (i = 0; i < 10; ++i) { + p[i] = (char*)malloc(sizeof(int)+i); + } + CHECK_LEAKS(); + for (i = 1; i < 10; ++i) { + free(p[i]); + } + for (i = 0; i < 9; ++i) { + p[i] = (char*)malloc(sizeof(int)+i); + } + CHECK_LEAKS(); + CHECK_LEAKS(); + CHECK_LEAKS(); + return 0; +} diff --git a/bdwgc/tests/middle.c b/bdwgc/tests/middle.c new file mode 100644 index 000000000..c9f24c4e4 --- /dev/null +++ b/bdwgc/tests/middle.c @@ -0,0 +1,31 @@ +/* + * Test at the boundary between small and large objects. + * Inspired by a test case from Zoltan Varga. + */ +#include "gc.h" +#include + +int main (void) +{ + int i; + + GC_set_all_interior_pointers(0); + GC_INIT(); + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); + + for (i = 0; i < 20000; ++i) { + (void)GC_malloc_atomic(4096); + (void)GC_malloc(4096); + } + + /* Test delayed start of marker threads, if they are enabled. */ + GC_start_mark_threads(); + + for (i = 0; i < 20000; ++i) { + (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/bdwgc/tests/realloc_test.c b/bdwgc/tests/realloc_test.c new file mode 100644 index 000000000..8870971b4 --- /dev/null +++ b/bdwgc/tests/realloc_test.c @@ -0,0 +1,36 @@ + +#include +#include +#include "gc.h" + +#define COUNT 10000000 + +int main(void) { + int i; + unsigned long last_heap_size = 0; + + GC_INIT(); + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); + + for (i = 0; i < COUNT; i++) { + int **p = GC_NEW(int *); + int *q = (int*)GC_MALLOC_ATOMIC(sizeof(int)); + + if (p == 0 || *p != 0) { + fprintf(stderr, "GC_malloc returned garbage (or NULL)\n"); + exit(1); + } + + *p = (int*)GC_REALLOC(q, 2 * sizeof(int)); + + if (i % 10 == 0) { + unsigned long heap_size = (unsigned long)GC_get_heap_size(); + if (heap_size != last_heap_size) { + printf("Heap size: %lu\n", heap_size); + last_heap_size = heap_size; + } + } + } + return 0; +} diff --git a/bdwgc/tests/smash_test.c b/bdwgc/tests/smash_test.c new file mode 100644 index 000000000..9aab02de6 --- /dev/null +++ b/bdwgc/tests/smash_test.c @@ -0,0 +1,41 @@ +/* Test that overwrite error detection works reasonably. */ + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif + +#include "gc.h" + +#include + +#define COUNT 7000 +#define SIZE 40 + +char * A[COUNT]; + +char * volatile q; + +int main(void) +{ + int i; + char *p; + + GC_INIT(); + + for (i = 0; i < COUNT; ++i) { + A[i] = p = (char*)GC_MALLOC(SIZE); + + if (i%3000 == 0) { + q = NULL; + GC_gcollect(); + } else if (i%5678 == 0 && p != 0) { + /* Write a byte past the end of the allocated object */ + /* but not beyond the last word of the object's memory. */ + /* A volatile intermediate pointer variable is used to */ + /* avoid a compiler complain of out-of-bounds access. */ + q = &p[(SIZE + i/2000) /* 42 */]; + *q = 42; + } + } + return 0; +} diff --git a/bdwgc/tests/staticrootslib.c b/bdwgc/tests/staticrootslib.c new file mode 100644 index 000000000..5275ec8b4 --- /dev/null +++ b/bdwgc/tests/staticrootslib.c @@ -0,0 +1,72 @@ + +/* This test file is intended to be compiled into a DLL. */ + +#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; +}; + +static struct treenode *root[10] = { 0 }; +static struct treenode *root_nz[10] = { (struct treenode *)(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_NEW(struct treenode); + if (0 == i) + return 0; + if (1 == i) + r = (struct treenode *)GC_MALLOC_ATOMIC(sizeof(struct treenode)); + if (r) { + struct treenode *x = libsrl_mktree(i - 1); + struct treenode *y = libsrl_mktree(i - 1); + r -> x = x; + r -> y = y; + if (i != 1) { + GC_END_STUBBORN_CHANGE(r); + GC_reachable_here(x); + GC_reachable_here(y); + } + } + return r; + } + + GC_TEST_EXPORT_API void * libsrl_init(void) + { +# ifdef TEST_MANUAL_VDB + GC_set_manual_vdb_allowed(1); +# endif +# ifndef STATICROOTSLIB_INIT_IN_MAIN + GC_INIT(); +# endif +# ifndef NO_INCREMENTAL + GC_enable_incremental(); +# endif + return GC_MALLOC(sizeof(struct treenode)); + } + +#endif /* !STATICROOTSLIB2 */ + +GC_TEST_EXPORT_API struct treenode ** libsrl_getpelem(int i, int j) +{ + return &((j & 1) != 0 ? root_nz : root)[i]; +} diff --git a/bdwgc/tests/staticrootstest.c b/bdwgc/tests/staticrootstest.c new file mode 100644 index 000000000..66aca2997 --- /dev/null +++ b/bdwgc/tests/staticrootstest.c @@ -0,0 +1,77 @@ + +#include +#include + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif + +#include "gc.h" +#include "gc_backptr.h" + +#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; +}; + +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] = { (struct treenode *)(GC_word)1 }; + +static char *staticroot; /* intentionally static */ + +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); + +GC_TEST_IMPORT_API struct treenode ** libsrl_getpelem2(int i, int j); + +void init_staticroot(void) +{ + /* Intentionally put staticroot initialization in a function other */ + /* than main to prevent CSA warning that staticroot variable can be */ + /* changed to be a local one). */ + staticroot = (char *)libsrl_init(); +} + +int main(void) +{ + int i, j; + +# ifdef STATICROOTSLIB_INIT_IN_MAIN + GC_INIT(); +# endif + init_staticroot(); + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); + if (NULL == staticroot) { + fprintf(stderr, "GC_malloc returned NULL\n"); + return 2; + } + memset(staticroot, 0x42, sizeof(struct treenode)); + GC_gcollect(); + 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; +} diff --git a/bdwgc/tests/subthread_create.c b/bdwgc/tests/subthread_create.c new file mode 100644 index 000000000..d1f095358 --- /dev/null +++ b/bdwgc/tests/subthread_create.c @@ -0,0 +1,179 @@ + +#ifdef HAVE_CONFIG_H + /* For GC_THREADS and PARALLEL_MARK */ +# include "config.h" +#endif + +#ifdef GC_THREADS +# include "gc.h" + +# ifdef PARALLEL_MARK +# define AO_REQUIRE_CAS +# endif +# include "private/gc_atomic_ops.h" +#endif /* GC_THREADS */ + +#include + +#ifdef AO_HAVE_fetch_and_add1 + +#ifdef GC_PTHREADS +# include /* for EAGAIN */ +# include +#else +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +#endif /* !GC_PTHREADS */ + +#if defined(__HAIKU__) +# include +#endif + +#include +#include + +#ifndef NTHREADS +# define NTHREADS 5 +#endif + +#define NTHREADS_INNER (NTHREADS * 6) /* number of threads to create */ + +#ifndef MAX_SUBTHREAD_DEPTH +# define MAX_ALIVE_THREAD_COUNT 55 +# define MAX_SUBTHREAD_DEPTH 7 +# define MAX_SUBTHREAD_COUNT 200 +#endif + +#ifndef DECAY_NUMER +# define DECAY_NUMER 15 +# define DECAY_DENOM 16 +#endif + +volatile AO_t thread_created_cnt = 0; +volatile AO_t thread_ended_cnt = 0; + +#ifdef GC_PTHREADS + void *entry(void *arg) +#else + DWORD WINAPI entry(LPVOID arg) +#endif +{ + int thread_num = (int)AO_fetch_and_add1(&thread_created_cnt); + GC_word my_depth = (GC_word)arg + 1; + + if (my_depth <= MAX_SUBTHREAD_DEPTH + && thread_num < MAX_SUBTHREAD_COUNT + && (thread_num % DECAY_DENOM) < DECAY_NUMER + && thread_num - (int)AO_load(&thread_ended_cnt) + <= MAX_ALIVE_THREAD_COUNT) { +# ifdef GC_PTHREADS + int err; + pthread_t th; + + err = pthread_create(&th, NULL, entry, (void *)my_depth); + if (err != 0) { + fprintf(stderr, "Thread #%d creation failed, error: %s\n", + thread_num, strerror(err)); + if (err != EAGAIN) + exit(2); + } else { + err = pthread_detach(th); + if (err != 0) { + fprintf(stderr, "Thread #%d detach failed, error: %s\n", + thread_num, strerror(err)); + exit(2); + } + } +# else + HANDLE th; + DWORD thread_id; + + th = CreateThread(NULL, 0, entry, (LPVOID)my_depth, 0, &thread_id); + if (th == NULL) { + fprintf(stderr, "Thread #%d creation failed, errcode= %d\n", + thread_num, (int)GetLastError()); + exit(2); + } + CloseHandle(th); +# endif + } + + (void)AO_fetch_and_add1(&thread_ended_cnt); + return 0; +} + +int main(void) +{ +#if NTHREADS > 0 + int i, n; +# ifdef GC_PTHREADS + int err; + pthread_t th[NTHREADS_INNER]; +# else + HANDLE th[NTHREADS_INNER]; +# endif + + GC_INIT(); + for (i = 0; i < NTHREADS_INNER; ++i) { +# ifdef GC_PTHREADS + err = pthread_create(&th[i], NULL, entry, 0); + if (err) { + fprintf(stderr, "Thread creation failed, error: %s\n", + strerror(err)); + if (i > 0 && EAGAIN == err) break; + exit(1); + } +# else + DWORD thread_id; + th[i] = CreateThread(NULL, 0, entry, 0, 0, &thread_id); + if (th[i] == NULL) { + fprintf(stderr, "Thread creation failed, errcode= %d\n", + (int)GetLastError()); + exit(1); + } +# endif + } + n = i; + for (i = 0; i < n; ++i) { +# ifdef GC_PTHREADS + void *res; + err = pthread_join(th[i], &res); + if (err) { + fprintf(stderr, "Failed to join thread, error: %s\n", + strerror(err)); +# if defined(__HAIKU__) + /* The error is just ignored (and the test is ended) to */ + /* workaround some bug in Haiku pthread_join. */ + /* TODO: The thread is not deleted from GC_threads. */ + if (ESRCH == err) break; +# endif + exit(1); + } +# else + if (WaitForSingleObject(th[i], INFINITE) != WAIT_OBJECT_0) { + fprintf(stderr, "Failed to join thread, errcode= %d\n", + (int)GetLastError()); + CloseHandle(th[i]); + exit(1); + } + CloseHandle(th[i]); +# endif + } +#endif + printf("subthread_create: created %d threads (%d ended)\n", + (int)AO_load(&thread_created_cnt), (int)AO_load(&thread_ended_cnt)); + return 0; +} + +#else + +int main(void) +{ + printf("subthread_create test skipped\n"); + return 0; +} + +#endif /* !AO_HAVE_fetch_and_add1 */ diff --git a/bdwgc/tests/test.c b/bdwgc/tests/test.c new file mode 100644 index 000000000..8afc0fe7a --- /dev/null +++ b/bdwgc/tests/test.c @@ -0,0 +1,2525 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * 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. + */ +/* An incomplete test for the garbage collector. */ +/* Some more obscure entry points are not tested at all. */ +/* This must be compiled with the same flags used to build the */ +/* GC. It uses GC internals to allow more precise results */ +/* checking for some of the tests. */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# undef GC_BUILD + +#if (defined(DBG_HDRS_ALL) || defined(MAKE_BACK_GRAPH)) \ + && !defined(GC_DEBUG) && !defined(CPPCHECK) +# define GC_DEBUG +#endif + +#ifdef DEFAULT_VDB /* specified manually (e.g. passed to CFLAGS) */ +# define TEST_DEFAULT_VDB +#endif + +#if defined(CPPCHECK) && defined(GC_PTHREADS) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE 1 +#endif +#ifdef GC_NO_THREADS_DISCOVERY +# undef GC_NO_THREAD_REDIRECTS +#endif +#include "gc.h" + +#ifndef NTHREADS /* Number of additional threads to fork. */ +# define NTHREADS 5 /* Excludes main thread, which also runs a test. */ + /* In the single-threaded case, the number of times to rerun it. */ + /* Not respected by PCR test. */ +#endif + +# if defined(mips) && defined(SYSTYPE_BSD43) + /* MIPS RISCOS 4 */ +# else +# include +# endif +# include +# if defined(_WIN32_WCE) && !defined(__GNUC__) +# include +/* # define assert ASSERT */ +# else +# include /* Not normally used, but handy for debugging. */ +# endif + +#if defined(GC_NO_FINALIZATION) && !defined(NO_TYPED_TEST) +# define NO_TYPED_TEST +#endif + +#ifndef NO_TYPED_TEST +# include "gc_typed.h" +#endif + +#define NOT_GCBUILD +#include "private/gc_priv.h" /* For output, locking, */ + /* some statistics and gcconfig.h. */ + +#if defined(GC_PRINT_VERBOSE_STATS) || defined(GCTEST_PRINT_VERBOSE) +# 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" +# include "th/PCR_Th.h" +# define GC_printf printf +# endif + +# if defined(GC_PTHREADS) && !defined(GC_WIN32_PTHREADS) +# include +# endif + +# if ((defined(DARWIN) && defined(MPROTECT_VDB) \ + && !defined(MAKE_BACK_GRAPH) && !defined(TEST_HANDLE_FORK)) \ + || (defined(THREADS) && !defined(CAN_HANDLE_FORK)) \ + || defined(HAVE_NO_FORK) || defined(USE_WINALLOC)) \ + && !defined(NO_TEST_HANDLE_FORK) +# define NO_TEST_HANDLE_FORK +# endif + +# ifndef NO_TEST_HANDLE_FORK +# include +# include +# include +# if defined(HANDLE_FORK) && defined(CAN_CALL_ATFORK) +# 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 + +#ifdef PCR +# define FINALIZER_LOCK() PCR_ThCrSec_EnterSys() +# define FINALIZER_UNLOCK() PCR_ThCrSec_ExitSys() +#elif defined(GC_PTHREADS) + static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; +# define FINALIZER_LOCK() pthread_mutex_lock(&incr_lock) +# define FINALIZER_UNLOCK() pthread_mutex_unlock(&incr_lock) +#elif defined(GC_WIN32_THREADS) + static CRITICAL_SECTION incr_cs; +# define FINALIZER_LOCK() EnterCriticalSection(&incr_cs) +# define FINALIZER_UNLOCK() LeaveCriticalSection(&incr_cs) +#else +# define FINALIZER_LOCK() (void)0 +# define FINALIZER_UNLOCK() (void)0 +#endif /* !THREADS */ + +#include + +#ifdef TEST_MANUAL_VDB +# define INIT_MANUAL_VDB_ALLOWED GC_set_manual_vdb_allowed(1) +#elif !defined(SMALL_CONFIG) +# define INIT_MANUAL_VDB_ALLOWED GC_set_manual_vdb_allowed(0) +#else +# define INIT_MANUAL_VDB_ALLOWED /* empty */ +#endif + +#ifdef TEST_PAGES_EXECUTABLE +# define INIT_PAGES_EXECUTABLE GC_set_pages_executable(1) +#else +# define INIT_PAGES_EXECUTABLE (void)0 +#endif + +#define CHECK_GCLIB_VERSION \ + if (GC_get_version() != ((GC_VERSION_MAJOR<<16) \ + | (GC_VERSION_MINOR<<8) \ + | GC_VERSION_MICRO)) { \ + GC_printf("libgc version mismatch\n"); \ + exit(1); \ + } + +/* 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(TEST_EXPLICIT_GC_INIT) || defined(AIX) || defined(CYGWIN32) \ + || defined(DARWIN) || defined(HOST_ANDROID) \ + || (defined(MSWINCE) && !defined(GC_WINMAIN_REDIRECT)) +# define GC_OPT_INIT GC_INIT() +#else +# define GC_OPT_INIT /* empty */ +#endif + +#define INIT_FIND_LEAK \ + if (!GC_get_find_leak()) {} else \ + GC_printf("This test program is not designed for leak detection mode\n") + +#ifdef NO_CLOCK +# define INIT_PERF_MEASUREMENT (void)0 +#else +# define INIT_PERF_MEASUREMENT GC_start_performance_measurement() +#endif + +#define GC_COND_INIT() \ + INIT_FORK_SUPPORT; INIT_MANUAL_VDB_ALLOWED; INIT_PAGES_EXECUTABLE; \ + GC_OPT_INIT; CHECK_GCLIB_VERSION; \ + INIT_PRINT_STATS; INIT_FIND_LEAK; INIT_PERF_MEASUREMENT + +#define CHECK_OUT_OF_MEMORY(p) \ + if ((p) == NULL) { \ + GC_printf("Out of memory\n"); \ + exit(1); \ + } + +/* Define AO primitives for a single-threaded mode. */ +#ifndef AO_HAVE_compiler_barrier + /* AO_t not defined. */ +# define AO_t GC_word +#endif +#ifndef AO_HAVE_load_acquire + static AO_t AO_load_acquire(const volatile AO_t *addr) + { + AO_t result; + + FINALIZER_LOCK(); + result = *addr; + FINALIZER_UNLOCK(); + return result; + } +#endif +#ifndef AO_HAVE_store_release + /* Not a macro as new_val argument should be evaluated before the lock. */ + static void AO_store_release(volatile AO_t *addr, AO_t new_val) + { + FINALIZER_LOCK(); + *addr = new_val; + FINALIZER_UNLOCK(); + } +#endif +#ifndef AO_HAVE_fetch_and_add1 +# define AO_fetch_and_add1(p) ((*(p))++) + /* This is used only to update counters. */ +#endif + +/* Allocation Statistics. Synchronization is not strictly necessary. */ +volatile AO_t uncollectable_count = 0; +volatile AO_t collectable_count = 0; +volatile AO_t atomic_count = 0; +volatile AO_t realloc_count = 0; + +volatile AO_t extra_count = 0; /* Amount of space wasted in cons node; */ + /* also used in gcj_cons, mktree and */ + /* chktree (for other purposes). */ + +#if defined(GC_AMIGA_FASTALLOC) && defined(AMIGA) + EXTERN_C_BEGIN + void GC_amiga_free_all_mem(void); + EXTERN_C_END + + void Amiga_Fail(void){GC_amiga_free_all_mem();abort();} +# define FAIL Amiga_Fail() +#ifndef NO_TYPED_TEST + void *GC_amiga_gctest_malloc_explicitly_typed(size_t lb, GC_descr d){ + void *ret=GC_malloc_explicitly_typed(lb,d); + if(ret==NULL){ + 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"); + FAIL; + } + } + return ret; + } + 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){ + 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"); + FAIL; + } + } + return ret; + } +# define GC_malloc_explicitly_typed(a,b) GC_amiga_gctest_malloc_explicitly_typed(a,b) +# define GC_calloc_explicitly_typed(a,b,c) GC_amiga_gctest_calloc_explicitly_typed(a,b,c) +#endif /* !NO_TYPED_TEST */ + +#else /* !AMIGA_FASTALLOC */ + +# if defined(PCR) || defined(LINT2) +# define FAIL abort() +# else +# define FAIL ABORT("Test failed") +# endif + +#endif /* !AMIGA_FASTALLOC */ + +/* AT_END may be defined to exercise the interior pointer test */ +/* if the collector is configured with ALL_INTERIOR_POINTERS. */ +/* As it stands, this test should succeed with either */ +/* configuration. In the FIND_LEAK configuration, it should */ +/* find lots of leaks, since we free almost nothing. */ + +struct SEXPR { + struct SEXPR * sexpr_car; + struct SEXPR * sexpr_cdr; +}; + + +typedef struct SEXPR * sexpr; + +# define INT_TO_SEXPR(x) ((sexpr)(GC_word)(x)) +# define SEXPR_TO_INT(x) ((int)(GC_word)(x)) + +# undef nil +# define nil (INT_TO_SEXPR(0)) +# define car(x) ((x) -> sexpr_car) +# define cdr(x) ((x) -> sexpr_cdr) +# define is_nil(x) ((x) == nil) + +/* Silly implementation of Lisp cons. Intentionally wastes lots of space */ +/* to test collector. */ +# ifdef VERY_SMALL_CONFIG +# define cons small_cons +# else +sexpr cons (sexpr x, sexpr y) +{ + sexpr r; + int *p; + unsigned my_extra = (unsigned)AO_fetch_and_add1(&extra_count) % 5000; + + r = (sexpr)GC_MALLOC(sizeof(struct SEXPR) + my_extra); + CHECK_OUT_OF_MEMORY(r); + AO_fetch_and_add1(&collectable_count); + for (p = (int *)r; + (word)p < (word)r + my_extra + sizeof(struct SEXPR); p++) { + if (*p) { + GC_printf("Found nonzero at %p - allocator is broken\n", + (void *)p); + FAIL; + } + *p = (int)((13 << 12) + ((p - (int *)r) & 0xfff)); + } +# ifdef AT_END + r = (sexpr)((char *)r + (my_extra & ~7)); +# endif + r -> sexpr_car = x; + GC_PTR_STORE_AND_DIRTY(&r->sexpr_cdr, y); + GC_reachable_here(x); + return r; +} +# endif + +#include "gc_mark.h" + +#ifdef GC_GCJ_SUPPORT + +#include "gc_gcj.h" + +/* The following struct emulates the vtable in gcj. */ +/* This assumes the default value of MARK_DESCR_OFFSET. */ +struct fake_vtable { + void * dummy; /* class pointer in real gcj. */ + GC_word descr; +}; + +struct fake_vtable gcj_class_struct1 = { 0, sizeof(struct SEXPR) + + sizeof(struct fake_vtable *) }; + /* length based descriptor. */ +struct fake_vtable gcj_class_struct2 = + { 0, ((GC_word)3 << (CPP_WORDSZ - 3)) | GC_DS_BITMAP}; + /* Bitmap based descriptor. */ + +struct GC_ms_entry * fake_gcj_mark_proc(word * addr, + struct GC_ms_entry *mark_stack_ptr, + struct GC_ms_entry *mark_stack_limit, + word env ) +{ + sexpr x; + if (1 == env) { + /* Object allocated with debug allocator. */ + addr = (word *)GC_USR_PTR_FROM_BASE(addr); + } + x = (sexpr)(addr + 1); /* Skip the vtable pointer. */ + mark_stack_ptr = GC_MARK_AND_PUSH( + (void *)(x -> sexpr_cdr), mark_stack_ptr, + mark_stack_limit, (void * *)&(x -> sexpr_cdr)); + mark_stack_ptr = GC_MARK_AND_PUSH( + (void *)(x -> sexpr_car), mark_stack_ptr, + mark_stack_limit, (void * *)&(x -> sexpr_car)); + return(mark_stack_ptr); +} + +#endif /* GC_GCJ_SUPPORT */ + + +sexpr small_cons (sexpr x, sexpr y) +{ + sexpr r = GC_NEW(struct SEXPR); + + CHECK_OUT_OF_MEMORY(r); + AO_fetch_and_add1(&collectable_count); + r -> sexpr_car = x; + GC_PTR_STORE_AND_DIRTY(&r->sexpr_cdr, y); + GC_reachable_here(x); + return r; +} + +#ifdef NO_CONS_ATOMIC_LEAF +# define small_cons_leaf(x) small_cons(INT_TO_SEXPR(x), nil) +#else + sexpr small_cons_leaf(int x) + { + sexpr r = (sexpr)GC_MALLOC_ATOMIC(sizeof(struct SEXPR)); + + CHECK_OUT_OF_MEMORY(r); + AO_fetch_and_add1(&atomic_count); + r -> sexpr_car = INT_TO_SEXPR(x); + r -> sexpr_cdr = nil; + return r; + } +#endif + +sexpr small_cons_uncollectable (sexpr x, sexpr y) +{ + sexpr r = (sexpr)GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR)); + + CHECK_OUT_OF_MEMORY(r); + AO_fetch_and_add1(&uncollectable_count); + r -> sexpr_cdr = (sexpr)(~(GC_word)y); + GC_PTR_STORE_AND_DIRTY(&r->sexpr_car, x); + return r; +} + +#ifdef GC_GCJ_SUPPORT + sexpr gcj_cons(sexpr x, sexpr y) + { + sexpr result; + GC_word * r = (GC_word *)GC_GCJ_MALLOC( + sizeof(struct SEXPR) + sizeof(struct fake_vtable*), + (AO_fetch_and_add1(&extra_count) & 1) != 0 + ? &gcj_class_struct1 + : &gcj_class_struct2); + + CHECK_OUT_OF_MEMORY(r); + AO_fetch_and_add1(&collectable_count); + result = (sexpr)(r + 1); + result -> sexpr_car = x; + GC_PTR_STORE_AND_DIRTY(&result->sexpr_cdr, y); + GC_reachable_here(x); + return result; + } +#endif /* GC_GCJ_SUPPORT */ + +/* Return reverse(x) concatenated with y */ +sexpr reverse1(sexpr x, sexpr y) +{ + if (is_nil(x)) { + return(y); + } else { + return( reverse1(cdr(x), cons(car(x), y)) ); + } +} + +sexpr reverse(sexpr x) +{ +# ifdef TEST_WITH_SYSTEM_MALLOC + GC_noop1(GC_HIDE_POINTER(malloc(100000))); +# endif + return( reverse1(x, nil) ); +} + +sexpr ints(int low, int up) +{ + if (low > up) { + return(nil); + } else { + return small_cons(small_cons_leaf(low), ints(low + 1, up)); + } +} + +#ifdef GC_GCJ_SUPPORT +/* Return reverse(x) concatenated with y */ +sexpr gcj_reverse1(sexpr x, sexpr y) +{ + if (is_nil(x)) { + return(y); + } else { + return( gcj_reverse1(cdr(x), gcj_cons(car(x), y)) ); + } +} + +sexpr gcj_reverse(sexpr x) +{ + return( gcj_reverse1(x, nil) ); +} + +sexpr gcj_ints(int low, int up) +{ + if (low > up) { + return(nil); + } else { + return(gcj_cons(gcj_cons(INT_TO_SEXPR(low), nil), gcj_ints(low+1, up))); + } +} +#endif /* GC_GCJ_SUPPORT */ + +/* 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) +{ + if (low > up) { + return(nil); + } else { + return(small_cons_uncollectable(small_cons_leaf(low), + uncollectable_ints(low+1, up))); + } +} + +void check_ints(sexpr list, int low, int up) +{ + if (is_nil(list)) { + GC_printf("list is nil\n"); + FAIL; + } + if (SEXPR_TO_INT(car(car(list))) != low) { + GC_printf( + "List reversal produced incorrect list - collector is broken\n"); + FAIL; + } + if (low == up) { + if (cdr(list) != nil) { + GC_printf("List too long - collector is broken\n"); + FAIL; + } + } else { + check_ints(cdr(list), low+1, up); + } +} + +# define UNCOLLECTABLE_CDR(x) (sexpr)(~(GC_word)(cdr(x))) + +void check_uncollectable_ints(sexpr list, int low, int up) +{ + if (SEXPR_TO_INT(car(car(list))) != low) { + GC_printf("Uncollectable list corrupted - collector is broken\n"); + FAIL; + } + if (low == up) { + if (UNCOLLECTABLE_CDR(list) != nil) { + GC_printf("Uncollectable list too long - collector is broken\n"); + FAIL; + } + } else { + check_uncollectable_ints(UNCOLLECTABLE_CDR(list), low+1, up); + } +} + +/* Not used, but useful for debugging: */ +void print_int_list(sexpr x) +{ + if (is_nil(x)) { + GC_printf("NIL\n"); + } else { + GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); + if (!is_nil(cdr(x))) { + GC_printf(", "); + print_int_list(cdr(x)); + } else { + GC_printf("\n"); + } + } +} + +/* ditto: */ +void check_marks_int_list(sexpr x) +{ + if (!GC_is_marked(x)) + GC_printf("[unm:%p]", (void *)x); + else + GC_printf("[mkd:%p]", (void *)x); + if (is_nil(x)) { + GC_printf("NIL\n"); + } else { + if (!GC_is_marked(car(x))) + GC_printf("[unm car:%p]", (void *)car(x)); + GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); + if (!is_nil(cdr(x))) { + GC_printf(", "); + check_marks_int_list(cdr(x)); + } else { + GC_printf("\n"); + } + } +} + +/* + * A tiny list reversal test to check thread creation. + */ +#ifdef THREADS + +# ifdef VERY_SMALL_CONFIG +# define TINY_REVERSE_UPPER_VALUE 4 +# else +# define TINY_REVERSE_UPPER_VALUE 10 +# endif + +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + DWORD __stdcall tiny_reverse_test(void * arg GC_ATTR_UNUSED) +# else + void * tiny_reverse_test(void * arg GC_ATTR_UNUSED) +# endif +{ + int i; + for (i = 0; i < 5; ++i) { + check_ints(reverse(reverse(ints(1, TINY_REVERSE_UPPER_VALUE))), + 1, TINY_REVERSE_UPPER_VALUE); + } +# if defined(GC_ENABLE_SUSPEND_THREAD) + /* Force collection from a thread. */ + GC_gcollect(); +# endif + return 0; +} + +# if defined(GC_PTHREADS) +# if defined(GC_ENABLE_SUSPEND_THREAD) +# include "javaxfc.h" +# endif + + void fork_a_thread(void) + { + pthread_t t; + int code; + + code = pthread_create(&t, NULL, tiny_reverse_test, 0); + if (code != 0) { + GC_printf("Small thread creation failed %d\n", code); + FAIL; + } +# if defined(GC_ENABLE_SUSPEND_THREAD) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_OPENBSD_UTHREADS) && !defined(GC_WIN32_THREADS) \ + && !defined(NACL) && !defined(GC_OSF1_THREADS) + if (GC_is_thread_suspended(t)) { + GC_printf("Running thread should be not suspended\n"); + FAIL; + } + /* Thread could be running or already terminated (but not joined). */ + GC_suspend_thread(t); + if (!GC_is_thread_suspended(t)) { + GC_printf("Thread expected to be suspended\n"); + FAIL; + } + GC_suspend_thread(t); /* should be no-op */ + GC_resume_thread(t); + if (GC_is_thread_suspended(t)) { + GC_printf("Resumed thread should be not suspended\n"); + FAIL; + } + GC_resume_thread(t); /* should be no-op */ +# endif + if ((code = pthread_join(t, 0)) != 0) { + GC_printf("Small thread join failed %d\n", code); + FAIL; + } + } + +# elif defined(GC_WIN32_THREADS) + void fork_a_thread(void) + { + DWORD thread_id; + HANDLE h; + h = 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, errcode= %d\n", + (int)GetLastError()); + FAIL; + } + if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { + GC_printf("Small thread wait failed, errcode= %d\n", + (int)GetLastError()); + FAIL; + } + } + +# endif + +#endif + +void test_generic_malloc_or_special(void *p) { + size_t size; + int kind; + void *p2; + + CHECK_OUT_OF_MEMORY(p); + kind = GC_get_kind_and_size(p, &size); + if (size != GC_size(p)) { + GC_printf("GC_get_kind_and_size returned size not matching GC_size\n"); + FAIL; + } + p2 = GC_GENERIC_OR_SPECIAL_MALLOC(10, kind); + CHECK_OUT_OF_MEMORY(p2); + if (GC_get_kind_and_size(p2, NULL) != kind) { + GC_printf("GC_generic_or_special_malloc:" + " unexpected kind of returned object\n"); + FAIL; + } + GC_FREE(p2); +} + +/* Try to force a to be strangely aligned */ +volatile struct A_s { + char dummy; + AO_t aa; +} A; +#define a_set(p) AO_store_release(&A.aa, (AO_t)(p)) +#define a_get() (sexpr)AO_load_acquire(&A.aa) + +/* + * Repeatedly reverse lists built out of very different sized cons cells. + * Check that we didn't lose anything. + */ +void *GC_CALLBACK reverse_test_inner(void *data) +{ + int i; + sexpr b; + sexpr c; + sexpr d; + sexpr e; + sexpr *f, *g, *h; + + if (data == 0) { + /* This stack frame is not guaranteed to be scanned. */ + return GC_call_with_gc_active(reverse_test_inner, (void*)(word)1); + } + +# ifndef BIG +# if defined(MACOS) \ + || (defined(UNIX_LIKE) && defined(NO_GETCONTEXT)) /* e.g. musl */ + /* Assume 128 KB stacks at least. */ +# if defined(__s390x__) +# define BIG 600 +# else +# define BIG 1000 +# endif +# elif defined(PCR) + /* PCR default stack is 100 KB. Stack frames are up to 120 bytes. */ +# define BIG 700 +# elif defined(MSWINCE) || defined(RTEMS) + /* WinCE only allows 64 KB stacks. */ +# define BIG 500 +# elif defined(EMSCRIPTEN) || defined(OSF1) + /* Wasm reports "Maximum call stack size exceeded" error otherwise. */ + /* OSF has limited stack space by default, and large frames. */ +# define BIG 200 +# elif defined(__MACH__) && defined(__ppc64__) +# define BIG 2500 +# else +# define BIG 4500 +# endif +# endif + + a_set(ints(1, 49)); + b = ints(1, 50); + c = ints(1, BIG); + d = uncollectable_ints(1, 100); + test_generic_malloc_or_special(d); + e = uncollectable_ints(1, 1); + /* Check that realloc updates object descriptors correctly */ + AO_fetch_and_add1(&collectable_count); + f = (sexpr *)GC_MALLOC(4 * sizeof(sexpr)); + f = (sexpr *)GC_REALLOC((void *)f, 6 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(f); + AO_fetch_and_add1(&realloc_count); + GC_PTR_STORE_AND_DIRTY(f + 5, ints(1, 17)); + AO_fetch_and_add1(&collectable_count); + g = (sexpr *)GC_MALLOC(513 * sizeof(sexpr)); + test_generic_malloc_or_special(g); + g = (sexpr *)GC_REALLOC((void *)g, 800 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(g); + AO_fetch_and_add1(&realloc_count); + GC_PTR_STORE_AND_DIRTY(g + 799, ints(1, 18)); + AO_fetch_and_add1(&collectable_count); + h = (sexpr *)GC_MALLOC(1025 * sizeof(sexpr)); + h = (sexpr *)GC_REALLOC((void *)h, 2000 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(h); + AO_fetch_and_add1(&realloc_count); +# ifdef GC_GCJ_SUPPORT + GC_PTR_STORE_AND_DIRTY(h + 1999, gcj_ints(1, 200)); + for (i = 0; i < 51; ++i) { + GC_PTR_STORE_AND_DIRTY(h + 1999, gcj_reverse(h[1999])); + } + /* Leave it as the reversed list for now. */ +# else + GC_PTR_STORE_AND_DIRTY(h + 1999, ints(1, 200)); +# endif + /* Try to force some collections and reuse of small list elements */ + for (i = 0; i < 10; i++) { + (void)ints(1, BIG); + } + /* Superficially test interior pointer recognition on stack */ + c = (sexpr)((char *)c + sizeof(char *)); + d = (sexpr)((char *)d + sizeof(char *)); + + GC_FREE((void *)e); + + check_ints(b,1,50); +# ifndef EMSCRIPTEN + check_ints(a_get(),1,49); +# else + /* FIXME: gctest fails unless check_ints(a_get(), ...) are skipped. */ +# endif + for (i = 0; i < 50; i++) { + check_ints(b,1,50); + b = reverse(reverse(b)); + } + check_ints(b,1,50); +# ifndef EMSCRIPTEN + check_ints(a_get(),1,49); +# endif + for (i = 0; i < 10 * (NTHREADS+1); i++) { +# if (defined(GC_PTHREADS) || defined(GC_WIN32_THREADS)) \ + && (NTHREADS > 0) + if (i % 10 == 0) fork_a_thread(); +# endif + /* This maintains the invariant that a always points to a list */ + /* of 49 integers. Thus, this is thread safe without locks, */ + /* assuming acquire/release barriers in a_get/set() and atomic */ + /* pointer assignments (otherwise, e.g., check_ints() may see */ + /* an uninitialized object returned by GC_MALLOC). */ + a_set(reverse(reverse(a_get()))); +# if !defined(AT_END) && !defined(THREADS) + /* This is not thread safe, since realloc explicitly deallocates */ + a_set(GC_REALLOC(a_get(), (i & 1) != 0 ? 500 : 8200)); + AO_fetch_and_add1(&realloc_count); +# endif + } +# ifndef EMSCRIPTEN + check_ints(a_get(),1,49); +# endif + check_ints(b,1,50); + + /* Restore c and d values. */ + c = (sexpr)((char *)c - sizeof(char *)); + d = (sexpr)((char *)d - sizeof(char *)); + + check_ints(c,1,BIG); + check_uncollectable_ints(d, 1, 100); + check_ints(f[5], 1,17); + check_ints(g[799], 1,18); +# ifdef GC_GCJ_SUPPORT + GC_PTR_STORE_AND_DIRTY(h + 1999, gcj_reverse(h[1999])); +# endif + check_ints(h[1999], 1,200); +# ifndef THREADS + a_set(NULL); +# endif + *(sexpr volatile *)&b = 0; + *(sexpr volatile *)&c = 0; + return 0; +} + +void reverse_test(void) +{ + /* Test GC_do_blocking/GC_call_with_gc_active. */ + (void)GC_do_blocking(reverse_test_inner, 0); +} + +/* + * The rest of this builds balanced binary trees, checks that they don't + * disappear, and tests finalization. + */ +typedef struct treenode { + int level; + struct treenode * lchild; + struct treenode * rchild; +} tn; + +#ifndef GC_NO_FINALIZATION + int finalizable_count = 0; +#endif + +int finalized_count = 0; +int dropped_something = 0; + +void GC_CALLBACK finalizer(void * obj, void * client_data) +{ + tn * t = (tn *)obj; + + FINALIZER_LOCK(); + if ((int)(GC_word)client_data != t -> level) { + GC_printf("Wrong finalization data - collector is broken\n"); + FAIL; + } + finalized_count++; + t -> level = -1; /* detect duplicate finalization immediately */ + FINALIZER_UNLOCK(); +} + +# define MAX_FINALIZED ((NTHREADS+1)*4000) + +# 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; + +tn * mktree(int n) +{ + tn * result = GC_NEW(tn); + tn * left, * right; + + AO_fetch_and_add1(&collectable_count); +# if defined(MACOS) + /* get around static data limitations. */ + if (!live_indicators) { + live_indicators = + (GC_word*)NewPtrClear(MAX_FINALIZED * sizeof(GC_word)); + CHECK_OUT_OF_MEMORY(live_indicators); + } +# endif + if (n == 0) return(0); + CHECK_OUT_OF_MEMORY(result); + result -> level = n; + result -> lchild = left = mktree(n - 1); + result -> rchild = right = mktree(n - 1); + if (AO_fetch_and_add1(&extra_count) % 17 == 0 && n >= 2) { + tn * tmp; + + CHECK_OUT_OF_MEMORY(left); + tmp = left -> rchild; + CHECK_OUT_OF_MEMORY(right); + GC_PTR_STORE_AND_DIRTY(&left->rchild, right->lchild); + GC_PTR_STORE_AND_DIRTY(&right->lchild, tmp); + } + if (AO_fetch_and_add1(&extra_count) % 119 == 0) { +# ifndef GC_NO_FINALIZATION + int my_index; + void **new_link = GC_NEW(void *); + + CHECK_OUT_OF_MEMORY(new_link); + AO_fetch_and_add1(&collectable_count); +# endif + { + FINALIZER_LOCK(); + /* Losing a count here causes erroneous report of failure. */ +# ifndef GC_NO_FINALIZATION + finalizable_count++; + my_index = live_indicators_count++; +# endif + FINALIZER_UNLOCK(); + } + +# ifndef GC_NO_FINALIZATION + if (!GC_get_find_leak()) { + GC_REGISTER_FINALIZER((void *)result, finalizer, (void *)(GC_word)n, + (GC_finalization_proc *)0, (void * *)0); + if (my_index >= MAX_FINALIZED) { + GC_printf("live_indicators overflowed\n"); + FAIL; + } + live_indicators[my_index] = 13; + if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( + (void * *)(&(live_indicators[my_index])), result) != 0) { + GC_printf("GC_general_register_disappearing_link failed\n"); + FAIL; + } + if (GC_move_disappearing_link((void **)(&(live_indicators[my_index])), + (void **)(&(live_indicators[my_index]))) != GC_SUCCESS) { + GC_printf("GC_move_disappearing_link(link,link) failed\n"); + FAIL; + } + *new_link = (void *)live_indicators[my_index]; + if (GC_move_disappearing_link((void **)(&(live_indicators[my_index])), + new_link) != GC_SUCCESS) { + GC_printf("GC_move_disappearing_link(new_link) failed\n"); + FAIL; + } + /* Note: if other thread is performing fork at this moment, */ + /* then the stack of the current thread is dropped (together */ + /* with new_link variable) in the child process, and */ + /* GC_dl_hashtbl entry with the link equal to new_link will be */ + /* removed when a collection occurs (as expected). */ + if (GC_unregister_disappearing_link(new_link) == 0) { + GC_printf("GC_unregister_disappearing_link failed\n"); + FAIL; + } + if (GC_move_disappearing_link((void **)(&(live_indicators[my_index])), + new_link) != GC_NOT_FOUND) { + GC_printf("GC_move_disappearing_link(new_link) failed 2\n"); + FAIL; + } + if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( + (void * *)(&(live_indicators[my_index])), result) != 0) { + 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); + } + GC_END_STUBBORN_CHANGE(result); + GC_reachable_here(left); + GC_reachable_here(right); + return(result); +} + +void chktree(tn *t, int n) +{ + if (0 == n) { + if (NULL == t) /* is a leaf? */ + return; + GC_printf("Clobbered a leaf - collector is broken\n"); + FAIL; + } + if (t -> level != n) { + GC_printf("Lost a node at level %d - collector is broken\n", n); + FAIL; + } + if (AO_fetch_and_add1(&extra_count) % 373 == 0) { + (void)GC_MALLOC((unsigned)AO_fetch_and_add1(&extra_count) % 5001); + AO_fetch_and_add1(&collectable_count); + } + chktree(t -> lchild, n-1); + if (AO_fetch_and_add1(&extra_count) % 73 == 0) { + (void)GC_MALLOC((unsigned)AO_fetch_and_add1(&extra_count) % 373); + AO_fetch_and_add1(&collectable_count); + } + chktree(t -> rchild, n-1); +} + +#if defined(GC_PTHREADS) + pthread_key_t fl_key; +#endif + +void * alloc8bytes(void) +{ +# ifndef GC_PTHREADS + AO_fetch_and_add1(&atomic_count); + return GC_MALLOC_ATOMIC(8); +# elif defined(SMALL_CONFIG) || defined(GC_DEBUG) + AO_fetch_and_add1(&collectable_count); + return(GC_MALLOC(8)); +# else + void ** my_free_list_ptr; + void * my_free_list; + void * next; + + my_free_list_ptr = (void **)pthread_getspecific(fl_key); + if (my_free_list_ptr == 0) { + my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); + if (NULL == my_free_list_ptr) return NULL; + AO_fetch_and_add1(&uncollectable_count); + if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) { + GC_printf("pthread_setspecific failed\n"); + FAIL; + } + } + my_free_list = *my_free_list_ptr; + if (my_free_list == 0) { + my_free_list = GC_malloc_many(8); + if (NULL == my_free_list) return NULL; + } + next = GC_NEXT(my_free_list); + GC_PTR_STORE_AND_DIRTY(my_free_list_ptr, next); + GC_NEXT(my_free_list) = 0; + AO_fetch_and_add1(&collectable_count); + return(my_free_list); +# endif +} + +#include "gc_inline.h" + +void test_tinyfl(void) +{ + void *results[3]; + void *tfls[3][GC_TINY_FREELISTS]; + +# ifndef DONT_ADD_BYTE_AT_END + if (GC_get_all_interior_pointers()) return; /* skip */ +# endif + BZERO(tfls, sizeof(tfls)); + /* TODO: Improve testing of FAST_MALLOC functionality. */ + GC_MALLOC_WORDS(results[0], 11, tfls[0]); + GC_MALLOC_ATOMIC_WORDS(results[1], 20, tfls[1]); + GC_CONS(results[2], results[0], results[1], tfls[2]); +} + +void alloc_small(int n) +{ + int i; + + for (i = 0; i < n; i += 8) { + void *p = alloc8bytes(); + + CHECK_OUT_OF_MEMORY(p); + } +} + +# if defined(THREADS) && defined(GC_DEBUG) +# ifdef VERY_SMALL_CONFIG +# define TREE_HEIGHT 12 +# else +# define TREE_HEIGHT 15 +# endif +# else +# ifdef VERY_SMALL_CONFIG +# define TREE_HEIGHT 13 +# else +# define TREE_HEIGHT 16 +# endif +# endif +void tree_test(void) +{ + tn * root; + int i; + + root = mktree(TREE_HEIGHT); +# ifndef VERY_SMALL_CONFIG + alloc_small(5000000); +# endif + chktree(root, TREE_HEIGHT); + FINALIZER_LOCK(); + if (finalized_count && !dropped_something) { + GC_printf("Premature finalization - collector is broken\n"); + FAIL; + } + dropped_something = 1; + FINALIZER_UNLOCK(); + GC_reachable_here(root); /* Root needs to remain live until */ + /* dropped_something is set. */ + root = mktree(TREE_HEIGHT); + chktree(root, TREE_HEIGHT); + for (i = TREE_HEIGHT; i >= 0; i--) { + root = mktree(i); + chktree(root, i); + } +# ifndef VERY_SMALL_CONFIG + alloc_small(5000000); +# endif +} + +unsigned n_tests = 0; + +#ifndef NO_TYPED_TEST +const GC_word bm_huge[320 / CPP_WORDSZ] = { +# if CPP_WORDSZ == 32 + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, +# endif + (GC_word)((GC_signed_word)-1), + (GC_word)((GC_signed_word)-1), + (GC_word)((GC_signed_word)-1), + (GC_word)((GC_signed_word)-1), + ((GC_word)((GC_signed_word)-1)) >> 8 /* highest byte is zero */ +}; + +/* A very simple test of explicitly typed allocation */ +void typed_test(void) +{ + GC_word * old, * newP; + GC_word bm3[1] = {0}; + GC_word bm2[1] = {0}; + GC_word bm_large[1] = { 0xf7ff7fff }; + GC_descr d1; + GC_descr d2; + GC_descr d3 = GC_make_descriptor(bm_large, 32); + GC_descr d4 = GC_make_descriptor(bm_huge, 320); + GC_word * x = (GC_word *)GC_MALLOC_EXPLICITLY_TYPED( + 320 * sizeof(GC_word) + 123, d4); + int i; + + AO_fetch_and_add1(&collectable_count); + (void)GC_make_descriptor(bm_large, 32); + if (GC_get_bit(bm_huge, 32) == 0 || GC_get_bit(bm_huge, 311) == 0 + || GC_get_bit(bm_huge, 319) != 0) { + GC_printf("Bad GC_get_bit() or bm_huge initialization\n"); + FAIL; + } + GC_set_bit(bm3, 0); + GC_set_bit(bm3, 1); + d1 = GC_make_descriptor(bm3, 2); + GC_set_bit(bm2, 1); + d2 = GC_make_descriptor(bm2, 2); + old = 0; + for (i = 0; i < 4000; i++) { + newP = (GC_word *)GC_MALLOC_EXPLICITLY_TYPED(4 * sizeof(GC_word), d1); + CHECK_OUT_OF_MEMORY(newP); + AO_fetch_and_add1(&collectable_count); + if (newP[0] != 0 || newP[1] != 0) { + GC_printf("Bad initialization by GC_malloc_explicitly_typed\n"); + FAIL; + } + newP[0] = 17; + GC_PTR_STORE_AND_DIRTY(newP + 1, old); + old = newP; + AO_fetch_and_add1(&collectable_count); + newP = (GC_word *)GC_MALLOC_EXPLICITLY_TYPED(4 * sizeof(GC_word), d2); + CHECK_OUT_OF_MEMORY(newP); + newP[0] = 17; + GC_PTR_STORE_AND_DIRTY(newP + 1, old); + old = newP; + AO_fetch_and_add1(&collectable_count); + newP = (GC_word*)GC_MALLOC_EXPLICITLY_TYPED(33 * sizeof(GC_word), d3); + CHECK_OUT_OF_MEMORY(newP); + newP[0] = 17; + GC_PTR_STORE_AND_DIRTY(newP + 1, old); + old = newP; + AO_fetch_and_add1(&collectable_count); + newP = (GC_word *)GC_CALLOC_EXPLICITLY_TYPED(4, 2 * sizeof(GC_word), + d1); + CHECK_OUT_OF_MEMORY(newP); + newP[0] = 17; + GC_PTR_STORE_AND_DIRTY(newP + 1, old); + old = newP; + AO_fetch_and_add1(&collectable_count); + if (i & 0xff) { + newP = (GC_word *)GC_CALLOC_EXPLICITLY_TYPED(7, 3 * sizeof(GC_word), + d2); + } else { + newP = (GC_word *)GC_CALLOC_EXPLICITLY_TYPED(1001, + 3 * sizeof(GC_word), + d2); + if (newP != NULL && (newP[0] != 0 || newP[1] != 0)) { + GC_printf("Bad initialization by GC_malloc_explicitly_typed\n"); + FAIL; + } + } + CHECK_OUT_OF_MEMORY(newP); + newP[0] = 17; + GC_PTR_STORE_AND_DIRTY(newP + 1, old); + old = newP; + } + for (i = 0; i < 20000; i++) { + if (newP[0] != 17) { + GC_printf("Typed alloc failed at %d\n", i); + FAIL; + } + newP[0] = 0; + old = newP; + newP = (GC_word *)old[1]; + } + GC_gcollect(); + GC_noop1((word)x); +} +#endif /* !NO_TYPED_TEST */ + +#ifdef DBG_HDRS_ALL +# define set_print_procs() (void)(A.dummy = 17) +#else + volatile AO_t fail_count = 0; + + void GC_CALLBACK fail_proc1(void *x GC_ATTR_UNUSED) + { + AO_fetch_and_add1(&fail_count); + } + + void set_print_procs(void) + { + /* Set these global variables just once to avoid TSan false positives. */ + A.dummy = 17; + GC_is_valid_displacement_print_proc = fail_proc1; + GC_is_visible_print_proc = fail_proc1; + } + +# ifdef THREADS +# define TEST_FAIL_COUNT(n) 1 +# else +# define TEST_FAIL_COUNT(n) (fail_count >= (AO_t)(n)) +# endif +#endif /* !DBG_HDRS_ALL */ + +static void uniq(void *p, ...) { + va_list a; + void *q[100]; + int n = 0, i, j; + q[n++] = p; + va_start(a,p); + for (;(q[n] = va_arg(a,void *)) != NULL;n++) ; + va_end(a); + for (i=0; igc_thread_handle, + &((struct thr_hndl_sb_s *)cd)->sb); + return NULL; +} + +#ifndef MIN_WORDS +# define MIN_WORDS 2 +#endif + +void run_one_test(void) +{ +# ifndef DBG_HDRS_ALL + char *x, *y; + char **z; +# endif +# ifndef NO_CLOCK + CLOCK_TYPE start_time; + CLOCK_TYPE reverse_time; + unsigned long time_diff; +# endif +# ifndef NO_TEST_HANDLE_FORK + pid_t pid; + int wstatus; +# endif + struct thr_hndl_sb_s thr_hndl_sb; + + GC_FREE(0); +# ifdef THREADS + if (!GC_thread_is_registered() && GC_is_init_called()) { + GC_printf("Current thread is not registered with GC\n"); + FAIL; + } +# endif + test_tinyfl(); +# ifndef DBG_HDRS_ALL + AO_fetch_and_add1(&collectable_count); + x = (char*)GC_malloc(7); + CHECK_OUT_OF_MEMORY(x); + AO_fetch_and_add1(&collectable_count); + y = (char*)GC_malloc(7); + CHECK_OUT_OF_MEMORY(y); + if (GC_size(x) != 8 && GC_size(y) != MIN_WORDS * sizeof(GC_word)) { + GC_printf("GC_size produced unexpected results\n"); + FAIL; + } + AO_fetch_and_add1(&collectable_count); + x = (char*)GC_malloc(15); + CHECK_OUT_OF_MEMORY(x); + if (GC_size(x) != 16) { + GC_printf("GC_size produced unexpected results 2\n"); + FAIL; + } + AO_fetch_and_add1(&collectable_count); + x = (char*)GC_malloc(0); + CHECK_OUT_OF_MEMORY(x); + if (GC_size(x) != MIN_WORDS * sizeof(GC_word)) { + GC_printf("GC_malloc(0) failed: GC_size returns %lu\n", + (unsigned long)GC_size(x)); + FAIL; + } + AO_fetch_and_add1(&uncollectable_count); + x = (char*)GC_malloc_uncollectable(0); + CHECK_OUT_OF_MEMORY(x); + if (GC_size(x) != MIN_WORDS * sizeof(GC_word)) { + GC_printf("GC_malloc_uncollectable(0) failed\n"); + FAIL; + } + AO_fetch_and_add1(&collectable_count); + x = (char*)GC_malloc(16); + CHECK_OUT_OF_MEMORY(x); + if (GC_base(GC_PTR_ADD(x, 13)) != x) { + GC_printf("GC_base(heap ptr) produced incorrect result\n"); + FAIL; + } + if (!GC_is_heap_ptr(x)) { + GC_printf("GC_is_heap_ptr(heap_ptr) produced incorrect result\n"); + FAIL; + } + if (GC_is_heap_ptr(&x)) { + GC_printf("GC_is_heap_ptr(&local_var) produced incorrect result\n"); + FAIL; + } + if (GC_is_heap_ptr((void *)&fail_count) || GC_is_heap_ptr(NULL)) { + GC_printf("GC_is_heap_ptr(&global_var) produced incorrect result\n"); + FAIL; + } + (void)GC_PRE_INCR(x, 0); + (void)GC_POST_INCR(x); + (void)GC_POST_DECR(x); + if (GC_base(x) != x) { + GC_printf("Bad INCR/DECR result\n"); + FAIL; + } + y = (char *)(GC_word)fail_proc1; +# ifndef PCR + if (GC_base(y) != 0) { + GC_printf("GC_base(fn_ptr) produced incorrect result\n"); + FAIL; + } +# endif + if (GC_same_obj(x+5, x) != x + 5) { + GC_printf("GC_same_obj produced incorrect result\n"); + FAIL; + } + if (GC_is_visible(y) != y || GC_is_visible(x) != x) { + GC_printf("GC_is_visible produced incorrect result\n"); + FAIL; + } + z = (char**)GC_malloc(8); + CHECK_OUT_OF_MEMORY(z); + AO_fetch_and_add1(&collectable_count); + GC_PTR_STORE(z, x); + GC_end_stubborn_change(z); + if (*z != x) { + GC_printf("GC_PTR_STORE failed: %p != %p\n", (void *)(*z), (void *)x); + FAIL; + } +# if !defined(IA64) && !defined(POWERPC) + if (!TEST_FAIL_COUNT(1)) { + /* On POWERPCs function pointers point to a descriptor in the */ + /* data segment, so there should have been no failures. */ + /* The same applies to IA64. */ + GC_printf("GC_is_visible produced wrong failure indication\n"); + FAIL; + } +# endif + if (GC_is_valid_displacement(y) != y + || GC_is_valid_displacement(x) != x + || GC_is_valid_displacement(x + 3) != x + 3) { + GC_printf("GC_is_valid_displacement produced incorrect result\n"); + FAIL; + } + { + size_t i; + + (void)GC_malloc(17); + AO_fetch_and_add1(&collectable_count); + 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; + } + } +# ifndef ALL_INTERIOR_POINTERS +# if defined(POWERPC) + if (!TEST_FAIL_COUNT(1)) +# else + if (!TEST_FAIL_COUNT(GC_get_all_interior_pointers() ? 1 : 2)) +# endif + { + GC_printf( + "GC_is_valid_displacement produced wrong failure indication\n"); + FAIL; + } +# endif +# endif /* DBG_HDRS_ALL */ + /* Test floating point alignment */ + { + double *dp = GC_NEW(double); + CHECK_OUT_OF_MEMORY(dp); + AO_fetch_and_add1(&collectable_count); + *dp = 1.0; + dp = GC_NEW(double); + CHECK_OUT_OF_MEMORY(dp); + AO_fetch_and_add1(&collectable_count); + *dp = 1.0; + } + /* Test size 0 allocation a bit more */ + { + size_t i; + for (i = 0; i < 10000; ++i) { + (void)GC_MALLOC(0); + AO_fetch_and_add1(&collectable_count); + GC_FREE(GC_MALLOC(0)); + (void)GC_MALLOC_ATOMIC(0); + AO_fetch_and_add1(&atomic_count); + GC_FREE(GC_MALLOC_ATOMIC(0)); + test_generic_malloc_or_special(GC_malloc_atomic(1)); + AO_fetch_and_add1(&atomic_count); + GC_FREE(GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(1)); + GC_FREE(GC_MALLOC_IGNORE_OFF_PAGE(2)); + } + } + thr_hndl_sb.gc_thread_handle = GC_get_my_stackbottom(&thr_hndl_sb.sb); +# ifdef GC_GCJ_SUPPORT + GC_REGISTER_DISPLACEMENT(sizeof(struct fake_vtable *)); + GC_init_gcj_malloc(0, (void *)(GC_word)fake_gcj_mark_proc); +# endif + /* Make sure that fn arguments are visible to the collector. */ + uniq( + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + (void *)0); + /* GC_malloc(0) must return NULL or something we can deallocate. */ + GC_free(GC_malloc(0)); + 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(); + pid = fork(); + if (pid != 0) { + GC_atfork_parent(); + if (pid == -1) { + GC_printf("Process fork failed\n"); + FAIL; + } + if (print_stats) + GC_log_printf("Forked child process, pid= %ld\n", (long)pid); + if (waitpid(pid, &wstatus, 0) == -1) { + GC_printf("Wait for child process failed\n"); + FAIL; + } + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { + GC_printf("Child process failed, pid= %ld, status= 0x%x\n", + (long)pid, wstatus); + FAIL; + } + } else { + pid_t child_pid = getpid(); + + GC_atfork_child(); + if (print_stats) + GC_log_printf("Started a child process, pid= %ld\n", + (long)child_pid); +# ifdef PARALLEL_MARK + GC_gcollect(); /* no parallel markers */ +# endif + GC_start_mark_threads(); + GC_gcollect(); +# ifdef THREADS + /* Skip "Premature finalization" check in the */ + /* child process because there could be a chance */ + /* that some other thread of the parent was */ + /* executing mktree at the moment of fork. */ + dropped_something = 1; +# endif + tree_test(); +# if !defined(DBG_HDRS_ALL) && !defined(NO_TYPED_TEST) + typed_test(); +# endif +# ifdef THREADS + if (print_stats) + GC_log_printf("Starting tiny reverse test, pid= %ld\n", + (long)child_pid); + tiny_reverse_test(0); + GC_gcollect(); +# endif + if (print_stats) + GC_log_printf("Finished a child process, pid= %ld\n", + (long)child_pid); + exit(0); + } +# endif + (void)GC_call_with_alloc_lock(set_stackbottom, &thr_hndl_sb); + + /* Repeated list reversal test. */ +# ifndef NO_CLOCK + GET_TIME(start_time); +# endif + reverse_test(); +# ifndef NO_CLOCK + 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", + (unsigned) time_diff, (void *)&start_time); + } +# endif +# if !defined(DBG_HDRS_ALL) && !defined(NO_TYPED_TEST) + typed_test(); +# ifndef NO_CLOCK + if (print_stats) { + CLOCK_TYPE typed_time; + + GET_TIME(typed_time); + time_diff = MS_TIME_DIFF(typed_time, start_time); + GC_log_printf("Finished typed_test at time %u (%p)\n", + (unsigned) time_diff, (void *)&start_time); + } +# endif +# endif /* DBG_HDRS_ALL */ + tree_test(); +# ifdef TEST_WITH_SYSTEM_MALLOC + free(calloc(1,1)); + free(realloc(NULL, 64)); +# endif +# ifndef NO_CLOCK + if (print_stats) { + CLOCK_TYPE tree_time; + + GET_TIME(tree_time); + time_diff = MS_TIME_DIFF(tree_time, start_time); + GC_log_printf("Finished tree_test at time %u (%p)\n", + (unsigned) time_diff, (void *)&start_time); + } +# endif + /* Run reverse_test a second time, so we hopefully notice corruption. */ + reverse_test(); +# ifndef NO_CLOCK + if (print_stats) { + GET_TIME(reverse_time); + time_diff = MS_TIME_DIFF(reverse_time, start_time); + GC_log_printf("Finished second reverse_test at time %u (%p)\n", + (unsigned)time_diff, (void *)&start_time); + } +# endif + /* 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); +# ifndef NO_CLOCK + if (print_stats) + GC_log_printf("Finished %p\n", (void *)&start_time); +# endif +} + +/* Execute some tests after termination of other test threads (if any). */ +void run_single_threaded_test(void) { + GC_disable(); + GC_FREE(GC_MALLOC(100)); + GC_enable(); +} + +void GC_CALLBACK reachable_objs_counter(void *obj, size_t size, + void *pcounter) +{ + if (0 == size) { + GC_printf("Reachable object has zero size\n"); + FAIL; + } + if (GC_base(obj) != obj) { + GC_printf("Invalid reachable object base passed by enumerator: %p\n", + obj); + FAIL; + } + if (GC_size(obj) != size) { + GC_printf("Invalid reachable object size passed by enumerator: %lu\n", + (unsigned long)size); + FAIL; + } + (*(unsigned *)pcounter)++; +} + +#define NUMBER_ROUND_UP(v, bound) ((((v) + (bound) - 1) / (bound)) * (bound)) + +void check_heap_stats(void) +{ + size_t max_heap_sz; + int i; +# ifndef GC_NO_FINALIZATION +# ifdef FINALIZE_ON_DEMAND + int late_finalize_count = 0; +# endif +# endif + unsigned obj_count = 0; + + if (!GC_is_init_called()) { + GC_printf("GC should be initialized!\n"); + FAIL; + } +# ifdef VERY_SMALL_CONFIG + /* The upper bounds are a guess, which has been empirically */ + /* adjusted. On low end uniprocessors with incremental GC */ + /* these may be particularly dubious, since empirically the */ + /* heap tends to grow largely as a result of the GC not */ + /* getting enough cycles. */ +# if CPP_WORDSZ == 64 + max_heap_sz = 4500000; +# else + max_heap_sz = 2800000; +# endif +# else +# if CPP_WORDSZ == 64 + max_heap_sz = 26000000; +# else + max_heap_sz = 16000000; +# endif +# endif +# ifdef GC_DEBUG + max_heap_sz *= 2; +# ifdef SAVE_CALL_CHAIN + max_heap_sz *= 3; +# ifdef SAVE_CALL_COUNT + max_heap_sz += max_heap_sz * NFRAMES / 4; +# endif +# endif +# endif +# if defined(ADDRESS_SANITIZER) && !defined(__clang__) + max_heap_sz = max_heap_sz * 2 - max_heap_sz / 3; +# endif +# ifdef MEMORY_SANITIZER + max_heap_sz += max_heap_sz / 4; +# endif + max_heap_sz *= n_tests; +# if defined(USE_MMAP) || defined(MSWIN32) + max_heap_sz = NUMBER_ROUND_UP(max_heap_sz, 4 * 1024 * 1024); +# endif + /* Garbage collect repeatedly so that all inaccessible objects */ + /* can be finalized. */ + if (!GC_is_disabled()) + while (GC_collect_a_little()) { } + for (i = 0; i < 16; i++) { + GC_gcollect(); +# ifndef GC_NO_FINALIZATION +# ifdef FINALIZE_ON_DEMAND + late_finalize_count += +# endif + GC_invoke_finalizers(); +# endif + } + 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_alloc_lock(); + GC_enumerate_reachable_objects_inner(reachable_objs_counter, &obj_count); + GC_alloc_unlock(); + GC_printf("Completed %u tests\n", n_tests); + GC_printf("Allocated %d collectable objects\n", (int)collectable_count); + GC_printf("Allocated %d uncollectable objects\n", + (int)uncollectable_count); + GC_printf("Allocated %d atomic objects\n", (int)atomic_count); + GC_printf("Reallocated %d objects\n", (int)realloc_count); +# ifndef NO_TEST_HANDLE_FORK + GC_printf("Garbage collection after fork is tested too\n"); +# endif +# ifndef GC_NO_FINALIZATION + if (!GC_get_find_leak()) { + int still_live = 0; +# ifndef GC_LONG_REFS_NOT_NEEDED + int still_long_live = 0; +# endif + +# ifdef FINALIZE_ON_DEMAND + if (finalized_count != late_finalize_count) { + GC_printf("Finalized %d/%d objects - demand finalization error\n", + finalized_count, finalizable_count); + FAIL; + } +# endif + if (finalized_count > finalizable_count + || finalized_count < finalizable_count/2) { + GC_printf("Finalized %d/%d objects - " + "finalization is probably broken\n", + finalized_count, finalizable_count); + FAIL; + } else { + GC_printf("Finalized %d/%d objects - finalization is probably OK\n", + finalized_count, finalizable_count); + } + for (i = 0; i < MAX_FINALIZED; i++) { + if (live_indicators[i] != 0) { + still_live++; + } +# 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) { + GC_printf("\tVery suspicious!\n"); + } 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("Total memory use by allocated blocks is %lu bytes\n", + (unsigned long)GC_get_memory_use()); + GC_printf("Final heap size is %lu bytes\n", + (unsigned long)GC_get_heap_size()); + if (GC_get_total_bytes() < (size_t)n_tests * +# ifdef VERY_SMALL_CONFIG + 2700000 +# else + 33500000 +# endif + ) { + GC_printf("Incorrect execution - missed some allocations\n"); + FAIL; + } + if (GC_get_heap_size() + GC_get_unmapped_bytes() > max_heap_sz + && !GC_get_find_leak()) { + GC_printf("Unexpected heap growth - collector may be broken" + " (heapsize: %lu, expected: %lu)\n", + (unsigned long)(GC_get_heap_size() + GC_get_unmapped_bytes()), + (unsigned long)max_heap_sz); + FAIL; + } +# ifdef USE_MUNMAP + GC_printf("Obtained %lu bytes from OS (of which %lu bytes unmapped)\n", + (unsigned long)GC_get_obtained_from_os_bytes(), + (unsigned long)GC_get_unmapped_bytes()); +# else + GC_printf("Obtained %lu bytes from OS\n", + (unsigned long)GC_get_obtained_from_os_bytes()); +# endif + GC_printf("Final number of reachable objects is %u\n", obj_count); + +# 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 + } + (void)GC_get_size_map_at(-1); + (void)GC_get_size_map_at(1); +# endif + +# ifdef NO_CLOCK + GC_printf("Completed %u collections\n", (unsigned)GC_get_gc_no()); +# elif !defined(PARALLEL_MARK) + GC_printf("Completed %u collections in %lu ms\n", + (unsigned)GC_get_gc_no(), GC_get_full_gc_total_time()); +# else + GC_printf("Completed %u collections in %lu ms" + " (using %d marker threads)\n", + (unsigned)GC_get_gc_no(), GC_get_full_gc_total_time(), + GC_get_parallel() + 1); +# endif + GC_printf("Collector appears to work\n"); +} + +#if defined(MACOS) +void SetMinimumStack(long minSize) +{ + if (minSize > LMGetDefltStack()) + { + long newApplLimit = (long) GetApplLimit() + - (minSize - LMGetDefltStack()); + SetApplLimit((Ptr) newApplLimit); + MaxApplZone(); + } +} + +#define cMinStackSpace (512L * 1024L) + +#endif + +void GC_CALLBACK warn_proc(char *msg, GC_word p) +{ + GC_printf(msg, (unsigned long)p); + /*FAIL;*/ +} + +void enable_incremental_mode(void) +{ +# if (defined(TEST_DEFAULT_VDB) || defined(TEST_MANUAL_VDB) \ + || !defined(DEFAULT_VDB)) && !defined(GC_DISABLE_INCREMENTAL) +# if !defined(MAKE_BACK_GRAPH) && !defined(NO_INCREMENTAL) \ + && !defined(REDIRECT_MALLOC) && !defined(USE_PROC_FOR_LIBRARIES) + GC_enable_incremental(); +# endif + if (GC_is_incremental_mode()) { +# ifndef SMALL_CONFIG + if (GC_get_manual_vdb_allowed()) { + GC_printf("Switched to incremental mode (manual VDB)\n"); + } else +# endif + /* else */ { + GC_printf("Switched to incremental mode\n"); + if (GC_incremental_protection_needs() == GC_PROTECTS_NONE) { +# if defined(PROC_VDB) || defined(SOFT_VDB) + GC_printf("Reading dirty bits from /proc\n"); +# elif defined(GWW_VDB) + GC_printf("Using GetWriteWatch-based implementation\n"); +# endif + } else { + GC_printf("Emulating dirty bits with mprotect/signals\n"); + } + } + } +# endif +} + +#if defined(CPPCHECK) +# include "javaxfc.h" /* for GC_finalize_all */ +# define UNTESTED(sym) GC_noop1((word)&sym) +#endif + +#if defined(MSWINCE) && defined(UNDER_CE) +# define WINMAIN_LPTSTR LPWSTR +#else +# define WINMAIN_LPTSTR LPSTR +#endif + +#if !defined(PCR) && !defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + +#if defined(CPPCHECK) && defined(_MSC_VER) && !defined(_M_ARM) \ + && !defined(_M_ARM64) && !defined(_M_X64) +# include "private/msvc_dbg.h" +#endif + +#if defined(_DEBUG) && (_MSC_VER >= 1900) /* VS 2015+ */ +# ifndef _CRTDBG_MAP_ALLOC +# define _CRTDBG_MAP_ALLOC +# endif +# include + /* Ensure that there is no system-malloc-allocated objects at normal */ + /* exit (i.e. no such memory leaked). */ +# define CRTMEM_CHECK_INIT() \ + (void)_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF) +# define CRTMEM_DUMP_LEAKS() \ + do { \ + if (_CrtDumpMemoryLeaks()) { \ + GC_printf("System-malloc-allocated memory leaked\n"); \ + FAIL; \ + } \ + } while (0) +#else +# define CRTMEM_CHECK_INIT() (void)0 +# define CRTMEM_DUMP_LEAKS() (void)0 +#endif /* !_MSC_VER */ + +#if ((defined(MSWIN32) && !defined(__MINGW32__)) || defined(MSWINCE)) \ + && !defined(NO_WINMAIN_ENTRY) + int APIENTRY WinMain(HINSTANCE instance GC_ATTR_UNUSED, + HINSTANCE prev GC_ATTR_UNUSED, + WINMAIN_LPTSTR cmd GC_ATTR_UNUSED, + int n GC_ATTR_UNUSED) +#elif defined(RTEMS) +# include +# define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +# define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +# define CONFIGURE_RTEMS_INIT_TASKS_TABLE +# define CONFIGURE_MAXIMUM_TASKS 1 +# define CONFIGURE_INIT +# define CONFIGURE_INIT_TASK_STACK_SIZE (64*1024) +# include + rtems_task Init(rtems_task_argument ignord) +#else + int main(void) +#endif +{ + CRTMEM_CHECK_INIT(); +# if defined(CPPCHECK) && !defined(NO_WINMAIN_ENTRY) \ + && ((defined(MSWIN32) && !defined(__MINGW32__)) || defined(MSWINCE)) + GC_noop1((GC_word)&WinMain); +# elif defined(CPPCHECK) && defined(RTEMS) + GC_noop1((GC_word)&Init); +# endif + n_tests = 0; + GC_clear_exclusion_table(); /* no-op as called before GC init */ +# if defined(MACOS) + /* Make sure we have lots and lots of stack space. */ + SetMinimumStack(cMinStackSpace); + /* Cheat and let stdio initialize toolbox for us. */ + printf("Testing GC Macintosh port\n"); +# endif + GC_COND_INIT(); + GC_set_warn_proc(warn_proc); + enable_incremental_mode(); + set_print_procs(); + GC_start_incremental_collection(); + run_one_test(); +# if NTHREADS > 0 + { + int i; + for (i = 0; i < NTHREADS; i++) + run_one_test(); + } +# endif + run_single_threaded_test(); + check_heap_stats(); +# ifndef MSWINCE + fflush(stdout); +# endif +# if defined(CPPCHECK) + /* Entry points we should be testing, but aren't. */ +# ifndef GC_DEBUG + UNTESTED(GC_debug_generic_or_special_malloc); + UNTESTED(GC_debug_register_displacement); + UNTESTED(GC_post_incr); + UNTESTED(GC_pre_incr); +# ifdef GC_GCJ_SUPPORT + UNTESTED(GC_debug_gcj_malloc); +# endif +# endif +# ifdef AMIGA +# ifdef GC_AMIGA_FASTALLOC + UNTESTED(GC_amiga_get_mem); +# endif +# ifndef GC_AMIGA_ONLYFAST + UNTESTED(GC_amiga_set_toany); +# endif +# endif +# if defined(MACOS) && defined(USE_TEMPORARY_MEMORY) + UNTESTED(GC_MacTemporaryNewPtr); +# endif +# if !defined(_M_ARM) && !defined(_M_ARM64) \ + && !defined(_M_X64) && defined(_MSC_VER) + UNTESTED(GetFileLineFromStack); + UNTESTED(GetModuleNameFromStack); + UNTESTED(GetSymbolNameFromStack); +# endif + UNTESTED(GC_abort_on_oom); + UNTESTED(GC_malloc_explicitly_typed_ignore_off_page); + UNTESTED(GC_debug_strndup); + UNTESTED(GC_deinit); + UNTESTED(GC_strndup); + UNTESTED(GC_posix_memalign); + UNTESTED(GC_new_proc); + UNTESTED(GC_clear_roots); + UNTESTED(GC_exclude_static_roots); + UNTESTED(GC_expand_hp); + UNTESTED(GC_register_describe_type_fn); + UNTESTED(GC_register_has_static_roots_callback); +# ifdef GC_GCJ_SUPPORT + UNTESTED(GC_gcj_malloc_ignore_off_page); +# endif +# ifndef NO_DEBUGGING + UNTESTED(GC_dump); + UNTESTED(GC_dump_regions); + UNTESTED(GC_is_tmp_root); + UNTESTED(GC_print_free_list); +# endif +# ifdef TRACE_BUF + UNTESTED(GC_print_trace); +# endif +# ifndef GC_NO_FINALIZATION + UNTESTED(GC_debug_register_finalizer_unreachable); + UNTESTED(GC_register_disappearing_link); + UNTESTED(GC_should_invoke_finalizers); +# ifndef JAVA_FINALIZATION_NOT_NEEDED + UNTESTED(GC_finalize_all); +# endif +# ifndef NO_DEBUGGING + UNTESTED(GC_dump_finalization); +# endif +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + UNTESTED(GC_toggleref_add); +# endif +# endif +# if !defined(OS2) && !defined(MACOS) && !defined(GC_ANDROID_LOG) \ + && !defined(MSWIN32) && !defined(MSWINCE) + UNTESTED(GC_set_log_fd); +# endif +# ifndef REDIRECT_MALLOC_IN_HEADER +# ifdef REDIRECT_MALLOC +# ifndef strndup + UNTESTED(strndup); +# endif +# ifndef strdup + UNTESTED(strdup); +# endif +# endif +# ifdef REDIRECT_REALLOC + UNTESTED(realloc); +# endif +# endif /* !REDIRECT_MALLOC_IN_HEADER */ +# ifdef GC_REQUIRE_WCSDUP + UNTESTED(GC_wcsdup); + UNTESTED(GC_debug_wcsdup); +# endif +# endif +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + GC_win32_free_heap(); +# endif + CRTMEM_DUMP_LEAKS(); +# ifdef RTEMS + exit(0); +# else + return(0); +# endif +} +# endif /* !GC_WIN32_THREADS && !GC_PTHREADS */ + +#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + +DWORD __stdcall thr_run_one_test(void * arg GC_ATTR_UNUSED) +{ + run_one_test(); + return 0; +} + +#ifdef MSWINCE +HANDLE win_created_h; +HWND win_handle; + +LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) +{ + LRESULT ret = 0; + switch (uMsg) { + case WM_HIBERNATE: + GC_printf("Received WM_HIBERNATE, calling GC_gcollect\n"); + /* Force "unmap as much memory as possible" mode. */ + GC_gcollect_and_unmap(); + break; + case WM_CLOSE: + GC_printf("Received WM_CLOSE, closing window\n"); + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + ret = DefWindowProc(hwnd, uMsg, wParam, lParam); + break; + } + return ret; +} + +DWORD __stdcall thr_window(void * arg GC_ATTR_UNUSED) +{ + WNDCLASS win_class = { + CS_NOCLOSE, + window_proc, + 0, + 0, + GetModuleHandle(NULL), + NULL, + NULL, + (HBRUSH)(COLOR_APPWORKSPACE+1), + NULL, + TEXT("GCtestWindow") + }; + MSG msg; + + if (!RegisterClass(&win_class)) + FAIL; + + win_handle = CreateWindowEx( + 0, + TEXT("GCtestWindow"), + TEXT("GCtest"), + 0, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + + if (win_handle == NULL) + FAIL; + + SetEvent(win_created_h); + + ShowWindow(win_handle, SW_SHOW); + UpdateWindow(win_handle); + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} +#endif + +#if !defined(NO_WINMAIN_ENTRY) + int APIENTRY WinMain(HINSTANCE instance GC_ATTR_UNUSED, + HINSTANCE prev GC_ATTR_UNUSED, + WINMAIN_LPTSTR cmd GC_ATTR_UNUSED, + int n GC_ATTR_UNUSED) +#else + int main(void) +#endif +{ +# if NTHREADS > 0 + HANDLE h[NTHREADS]; + int i; +# endif +# ifdef MSWINCE + HANDLE win_thr_h; +# endif + DWORD thread_id; + +# if defined(CPPCHECK) && !defined(NO_WINMAIN_ENTRY) + GC_noop1((GC_word)&WinMain); +# endif +# if defined(GC_DLL) && !defined(GC_NO_THREADS_DISCOVERY) \ + && !defined(MSWINCE) && !defined(THREAD_LOCAL_ALLOC) + GC_use_threads_discovery(); + /* Test with implicit thread registration if possible. */ + GC_printf("Using DllMain to track threads\n"); +# endif + GC_COND_INIT(); + enable_incremental_mode(); + InitializeCriticalSection(&incr_cs); + GC_set_warn_proc(warn_proc); +# ifdef MSWINCE + win_created_h = CreateEvent(NULL, FALSE, FALSE, NULL); + if (win_created_h == (HANDLE)NULL) { + GC_printf("Event creation failed, errcode= %d\n", (int)GetLastError()); + FAIL; + } + win_thr_h = CreateThread(NULL, 0, thr_window, 0, 0, &thread_id); + if (win_thr_h == (HANDLE)NULL) { + GC_printf("Thread creation failed, errcode= %d\n", (int)GetLastError()); + FAIL; + } + if (WaitForSingleObject(win_created_h, INFINITE) != WAIT_OBJECT_0) + FAIL; + CloseHandle(win_created_h); +# endif + set_print_procs(); +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; i++) { + h[i] = CreateThread(NULL, 0, thr_run_one_test, 0, 0, &thread_id); + if (h[i] == (HANDLE)NULL) { + GC_printf("Thread creation failed, errcode= %d\n", (int)GetLastError()); + FAIL; + } + } +# endif /* NTHREADS > 0 */ + run_one_test(); +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; i++) { + if (WaitForSingleObject(h[i], INFINITE) != WAIT_OBJECT_0) { + GC_printf("Thread wait failed, errcode= %d\n", (int)GetLastError()); + FAIL; + } + } +# endif /* NTHREADS > 0 */ +# ifdef MSWINCE + PostMessage(win_handle, WM_CLOSE, 0, 0); + if (WaitForSingleObject(win_thr_h, INFINITE) != WAIT_OBJECT_0) + FAIL; +# endif + run_single_threaded_test(); + check_heap_stats(); +# if defined(CPPCHECK) && defined(GC_WIN32_THREADS) + UNTESTED(GC_ExitThread); +# if !defined(MSWINCE) && !defined(CYGWIN32) + UNTESTED(GC_beginthreadex); + UNTESTED(GC_endthreadex); +# endif +# endif + (void)GC_unregister_my_thread(); /* just to check it works (for main) */ + return(0); +} + +#endif /* GC_WIN32_THREADS */ + + +#ifdef PCR +int test(void) +{ + PCR_Th_T * th1; + PCR_Th_T * th2; + int code; + +# if defined(CPPCHECK) + GC_noop1((word)&PCR_GC_Run); + GC_noop1((word)&PCR_GC_Setup); + GC_noop1((word)&test); +# endif + n_tests = 0; + /* GC_enable_incremental(); */ + GC_set_warn_proc(warn_proc); + set_print_procs(); + th1 = PCR_Th_Fork(run_one_test, 0); + th2 = PCR_Th_Fork(run_one_test, 0); + run_one_test(); + if (PCR_Th_T_Join(th1, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay || code != 0) { + GC_printf("Thread 1 failed\n"); + } + if (PCR_Th_T_Join(th2, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay || code != 0) { + GC_printf("Thread 2 failed\n"); + } + run_single_threaded_test(); + check_heap_stats(); + return(0); +} +#endif + +#if defined(GC_PTHREADS) +# include /* for EAGAIN */ + +void * thr_run_one_test(void * arg GC_ATTR_UNUSED) +{ + run_one_test(); + return(0); +} + +#ifdef GC_DEBUG +# define GC_free GC_debug_free +#endif + +int main(void) +{ +# if NTHREADS > 0 + pthread_t th[NTHREADS]; + int i, nthreads; +# endif + pthread_attr_t attr; + int code; +# ifdef GC_IRIX_THREADS + /* Force a larger stack to be preallocated */ + /* Since the initial can't always grow later. */ + *((volatile char *)&code - 1024*1024) = 0; /* Require 1 MB */ +# endif /* GC_IRIX_THREADS */ +# if defined(GC_HPUX_THREADS) + /* Default stack size is too small, especially with the 64 bit ABI */ + /* Increase it. */ + if (pthread_default_stacksize_np(1024*1024, 0) != 0) { + GC_printf("pthread_default_stacksize_np failed\n"); + } +# endif /* GC_HPUX_THREADS */ +# ifdef PTW32_STATIC_LIB + pthread_win32_process_attach_np (); + pthread_win32_thread_attach_np (); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) \ + && !defined(DARWIN_DONT_PARSE_STACK) && !defined(THREAD_LOCAL_ALLOC) + /* Test with the Darwin implicit thread registration. */ + GC_use_threads_discovery(); + GC_printf("Using Darwin task-threads-based world stop and push\n"); +# endif + GC_set_markers_count(0); + GC_COND_INIT(); + + if ((code = pthread_attr_init(&attr)) != 0) { + GC_printf("pthread_attr_init failed, errno= %d\n", code); + FAIL; + } +# if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS) \ + || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) \ + || defined(GC_OPENBSD_THREADS) + if ((code = pthread_attr_setstacksize(&attr, 1000 * 1024)) != 0) { + GC_printf("pthread_attr_setstacksize failed, errno= %d\n", code); + FAIL; + } +# endif + n_tests = 0; + enable_incremental_mode(); + GC_set_min_bytes_allocd(1); + if (GC_get_min_bytes_allocd() != 1) + FAIL; + GC_set_rate(10); + GC_set_max_prior_attempts(1); + if (GC_get_rate() != 10 || GC_get_max_prior_attempts() != 1) + FAIL; + GC_set_warn_proc(warn_proc); + if ((code = pthread_key_create(&fl_key, 0)) != 0) { + GC_printf("Key creation failed, errno= %d\n", code); + FAIL; + } + set_print_procs(); +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; ++i) { + if ((code = pthread_create(th+i, &attr, thr_run_one_test, 0)) != 0) { + GC_printf("Thread %d creation failed, errno= %d\n", i, code); + if (i > 0 && EAGAIN == code) + break; /* Resource temporarily unavailable */ + FAIL; + } + } + nthreads = i; + for (; i <= NTHREADS; i++) + run_one_test(); + for (i = 0; i < nthreads; ++i) { + if ((code = pthread_join(th[i], 0)) != 0) { + GC_printf("Thread %d join failed, errno= %d\n", i, code); + FAIL; + } + } +# else + run_one_test(); +# endif + run_single_threaded_test(); + check_heap_stats(); + (void)fflush(stdout); + (void)pthread_attr_destroy(&attr); + + /* Dummy checking of various getters and setters. */ + (void)GC_get_bytes_since_gc(); + (void)GC_get_free_bytes(); + (void)GC_get_pages_executable(); + (void)GC_get_warn_proc(); + (void)GC_is_disabled(); + GC_set_allocd_bytes_per_finalizer(GC_get_allocd_bytes_per_finalizer()); + GC_set_disable_automatic_collection(GC_get_disable_automatic_collection()); + GC_set_dont_expand(GC_get_dont_expand()); + GC_set_dont_precollect(GC_get_dont_precollect()); + GC_set_finalize_on_demand(GC_get_finalize_on_demand()); + GC_set_finalizer_notifier(GC_get_finalizer_notifier()); + GC_set_force_unmap_on_gcollect(GC_get_force_unmap_on_gcollect()); + GC_set_free_space_divisor(GC_get_free_space_divisor()); + GC_set_full_freq(GC_get_full_freq()); + GC_set_java_finalization(GC_get_java_finalization()); + GC_set_max_retries(GC_get_max_retries()); + GC_set_no_dls(GC_get_no_dls()); + GC_set_non_gc_bytes(GC_get_non_gc_bytes()); + GC_set_on_collection_event(GC_get_on_collection_event()); + GC_set_on_heap_resize(GC_get_on_heap_resize()); + GC_set_on_thread_event(GC_get_on_thread_event()); + GC_set_oom_fn(GC_get_oom_fn()); + GC_set_push_other_roots(GC_get_push_other_roots()); + GC_set_sp_corrector(GC_get_sp_corrector()); + GC_set_start_callback(GC_get_start_callback()); + GC_set_stop_func(GC_get_stop_func()); + GC_set_suspend_signal(GC_get_suspend_signal()); + GC_set_thr_restart_signal(GC_get_thr_restart_signal()); + GC_set_time_limit(GC_get_time_limit()); +# if !defined(PCR) && !defined(SMALL_CONFIG) + GC_set_abort_func(GC_get_abort_func()); +# endif +# ifndef NO_CLOCK + GC_set_time_limit_tv(GC_get_time_limit_tv()); +# endif +# ifndef GC_NO_FINALIZATION + GC_set_await_finalize_proc(GC_get_await_finalize_proc()); +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_set_toggleref_func(GC_get_toggleref_func()); +# endif +# endif +# if defined(CPPCHECK) + UNTESTED(GC_allow_register_threads); + UNTESTED(GC_register_altstack); + UNTESTED(GC_stop_world_external); + UNTESTED(GC_start_world_external); +# ifndef GC_NO_DLOPEN + UNTESTED(GC_dlopen); +# endif +# ifndef GC_NO_PTHREAD_CANCEL + UNTESTED(GC_pthread_cancel); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + UNTESTED(GC_pthread_exit); +# endif +# ifndef GC_NO_PTHREAD_SIGMASK + UNTESTED(GC_pthread_sigmask); +# endif +# ifdef NO_TEST_HANDLE_FORK + UNTESTED(GC_atfork_child); + UNTESTED(GC_atfork_parent); + UNTESTED(GC_atfork_prepare); + UNTESTED(GC_set_handle_fork); + UNTESTED(GC_start_mark_threads); +# endif +# endif /* CPPCHECK */ +# ifdef PTW32_STATIC_LIB + pthread_win32_thread_detach_np (); + pthread_win32_process_detach_np (); +# else + (void)GC_unregister_my_thread(); +# endif + return(0); +} +#endif /* GC_PTHREADS */ diff --git a/bdwgc/tests/test_atomic_ops.c b/bdwgc/tests/test_atomic_ops.c new file mode 100644 index 000000000..23bb405b7 --- /dev/null +++ b/bdwgc/tests/test_atomic_ops.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017 Ivan Maidanski + * + * 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. + */ + +/* Minimal testing of atomic operations used by the BDWGC. Primary use */ +/* is to determine whether compiler atomic intrinsics can be relied on. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#if defined(GC_BUILTIN_ATOMIC) || defined(GC_THREADS) + +# include + +# ifdef PARALLEL_MARK +# define AO_REQUIRE_CAS +# endif + +# include "private/gc_atomic_ops.h" + +# define TA_assert(e) \ + if (!(e)) { \ + fprintf(stderr, "Assertion failure, line %d: " #e "\n", __LINE__); \ + exit(-1); \ + } + + int main(void) { + AO_t x = 13; +# if defined(AO_HAVE_char_load) || defined(AO_HAVE_char_store) + unsigned char c = 117; +# endif +# ifdef AO_HAVE_test_and_set_acquire + AO_TS_t z = AO_TS_INITIALIZER; + + TA_assert(AO_test_and_set_acquire(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_acquire(&z) == AO_TS_SET); + AO_CLEAR(&z); +# endif + AO_compiler_barrier(); +# ifdef AO_HAVE_nop_full + AO_nop_full(); +# endif +# ifdef AO_HAVE_char_load + TA_assert(AO_char_load(&c) == 117); +# endif +# ifdef AO_HAVE_char_store + AO_char_store(&c, 119); + TA_assert(c == 119); +# endif +# ifdef AO_HAVE_load_acquire + TA_assert(AO_load_acquire(&x) == 13); +# endif +# if defined(AO_HAVE_fetch_and_add) && defined(AO_HAVE_fetch_and_add1) + TA_assert(AO_fetch_and_add(&x, 42) == 13); + TA_assert(AO_fetch_and_add(&x, (AO_t)(-43)) == 55); + TA_assert(AO_fetch_and_add1(&x) == 12); +# endif +# ifdef AO_HAVE_compare_and_swap_release + TA_assert(!AO_compare_and_swap(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_release(&x, 13, 42)); + TA_assert(x == 42); +# else + if (*(volatile AO_t *)&x == 13) + *(volatile AO_t *)&x = 42; +# endif +# ifdef AO_HAVE_or + AO_or(&x, 66); + TA_assert(x == 106); +# endif +# ifdef AO_HAVE_store_release + AO_store_release(&x, 113); + TA_assert(x == 113); +# endif + return 0; + } + +#else + + int main(void) + { + printf("test_atomic_ops skipped\n"); + return 0; + } + +#endif diff --git a/bdwgc/tests/test_cpp.cc b/bdwgc/tests/test_cpp.cc new file mode 100644 index 000000000..659908932 --- /dev/null +++ b/bdwgc/tests/test_cpp.cc @@ -0,0 +1,425 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/**************************************************************************** +usage: test_cpp number-of-iterations + +This program tries to test the specific C++ functionality provided by +gc_cpp.h that isn't tested by the more general test routines of the +collector. + +A recommended value for number-of-iterations is 10, which will take a +few minutes to complete. + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#undef GC_BUILD + +#define GC_DONT_INCL_WINDOWS_H +#include "gc_cpp.h" + +#include +#include +#include + +#include "gc_allocator.h" + +# include "private/gcconfig.h" + +# ifndef GC_API_PRIV +# define GC_API_PRIV GC_API +# endif +extern "C" { + GC_API_PRIV void GC_printf(const char * format, ...); + /* Use GC private output to reach the same log file. */ + /* Don't include gc_priv.h, since that may include Windows system */ + /* header files that don't take kindly to this context. */ +} + +#ifdef MSWIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +#endif + +#ifdef GC_NAME_CONFLICT +# define USE_GC GC_NS_QUALIFY(UseGC) + struct foo * GC; +#else +# define USE_GC GC_NS_QUALIFY(GC) +#endif + +#define my_assert( e ) \ + if (! (e)) { \ + GC_printf( "Assertion failure in " __FILE__ ", line %d: " #e "\n", \ + __LINE__ ); \ + exit( 1 ); } + +#if defined(__powerpc64__) && !defined(__clang__) && GC_GNUC_PREREQ(10, 0) + /* Suppress "layout of aggregates ... has changed" GCC note. */ +# define A_I_TYPE short +#else +# define A_I_TYPE int +#endif + +class A {public: + /* An uncollectible class. */ + + GC_ATTR_EXPLICIT A( int iArg ): i((A_I_TYPE)iArg) {} + void Test( int iArg ) { + my_assert( i == iArg );} + virtual ~A() {} + A_I_TYPE i; }; + + +class B: public GC_NS_QUALIFY(gc), public A { public: + /* A collectible class. */ + + GC_ATTR_EXPLICIT B( int j ): A( j ) {} + virtual ~B() { + my_assert( deleting );} + static void Deleting( int on ) { + deleting = on;} + static int deleting;}; + +int B::deleting = 0; + +#define C_INIT_LEFT_RIGHT(arg_l, arg_r) \ + { \ + C *l = new C(arg_l); \ + C *r = new C(arg_r); \ + left = l; \ + right = r; \ + if (GC_is_heap_ptr(this)) { \ + GC_END_STUBBORN_CHANGE(this); \ + GC_reachable_here(l); \ + GC_reachable_here(r); \ + } \ + } + +class C: public GC_NS_QUALIFY(gc_cleanup), public A { public: + /* A collectible class with cleanup and virtual multiple inheritance. */ + + // The class uses dynamic memory/resource allocation, so provide both + // a copy constructor and an assignment operator to workaround a cppcheck + // warning. + C(const C& c) : A(c.i), level(c.level), left(0), right(0) { + if (level > 0) + C_INIT_LEFT_RIGHT(*c.left, *c.right); + } + + C& operator=(const C& c) { + if (this != &c) { + delete left; + delete right; + i = c.i; + level = c.level; + left = 0; + right = 0; + if (level > 0) + C_INIT_LEFT_RIGHT(*c.left, *c.right); + } + return *this; + } + + GC_ATTR_EXPLICIT C( int levelArg ): A( levelArg ), level( levelArg ) { + nAllocated++; + if (level > 0) { + C_INIT_LEFT_RIGHT(level - 1, level - 1); + } else { + left = right = 0;}} + ~C() { + this->A::Test( level ); + nFreed++; + my_assert( level == 0 ? + left == 0 && right == 0 : + level == left->level + 1 && level == right->level + 1 ); + left = right = 0; + level = -123456;} + static void Test() { + if (GC_is_incremental_mode() && nFreed < (nAllocated / 5) * 4) { + // An explicit GC might be needed to reach the expected number + // of the finalized objects. + GC_gcollect(); + } + my_assert(nFreed <= nAllocated); +# ifndef GC_NO_FINALIZATION + my_assert(nFreed >= (nAllocated / 5) * 4 || GC_get_find_leak()); +# endif + } + + static int nFreed; + static int nAllocated; + int level; + C* left; + C* right;}; + +int C::nFreed = 0; +int C::nAllocated = 0; + + +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. */ + + GC_ATTR_EXPLICIT D( int iArg ): i( iArg ) { + nAllocated++;} + static void CleanUp( void* obj, void* data ) { + D* self = static_cast(obj); + nFreed++; + my_assert( (GC_word)self->i == (GC_word)data );} + static void Test() { +# ifndef GC_NO_FINALIZATION + my_assert(nFreed >= (nAllocated / 5) * 4 || GC_get_find_leak()); +# endif + } + + int i; + static int nFreed; + static int nAllocated;}; + +int D::nFreed = 0; +int D::nAllocated = 0; + + +class E: public GC_NS_QUALIFY(gc_cleanup) { public: + /* A collectible class with clean-up for use by F. */ + + E() { + nAllocated++;} + ~E() { + nFreed++;} + + static int nFreed; + static int nAllocated;}; + +int E::nFreed = 0; +int E::nAllocated = 0; + + +class F: public E {public: + /* A collectible class with clean-up, a base with clean-up, and a + member with clean-up. */ + + F() { + nAllocatedF++; + } + + ~F() { + nFreedF++; + } + + static void Test() { +# ifndef GC_NO_FINALIZATION + my_assert(nFreedF >= (nAllocatedF / 5) * 4 || GC_get_find_leak()); +# endif + my_assert(2 * nFreedF == nFreed); + } + + E e; + static int nFreedF; + static int nAllocatedF; +}; + +int F::nFreedF = 0; +int F::nAllocatedF = 0; + + +GC_word Disguise( void* p ) { + return ~ (GC_word) p;} + +void* Undisguise( GC_word i ) { + return (void*) ~ i;} + +#define GC_CHECKED_DELETE(p) \ + { \ + size_t freed_before = GC_get_expl_freed_bytes_since_gc(); \ + delete p; /* the operator should invoke GC_FREE() */ \ + size_t freed_after = GC_get_expl_freed_bytes_since_gc(); \ + my_assert(freed_before != freed_after); \ + } + +#if ((defined(MSWIN32) && !defined(__MINGW32__)) || defined(MSWINCE)) \ + && !defined(NO_WINMAIN_ENTRY) + int APIENTRY WinMain( HINSTANCE /* instance */, HINSTANCE /* prev */, + LPSTR cmd, int /* cmdShow */) + { + int argc = 0; + char* argv[ 3 ]; + +# if defined(CPPCHECK) + GC_noop1((GC_word)&WinMain); +# endif + if (cmd != 0) + for (argc = 1; argc < (int)(sizeof(argv) / sizeof(argv[0])); argc++) { + // Parse the command-line string. Non-reentrant strtok() is not used + // to avoid complains of static analysis tools. (And, strtok_r() is + // not available on some platforms.) The code is equivalent to: + // if (!(argv[argc] = strtok(argc == 1 ? cmd : 0, " \t"))) break; + if (NULL == cmd) { + argv[argc] = NULL; + break; + } + for (; *cmd != '\0'; cmd++) { + if (*cmd != ' ' && *cmd != '\t') + break; + } + if ('\0' == *cmd) { + argv[argc] = NULL; + break; + } + argv[argc] = cmd; + while (*(++cmd) != '\0') { + if (*cmd == ' ' || *cmd == '\t') + break; + } + if (*cmd != '\0') { + *(cmd++) = '\0'; + } else { + cmd = NULL; + } + } +#elif defined(MACOS) + int main() { + char* argv_[] = {"test_cpp", "10"}; // MacOS doesn't have a command line + argv = argv_; + argc = sizeof(argv_)/sizeof(argv_[0]); +#else + int main( int argc, char* argv[] ) { +#endif + + GC_set_all_interior_pointers(1); + /* needed due to C++ multiple inheritance used */ + +# ifdef TEST_MANUAL_VDB + GC_set_manual_vdb_allowed(1); +# endif + GC_INIT(); +# ifndef NO_INCREMENTAL + GC_enable_incremental(); +# endif + if (GC_get_find_leak()) + GC_printf("This test program is not designed for leak detection mode\n"); + + int i, iters, n; + int *x = gc_allocator().allocate(1); + int *xio; + xio = gc_allocator_ignore_off_page().allocate(1); + GC_reachable_here(xio); + int **xptr = traceable_allocator().allocate(1); + *x = 29; + if (!xptr) { + fprintf(stderr, "Out of memory!\n"); + exit(3); + } + GC_PTR_STORE_AND_DIRTY(xptr, x); + x = 0; + if (argc != 2 + || (n = atoi(argv[1])) <= 0) { + GC_printf("usage: test_cpp number-of-iterations\n" + "Assuming 10 iters\n"); + n = 10; + } +# ifdef LINT2 + if (n > 100 * 1000) n = 100 * 1000; +# endif + + for (iters = 1; iters <= n; iters++) { + GC_printf( "Starting iteration %d\n", iters ); + + /* 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 uncollectible. */ + GC_word as[ 1000 ]; + GC_word bs[ 1000 ]; + for (i = 0; i < 1000; 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. */ + for (i = 0; i < 1000; i++) { + C* c = new C( 2 ); + C c1( 2 ); /* stack allocation should work too */ + D* d; + F* f; + d = ::new (USE_GC, D::CleanUp, (void*)(GC_word)i) D( i ); + GC_reachable_here(d); + f = new F; + F** fa = new F*[1]; + fa[0] = f; + (void)fa; + delete[] fa; + if (0 == i % 10) + GC_CHECKED_DELETE(c); + } + + /* 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 ); + GC_reachable_here(a); + B* b; + b = new B( i ); + (void)b; + b = new (USE_GC) B( i ); + if (0 == i % 10) { + B::Deleting( 1 ); + GC_CHECKED_DELETE(b); + B::Deleting( 0 );} +# if defined(FINALIZE_ON_DEMAND) && !defined(GC_NO_FINALIZATION) + GC_invoke_finalizers(); +# endif + } + + /* Make sure the uncollectible As and Bs are still there. */ + for (i = 0; i < 1000; i++) { + A* a = static_cast(Undisguise(as[i])); + B* b = static_cast(Undisguise(bs[i])); + a->Test( i ); +# if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) + // Workaround for ASan/MSan: the linker uses operator delete + // implementation from libclang_rt instead of gc_cpp (thus + // causing incompatible alloc/free). + GC_FREE(a); +# else + GC_CHECKED_DELETE(a); +# endif + b->Test( i ); + B::Deleting( 1 ); + GC_CHECKED_DELETE(b); + B::Deleting( 0 ); +# if defined(FINALIZE_ON_DEMAND) && !defined(GC_NO_FINALIZATION) + GC_invoke_finalizers(); +# endif + } + + /* Make sure most of the finalizable Cs, Ds, and Fs have + gone away. */ + C::Test(); + D::Test(); + F::Test();} + + x = *xptr; + my_assert (29 == x[0]); + GC_printf( "The test appears to have succeeded.\n" ); + return( 0 ); +} diff --git a/bdwgc/tests/tests.am b/bdwgc/tests/tests.am new file mode 100644 index 000000000..fddbab17f --- /dev/null +++ b/bdwgc/tests/tests.am @@ -0,0 +1,179 @@ +# +# 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. + +# Common libs to _LDADD for all tests. +test_ldadd = $(nodist_libgc_la_OBJECTS) $(top_builddir)/libgc.la \ + $(EXTRA_TEST_LIBS) + +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 + +if EMSCRIPTEN +# Note: because of libtool, you'll need to point your browser to +# .libs/gctest.html, not gctest.html at topdir. +check_PROGRAMS += gctest.html +gctest_html_SOURCES = $(gctest_SOURCES) +gctest_html_LDADD = $(gctest_LDADD) +# Bug in the linker not being able to determine that _memalign and _memset +# are needed? it's part of mmap. +gctest_html_LDFLAGS = -s "EXPORTED_FUNCTIONS=['_memalign', '_main', '_memset']" +endif + +TESTS += leaktest$(EXEEXT) +check_PROGRAMS += leaktest +leaktest_SOURCES = tests/leak_test.c +leaktest_LDADD = $(test_ldadd) + +TESTS += middletest$(EXEEXT) +check_PROGRAMS += middletest +middletest_SOURCES = tests/middle.c +middletest_LDADD = $(test_ldadd) + +TESTS += smashtest$(EXEEXT) +check_PROGRAMS += smashtest +smashtest_SOURCES = tests/smash_test.c +smashtest_LDADD = $(test_ldadd) + +TESTS += hugetest$(EXEEXT) +check_PROGRAMS += hugetest +hugetest_SOURCES = tests/huge_test.c +hugetest_LDADD = $(test_ldadd) + +TESTS += realloc_test$(EXEEXT) +check_PROGRAMS += realloc_test +realloc_test_SOURCES = tests/realloc_test.c +realloc_test_LDADD = $(test_ldadd) + +TESTS += staticrootstest$(EXEEXT) +check_PROGRAMS += staticrootstest +staticrootstest_SOURCES = tests/staticrootstest.c +staticrootstest_CFLAGS = -DSTATICROOTSLIB2 +staticrootstest_LDADD = $(nodist_libgc_la_OBJECTS) $(EXTRA_TEST_LIBS) \ + 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 = -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 = -no-undefined -rpath /nowhere +if ENABLE_SHARED +staticrootstest_LDADD += $(top_builddir)/libgc.la +endif + +if KEEP_BACK_PTRS +TESTS += tracetest$(EXEEXT) +check_PROGRAMS += tracetest +tracetest_SOURCES = tests/trace_test.c +tracetest_LDADD = $(test_ldadd) +endif + +if THREADS + +TESTS += test_atomic_ops$(EXEEXT) +check_PROGRAMS += test_atomic_ops +test_atomic_ops_SOURCES = tests/test_atomic_ops.c +# Really should need only $(ATOMIC_OPS_LIBS) +test_atomic_ops_LDADD = $(test_ldadd) $(THREADDLLIBS) + +TESTS += threadleaktest$(EXEEXT) +check_PROGRAMS += threadleaktest +threadleaktest_SOURCES = tests/thread_leak_test.c +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) $(THREADDLLIBS) + +TESTS += subthreadcreate_test$(EXEEXT) +check_PROGRAMS += subthreadcreate_test +subthreadcreate_test_SOURCES = tests/subthread_create.c +subthreadcreate_test_LDADD = $(test_ldadd) $(THREADDLLIBS) + +TESTS += initsecondarythread_test$(EXEEXT) +check_PROGRAMS += initsecondarythread_test +initsecondarythread_test_SOURCES = tests/initsecondarythread.c +initsecondarythread_test_LDADD = $(test_ldadd) $(THREADDLLIBS) + +endif + +if CPLUSPLUS +TESTS += test_cpp$(EXEEXT) +check_PROGRAMS += test_cpp +test_cpp_SOURCES = tests/test_cpp.cc +if AVOID_CPP_LIB +test_cpp_LDADD = gc_badalc.o gc_cpp.o $(test_ldadd) $(CXXLIBS) +else +test_cpp_LDADD = libgccpp.la $(nodist_libgc_la_OBJECTS) \ + $(EXTRA_TEST_LIBS) $(CXXLIBS) +## In case of static libraries build, libgc.a is already referenced in +## dependency_libs attribute of libgccpp.la file. +if ENABLE_SHARED +test_cpp_LDADD += $(top_builddir)/libgc.la +endif +endif +endif + +if ENABLE_DISCLAIM + +TESTS += disclaim_test$(EXEEXT) +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$(EXEEXT) +check_PROGRAMS += disclaim_bench +disclaim_bench_SOURCES = tests/disclaim_bench.c +disclaim_bench_LDADD = $(test_ldadd) + +TESTS += disclaim_weakmap_test$(EXEEXT) +check_PROGRAMS += disclaim_weakmap_test +disclaim_weakmap_test_SOURCES = tests/disclaim_weakmap_test.c +disclaim_weakmap_test_LDADD = $(test_ldadd) +if THREADS +disclaim_weakmap_test_LDADD += $(THREADDLLIBS) +endif + +endif + +# Run the tests directly (without test-driver): +.PHONY: check-without-test-driver +check-without-test-driver: $(TESTS) + ./gctest$(EXEEXT) + ./hugetest$(EXEEXT) + ./leaktest$(EXEEXT) + ./middletest$(EXEEXT) + ./realloc_test$(EXEEXT) + ./smashtest$(EXEEXT) + ./staticrootstest$(EXEEXT) + test ! -f disclaim_bench$(EXEEXT) || ./disclaim_bench$(EXEEXT) + test ! -f disclaim_test$(EXEEXT) || ./disclaim_test$(EXEEXT) + test ! -f disclaim_weakmap_test$(EXEEXT) || ./disclaim_weakmap_test$(EXEEXT) + test ! -f initsecondarythread_test$(EXEEXT) \ + || ./initsecondarythread_test$(EXEEXT) + test ! -f test_atomic_ops$(EXEEXT) || ./test_atomic_ops$(EXEEXT) + test ! -f threadkey_test$(EXEEXT) || ./threadkey_test$(EXEEXT) + test ! -f threadleaktest$(EXEEXT) || ./threadleaktest$(EXEEXT) + test ! -f subthreadcreate_test$(EXEEXT) || ./subthreadcreate_test$(EXEEXT) + test ! -f test_cpp$(EXEEXT) || ./test_cpp$(EXEEXT) + test ! -f tracetest$(EXEEXT) || ./tracetest$(EXEEXT) + ./cordtest$(EXEEXT) diff --git a/bdwgc/tests/thread_leak_test.c b/bdwgc/tests/thread_leak_test.c new file mode 100644 index 000000000..6902dcd7d --- /dev/null +++ b/bdwgc/tests/thread_leak_test.c @@ -0,0 +1,103 @@ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_THREADS +# define GC_THREADS +#endif + +#undef GC_NO_THREAD_REDIRECTS +#include "leak_detector.h" + +#ifdef GC_PTHREADS +# include /* for EAGAIN */ +# include +#else +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +#endif /* !GC_PTHREADS */ + +#include + +#ifdef GC_PTHREADS + void * test(void * arg) +#else + DWORD WINAPI test(LPVOID arg) +#endif +{ + int *p[10]; + int i; + for (i = 0; i < 10; ++i) { + p[i] = (int *)malloc(sizeof(int) + i); + } + CHECK_LEAKS(); + for (i = 1; i < 10; ++i) { + free(p[i]); + } +# ifdef GC_PTHREADS + return arg; +# else + return (DWORD)(GC_word)arg; +# endif +} + +#ifndef NTHREADS +# define NTHREADS 5 +#endif + +int main(void) { +# if NTHREADS > 0 + int i, n; +# ifdef GC_PTHREADS + pthread_t t[NTHREADS]; +# else + HANDLE t[NTHREADS]; + DWORD thread_id; +# endif + int code; +# endif + + GC_set_find_leak(1); /* for new collect versions not compiled */ + /* with -DFIND_LEAK. */ + GC_INIT(); + +# if NTHREADS > 0 + for (i = 0; i < NTHREADS; ++i) { +# ifdef GC_PTHREADS + code = pthread_create(t + i, 0, test, 0); +# else + t[i] = CreateThread(NULL, 0, test, 0, 0, &thread_id); + code = t[i] != NULL ? 0 : (int)GetLastError(); +# endif + if (code != 0) { + fprintf(stderr, "Thread creation failed, errcode= %d\n", code); +# ifdef GC_PTHREADS + if (i > 1 && EAGAIN == code) break; +# endif + exit(2); + } + } + n = i; + for (i = 0; i < n; ++i) { +# ifdef GC_PTHREADS + code = pthread_join(t[i], 0); +# else + code = WaitForSingleObject(t[i], INFINITE) == WAIT_OBJECT_0 ? 0 : + (int)GetLastError(); +# endif + if (code != 0) { + fprintf(stderr, "Thread join failed, errcode= %d\n", code); + exit(2); + } + } +# endif + + CHECK_LEAKS(); + CHECK_LEAKS(); + CHECK_LEAKS(); + return 0; +} diff --git a/bdwgc/tests/threadkey_test.c b/bdwgc/tests/threadkey_test.c new file mode 100644 index 000000000..44df1278a --- /dev/null +++ b/bdwgc/tests/threadkey_test.c @@ -0,0 +1,119 @@ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef GC_THREADS +# define GC_THREADS +#endif + +#define GC_NO_THREAD_REDIRECTS 1 + +#include "gc.h" + +#include +#include + +#if (!defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(__native_client__)) && !defined(SKIP_THREADKEY_TEST) + /* FIXME: Skip this test on Solaris for now. The test may fail on */ + /* other targets as well. Currently, tested only on Linux, Cygwin */ + /* and Darwin. */ +# define SKIP_THREADKEY_TEST +#endif + +#ifdef SKIP_THREADKEY_TEST + +int main(void) +{ + printf("threadkey_test skipped\n"); + return 0; +} + +#else + +#include + +pthread_key_t key; + +#ifdef GC_SOLARIS_THREADS + /* pthread_once_t key_once = { PTHREAD_ONCE_INIT }; */ +#else + pthread_once_t key_once = PTHREAD_ONCE_INIT; +#endif + +void * entry (void *arg) +{ + pthread_setspecific(key, + (void *)GC_HIDE_POINTER(GC_STRDUP("hello, world"))); + return arg; +} + +void * GC_CALLBACK on_thread_exit_inner (struct GC_stack_base * sb, void * arg) +{ + int res = GC_register_my_thread (sb); + pthread_t t; + int creation_res; /* Used to suppress a warning about */ + /* unchecked pthread_create() result. */ + pthread_attr_t attr; + + if (pthread_attr_init(&attr) != 0 + || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) { + fprintf(stderr, "Thread attribute init or setdetachstate failed\n"); + exit(2); + } + creation_res = GC_pthread_create(&t, &attr, entry, NULL); + (void)pthread_attr_destroy(&attr); + if (res == GC_SUCCESS) + GC_unregister_my_thread (); + + return arg ? (void*)(GC_word)creation_res : 0; +} + +void on_thread_exit (void *v) +{ + GC_call_with_stack_base (on_thread_exit_inner, v); +} + +void make_key (void) +{ + pthread_key_create (&key, on_thread_exit); +} + +#ifndef NTHREADS +# define NTHREADS 5 +#endif + +#define NTHREADS_INNER (NTHREADS * 6) /* number of threads to create */ + +int main(void) +{ + int i; + + GC_INIT(); + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); +# ifdef GC_SOLARIS_THREADS + pthread_key_create (&key, on_thread_exit); +# else + pthread_once (&key_once, make_key); +# endif + for (i = 0; i < NTHREADS_INNER; i++) { + pthread_t t; + + if (GC_pthread_create(&t, NULL, entry, NULL) == 0) { + void *res; + int code = (i & 1) != 0 ? GC_pthread_join(t, &res) + : GC_pthread_detach(t); + + if (code != 0) { + fprintf(stderr, "Thread %s failed, errno= %d\n", + (i & 1) != 0 ? "join" : "detach", code); + exit(2); + } + } + } + return 0; +} + +#endif /* !SKIP_THREADKEY_TEST */ diff --git a/bdwgc/tests/trace_test.c b/bdwgc/tests/trace_test.c new file mode 100644 index 000000000..d97bbed15 --- /dev/null +++ b/bdwgc/tests/trace_test.c @@ -0,0 +1,54 @@ +#include +#include + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif + +#include "gc.h" +#include "gc_backptr.h" + +struct treenode { + struct treenode *x; + struct treenode *y; +} * root[10]; + +struct treenode * mktree(int i) { + struct treenode * r = GC_NEW(struct treenode); + struct treenode *x, *y; + if (0 == i) + return 0; + if (1 == i) + r = (struct treenode *)GC_MALLOC_ATOMIC(sizeof(struct treenode)); + if (r == NULL) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + x = mktree(i - 1); + y = mktree(i - 1); + r -> x = x; + r -> y = y; + if (i != 1) { + GC_END_STUBBORN_CHANGE(r); + GC_reachable_here(x); + GC_reachable_here(y); + } + return r; +} + +int main(void) +{ + int i; + + GC_INIT(); + if (GC_get_find_leak()) + printf("This test program is not designed for leak detection mode\n"); + for (i = 0; i < 10; ++i) { + root[i] = mktree(12); + } + GC_generate_random_backtrace(); + GC_generate_random_backtrace(); + GC_generate_random_backtrace(); + GC_generate_random_backtrace(); + return 0; +} diff --git a/bdwgc/thread_local_alloc.c b/bdwgc/thread_local_alloc.c new file mode 100644 index 000000000..5d07f08e0 --- /dev/null +++ b/bdwgc/thread_local_alloc.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2000-2005 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. + * + * 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. + */ + +#include "private/gc_priv.h" + +#if defined(THREAD_LOCAL_ALLOC) + +#ifndef THREADS +# error "invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS" +#endif + +#include "private/thread_local_alloc.h" + +#include + +#if defined(USE_COMPILER_TLS) + __thread GC_ATTR_TLS_FAST +#elif defined(USE_WIN32_COMPILER_TLS) + __declspec(thread) GC_ATTR_TLS_FAST +#endif +GC_key_t GC_thread_key; + +static GC_bool keys_initialized; + +/* Return a single nonempty freelist fl to the global one pointed to */ +/* by gfl. */ + +static void return_single_freelist(void *fl, void **gfl) +{ + if (*gfl == 0) { + *gfl = fl; + } else { + void *q, **qptr; + + GC_ASSERT(GC_size(fl) == GC_size(*gfl)); + /* Concatenate: */ + qptr = &(obj_link(fl)); + while ((word)(q = *qptr) >= HBLKSIZE) + qptr = &(obj_link(q)); + GC_ASSERT(0 == q); + *qptr = *gfl; + *gfl = fl; + } +} + +/* Recover the contents of the freelist array fl into the global one gfl.*/ +/* We hold the allocator lock. */ +static void return_freelists(void **fl, void **gfl) +{ + int i; + + for (i = 1; i < TINY_FREELISTS; ++i) { + if ((word)(fl[i]) >= HBLKSIZE) { + return_single_freelist(fl[i], &gfl[i]); + } + /* Clear fl[i], since the thread structure may hang around. */ + /* Do it in a way that is likely to trap if we access it. */ + fl[i] = (ptr_t)HBLKSIZE; + } + /* The 0 granule freelist really contains 1 granule objects. */ +# ifdef GC_GCJ_SUPPORT + if (fl[0] == ERROR_FL) return; +# endif + if ((word)(fl[0]) >= HBLKSIZE) { + return_single_freelist(fl[0], &gfl[1]); + } +} + +#ifdef USE_PTHREAD_SPECIFIC + /* Re-set the TLS value on thread cleanup to allow thread-local */ + /* allocations to happen in the TLS destructors. */ + /* GC_unregister_my_thread (and similar routines) will finally set */ + /* the GC_thread_key to NULL preventing this destructor from being */ + /* called repeatedly. */ + static void reset_thread_key(void* v) { + pthread_setspecific(GC_thread_key, v); + } +#else +# define reset_thread_key 0 +#endif + +/* Each thread structure must be initialized. */ +/* This call must be made from the new thread. */ +GC_INNER void GC_init_thread_local(GC_tlfs p) +{ + int i, j, res; + + GC_ASSERT(I_HOLD_LOCK()); + if (!EXPECT(keys_initialized, TRUE)) { +# ifdef USE_CUSTOM_SPECIFIC + /* Ensure proper alignment of a "pushed" GC symbol. */ + GC_ASSERT((word)&GC_thread_key % sizeof(word) == 0); +# endif + res = GC_key_create(&GC_thread_key, reset_thread_key); + if (COVERT_DATAFLOW(res) != 0) { + ABORT("Failed to create key for local allocator"); + } + keys_initialized = TRUE; + } + res = GC_setspecific(GC_thread_key, p); + if (COVERT_DATAFLOW(res) != 0) { + ABORT("Failed to set thread specific allocation pointers"); + } + for (j = 0; j < TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + p -> _freelists[i][j] = (void *)(word)1; + } +# ifdef GC_GCJ_SUPPORT + p -> gcj_freelists[j] = (void *)(word)1; +# endif + } + /* The size 0 free lists are handled like the regular free lists, */ + /* to ensure that the explicit deallocation works. However, */ + /* allocation of a size 0 "gcj" object is always an error. */ +# ifdef GC_GCJ_SUPPORT + p -> gcj_freelists[0] = ERROR_FL; +# endif +} + +/* We hold the allocator lock. */ +GC_INNER void GC_destroy_thread_local(GC_tlfs p) +{ + int k; + + /* We currently only do this from the thread itself. */ + GC_STATIC_ASSERT(THREAD_FREELISTS_KINDS <= MAXOBJKINDS); + for (k = 0; k < THREAD_FREELISTS_KINDS; ++k) { + if (k == (int)GC_n_kinds) + break; /* kind is not created */ + return_freelists(p -> _freelists[k], GC_obj_kinds[k].ok_freelist); + } +# ifdef GC_GCJ_SUPPORT + return_freelists(p -> gcj_freelists, (void **)GC_gcjobjfreelist); +# endif +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind(size_t bytes, int kind) +{ + size_t granules; + void *tsd; + void *result; + +# if MAXOBJKINDS > THREAD_FREELISTS_KINDS + if (EXPECT(kind >= THREAD_FREELISTS_KINDS, FALSE)) { + return GC_malloc_kind_global(bytes, kind); + } +# endif +# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) + { + GC_key_t k = GC_thread_key; + + if (EXPECT(0 == k, FALSE)) { + /* We haven't yet run GC_init_parallel. That means */ + /* we also aren't locking, so this is fairly cheap. */ + return GC_malloc_kind_global(bytes, kind); + } + tsd = GC_getspecific(k); + } +# else + if (!EXPECT(keys_initialized, TRUE)) + return GC_malloc_kind_global(bytes, kind); + tsd = GC_getspecific(GC_thread_key); +# endif +# if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS) + if (EXPECT(0 == tsd, FALSE)) { + return GC_malloc_kind_global(bytes, kind); + } +# endif + GC_ASSERT(GC_is_initialized); + GC_ASSERT(GC_is_thread_tsd_valid(tsd)); + granules = ROUNDED_UP_GRANULES(bytes); +# if defined(CPPCHECK) +# define MALLOC_KIND_PTRFREE_INIT (void*)1 +# else +# define MALLOC_KIND_PTRFREE_INIT NULL +# endif + GC_FAST_MALLOC_GRANS(result, granules, + ((GC_tlfs)tsd) -> _freelists[kind], DIRECT_GRANULES, + kind, GC_malloc_kind_global(bytes, kind), + (void)(kind == PTRFREE ? MALLOC_KIND_PTRFREE_INIT + : (obj_link(result) = 0))); +# ifdef LOG_ALLOCS + GC_log_printf("GC_malloc_kind(%lu, %d) returned %p, recent GC #%lu\n", + (unsigned long)bytes, kind, result, + (unsigned long)GC_gc_no); +# endif + return result; +} + +#ifdef GC_GCJ_SUPPORT + +# include "gc_gcj.h" + +/* Gcj-style allocation without locks is extremely tricky. The */ +/* fundamental issue is that we may end up marking a free list, which */ +/* has freelist links instead of "vtable" pointers. That is usually */ +/* OK, since the next object on the free list will be cleared, and */ +/* will thus be interpreted as containing a zero descriptor. That's */ +/* fine if the object has not yet been initialized. But there are */ +/* interesting potential races. */ +/* In the case of incremental collection, this seems hopeless, since */ +/* the marker may run asynchronously, and may pick up the pointer to */ +/* the next freelist entry (which it thinks is a vtable pointer), get */ +/* suspended for a while, and then see an allocated object instead */ +/* of the vtable. This may be avoidable with either a handshake with */ +/* the collector or, probably more easily, by moving the free list */ +/* links to the second word of each object. The latter isn't a */ +/* universal win, since on architecture like Itanium, nonzero offsets */ +/* are not necessarily free. And there may be cache fill order issues. */ +/* For now, we punt with incremental GC. This probably means that */ +/* incremental GC should be enabled before we fork a second thread. */ +/* Unlike the other thread local allocation calls, we assume that the */ +/* collector has been explicitly initialized. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc(size_t bytes, + void * ptr_to_struct_containing_descr) +{ + if (EXPECT(GC_incremental, FALSE)) { + return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr); + } else { + size_t granules = ROUNDED_UP_GRANULES(bytes); + void *result; + void **tiny_fl; + + GC_ASSERT(GC_gcjobjfreelist != NULL); + tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))->gcj_freelists; + GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES, + GC_gcj_kind, + GC_core_gcj_malloc(bytes, + ptr_to_struct_containing_descr), + {AO_compiler_barrier(); + *(void **)result = ptr_to_struct_containing_descr;}); + /* This forces the initialization of the "method ptr". */ + /* This is necessary to ensure some very subtle properties */ + /* required if a GC is run in the middle of such an allocation. */ + /* Here we implicitly also assume atomicity for the free list. */ + /* and method pointer assignments. */ + /* We must update the freelist before we store the pointer. */ + /* Otherwise a GC at this point would see a corrupted */ + /* free list. */ + /* A real memory barrier is not needed, since the */ + /* action of stopping this thread will cause prior writes */ + /* to complete. */ + /* We assert that any concurrent marker will stop us. */ + /* Thus it is impossible for a mark procedure to see the */ + /* allocation of the next object, but to see this object */ + /* still containing a free list pointer. Otherwise the */ + /* marker, by misinterpreting the freelist link as a vtable */ + /* pointer, might find a random "mark descriptor" in the next */ + /* object. */ + return result; + } +} + +#endif /* GC_GCJ_SUPPORT */ + +/* The thread support layer must arrange to mark thread-local */ +/* free lists explicitly, since the link field is often */ +/* invisible to the marker. It knows how to find all threads; */ +/* we take care of an individual thread freelist structure. */ +GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p) +{ + ptr_t q; + int i, j; + + for (j = 0; j < TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + /* Load the pointer atomically as it might be updated */ + /* concurrently by GC_FAST_MALLOC_GRANS. */ + q = (ptr_t)AO_load((volatile AO_t *)&p->_freelists[i][j]); + if ((word)q > HBLKSIZE) + GC_set_fl_marks(q); + } +# ifdef GC_GCJ_SUPPORT + if (EXPECT(j > 0, TRUE)) { + q = (ptr_t)AO_load((volatile AO_t *)&p->gcj_freelists[j]); + if ((word)q > HBLKSIZE) + GC_set_fl_marks(q); + } +# endif + } +} + +#if defined(GC_ASSERTIONS) + /* Check that all thread-local free-lists in p are completely marked. */ + void GC_check_tls_for(GC_tlfs p) + { + int i, j; + + for (j = 1; j < TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + GC_check_fl_marks(&p->_freelists[i][j]); + } +# ifdef GC_GCJ_SUPPORT + GC_check_fl_marks(&p->gcj_freelists[j]); +# endif + } + } +#endif /* GC_ASSERTIONS */ + +#endif /* THREAD_LOCAL_ALLOC */ diff --git a/bdwgc/tools/callprocs.sh b/bdwgc/tools/callprocs.sh new file mode 100755 index 000000000..a8793f0b7 --- /dev/null +++ b/bdwgc/tools/callprocs.sh @@ -0,0 +1,4 @@ +#!/bin/sh +GC_DEBUG=1 +export GC_DEBUG +$* 2>&1 | awk '{print "0x3e=c\""$0"\""};/^\t##PC##=/ {if ($2 != 0) {print $2"?i"}}' | adb $1 | sed "s/^ >/>/" diff --git a/bdwgc/tools/if_mach.c b/bdwgc/tools/if_mach.c new file mode 100644 index 000000000..2ddad6f2e --- /dev/null +++ b/bdwgc/tools/if_mach.c @@ -0,0 +1,32 @@ +/* Conditionally execute a command based on machine and OS from gcconfig.h */ + +# include "private/gc_priv.h" +# include +# include +# include + +#ifdef __cplusplus +# define EXECV_ARGV_T char** +#else + /* The 2nd argument of execvp() prototype may be either char**, or */ + /* char* const*, or const char* const*. */ +# define EXECV_ARGV_T void* +#endif + +int main(int argc, char **argv) +{ + if (argc < 4) goto Usage; + if (strcmp(MACH_TYPE, argv[1]) != 0) return(0); + if (strlen(OS_TYPE) > 0 && strlen(argv[2]) > 0 + && strcmp(OS_TYPE, argv[2]) != 0) return(0); + fprintf(stderr, "^^^^Starting command^^^^\n"); + fflush(stdout); + execvp(TRUSTED_STRING(argv[3]), (EXECV_ARGV_T)(argv + 3)); + perror("Couldn't execute"); + +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); + return(1); +} diff --git a/bdwgc/tools/if_not_there.c b/bdwgc/tools/if_not_there.c new file mode 100644 index 000000000..d388d04b5 --- /dev/null +++ b/bdwgc/tools/if_not_there.c @@ -0,0 +1,58 @@ +/* Conditionally execute the command argv[2] based if the file argv[1] */ +/* does not exist. If the command is omitted (and the file does not */ +/* exist) then just exit with a non-zero code. */ + +# include "private/gc_priv.h" +# include +# include +# include +#ifdef __DJGPP__ +#include +#endif /* __DJGPP__ */ + +#ifdef __cplusplus +# define EXECV_ARGV_T char** +#else +# define EXECV_ARGV_T void* /* see the comment in if_mach.c */ +#endif + +int main(int argc, char **argv) +{ + FILE * f; +#ifdef __DJGPP__ + DIR * d; +#endif /* __DJGPP__ */ + char *fname; + + if (argc < 2 || argc > 3) + goto Usage; + + fname = TRUSTED_STRING(argv[1]); + f = fopen(fname, "rb"); + if (f != NULL) { + fclose(f); + return(0); + } + f = fopen(fname, "r"); + if (f != NULL) { + fclose(f); + return(0); + } +#ifdef __DJGPP__ + if ((d = opendir(fname)) != 0) { + closedir(d); + return(0); + } +#endif + printf("^^^^Starting command^^^^\n"); + fflush(stdout); + if (argc == 2) + return(2); /* the file does not exist but no command is given */ + + execvp(TRUSTED_STRING(argv[2]), (EXECV_ARGV_T)(argv + 2)); + exit(1); + +Usage: + fprintf(stderr, "Usage: %s file_name [command]\n", argv[0]); + return(1); +} diff --git a/bdwgc/tools/setjmp_t.c b/bdwgc/tools/setjmp_t.c new file mode 100644 index 000000000..3bcf3ff12 --- /dev/null +++ b/bdwgc/tools/setjmp_t.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * 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. + */ + +/* Check whether setjmp actually saves registers in jmp_buf. */ +/* If it doesn't, the generic mark_regs code won't work. */ +/* Compilers vary as to whether they will put x in a */ +/* (callee-save) register without -O. The code is */ +/* contrived such that any decent compiler should put x in */ +/* a callee-save register with -O. Thus it is */ +/* recommended that this be run optimized. (If the machine */ +/* has no callee-save registers, then the generic code is */ +/* safe, but this will not be noticed by this piece of */ +/* code.) This test appears to be far from perfect. */ +#include +#include +#include +#include "private/gc_priv.h" + +#ifdef OS2 +/* GETPAGESIZE() is set to getpagesize() by default, but that */ +/* doesn't really exist, and the collector doesn't need it. */ +#define INCL_DOSFILEMGR +#define INCL_DOSMISC +#define INCL_DOSERRORS +#include + +int getpagesize(void) +{ + ULONG result[1]; + + if (DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, + (void *)result, sizeof(ULONG)) != NO_ERROR) { + fprintf(stderr, "DosQuerySysInfo failed\n"); + result[0] = 4096; + } + return((int)(result[0])); +} + +#elif defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + int getpagesize(void) + { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwPageSize; + } +#endif + +struct a_s { + char a_a; + char * a_b; +} a; + +word nested_sp(void) +{ +# if GC_GNUC_PREREQ(4, 0) + return (word)__builtin_frame_address(0); +# else + volatile word sp; + sp = (word)(&sp); + return sp; +# endif +} + +/* To prevent nested_sp inlining. */ +word (*volatile nested_sp_fn)(void) = nested_sp; + +int g(int x); + +#if defined(CPPCHECK) || !defined(__cplusplus) + const char *a_str = "a"; +#else +# define a_str "a" +#endif + +int main(void) +{ + volatile word sp; + unsigned ps = GETPAGESIZE(); + JMP_BUF b; +# if !defined(__cplusplus) || __cplusplus < 201703L /* before c++17 */ + register +# endif + int x = (int)strlen(a_str); /* 1, slightly disguised */ + static volatile int y = 0; + + sp = (word)(&sp); + printf("This appears to be a %s running %s\n", MACH_TYPE, OS_TYPE); +# if defined(CPPCHECK) + (void)nested_sp(); /* to workaround a bug in cppcheck */ +# endif + if (nested_sp_fn() < 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)sp + ps) & ~(ps-1)); + } else { + printf("Stack appears to grow up.\n"); + printf("Define STACK_GROWS_UP in gc_priv.h\n"); + printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", + ((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 %lu.\n", + (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("\n"); + x = 2*x-1; + (void)SETJMP(b); + if (y == 1) { + if (x == 2) { + printf("Setjmp-based generic mark_regs code probably won't work.\n"); + printf("But we rarely try that anymore. If you have getcontect()\n"); + printf("this probably doesn't matter.\n"); + } else if (x == 1) { + printf("Setjmp-based register marking code may work.\n"); + } else { + printf("Very strange setjmp implementation.\n"); + } + } + y++; + x = 2; + if (y == 1) LONGJMP(b, 1); + printf("Some GC internal configuration stuff: \n"); + 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"); +# else + printf("bit"); +# endif + printf(" per "); +# if defined(MARK_BIT_PER_OBJ) + printf("object.\n"); +# elif defined(MARK_BIT_PER_GRANULE) + printf("granule.\n"); +# endif +# ifdef THREAD_LOCAL_ALLOC + printf("Thread local allocation enabled.\n"); +# endif +# ifdef PARALLEL_MARK + printf("Parallel marking enabled.\n"); +# endif + (void)g(x); + return(0); +} + +int g(int x) +{ + return(x); +} diff --git a/bdwgc/tools/threadlibs.c b/bdwgc/tools/threadlibs.c new file mode 100644 index 000000000..983b487e0 --- /dev/null +++ b/bdwgc/tools/threadlibs.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * 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. + */ + +# include "private/gc_priv.h" + +# include + +int main(void) +{ +# if defined(GC_USE_LD_WRAP) + printf("-Wl,--wrap -Wl,dlopen " + "-Wl,--wrap -Wl,pthread_create -Wl,--wrap -Wl,pthread_join " + "-Wl,--wrap -Wl,pthread_detach -Wl,--wrap -Wl,pthread_sigmask " + "-Wl,--wrap -Wl,pthread_exit -Wl,--wrap -Wl,pthread_cancel\n"); +# endif +# if (defined(GC_LINUX_THREADS) && !defined(HOST_ANDROID)) \ + || defined(GC_IRIX_THREADS) || defined(GC_DARWIN_THREADS) \ + || defined(GC_AIX_THREADS) || (defined(HURD) && defined(GC_THREADS)) +# ifdef GC_USE_DLOPEN_WRAP + printf("-ldl "); +# endif + printf("-lpthread\n"); +# endif +# if defined(GC_OPENBSD_THREADS) + printf("-pthread\n"); +# endif +# if defined(GC_FREEBSD_THREADS) +# ifdef GC_USE_DLOPEN_WRAP + printf("-ldl "); +# endif +# if (__FREEBSD_version < 500000) + printf("-pthread\n"); +# else /* __FREEBSD__ || __DragonFly__ */ + printf("-lpthread\n"); +# endif +# endif +# if defined(GC_NETBSD_THREADS) + printf("-lpthread -lrt\n"); +# endif + +# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) + printf("-lpthread -lrt\n"); +# endif +# if defined(GC_SOLARIS_THREADS) + printf("-lthread -lposix4\n"); + /* Is this right for recent versions? */ +# endif +# if defined(GC_WIN32_THREADS) && defined(CYGWIN32) + printf("-lpthread\n"); +# endif +# if defined(GC_WIN32_PTHREADS) +# ifdef PTW32_STATIC_LIB + /* assume suffix s for static version of the pthreads-win32 library */ + printf("-lpthreadGC2s -lws2_32\n"); +# else + printf("-lpthreadGC2\n"); +# endif +# endif +# if defined(GC_OSF1_THREADS) + printf("-pthread -lrt\n"); /* DOB: must be -pthread, not -lpthread */ +# endif + /* You need GCC 3.0.3 to build this one! */ + /* DG/UX native gcc doesn't know what "-pthread" is */ +# if defined(GC_DGUX386_THREADS) + printf("-ldl -pthread\n"); +# endif + return 0; +} diff --git a/bdwgc/typd_mlc.c b/bdwgc/typd_mlc.c new file mode 100644 index 000000000..741598a0d --- /dev/null +++ b/bdwgc/typd_mlc.c @@ -0,0 +1,734 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. 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. + * + * 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. + * + */ + +#include "private/gc_pmark.h" +#include "gc_inline.h" /* for GC_malloc_kind */ + +/* + * Some simple primitives for allocation with explicit type information. + * Simple objects are allocated such that they contain a GC_descr at the + * end (in the last allocated word). This descriptor may be a procedure + * which then examines an extended descriptor passed as its environment. + * + * Arrays are treated as simple objects if they have sufficiently simple + * structure. Otherwise they are allocated from an array kind that supplies + * a special mark procedure. These arrays contain a pointer to a + * complex_descriptor as their last word. + * This is done because the environment field is too small, and the collector + * must trace the complex_descriptor. + * + * Note that descriptors inside objects may appear cleared, if we encounter a + * false reference to an object on a free list. In the GC_descr case, this + * is OK, since a 0 descriptor corresponds to examining no fields. + * In the complex_descriptor case, we explicitly check for that case. + * + * MAJOR PARTS OF THIS CODE HAVE NOT BEEN TESTED AT ALL and are not testable, + * since they are not accessible through the current interface. + */ + +#include "gc_typed.h" + +#define TYPD_EXTRA_BYTES (sizeof(word) - EXTRA_BYTES) + +STATIC int GC_explicit_kind = 0; + /* Object kind for objects with indirect */ + /* (possibly extended) descriptors. */ + +STATIC int GC_array_kind = 0; + /* Object kind for objects with complex */ + /* descriptors and GC_array_mark_proc. */ + +/* Array descriptors. GC_array_mark_proc understands these. */ +/* We may eventually need to add provisions for headers and */ +/* trailers. Hence we provide for tree structured descriptors, */ +/* though we don't really use them currently. */ + +struct LeafDescriptor { /* Describes simple array. */ + word ld_tag; +# define LEAF_TAG 1 + size_t ld_size; /* bytes per element */ + /* multiple of ALIGNMENT. */ + size_t ld_nelements; /* Number of elements. */ + GC_descr ld_descriptor; /* A simple length, bitmap, */ + /* or procedure descriptor. */ +}; + +struct ComplexArrayDescriptor { + word ad_tag; +# define ARRAY_TAG 2 + size_t ad_nelements; + union ComplexDescriptor * ad_element_descr; +}; + +struct SequenceDescriptor { + word sd_tag; +# define SEQUENCE_TAG 3 + union ComplexDescriptor * sd_first; + union ComplexDescriptor * sd_second; +}; + +typedef union ComplexDescriptor { + struct LeafDescriptor ld; + struct ComplexArrayDescriptor ad; + struct SequenceDescriptor sd; +} complex_descriptor; +#define TAG ad.ad_tag + +#define ED_INITIAL_SIZE 100 + +STATIC int GC_typed_mark_proc_index = 0; /* Indices of my mark */ +STATIC int GC_array_mark_proc_index = 0; /* procedures. */ + +STATIC void GC_push_typed_structures_proc(void) +{ + GC_PUSH_ALL_SYM(GC_ext_descriptors); +} + +/* Add a multiword bitmap to GC_ext_descriptors arrays. Return */ +/* starting index. */ +/* Returns -1 on failure. */ +/* Caller does not hold allocation lock. */ +STATIC signed_word GC_add_ext_descriptor(const word * bm, word nbits) +{ + size_t nwords = divWORDSZ(nbits + WORDSZ-1); + signed_word result; + size_t i; + word last_part; + size_t extra_bits; + DCL_LOCK_STATE; + + LOCK(); + while (GC_avail_descr + nwords >= GC_ed_size) { + typed_ext_descr_t *newExtD; + size_t new_size; + 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; + } else { + UNLOCK(); + new_size = 2 * ed_size; + if (new_size > MAX_ENV) return(-1); + } + newExtD = (typed_ext_descr_t*)GC_malloc_atomic(new_size + * sizeof(typed_ext_descr_t)); + if (NULL == newExtD) + return -1; + LOCK(); + if (ed_size == GC_ed_size) { + if (GC_avail_descr != 0) { + BCOPY(GC_ext_descriptors, newExtD, + GC_avail_descr * sizeof(typed_ext_descr_t)); + } + GC_ed_size = new_size; + GC_ext_descriptors = newExtD; + } /* else another thread already resized it in the meantime */ + } + result = GC_avail_descr; + for (i = 0; i < nwords-1; i++) { + GC_ext_descriptors[result + i].ed_bitmap = bm[i]; + GC_ext_descriptors[result + i].ed_continued = TRUE; + } + last_part = bm[i]; + /* Clear irrelevant bits. */ + extra_bits = nwords * WORDSZ - nbits; + last_part <<= extra_bits; + last_part >>= extra_bits; + GC_ext_descriptors[result + i].ed_bitmap = last_part; + GC_ext_descriptors[result + i].ed_continued = FALSE; + GC_avail_descr += nwords; + UNLOCK(); + return(result); +} + +/* Table of bitmap descriptors for n word long all pointer objects. */ +STATIC GC_descr GC_bm_table[WORDSZ/2]; + +/* Return a descriptor for the concatenation of 2 nwords long objects, */ +/* each of which is described by descriptor. */ +/* The result is known to be short enough to fit into a bitmap */ +/* descriptor. */ +/* Descriptor is a GC_DS_LENGTH or GC_DS_BITMAP descriptor. */ +STATIC GC_descr GC_double_descr(GC_descr descriptor, word nwords) +{ + if ((descriptor & GC_DS_TAGS) == GC_DS_LENGTH) { + descriptor = GC_bm_table[BYTES_TO_WORDS((word)descriptor)]; + } + descriptor |= (descriptor & ~GC_DS_TAGS) >> nwords; + return(descriptor); +} + +STATIC complex_descriptor * +GC_make_sequence_descriptor(complex_descriptor *first, + complex_descriptor *second); + +/* Build a descriptor for an array with nelements elements, */ +/* each of which can be described by a simple descriptor. */ +/* We try to optimize some common cases. */ +/* If the result is COMPLEX, then a complex_descr* is returned */ +/* in *complex_d. */ +/* If the result is LEAF, then we built a LeafDescriptor in */ +/* the structure pointed to by leaf. */ +/* The tag in the leaf structure is not set. */ +/* If the result is SIMPLE, then a GC_descr */ +/* is returned in *simple_d. */ +/* If the result is NO_MEM, then */ +/* we failed to allocate the descriptor. */ +/* The implementation knows that GC_DS_LENGTH is 0. */ +/* *leaf, *complex_d, and *simple_d may be used as temporaries */ +/* during the construction. */ +#define COMPLEX 2 +#define LEAF 1 +#define SIMPLE 0 +#define NO_MEM (-1) +STATIC int GC_make_array_descriptor(size_t nelements, size_t size, + GC_descr descriptor, GC_descr *simple_d, + complex_descriptor **complex_d, + struct LeafDescriptor * leaf) +{ +# define OPT_THRESHOLD 50 + /* For larger arrays, we try to combine descriptors of adjacent */ + /* descriptors to speed up marking, and to reduce the amount */ + /* of space needed on the mark stack. */ + if ((descriptor & GC_DS_TAGS) == GC_DS_LENGTH) { + if (descriptor == (GC_descr)size) { + *simple_d = nelements * descriptor; /* no overflow */ + return(SIMPLE); + } else if ((word)descriptor == 0) { + *simple_d = (GC_descr)0; + return(SIMPLE); + } + } + if (nelements <= OPT_THRESHOLD) { + if (nelements <= 1) { + if (nelements == 1) { + *simple_d = descriptor; + return(SIMPLE); + } else { + *simple_d = (GC_descr)0; + return(SIMPLE); + } + } + } else if (size <= BITMAP_BITS/2 + && (descriptor & GC_DS_TAGS) != GC_DS_PROC + && (size & (sizeof(word)-1)) == 0) { + int result = + GC_make_array_descriptor(nelements/2, 2*size, + GC_double_descr(descriptor, + BYTES_TO_WORDS(size)), + simple_d, complex_d, leaf); + if ((nelements & 1) == 0) { + return(result); + } else { + struct LeafDescriptor * one_element = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + + if (result == NO_MEM || one_element == 0) return(NO_MEM); + one_element -> ld_tag = LEAF_TAG; + one_element -> ld_size = size; + one_element -> ld_nelements = 1; + one_element -> ld_descriptor = descriptor; + switch(result) { + case SIMPLE: + { + struct LeafDescriptor * beginning = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + if (beginning == 0) return(NO_MEM); + beginning -> ld_tag = LEAF_TAG; + beginning -> ld_size = size; + beginning -> ld_nelements = 1; + beginning -> ld_descriptor = *simple_d; + *complex_d = GC_make_sequence_descriptor( + (complex_descriptor *)beginning, + (complex_descriptor *)one_element); + break; + } + case LEAF: + { + struct LeafDescriptor * beginning = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + if (beginning == 0) return(NO_MEM); + beginning -> ld_tag = LEAF_TAG; + beginning -> ld_size = leaf -> ld_size; + beginning -> ld_nelements = leaf -> ld_nelements; + beginning -> ld_descriptor = leaf -> ld_descriptor; + *complex_d = GC_make_sequence_descriptor( + (complex_descriptor *)beginning, + (complex_descriptor *)one_element); + break; + } + case COMPLEX: + *complex_d = GC_make_sequence_descriptor( + *complex_d, + (complex_descriptor *)one_element); + break; + } + if (EXPECT(NULL == *complex_d, FALSE)) return NO_MEM; + + return(COMPLEX); + } + } + + leaf -> ld_size = size; + leaf -> ld_nelements = nelements; + leaf -> ld_descriptor = descriptor; + return(LEAF); +} + +STATIC complex_descriptor * +GC_make_sequence_descriptor(complex_descriptor *first, + complex_descriptor *second) +{ + struct SequenceDescriptor * result = + (struct SequenceDescriptor *) + GC_malloc(sizeof(struct SequenceDescriptor)); + /* Can't result in overly conservative marking, since tags are */ + /* very small integers. Probably faster than maintaining type */ + /* info. */ + if (result != 0) { + result -> sd_tag = SEQUENCE_TAG; + result -> sd_first = first; + result -> sd_second = second; + GC_dirty(result); + REACHABLE_AFTER_DIRTY(first); + REACHABLE_AFTER_DIRTY(second); + } + return((complex_descriptor *)result); +} + +STATIC mse * GC_typed_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env); + +STATIC mse * GC_array_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env); + +STATIC void GC_init_explicit_typing(void) +{ + unsigned i; + + GC_STATIC_ASSERT(sizeof(struct LeafDescriptor) % sizeof(word) == 0); + /* Set up object kind with simple indirect descriptor. */ + GC_explicit_kind = GC_new_kind_inner(GC_new_free_list_inner(), + (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); + /* Set up object kind with array descriptor. */ + GC_array_mark_proc_index = GC_new_proc_inner(GC_array_mark_proc); + GC_array_kind = GC_new_kind_inner(GC_new_free_list_inner(), + GC_MAKE_PROC(GC_array_mark_proc_index, 0), + FALSE, TRUE); + GC_bm_table[0] = GC_DS_BITMAP; + for (i = 1; i < WORDSZ/2; i++) { + GC_bm_table[i] = (((word)-1) << (WORDSZ - i)) | GC_DS_BITMAP; + } +} + +STATIC mse * GC_typed_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env) +{ + word bm = GC_ext_descriptors[env].ed_bitmap; + word * current_p = addr; + word current; + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + DECLARE_HDR_CACHE; + + INIT_HDR_CACHE; + for (; bm != 0; bm >>= 1, current_p++) { + if (bm & 1) { + current = *current_p; + FIXUP_POINTER(current); + if (current >= (word)least_ha && current <= (word)greatest_ha) { + PUSH_CONTENTS((ptr_t)current, mark_stack_ptr, + mark_stack_limit, (ptr_t)current_p); + } + } + } + if (GC_ext_descriptors[env].ed_continued) { + /* Push an entry with the rest of the descriptor back onto the */ + /* stack. Thus we never do too much work at once. Note that */ + /* we also can't overflow the mark stack unless we actually */ + /* mark something. */ + mark_stack_ptr++; + 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.w = + GC_MAKE_PROC(GC_typed_mark_proc_index, env + 1); + } + return(mark_stack_ptr); +} + +/* Return the size of the object described by d. It would be faster to */ +/* store this directly, or to compute it as part of */ +/* GC_push_complex_descriptor, but hopefully it doesn't matter. */ +STATIC word GC_descr_obj_size(complex_descriptor *d) +{ + switch(d -> TAG) { + case LEAF_TAG: + return(d -> ld.ld_nelements * d -> ld.ld_size); + case ARRAY_TAG: + return(d -> ad.ad_nelements + * GC_descr_obj_size(d -> ad.ad_element_descr)); + case SEQUENCE_TAG: + return(GC_descr_obj_size(d -> sd.sd_first) + + GC_descr_obj_size(d -> sd.sd_second)); + default: + ABORT_RET("Bad complex descriptor"); + return 0; + } +} + +/* Push descriptors for the object at addr with complex descriptor d */ +/* onto the mark stack. Return 0 if the mark stack overflowed. */ +STATIC mse * GC_push_complex_descriptor(word *addr, complex_descriptor *d, + mse *msp, mse *msl) +{ + ptr_t current = (ptr_t)addr; + word nelements; + word sz; + word i; + + switch(d -> TAG) { + case LEAF_TAG: + { + GC_descr descr = d -> ld.ld_descriptor; + + nelements = d -> ld.ld_nelements; + if (msl - msp <= (ptrdiff_t)nelements) return(0); + sz = d -> ld.ld_size; + for (i = 0; i < nelements; i++) { + msp++; + msp -> mse_start = current; + msp -> mse_descr.w = descr; + current += sz; + } + return(msp); + } + case ARRAY_TAG: + { + complex_descriptor *descr = d -> ad.ad_element_descr; + + nelements = d -> ad.ad_nelements; + sz = GC_descr_obj_size(descr); + for (i = 0; i < nelements; i++) { + msp = GC_push_complex_descriptor((word *)current, descr, + msp, msl); + if (msp == 0) return(0); + current += sz; + } + return(msp); + } + case SEQUENCE_TAG: + { + sz = GC_descr_obj_size(d -> sd.sd_first); + msp = GC_push_complex_descriptor((word *)current, d -> sd.sd_first, + msp, msl); + if (msp == 0) return(0); + current += sz; + msp = GC_push_complex_descriptor((word *)current, d -> sd.sd_second, + msp, msl); + return(msp); + } + default: + ABORT_RET("Bad complex descriptor"); + return 0; + } +} + +GC_ATTR_NO_SANITIZE_THREAD +static complex_descriptor *get_complex_descr(word *addr, word nwords) +{ + return (complex_descriptor *)addr[nwords - 1]; +} + +#ifdef AO_HAVE_store_release +# define set_obj_descr(op, nwords, d) \ + AO_store_release((volatile AO_t *)(op) + (nwords) - 1, (AO_t)(d)) +#else +# define set_obj_descr(op, nwords, d) \ + (void)(((word *)(op))[(nwords) - 1] = (word)(d)) +#endif + +STATIC mse * GC_array_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, + word env GC_ATTR_UNUSED) +{ + hdr * hhdr = HDR(addr); + word sz = hhdr -> hb_sz; + word nwords = BYTES_TO_WORDS(sz); + complex_descriptor *descr = get_complex_descr(addr, nwords); + mse * orig_mark_stack_ptr = mark_stack_ptr; + mse * new_mark_stack_ptr; + + if (descr == 0) { + /* Found a reference to a free list entry. Ignore it. */ + return(orig_mark_stack_ptr); + } + /* In use counts were already updated when array descriptor was */ + /* pushed. Here we only replace it by subobject descriptors, so */ + /* no update is necessary. */ + new_mark_stack_ptr = GC_push_complex_descriptor(addr, descr, + mark_stack_ptr, + mark_stack_limit-1); + if (new_mark_stack_ptr == 0) { + /* Explicitly instruct Clang Static Analyzer that ptr is non-null. */ + if (NULL == mark_stack_ptr) ABORT("Bad mark_stack_ptr"); + + /* Doesn't fit. Conservatively push the whole array as a unit */ + /* and request a mark stack expansion. */ + /* This cannot cause a mark stack overflow, since it replaces */ + /* the original array entry. */ +# ifdef PARALLEL_MARK + /* We might be using a local_mark_stack in parallel mode. */ + if (GC_mark_stack + GC_mark_stack_size == mark_stack_limit) +# endif + { + 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.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.w = sizeof(word) | GC_DS_LENGTH; + } + return new_mark_stack_ptr; +} + +GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * bm, size_t len) +{ + signed_word last_set_bit = (signed_word)len - 1; + GC_descr result; + DCL_LOCK_STATE; + +# if defined(AO_HAVE_load_acquire) && defined(AO_HAVE_store_release) + if (!EXPECT(AO_load_acquire(&GC_explicit_typing_initialized), TRUE)) { + LOCK(); + if (!GC_explicit_typing_initialized) { + GC_init_explicit_typing(); + AO_store_release(&GC_explicit_typing_initialized, TRUE); + } + UNLOCK(); + } +# else + LOCK(); + if (!EXPECT(GC_explicit_typing_initialized, TRUE)) { + GC_init_explicit_typing(); + GC_explicit_typing_initialized = TRUE; + } + UNLOCK(); +# endif + + while (last_set_bit >= 0 && !GC_get_bit(bm, last_set_bit)) + last_set_bit--; + if (last_set_bit < 0) return(0 /* no pointers */); + +# if ALIGNMENT == CPP_WORDSZ/8 + { + signed_word i; + + for (i = 0; i < last_set_bit; i++) { + if (!GC_get_bit(bm, i)) { + break; + } + } + if (i == last_set_bit) { + /* An initial section contains all pointers. Use length descriptor. */ + return (WORDS_TO_BYTES(last_set_bit+1) | GC_DS_LENGTH); + } + } +# endif + if ((word)last_set_bit < BITMAP_BITS) { + signed_word i; + + /* Hopefully the common case. */ + /* Build bitmap descriptor (with bits reversed) */ + result = SIGNB; + for (i = last_set_bit - 1; i >= 0; i--) { + result >>= 1; + if (GC_get_bit(bm, i)) result |= SIGNB; + } + result |= GC_DS_BITMAP; + } else { + signed_word index = GC_add_ext_descriptor(bm, (word)last_set_bit + 1); + if (index == -1) return(WORDS_TO_BYTES(last_set_bit+1) | GC_DS_LENGTH); + /* Out of memory: use conservative */ + /* approximation. */ + result = GC_MAKE_PROC(GC_typed_mark_proc_index, (word)index); + } + return result; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_explicitly_typed(size_t lb, + GC_descr d) +{ + word *op; + size_t lg; + + GC_ASSERT(GC_explicit_typing_initialized); + if (EXPECT(0 == lb, FALSE)) lb = 1; /* ensure nwords > 1 */ + lb = SIZET_SAT_ADD(lb, TYPD_EXTRA_BYTES); + op = (word *)GC_malloc_kind(lb, GC_explicit_kind); + if (EXPECT(NULL == op, FALSE)) + return NULL; + /* It is not safe to use GC_size_map[lb] to compute lg here as */ + /* the former might be updated asynchronously. */ + lg = BYTES_TO_GRANULES(GC_size(op)); + set_obj_descr(op, GRANULES_TO_WORDS(lg), d); + GC_dirty(op + GRANULES_TO_WORDS(lg) - 1); + REACHABLE_AFTER_DIRTY(d); + return op; +} + +/* We make the GC_clear_stack() call a tail one, hoping to get more of */ +/* the stack. */ +#define GENERAL_MALLOC_IOP(lb, k) \ + GC_clear_stack(GC_generic_malloc_ignore_off_page(lb, k)) + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_malloc_explicitly_typed_ignore_off_page(size_t lb, GC_descr d) +{ + ptr_t op; + size_t lg; + DCL_LOCK_STATE; + + GC_ASSERT(GC_explicit_typing_initialized); + if (EXPECT(0 == lb, FALSE)) lb = 1; + lb = SIZET_SAT_ADD(lb, TYPD_EXTRA_BYTES); + if (SMALL_OBJ(lb)) { + void **opp; + + GC_DBG_COLLECT_AT_MALLOC(lb); + LOCK(); + lg = GC_size_map[lb]; + opp = &GC_obj_kinds[GC_explicit_kind].ok_freelist[lg]; + op = (ptr_t)(*opp); + if (EXPECT(0 == op, FALSE)) { + UNLOCK(); + op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind); + if (0 == op) return 0; + /* See the comment in GC_malloc_explicitly_typed. */ + lg = BYTES_TO_GRANULES(GC_size(op)); + } else { + *opp = obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + UNLOCK(); + } + } else { + op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind); + if (NULL == op) return NULL; + lg = BYTES_TO_GRANULES(GC_size(op)); + } + set_obj_descr(op, GRANULES_TO_WORDS(lg), d); + GC_dirty((word *)op + GRANULES_TO_WORDS(lg) - 1); + REACHABLE_AFTER_DIRTY(d); + return op; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_calloc_explicitly_typed(size_t n, + size_t lb, GC_descr d) +{ + word *op; + size_t lg; + GC_descr simple_descr; + complex_descriptor *complex_descr; + int descr_type; + struct LeafDescriptor leaf; + DCL_LOCK_STATE; + + GC_ASSERT(GC_explicit_typing_initialized); + if (EXPECT(0 == lb || 0 == n, FALSE)) lb = n = 1; + if ((lb | n) > GC_SQRT_SIZE_MAX /* fast initial check */ + && n > GC_SIZE_MAX / lb) + return (*GC_get_oom_fn())(GC_SIZE_MAX); /* n*lb overflow */ + + descr_type = GC_make_array_descriptor((word)n, (word)lb, d, &simple_descr, + &complex_descr, &leaf); + lb *= n; + switch(descr_type) { + case NO_MEM: + return (*GC_get_oom_fn())(lb); + case SIMPLE: + return GC_malloc_explicitly_typed(lb, simple_descr); + case LEAF: + lb = SIZET_SAT_ADD(lb, + sizeof(struct LeafDescriptor) + TYPD_EXTRA_BYTES); + break; + case COMPLEX: + lb = SIZET_SAT_ADD(lb, TYPD_EXTRA_BYTES); + break; + } + op = (word *)GC_malloc_kind(lb, GC_array_kind); + if (EXPECT(NULL == op, FALSE)) + return NULL; + lg = BYTES_TO_GRANULES(GC_size(op)); + if (descr_type == LEAF) { + /* Set up the descriptor inside the object itself. */ + struct LeafDescriptor * lp = + (struct LeafDescriptor *) + (op + GRANULES_TO_WORDS(lg) + - (BYTES_TO_WORDS(sizeof(struct LeafDescriptor)) + 1)); + + lp -> ld_tag = LEAF_TAG; + lp -> ld_size = leaf.ld_size; + lp -> ld_nelements = leaf.ld_nelements; + lp -> ld_descriptor = leaf.ld_descriptor; + /* Hold the allocation lock while writing the descriptor word */ + /* to the object to ensure that the descriptor contents are */ + /* seen by GC_array_mark_proc as expected. */ + /* TODO: It should be possible to replace locking with the */ + /* atomic operations (with the release barrier here) but, in */ + /* this case, avoiding the acquire barrier in */ + /* GC_array_mark_proc seems to be tricky as GC_mark_some might */ + /* be invoked with the world running. */ + LOCK(); + op[GRANULES_TO_WORDS(lg) - 1] = (word)lp; + UNLOCK(); + } else { +# ifndef GC_NO_FINALIZATION + size_t lw = GRANULES_TO_WORDS(lg); + + LOCK(); + op[lw - 1] = (word)complex_descr; + UNLOCK(); + + GC_dirty(op + lw - 1); + REACHABLE_AFTER_DIRTY(complex_descr); + + /* Make sure the descriptor is cleared once there is any danger */ + /* it may have been collected. */ + if (EXPECT(GC_general_register_disappearing_link( + (void **)(op + lw - 1), op) + == GC_NO_MEMORY, FALSE)) +# endif + { + /* Couldn't register it due to lack of memory. Punt. */ + return (*GC_get_oom_fn())(lb); + } + } + return op; +} diff --git a/bdwgc/win32_threads.c b/bdwgc/win32_threads.c new file mode 100644 index 000000000..f649f03a9 --- /dev/null +++ b/bdwgc/win32_threads.c @@ -0,0 +1,3365 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2008 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2008-2021 Ivan Maidanski + * + * 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. + */ + +#include "private/gc_priv.h" + +#if defined(GC_WIN32_THREADS) + +#ifdef THREAD_LOCAL_ALLOC +# include "private/thread_local_alloc.h" +#endif /* THREAD_LOCAL_ALLOC */ + +/* Allocation lock declarations. */ +#if !defined(USE_PTHREAD_LOCKS) + GC_INNER CRITICAL_SECTION GC_allocate_ml; +# 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; +# 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 /* for EINTR */ + + /* Cygwin-specific forward decls */ +# undef pthread_create +# undef pthread_join +# undef pthread_detach + +# ifndef GC_NO_PTHREAD_SIGMASK +# undef pthread_sigmask +# endif + + STATIC void * GC_pthread_start(void * arg); + STATIC void GC_thread_exit_proc(void *arg); + +# include +# ifdef CAN_CALL_ATFORK +# include +# endif + +# ifdef EMULATE_PTHREAD_SEMAPHORE +# include "private/darwin_semaphore.h" +# else +# include +# endif + +#elif !defined(MSWINCE) +# include /* For _beginthreadex, _endthreadex */ +# include /* for errno, EAGAIN */ + +#endif /* !GC_PTHREADS && !MSWINCE */ + +/* PUSHED_REGS_COUNT is the number of copied registers in copy_ptr_regs. */ +static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext); +#if defined(I386) +# ifdef WOW64_THREAD_CONTEXT_WORKAROUND +# define PUSHED_REGS_COUNT 9 +# else +# define PUSHED_REGS_COUNT 7 +# endif +#elif defined(X86_64) || defined(SHx) +# define PUSHED_REGS_COUNT 15 +#elif defined(ARM32) +# define PUSHED_REGS_COUNT 13 +#elif defined(AARCH64) +# define PUSHED_REGS_COUNT 30 +#elif defined(MIPS) || defined(ALPHA) +# define PUSHED_REGS_COUNT 28 +#elif defined(PPC) +# define PUSHED_REGS_COUNT 29 +#endif + +/* DllMain-based thread registration is currently incompatible */ +/* with thread-local allocation, pthreads and WinCE. */ +#if (defined(GC_DLL) || defined(GC_INSIDE_DLL)) && !defined(NO_CRT) \ + && !defined(GC_NO_THREADS_DISCOVERY) && !defined(MSWINCE) \ + && !defined(THREAD_LOCAL_ALLOC) && !defined(GC_PTHREADS) + + /* This code operates in two distinct modes, depending on */ + /* the setting of GC_win32_dll_threads. */ + /* If GC_win32_dll_threads is set, all threads in the process */ + /* are implicitly registered with the GC by DllMain. */ + /* No explicit registration is required, and attempts at */ + /* explicit registration are ignored. This mode is */ + /* very different from the Posix operation of the collector. */ + /* In this mode access to the thread table is lock-free. */ + /* Hence there is a static limit on the number of threads. */ + +# ifdef GC_DISCOVER_TASK_THREADS + /* GC_DISCOVER_TASK_THREADS should be used if DllMain-based */ + /* thread registration is required but it is impossible to */ + /* call GC_use_threads_discovery before other GC routines. */ +# define GC_win32_dll_threads TRUE +# else + STATIC GC_bool GC_win32_dll_threads = FALSE; + /* GC_win32_dll_threads must be set (if needed) at the */ + /* application initialization time, i.e. before any */ + /* collector or thread calls. We make it a "dynamic" */ + /* option only to avoid multiple library versions. */ +# endif + +#else + /* If GC_win32_dll_threads is FALSE (or the collector is */ + /* built without GC_DLL defined), things operate in a way */ + /* that is very similar to Posix platforms, and new threads */ + /* must be registered with the collector, e.g. by using */ + /* preprocessor-based interception of the thread primitives. */ + /* In this case, we use a real data structure for the thread */ + /* table. Note that there is no equivalent of linker-based */ + /* call interception, since we don't have ELF-like */ + /* facilities. The Windows analog appears to be "API */ + /* hooking", which really seems to be a standard way to */ + /* do minor binary rewriting (?). I'd prefer not to have */ + /* the basic collector rely on such facilities, but an */ + /* optional package that intercepts thread calls this way */ + /* would probably be nice. */ +# ifndef GC_NO_THREADS_DISCOVERY +# define GC_NO_THREADS_DISCOVERY +# endif +# define GC_win32_dll_threads FALSE +# undef MAX_THREADS +# define MAX_THREADS 1 /* dll_thread_table[] is always empty. */ +#endif /* GC_NO_THREADS_DISCOVERY */ + +/* We have two versions of the thread table. Which one */ +/* we use depends on whether GC_win32_dll_threads */ +/* is set. Note that before initialization, we don't */ +/* add any entries to either table, even if DllMain is */ +/* called. The main thread will be added on */ +/* initialization. */ + +/* The type of the first argument to InterlockedExchange. */ +/* Documented to be LONG volatile *, but at least gcc likes */ +/* this better. */ +typedef LONG * IE_t; + +STATIC GC_bool GC_thr_initialized = FALSE; + +#ifndef GC_ALWAYS_MULTITHREADED + GC_INNER GC_bool GC_need_to_lock = FALSE; +#endif + +static GC_bool parallel_initialized = FALSE; + +/* GC_use_threads_discovery() is currently incompatible with pthreads */ +/* and WinCE. It might be possible to get DllMain-based thread */ +/* registration to work with Cygwin, but if you try it then you are on */ +/* your own. */ +GC_API void GC_CALL GC_use_threads_discovery(void) +{ +# ifdef GC_NO_THREADS_DISCOVERY + ABORT("GC DllMain-based thread registration unsupported"); +# else + /* Turn on GC_win32_dll_threads. */ + GC_ASSERT(!parallel_initialized); + /* Note that GC_use_threads_discovery is expected to be called by */ + /* the client application (not from DllMain) at start-up. */ +# ifndef GC_DISCOVER_TASK_THREADS + GC_win32_dll_threads = TRUE; +# endif + GC_init_parallel(); +# endif +} + +#define ADDR_LIMIT ((ptr_t)GC_WORD_MAX) + +struct GC_Thread_Rep { + union { +# ifndef GC_NO_THREADS_DISCOVERY + volatile AO_t in_use; + /* Updated without lock. */ + /* We assert that unused */ + /* entries have invalid ids of */ + /* zero and zero stack fields. */ + /* Used only with GC_win32_dll_threads. */ + LONG long_in_use; /* The same but of the type that */ + /* matches the first argument of */ + /* InterlockedExchange(); volatile is */ + /* omitted because the ancient version */ + /* of the prototype lacks the qualifier.*/ +# endif + struct GC_Thread_Rep * next; + /* Hash table link without */ + /* GC_win32_dll_threads. */ + /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + } tm; /* table_management */ + DWORD id; + +# ifdef MSWINCE + /* According to MSDN specs for WinCE targets: */ + /* - DuplicateHandle() is not applicable to thread handles; and */ + /* - the value returned by GetCurrentThreadId() could be used as */ + /* a "real" thread handle (for SuspendThread(), ResumeThread() and */ + /* GetThreadContext()). */ +# define THREAD_HANDLE(t) (HANDLE)(word)(t)->id +# else + HANDLE handle; +# define THREAD_HANDLE(t) (t)->handle +# endif + + ptr_t stack_base; /* The cold end of the stack. */ + /* 0 ==> entry not valid. */ + /* !in_use ==> stack_base == 0 */ + ptr_t last_stack_min; /* Last known minimum (hottest) address */ + /* in stack or ADDR_LIMIT if unset */ +# ifdef IA64 + ptr_t backing_store_end; + ptr_t backing_store_ptr; +# elif defined(I386) + ptr_t initial_stack_base; + /* The cold end of the stack saved by */ + /* GC_record_stack_base (never modified */ + /* by GC_set_stackbottom). */ +# endif + + ptr_t thread_blocked_sp; /* Protected by GC lock. */ + /* NULL value means thread unblocked. */ + /* If set to non-NULL, thread will */ + /* acquire GC lock before doing any */ + /* pointer manipulations. Thus it does */ + /* not need to stop this thread. */ + + struct GC_traced_stack_sect_s *traced_stack_sect; + /* Points to the "stack section" data */ + /* held in stack by the innermost */ + /* GC_call_with_gc_active() of this */ + /* thread. May be NULL. */ + +# ifndef GC_NO_FINALIZATION + unsigned short finalizer_skipped; + unsigned char finalizer_nested; + /* Used by GC_check_finalizer_nested() */ + /* to minimize the level of recursion */ + /* when a client finalizer allocates */ + /* memory (initially both are 0). */ +# endif + + unsigned char suspended; /* really of GC_bool type */ + +# ifdef GC_PTHREADS + unsigned char flags; /* Protected by GC lock. */ +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is intended to be detached. */ +# define KNOWN_FINISHED(t) (((t) -> flags) & FINISHED) + pthread_t pthread_id; + void *status; /* hold exit value until join in case it's a pointer */ +# else +# define KNOWN_FINISHED(t) 0 +# endif + +# ifdef THREAD_LOCAL_ALLOC + struct thread_local_freelists tlfs; +# endif + +# if defined(WOW64_THREAD_CONTEXT_WORKAROUND) && defined(MSWINRT_FLAVOR) + PNT_TIB tib; +# endif + +# ifdef RETRY_GET_THREAD_CONTEXT + ptr_t context_sp; + word context_regs[PUSHED_REGS_COUNT]; + /* Populated as part of GC_suspend() as */ + /* resume/suspend loop may be needed for the */ + /* call to GetThreadContext() to succeed. */ +# endif +}; + +typedef struct GC_Thread_Rep * GC_thread; +typedef volatile struct GC_Thread_Rep * GC_vthread; + +STATIC DWORD GC_main_thread; + +#ifndef GC_NO_THREADS_DISCOVERY + /* We track thread attachments while the world is supposed to be */ + /* stopped. Unfortunately, we cannot stop them from starting, since */ + /* blocking in DllMain seems to cause the world to deadlock. Thus, */ + /* we have to recover if we notice this in the middle of marking. */ + STATIC volatile AO_t GC_attached_thread = FALSE; + + /* We assumed that volatile ==> memory ordering, at least among */ + /* volatiles. This code should consistently use atomic_ops. */ + STATIC volatile GC_bool GC_please_stop = FALSE; +#elif defined(GC_ASSERTIONS) + STATIC GC_bool GC_please_stop = FALSE; +#endif /* GC_NO_THREADS_DISCOVERY && GC_ASSERTIONS */ + +#if defined(WRAP_MARK_SOME) && !defined(GC_PTHREADS) + /* Return TRUE if an thread was attached since we last asked or */ + /* since GC_attached_thread was explicitly reset. */ + GC_INNER GC_bool GC_started_thread_while_stopped(void) + { +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { +# 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; + } +#endif /* WRAP_MARK_SOME */ + +/* Thread table used if GC_win32_dll_threads is set. */ +/* This is a fixed size array. */ +/* Since we use runtime conditionals, both versions */ +/* are always defined. */ +# ifndef MAX_THREADS +# define MAX_THREADS 512 +# endif + +/* Things may get quite slow for large numbers of threads, */ +/* since we look them up with sequential search. */ +volatile struct GC_Thread_Rep dll_thread_table[MAX_THREADS]; + +STATIC volatile LONG GC_max_thread_index = 0; + /* Largest index in dll_thread_table */ + /* that was ever used. */ + +/* And now the version used if GC_win32_dll_threads is not set. */ +/* This is a chained hash table, with much of the code borrowed */ +/* from the Posix implementation. */ +#ifndef THREAD_TABLE_SZ +# define THREAD_TABLE_SZ 256 /* Power of 2 (for speed). */ +#endif +#define THREAD_TABLE_INDEX(id) /* id is of DWORD type */ \ + (int)((((id) >> 8) ^ (id)) % THREAD_TABLE_SZ) +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. It does not contain any pointer */ +/* 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; + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* Caller holds allocation lock. */ +/* Unlike the pthreads version, the id field is set by the caller. */ +STATIC GC_thread GC_new_thread(DWORD id) +{ + int hv = THREAD_TABLE_INDEX(id); + GC_thread result; + +# ifdef DEBUG_THREADS + GC_log_printf("Creating thread 0x%lx\n", (long)id); + if (GC_threads[hv] != NULL) + GC_log_printf("Hash collision at GC_threads[%d]\n", hv); +# endif + GC_ASSERT(I_HOLD_LOCK()); + if (!EXPECT(first_thread_used, TRUE)) { + result = &first_thread; + first_thread_used = TRUE; + GC_ASSERT(NULL == GC_threads[hv]); + } else { + GC_ASSERT(!GC_win32_dll_threads); + result = (struct GC_Thread_Rep *) + GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL); + if (result == 0) return(0); + } + /* result -> id = id; Done by caller. */ + result -> tm.next = GC_threads[hv]; + GC_threads[hv] = result; +# ifdef GC_PTHREADS + GC_ASSERT(result -> flags == 0); +# endif + GC_ASSERT(result -> thread_blocked_sp == NULL); + if (EXPECT(result != &first_thread, TRUE)) + GC_dirty(result); + return(result); +} + +GC_INNER GC_bool GC_in_thread_creation = FALSE; + /* Protected by allocation lock. */ + +GC_INLINE void GC_record_stack_base(GC_vthread me, + const struct GC_stack_base *sb) +{ + me -> stack_base = (ptr_t)sb->mem_base; +# ifdef IA64 + me -> backing_store_end = (ptr_t)sb->reg_base; +# elif defined(I386) + me -> initial_stack_base = (ptr_t)sb->mem_base; +# endif + if (me -> stack_base == NULL) + ABORT("Bad stack base in GC_register_my_thread"); +} + +/* This may be called from DllMain, and hence operates under unusual */ +/* constraints. In particular, it must be lock-free if */ +/* GC_win32_dll_threads is set. Always called from the thread being */ +/* added. If GC_win32_dll_threads is not set, we already hold the */ +/* allocation lock except possibly during single-threaded startup code. */ +/* Does not initialize thread local free lists. */ +STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, + DWORD thread_id) +{ + GC_vthread me; + + /* The following should be a no-op according to the Win32 */ + /* documentation. There is empirical evidence that it */ + /* isn't. - HB */ +# if defined(MPROTECT_VDB) && !defined(CYGWIN32) + if (GC_auto_incremental +# ifdef GWW_VDB + && !GC_gww_dirty_init() +# endif + ) + GC_set_write_fault_handler(); +# endif + +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + /* It appears to be unsafe to acquire a lock here, since this */ + /* code is apparently not preemptible on some systems. */ + /* (This is based on complaints, not on Microsoft's official */ + /* documentation, which says this should perform "only simple */ + /* initialization tasks".) */ + /* Hence we make do with nonblocking synchronization. */ + /* It has been claimed that DllMain is really only executed with */ + /* a particular system lock held, and thus careful use of locking */ + /* around code that doesn't call back into the system libraries */ + /* might be OK. But this has not been tested across all Win32 */ + /* variants. */ + for (i = 0; + InterlockedExchange(&dll_thread_table[i].tm.long_in_use, 1) != 0; + i++) { + /* Compare-and-swap would make this cleaner, but that's not */ + /* supported before Windows 98 and NT 4.0. In Windows 2000, */ + /* InterlockedExchange is supposed to be replaced by */ + /* InterlockedExchangePointer, but that's not really what I */ + /* want here. */ + /* FIXME: We should eventually declare Windows 95 dead and use */ + /* AO_ primitives here. */ + if (i == MAX_THREADS - 1) + ABORT("Too many threads"); + } + /* Update GC_max_thread_index if necessary. The following is */ + /* safe, and unlike CompareExchange-based solutions seems to work */ + /* on all Windows95 and later platforms. */ + /* Unfortunately, GC_max_thread_index may be temporarily out of */ + /* bounds, so readers have to compensate. */ + while (i > GC_max_thread_index) { + InterlockedIncrement((IE_t)&GC_max_thread_index); + } + if (GC_max_thread_index >= MAX_THREADS) { + /* We overshot due to simultaneous increments. */ + /* Setting it to MAX_THREADS-1 is always safe. */ + GC_max_thread_index = MAX_THREADS - 1; + } + me = dll_thread_table + i; + } else +# endif + /* else */ /* Not using DllMain */ { + GC_ASSERT(I_HOLD_LOCK()); + GC_in_thread_creation = TRUE; /* OK to collect from unknown thread. */ + me = GC_new_thread(thread_id); + GC_in_thread_creation = FALSE; + if (me == 0) + ABORT("Failed to allocate memory for thread registering"); + } +# ifdef GC_PTHREADS + /* me can be NULL -> segfault */ + me -> pthread_id = pthread_self(); +# endif +# ifndef MSWINCE + /* GetCurrentThread() returns a pseudohandle (a const value). */ + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), + (HANDLE*)&(me -> handle), + 0 /* dwDesiredAccess */, FALSE /* bInheritHandle */, + DUPLICATE_SAME_ACCESS)) { + ABORT_ARG1("DuplicateHandle failed", + ": errcode= 0x%X", (unsigned)GetLastError()); + } +# endif +# if defined(WOW64_THREAD_CONTEXT_WORKAROUND) && defined(MSWINRT_FLAVOR) + /* Lookup TIB value via a call to NtCurrentTeb() on thread */ + /* registration rather than calling GetThreadSelectorEntry() which */ + /* is not available on UWP. */ + me -> tib = (PNT_TIB)NtCurrentTeb(); +# endif + me -> last_stack_min = ADDR_LIMIT; + GC_record_stack_base(me, sb); + /* Up until this point, GC_push_all_stacks considers this thread */ + /* invalid. */ + /* Up until this point, this entry is viewed as reserved but invalid */ + /* by GC_delete_thread. */ + me -> id = thread_id; +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + if (GC_please_stop) { + AO_store(&GC_attached_thread, TRUE); + AO_nop_full(); /* Later updates must become visible after this. */ + } + /* We'd like to wait here, but can't, since waiting in DllMain */ + /* provokes deadlocks. */ + /* Thus we force marking to be restarted instead. */ + } else +# endif + /* else */ { + GC_ASSERT(!GC_please_stop); + /* Otherwise both we and the thread stopping code would be */ + /* holding the allocation lock. */ + } + return (GC_thread)(me); +} + +/* + * GC_max_thread_index may temporarily be larger than MAX_THREADS. + * To avoid subscript errors, we check on access. + */ +GC_INLINE LONG GC_get_max_thread_index(void) +{ + LONG my_max = GC_max_thread_index; + if (my_max >= MAX_THREADS) return MAX_THREADS - 1; + return my_max; +} + +/* Return the GC_thread corresponding to a thread id. May be called */ +/* without a lock, but should be called in contexts in which the */ +/* requested thread cannot be asynchronously deleted, e.g. from the */ +/* thread itself. */ +/* This version assumes that either GC_win32_dll_threads is set, or */ +/* we hold the allocator lock. */ +/* Also used (for assertion checking only) from thread_local_alloc.c. */ +STATIC GC_thread GC_lookup_thread_inner(DWORD thread_id) +{ +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + LONG my_max = GC_get_max_thread_index(); + for (i = 0; i <= my_max && + (!AO_load_acquire(&dll_thread_table[i].tm.in_use) + || dll_thread_table[i].id != thread_id); + /* Must still be in_use, since nobody else can store our */ + /* thread_id. */ + i++) { + /* empty */ + } + return i <= my_max ? (GC_thread)(dll_thread_table + i) : NULL; + } else +# endif + /* else */ { + GC_thread p = GC_threads[THREAD_TABLE_INDEX(thread_id)]; + + GC_ASSERT(I_HOLD_LOCK()); + while (p != 0 && p -> id != thread_id) p = p -> tm.next; + return(p); + } +} + +#ifdef LINT2 +# define CHECK_LOOKUP_MY_THREAD(me) \ + if (!(me)) ABORT("GC_lookup_thread_inner(GetCurrentThreadId) failed") +#else +# define CHECK_LOOKUP_MY_THREAD(me) /* empty */ +#endif + +#ifndef GC_NO_FINALIZATION + /* Called by GC_finalize() (in case of an allocation failure observed). */ + /* GC_reset_finalizer_nested() is the same as in pthread_support.c. */ + GC_INNER void GC_reset_finalizer_nested(void) + { + GC_thread me = GC_lookup_thread_inner(GetCurrentThreadId()); + + CHECK_LOOKUP_MY_THREAD(me); + me->finalizer_nested = 0; + } + + /* GC_check_finalizer_nested() is the same as in pthread_support.c. */ + GC_INNER unsigned char *GC_check_finalizer_nested(void) + { + GC_thread me = GC_lookup_thread_inner(GetCurrentThreadId()); + unsigned nesting_level; + + CHECK_LOOKUP_MY_THREAD(me); + nesting_level = me->finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++me->finalizer_skipped < (1U << nesting_level)) return NULL; + me->finalizer_skipped = 0; + } + me->finalizer_nested = (unsigned char)(nesting_level + 1); + return &me->finalizer_nested; + } +#endif /* !GC_NO_FINALIZATION */ + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + /* This is called from thread-local GC_malloc(). */ + GC_bool GC_is_thread_tsd_valid(void *tsd) + { + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread_inner(GetCurrentThreadId()); + UNLOCK(); + return (word)tsd >= (word)(&me->tlfs) + && (word)tsd < (word)(&me->tlfs) + sizeof(me->tlfs); + } +#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */ + +GC_API int GC_CALL GC_thread_is_registered(void) +{ + DWORD thread_id = GetCurrentThreadId(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread_inner(thread_id); + UNLOCK(); + return me != NULL && !KNOWN_FINISHED(me); +} + +GC_API void GC_CALL GC_register_altstack(void *stack GC_ATTR_UNUSED, + GC_word stack_size GC_ATTR_UNUSED, + void *altstack GC_ATTR_UNUSED, + GC_word altstack_size GC_ATTR_UNUSED) +{ + /* TODO: Implement */ +} + +/* Make sure thread descriptor t is not protected by the VDB */ +/* implementation. */ +/* Used to prevent write faults when the world is (partially) stopped, */ +/* since it may have been stopped with a system lock held, and that */ +/* lock may be required for fault handling. */ +#if defined(MPROTECT_VDB) +# define UNPROTECT_THREAD(t) \ + if (!GC_win32_dll_threads && GC_auto_incremental \ + && 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) (void)0 +#endif + +#ifdef CYGWIN32 +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id +#elif defined(GC_WIN32_PTHREADS) || defined(GC_PTHREADS_PARAMARK) +# include /* to check for winpthreads */ +# if defined(__WINPTHREADS_VERSION_MAJOR) +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id +# else +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id.p +# endif +#endif + +/* If a thread has been joined, but we have not yet */ +/* been notified, then there may be more than one thread */ +/* in the table with the same Win32 thread 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. 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_no_free(GC_vthread t) +{ +# ifndef MSWINCE + CloseHandle(t->handle); +# endif +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + /* This is intended to be lock-free. */ + /* It is either called synchronously from the thread being */ + /* deleted, or by the joining thread. */ + /* In this branch asynchronous changes to (*t) are possible. */ + /* It's not allowed to call GC_printf (and the friends) here, */ + /* see GC_stop_world() for the information. */ + t -> stack_base = 0; + t -> id = 0; + t -> suspended = FALSE; +# ifdef RETRY_GET_THREAD_CONTEXT + t -> context_sp = NULL; +# endif + AO_store_release(&t->tm.in_use, FALSE); + } else +# endif + /* else */ { + DWORD id = ((GC_thread)t) -> id; + /* Cast away volatile qualifier, since we have lock. */ + int hv = THREAD_TABLE_INDEX(id); + GC_thread p = GC_threads[hv]; + GC_thread prev = NULL; + + GC_ASSERT(I_HOLD_LOCK()); + while (p != (GC_thread)t) { + prev = p; + p = p -> tm.next; + } + if (prev == 0) { + GC_threads[hv] = p -> tm.next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> tm.next = p -> tm.next; + GC_dirty(prev); + } + } +} + +/* Delete a thread from GC_threads. We assume it is there. */ +/* (The code intentionally traps if it wasn't.) Assumes we */ +/* hold the allocation lock unless GC_win32_dll_threads is set. */ +/* If GC_win32_dll_threads is set then it should be called from */ +/* the thread being deleted. It is also safe to delete the */ +/* main thread (unless GC_win32_dll_threads). */ +STATIC void GC_delete_thread(DWORD id) +{ + if (GC_win32_dll_threads) { + GC_vthread t = GC_lookup_thread_inner(id); + + if (0 == t) { + WARN("Removing nonexistent thread, id= %" WARN_PRIuPTR "\n", id); + } else { + GC_delete_gc_thread_no_free(t); + } + } else { + int hv = THREAD_TABLE_INDEX(id); + GC_thread p = GC_threads[hv]; + GC_thread prev = NULL; + + GC_ASSERT(I_HOLD_LOCK()); + while (p -> id != id) { + prev = p; + p = p -> tm.next; + } +# ifndef MSWINCE + CloseHandle(p->handle); +# endif + if (prev == 0) { + GC_threads[hv] = p -> tm.next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> tm.next = p -> tm.next; + GC_dirty(prev); + } + if (EXPECT(p != &first_thread, TRUE)) { + GC_INTERNAL_FREE(p); + } + } +} + +GC_API void GC_CALL GC_allow_register_threads(void) +{ +# ifdef GC_ASSERTIONS + DCL_LOCK_STATE; + + /* Check GC is initialized and the current thread is registered. */ + LOCK(); + GC_ASSERT(GC_lookup_thread_inner(GetCurrentThreadId()) != 0); + UNLOCK(); +# endif +# if !defined(GC_ALWAYS_MULTITHREADED) && !defined(PARALLEL_MARK) \ + && !defined(GC_NO_THREADS_DISCOVERY) + /* GC_init() does not call GC_init_parallel() in this case. */ + parallel_initialized = TRUE; +# endif + GC_start_mark_threads(); + set_need_to_lock(); +} + +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) +{ + GC_thread me; + DWORD thread_id = GetCurrentThreadId(); + DCL_LOCK_STATE; + + if (GC_need_to_lock == FALSE) + ABORT("Threads explicit registering is not previously enabled"); + + /* We lock here, since we want to wait for an ongoing GC. */ + LOCK(); + me = GC_lookup_thread_inner(thread_id); + if (me == 0) { + me = GC_register_my_thread_inner(sb, thread_id); +# ifdef GC_PTHREADS +# if defined(CPPCHECK) + GC_noop1(me->flags); +# endif + me -> flags |= DETACHED; + /* Treat as detached, since we do not need to worry about */ + /* pointer results. */ +# else + (void)me; +# endif + } else +# ifdef GC_PTHREADS + /* else */ if ((me -> flags & FINISHED) != 0) { + GC_record_stack_base(me, sb); + me -> flags &= ~FINISHED; /* but not DETACHED */ + } else +# endif + /* else */ { + UNLOCK(); + return GC_DUPLICATE; + } + +# ifdef THREAD_LOCAL_ALLOC + GC_init_thread_local(&me->tlfs); +# endif + UNLOCK(); + return GC_SUCCESS; +} + +#ifdef GC_DISABLE_INCREMENTAL +# define GC_wait_for_gc_completion(wait_for_all) (void)(wait_for_all) +#else +/* 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)); + } +} +#endif /* !GC_DISABLE_INCREMENTAL */ + +GC_API int GC_CALL GC_unregister_my_thread(void) +{ + DCL_LOCK_STATE; + +# ifdef DEBUG_THREADS + GC_log_printf("Unregistering thread 0x%lx\n", (long)GetCurrentThreadId()); +# endif + + if (GC_win32_dll_threads) { +# if defined(THREAD_LOCAL_ALLOC) + /* Can't happen: see GC_use_threads_discovery(). */ + GC_ASSERT(FALSE); +# else + /* FIXME: Should we just ignore this? */ + GC_delete_thread(GetCurrentThreadId()); +# endif + } else { +# if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS) + GC_thread me; +# endif + 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 + if ((me -> flags & DETACHED) == 0) { + me -> flags |= FINISHED; + } else +# endif + /* 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; +} + +/* Wrapper for functions that are likely to block for an appreciable */ +/* length of time. */ + +/* GC_do_blocking_inner() is nearly the same as in pthread_support.c */ +GC_INNER void GC_do_blocking_inner(ptr_t data, void * context GC_ATTR_UNUSED) +{ + struct blocking_data * d = (struct blocking_data *) data; + DWORD thread_id = GetCurrentThreadId(); + GC_thread me; +# ifdef IA64 + ptr_t stack_ptr = GC_save_regs_in_stack(); +# endif + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread_inner(thread_id); + CHECK_LOOKUP_MY_THREAD(me); + GC_ASSERT(me -> thread_blocked_sp == NULL); +# ifdef IA64 + me -> backing_store_ptr = stack_ptr; +# endif + me -> thread_blocked_sp = (ptr_t) &d; /* save approx. sp */ + /* Save context here if we want to support precise stack marking */ + UNLOCK(); + d -> client_data = (d -> fn)(d -> client_data); + LOCK(); /* This will block if the world is stopped. */ +# if defined(CPPCHECK) + GC_noop1((word)me->thread_blocked_sp); +# endif + me -> thread_blocked_sp = NULL; + UNLOCK(); +} + +/* GC_call_with_gc_active() has the opposite to GC_do_blocking() */ +/* functionality. It might be called from a user function invoked by */ +/* GC_do_blocking() to temporarily back allow calling any GC function */ +/* and/or manipulating pointers to the garbage collected heap. */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, + void * client_data) +{ + struct GC_traced_stack_sect_s stacksect; + DWORD thread_id = GetCurrentThreadId(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); /* This will block if the world is stopped. */ + me = GC_lookup_thread_inner(thread_id); + CHECK_LOOKUP_MY_THREAD(me); + /* Adjust our stack bottom pointer (this could happen unless */ + /* GC_get_stack_base() was used which returned GC_SUCCESS). */ + GC_ASSERT(me -> stack_base != NULL); + if ((word)me->stack_base < (word)(&stacksect)) { + me -> stack_base = (ptr_t)(&stacksect); +# if defined(I386) + me -> initial_stack_base = me -> stack_base; +# endif + } + + if (me -> thread_blocked_sp == NULL) { + /* We are not inside GC_do_blocking() - do nothing more. */ + UNLOCK(); + client_data = fn(client_data); + /* Prevent treating the above as a tail call. */ + GC_noop1(COVERT_DATAFLOW(&stacksect)); + return client_data; /* result */ + } + + /* Setup new "stack section". */ + stacksect.saved_stack_ptr = me -> thread_blocked_sp; +# ifdef IA64 + /* This is the same as in GC_call_with_stack_base(). */ + stacksect.backing_store_end = GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ + stacksect.saved_backing_store_ptr = me -> backing_store_ptr; +# endif + stacksect.prev = me -> traced_stack_sect; + me -> thread_blocked_sp = NULL; + me -> traced_stack_sect = &stacksect; + + UNLOCK(); + client_data = fn(client_data); + GC_ASSERT(me -> thread_blocked_sp == NULL); + GC_ASSERT(me -> traced_stack_sect == &stacksect); + + /* Restore original "stack section". */ + LOCK(); +# if defined(CPPCHECK) + GC_noop1((word)me->traced_stack_sect); +# endif + me -> traced_stack_sect = stacksect.prev; +# ifdef IA64 + me -> backing_store_ptr = stacksect.saved_backing_store_ptr; +# endif + me -> thread_blocked_sp = stacksect.saved_stack_ptr; + UNLOCK(); + + return client_data; /* result */ +} + +GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle, + const struct GC_stack_base *sb) +{ + GC_thread t = (GC_thread)gc_thread_handle; + + GC_ASSERT(sb -> mem_base != NULL); + if (!EXPECT(GC_is_initialized, TRUE)) { + GC_ASSERT(NULL == t); + GC_stackbottom = (char *)sb->mem_base; +# ifdef IA64 + GC_register_stackbottom = (ptr_t)sb->reg_base; +# endif + return; + } + + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == t) { /* current thread? */ + t = GC_lookup_thread_inner(GetCurrentThreadId()); + CHECK_LOOKUP_MY_THREAD(t); + } + GC_ASSERT(!KNOWN_FINISHED(t)); + GC_ASSERT(NULL == t -> thread_blocked_sp + && NULL == t -> traced_stack_sect); /* for now */ + t -> stack_base = (ptr_t)sb->mem_base; + t -> last_stack_min = ADDR_LIMIT; /* reset the known minimum */ +# ifdef IA64 + t -> backing_store_end = (ptr_t)sb->reg_base; +# endif +} + +GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb) +{ + DWORD thread_id = GetCurrentThreadId(); + GC_thread me; + DCL_LOCK_STATE; + + LOCK(); + me = GC_lookup_thread_inner(thread_id); + CHECK_LOOKUP_MY_THREAD(me); /* the thread is assumed to be registered */ + sb -> mem_base = me -> stack_base; +# ifdef IA64 + sb -> reg_base = me -> backing_store_end; +# endif + UNLOCK(); + return (void *)me; /* gc_thread_handle */ +} + +#ifdef GC_PTHREADS + + /* A quick-and-dirty cache of the mapping between pthread_t */ + /* and Win32 thread id. */ +# define PTHREAD_MAP_SIZE 512 + DWORD GC_pthread_map_cache[PTHREAD_MAP_SIZE] = {0}; +# define PTHREAD_MAP_INDEX(pthread_id) \ + ((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) \ + (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)] + + /* Return a GC_thread corresponding to a given pthread_t. */ + /* Returns 0 if it's not there. */ + /* We assume that this is only called for pthread ids that */ + /* have not yet terminated or are still joinable, and */ + /* cannot be concurrently terminated. */ + /* Assumes we do NOT hold the allocation lock. */ + STATIC GC_thread GC_lookup_pthread(pthread_t id) + { +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + LONG my_max = GC_get_max_thread_index(); + + for (i = 0; i <= my_max && + (!AO_load_acquire(&dll_thread_table[i].tm.in_use) + || !THREAD_EQUAL(dll_thread_table[i].pthread_id, id)); + /* Must still be in_use, since nobody else can */ + /* store our thread_id. */ + i++) { + /* empty */ + } + return i <= my_max ? (GC_thread)(dll_thread_table + i) : NULL; + } else +# endif + /* else */ { + /* We first try the cache. If that fails, we use a very slow */ + /* approach. */ + DWORD win32_id = GET_PTHREAD_MAP_CACHE(id); + int hv_guess = THREAD_TABLE_INDEX(win32_id); + int hv; + GC_thread p; + DCL_LOCK_STATE; + + LOCK(); + for (p = GC_threads[hv_guess]; 0 != p; p = p -> tm.next) { + if (THREAD_EQUAL(p -> pthread_id, id)) + goto foundit; + } + for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { + for (p = GC_threads[hv]; 0 != p; p = p -> tm.next) { + if (THREAD_EQUAL(p -> pthread_id, id)) + goto foundit; + } + } + p = 0; + foundit: + UNLOCK(); + return p; + } + } + +#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 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) { + GC_thread p, next; + + for (p = GC_threads[hv]; 0 != p; p = next) { + next = p -> tm.next; + if (THREAD_EQUAL(p -> pthread_id, pthread_id) + && me == NULL) { /* ignore dead threads with the same id */ + me = p; + p -> tm.next = 0; + } else { +# ifdef THREAD_LOCAL_ALLOC + if ((p -> flags & FINISHED) == 0) { + /* Cannot call GC_destroy_thread_local here (see the */ + /* corresponding comment in pthread_support.c). */ + GC_remove_specific_after_fork(GC_thread_key, p -> pthread_id); + } +# 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_inner 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 (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + 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 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 */ { + GC_PUSH_ALL_SYM(GC_threads); + } +# if defined(THREAD_LOCAL_ALLOC) && defined(USE_CUSTOM_SPECIFIC) + GC_PUSH_ALL_SYM(GC_thread_key); + /* Just in case we ever use our own TLS implementation. */ +# endif +} + +#ifdef WOW64_THREAD_CONTEXT_WORKAROUND +# ifndef CONTEXT_EXCEPTION_ACTIVE +# define CONTEXT_EXCEPTION_ACTIVE 0x08000000 +# define CONTEXT_EXCEPTION_REQUEST 0x40000000 +# define CONTEXT_EXCEPTION_REPORTING 0x80000000 +# endif + static GC_bool isWow64; /* Is running 32-bit code on Win64? */ +# define GET_THREAD_CONTEXT_FLAGS (isWow64 \ + ? CONTEXT_INTEGER | CONTEXT_CONTROL \ + | CONTEXT_EXCEPTION_REQUEST | CONTEXT_SEGMENTS \ + : CONTEXT_INTEGER | CONTEXT_CONTROL) +#else +# define GET_THREAD_CONTEXT_FLAGS (CONTEXT_INTEGER | CONTEXT_CONTROL) +#endif /* !WOW64_THREAD_CONTEXT_WORKAROUND */ + +/* Suspend the given thread, if it's still active. */ +STATIC void GC_suspend(GC_thread t) +{ +# ifndef MSWINCE + DWORD exitCode; +# ifdef RETRY_GET_THREAD_CONTEXT + int retry_cnt; +# define MAX_SUSPEND_THREAD_RETRIES (1000 * 1000) +# endif +# endif + +# ifdef DEBUG_THREADS + GC_log_printf("Suspending 0x%x\n", (int)t->id); +# endif + UNPROTECT_THREAD(t); + GC_acquire_dirty_lock(); + +# ifdef MSWINCE + /* SuspendThread() will fail if thread is running kernel code. */ + while (SuspendThread(THREAD_HANDLE(t)) == (DWORD)-1) { + GC_release_dirty_lock(); + Sleep(10); /* in millis */ + GC_acquire_dirty_lock(); + } +# elif defined(RETRY_GET_THREAD_CONTEXT) + for (retry_cnt = 0;;) { + /* Apparently the Windows 95 GetOpenFileName call creates */ + /* a thread that does not properly get cleaned up, and */ + /* SuspendThread on its descriptor may provoke a crash. */ + /* This reduces the probability of that event, though it still */ + /* appears there is a race here. */ + if (GetExitCodeThread(t -> handle, &exitCode) + && exitCode != STILL_ACTIVE) { + GC_release_dirty_lock(); +# ifdef GC_PTHREADS + t -> stack_base = 0; /* prevent stack from being pushed */ +# else + /* This breaks pthread_join on Cygwin, which is guaranteed to */ + /* only see user threads. */ + GC_ASSERT(GC_win32_dll_threads); + GC_delete_gc_thread_no_free(t); +# endif + return; + } + + if (SuspendThread(t->handle) != (DWORD)-1) { + CONTEXT context; + + context.ContextFlags = GET_THREAD_CONTEXT_FLAGS; + if (GetThreadContext(t->handle, &context)) { + /* TODO: WoW64 extra workaround: if CONTEXT_EXCEPTION_ACTIVE */ + /* then Sleep(1) and retry. */ + t->context_sp = copy_ptr_regs(t->context_regs, &context); + break; /* success; the context pointer registers are saved */ + } + + /* Resume the thread, try to suspend it in a better location. */ + if (ResumeThread(t->handle) == (DWORD)-1) + ABORT("ResumeThread failed in suspend loop"); + } + if (retry_cnt > 1) { + GC_release_dirty_lock(); + Sleep(0); /* yield */ + GC_acquire_dirty_lock(); + } + if (++retry_cnt >= MAX_SUSPEND_THREAD_RETRIES) + ABORT("SuspendThread loop failed"); /* something must be wrong */ + } +# else + if (GetExitCodeThread(t -> handle, &exitCode) + && exitCode != STILL_ACTIVE) { + GC_release_dirty_lock(); +# ifdef GC_PTHREADS + t -> stack_base = 0; /* prevent stack from being pushed */ +# else + GC_ASSERT(GC_win32_dll_threads); + GC_delete_gc_thread_no_free(t); +# endif + return; + } + if (SuspendThread(t -> handle) == (DWORD)-1) + ABORT("SuspendThread failed"); +# endif + t -> suspended = (unsigned char)TRUE; + GC_release_dirty_lock(); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, THREAD_HANDLE(t)); +} + +#if defined(GC_ASSERTIONS) \ + && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)) + GC_INNER GC_bool GC_write_disabled = FALSE; + /* TRUE only if GC_stop_world() acquired GC_write_cs. */ +#endif + +GC_INNER void GC_stop_world(void) +{ + DWORD thread_id = GetCurrentThreadId(); + + if (!GC_thr_initialized) + ABORT("GC_stop_world() called before GC_thr_init()"); + GC_ASSERT(I_HOLD_LOCK()); + + /* This code is the same as in pthread_stop_world.c */ +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + +# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS) + GC_please_stop = TRUE; +# endif +# if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE) + 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_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 +# endif +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + int my_max; + /* 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. */ + 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; + if (t -> stack_base != 0 && t -> thread_blocked_sp == NULL + && t -> id != thread_id) { + GC_suspend((GC_thread)t); + } + } + } else +# endif + /* else */ { + GC_thread t; + int i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (t = GC_threads[i]; t != 0; t = t -> tm.next) { + if (t -> stack_base != 0 && t -> thread_blocked_sp == NULL + && !KNOWN_FINISHED(t) && t -> id != thread_id) { + GC_suspend(t); + } + } + } + } +# if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE) +# ifdef GC_ASSERTIONS + GC_write_disabled = FALSE; +# endif + LeaveCriticalSection(&GC_write_cs); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif +} + +GC_INNER void GC_start_world(void) +{ +# ifdef GC_ASSERTIONS + DWORD thread_id = GetCurrentThreadId(); +# endif + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_win32_dll_threads) { + LONG my_max = GC_get_max_thread_index(); + int i; + + for (i = 0; i <= my_max; i++) { + GC_thread t = (GC_thread)(dll_thread_table + i); + if (t -> suspended) { +# ifdef DEBUG_THREADS + GC_log_printf("Resuming 0x%x\n", (int)t->id); +# endif + GC_ASSERT(t -> stack_base != 0 && t -> id != thread_id); + if (ResumeThread(THREAD_HANDLE(t)) == (DWORD)-1) + ABORT("ResumeThread failed"); + t -> suspended = FALSE; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, THREAD_HANDLE(t)); + } + /* Else thread is unregistered or not suspended. */ + } + } else { + GC_thread t; + int i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (t = GC_threads[i]; t != 0; t = t -> tm.next) { + if (t -> suspended) { +# ifdef DEBUG_THREADS + GC_log_printf("Resuming 0x%x\n", (int)t->id); +# endif + GC_ASSERT(t -> stack_base != 0 && t -> id != thread_id); + if (ResumeThread(THREAD_HANDLE(t)) == (DWORD)-1) + ABORT("ResumeThread failed"); + UNPROTECT_THREAD(t); + t -> suspended = FALSE; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, THREAD_HANDLE(t)); + } else { +# ifdef DEBUG_THREADS + GC_log_printf("Not resuming thread 0x%x as it is not suspended\n", + (int)t->id); +# endif + } + } + } + } +# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS) + GC_please_stop = FALSE; +# endif +} + +#ifdef MSWINCE + /* The VirtualQuery calls below won't work properly on some old WinCE */ + /* versions, but since each stack is restricted to an aligned 64 KiB */ + /* region of virtual memory we can just take the next lowest multiple */ + /* of 64 KiB. The result of this macro must not be used as its */ + /* argument later and must not be used as the lower bound for sp */ + /* check (since the stack may be bigger than 64 KiB). */ +# define GC_wince_evaluate_stack_min(s) \ + (ptr_t)(((word)(s) - 1) & ~(word)0xFFFF) +#elif defined(GC_ASSERTIONS) +# define GC_dont_query_stack_min FALSE +#endif + +/* A cache holding the results of the recent VirtualQuery call. */ +/* Protected by the allocation lock. */ +static ptr_t last_address = 0; +static MEMORY_BASIC_INFORMATION last_info; + +/* Probe stack memory region (starting at "s") to find out its */ +/* lowest address (i.e. stack top). */ +/* S must be a mapped address inside the region, NOT the first */ +/* unmapped address. */ +STATIC ptr_t GC_get_stack_min(ptr_t s) +{ + ptr_t bottom; + + GC_ASSERT(I_HOLD_LOCK()); + if (s != last_address) { + VirtualQuery(s, &last_info, sizeof(last_info)); + last_address = s; + } + do { + bottom = (ptr_t)last_info.BaseAddress; + VirtualQuery(bottom - 1, &last_info, sizeof(last_info)); + last_address = bottom - 1; + } while ((last_info.Protect & PAGE_READWRITE) + && !(last_info.Protect & PAGE_GUARD)); + return(bottom); +} + +/* Return true if the page at s has protections appropriate */ +/* for a stack page. */ +static GC_bool may_be_in_stack(ptr_t s) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (s != last_address) { + VirtualQuery(s, &last_info, sizeof(last_info)); + last_address = s; + } + return (last_info.Protect & PAGE_READWRITE) + && !(last_info.Protect & PAGE_GUARD); +} + +/* Copy all registers that might point into the heap. Frame */ +/* pointer registers are included in case client code was */ +/* compiled with the 'omit frame pointer' optimization. */ +/* The context register values are stored to regs argument */ +/* which is expected to be of PUSHED_REGS_COUNT length exactly. */ +/* The functions returns the context stack pointer value. */ +static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext) { + ptr_t sp; + int cnt = 0; +# define context (*pcontext) +# define PUSH1(reg) (regs[cnt++] = (word)pcontext->reg) +# define PUSH2(r1,r2) (PUSH1(r1), PUSH1(r2)) +# define PUSH4(r1,r2,r3,r4) (PUSH2(r1,r2), PUSH2(r3,r4)) +# if defined(I386) +# ifdef WOW64_THREAD_CONTEXT_WORKAROUND + PUSH2(ContextFlags, SegFs); /* cannot contain pointers */ +# endif + PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp); + sp = (ptr_t)context.Esp; +# elif defined(X86_64) + PUSH4(Rax,Rcx,Rdx,Rbx); PUSH2(Rbp, Rsi); PUSH1(Rdi); + PUSH4(R8, R9, R10, R11); PUSH4(R12, R13, R14, R15); + sp = (ptr_t)context.Rsp; +# elif defined(ARM32) + PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11); + PUSH1(R12); + sp = (ptr_t)context.Sp; +# elif defined(AARCH64) + PUSH4(X0,X1,X2,X3),PUSH4(X4,X5,X6,X7),PUSH4(X8,X9,X10,X11); + PUSH4(X12,X13,X14,X15),PUSH4(X16,X17,X18,X19),PUSH4(X20,X21,X22,X23); + PUSH4(X24,X25,X26,X27),PUSH1(X28); + PUSH1(Lr); + sp = (ptr_t)context.Sp; +# elif defined(SHx) + PUSH4(R0,R1,R2,R3), PUSH4(R4,R5,R6,R7), PUSH4(R8,R9,R10,R11); + PUSH2(R12,R13), PUSH1(R14); + sp = (ptr_t)context.R15; +# elif defined(MIPS) + PUSH4(IntAt,IntV0,IntV1,IntA0), PUSH4(IntA1,IntA2,IntA3,IntT0); + PUSH4(IntT1,IntT2,IntT3,IntT4), PUSH4(IntT5,IntT6,IntT7,IntS0); + PUSH4(IntS1,IntS2,IntS3,IntS4), PUSH4(IntS5,IntS6,IntS7,IntT8); + PUSH4(IntT9,IntK0,IntK1,IntS8); + sp = (ptr_t)context.IntSp; +# elif defined(PPC) + PUSH4(Gpr0, Gpr3, Gpr4, Gpr5), PUSH4(Gpr6, Gpr7, Gpr8, Gpr9); + PUSH4(Gpr10,Gpr11,Gpr12,Gpr14), PUSH4(Gpr15,Gpr16,Gpr17,Gpr18); + PUSH4(Gpr19,Gpr20,Gpr21,Gpr22), PUSH4(Gpr23,Gpr24,Gpr25,Gpr26); + PUSH4(Gpr27,Gpr28,Gpr29,Gpr30), PUSH1(Gpr31); + sp = (ptr_t)context.Gpr1; +# elif defined(ALPHA) + PUSH4(IntV0,IntT0,IntT1,IntT2), PUSH4(IntT3,IntT4,IntT5,IntT6); + PUSH4(IntT7,IntS0,IntS1,IntS2), PUSH4(IntS3,IntS4,IntS5,IntFp); + PUSH4(IntA0,IntA1,IntA2,IntA3), PUSH4(IntA4,IntA5,IntT8,IntT9); + PUSH4(IntT10,IntT11,IntT12,IntAt); + sp = (ptr_t)context.IntSp; +# elif defined(CPPCHECK) + sp = (ptr_t)(word)cnt; /* to workaround "cnt not used" false positive */ +# else +# error Architecture is not supported +# endif +# undef context + GC_ASSERT(cnt == PUSHED_REGS_COUNT); + return sp; +} + +STATIC word GC_push_stack_for(GC_thread thread, DWORD me) +{ + 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 = GC_approx_sp(); + } else if ((sp = thread -> thread_blocked_sp) == NULL) { + /* Use saved sp value for blocked threads. */ +# ifdef RETRY_GET_THREAD_CONTEXT + /* We cache context when suspending the thread since it may */ + /* require looping. */ + word *regs = thread->context_regs; + + if (thread->suspended) { + sp = thread->context_sp; + } else +# else + word regs[PUSHED_REGS_COUNT]; +# endif + + /* else */ { + CONTEXT context; + + /* For unblocked threads call GetThreadContext(). */ + context.ContextFlags = GET_THREAD_CONTEXT_FLAGS; + if (GetThreadContext(THREAD_HANDLE(thread), &context)) { + sp = copy_ptr_regs(regs, &context); + } else { +# ifdef RETRY_GET_THREAD_CONTEXT + /* At least, try to use the stale context if saved. */ + sp = thread->context_sp; + if (NULL == sp) { + /* Skip the current thread, anyway its stack will */ + /* be pushed when the world is stopped. */ + return 0; + } +# else + ABORT("GetThreadContext failed"); +# endif + } + } +# ifdef THREAD_LOCAL_ALLOC + GC_ASSERT(thread->suspended || !GC_world_stopped); +# endif + +# ifndef WOW64_THREAD_CONTEXT_WORKAROUND + GC_push_many_regs(regs, PUSHED_REGS_COUNT); +# else + GC_push_many_regs(regs + 2, PUSHED_REGS_COUNT - 2); + /* skip ContextFlags and SegFs */ + + /* WoW64 workaround. */ + if (isWow64) { + DWORD ContextFlags = (DWORD)regs[0]; + + if ((ContextFlags & CONTEXT_EXCEPTION_REPORTING) != 0 + && (ContextFlags & (CONTEXT_EXCEPTION_ACTIVE + /* | CONTEXT_SERVICE_ACTIVE */)) != 0) { + PNT_TIB tib; + +# ifdef MSWINRT_FLAVOR + tib = thread -> tib; +# else + WORD SegFs = (WORD)regs[1]; + LDT_ENTRY selector; + + if (!GetThreadSelectorEntry(THREAD_HANDLE(thread), SegFs, + &selector)) + ABORT("GetThreadSelectorEntry failed"); + tib = (PNT_TIB)(selector.BaseLow + | (selector.HighWord.Bits.BaseMid << 16) + | (selector.HighWord.Bits.BaseHi << 24)); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("TIB stack limit/base: %p .. %p\n", + (void *)tib->StackLimit, (void *)tib->StackBase); +# endif + GC_ASSERT(!((word)thread->stack_base + COOLER_THAN (word)tib->StackBase)); + if (thread->stack_base != thread->initial_stack_base + /* We are in a coroutine. */ + && ((word)thread->stack_base <= (word)tib->StackLimit + || (word)tib->StackBase < (word)thread->stack_base)) { + /* The coroutine stack is not within TIB stack. */ + WARN("GetThreadContext might return stale register values" + " including ESP= %p\n", sp); + /* TODO: Because of WoW64 bug, there is no guarantee that */ + /* sp really points to the stack top but, for now, we do */ + /* our best as the TIB stack limit/base cannot be used */ + /* while we are inside a coroutine. */ + } else { + /* GetThreadContext() might return stale register values, */ + /* so we scan the entire stack region (down to the stack */ + /* limit). There is no 100% guarantee that all the */ + /* registers are pushed but we do our best (the proper */ + /* solution would be to fix it inside Windows). */ + sp = (ptr_t)tib->StackLimit; + } + } /* else */ +# ifdef DEBUG_THREADS + else { + static GC_bool logged; + if (!logged + && (ContextFlags & CONTEXT_EXCEPTION_REPORTING) == 0) { + GC_log_printf("CONTEXT_EXCEPTION_REQUEST not supported\n"); + logged = TRUE; + } + } +# endif + } +# endif /* WOW64_THREAD_CONTEXT_WORKAROUND */ + } /* ! current thread */ +# ifdef STACKPTR_CORRECTOR_AVAILABLE + if (GC_sp_corrector != 0) + GC_sp_corrector((void **)&sp, (void *)(thread -> pthread_id)); +# endif + + /* Set stack_min to the lowest address in the thread stack, */ + /* or to an address in the thread stack no larger than sp, */ + /* taking advantage of the old value to avoid slow traversals */ + /* of large stacks. */ + if (thread -> last_stack_min == ADDR_LIMIT) { +# ifdef MSWINCE + if (GC_dont_query_stack_min) { + stack_min = GC_wince_evaluate_stack_min(traced_stack_sect != NULL ? + (ptr_t)traced_stack_sect : thread -> stack_base); + /* Keep last_stack_min value unmodified. */ + } else +# endif + /* else */ { + stack_min = GC_get_stack_min(traced_stack_sect != NULL ? + (ptr_t)traced_stack_sect : thread -> stack_base); + UNPROTECT_THREAD(thread); + thread -> last_stack_min = stack_min; + } + } else { + /* First, adjust the latest known minimum stack address if we */ + /* are inside GC_call_with_gc_active(). */ + if (traced_stack_sect != NULL && + (word)thread->last_stack_min > (word)traced_stack_sect) { + UNPROTECT_THREAD(thread); + thread -> last_stack_min = (ptr_t)traced_stack_sect; + } + + 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 && + (word)sp < (word)thread->last_stack_min ? + sp : thread -> last_stack_min)) { + stack_min = (ptr_t)last_info.BaseAddress; + /* Do not probe rest of the stack if sp is correct. */ + 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? */ + stack_min = GC_get_stack_min(thread -> stack_base); + } + UNPROTECT_THREAD(thread); + thread -> last_stack_min = stack_min; + } + } + + GC_ASSERT(GC_dont_query_stack_min + || 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 ((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, (void *)sp, (void *)thread->stack_base, + (int)me); +# endif + GC_push_all_stack_sections(sp, thread->stack_base, traced_stack_sect); + } else { + /* 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 || (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 + GC_log_printf("Pushing stack for 0x%x from (min) %p to %p from 0x%x\n", + (int)thread->id, (void *)stack_min, + (void *)thread->stack_base, (int)me); +# endif + /* Push everything - ignore "traced stack section" data. */ + GC_push_all_stack(stack_min, thread->stack_base); + } + return thread->stack_base - sp; /* stack grows down */ +} + +/* We hold allocation lock. Should do exactly the right thing if the */ +/* world is stopped. Should not fail if it isn't. */ +GC_INNER void GC_push_all_stacks(void) +{ + DWORD thread_id = GetCurrentThreadId(); + GC_bool found_me = FALSE; +# ifndef SMALL_CONFIG + unsigned nthreads = 0; +# endif + word total_size = 0; +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + LONG my_max = GC_get_max_thread_index(); + + for (i = 0; i <= my_max; i++) { + GC_thread t = (GC_thread)(dll_thread_table + i); + if (t -> tm.in_use && t -> stack_base) { +# ifndef SMALL_CONFIG + ++nthreads; +# endif + total_size += GC_push_stack_for(t, thread_id); + if (t -> id == thread_id) found_me = TRUE; + } + } + } else +# endif + /* else */ { + int i; + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread t; + for (t = GC_threads[i]; t != 0; t = t -> tm.next) { + if (!KNOWN_FINISHED(t) && t -> stack_base) { +# ifndef SMALL_CONFIG + ++nthreads; +# endif + total_size += GC_push_stack_for(t, thread_id); + if (t -> id == thread_id) found_me = TRUE; + } + } + } + } +# ifndef SMALL_CONFIG + 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"); + GC_total_stacksize = total_size; +} + +#ifdef PARALLEL_MARK + +# ifndef MAX_MARKERS +# define MAX_MARKERS 16 +# endif + + static ptr_t marker_sp[MAX_MARKERS - 1]; /* The cold end of the stack */ + /* for markers. */ +# ifdef IA64 + static ptr_t marker_bsp[MAX_MARKERS - 1]; +# endif + + static ptr_t marker_last_stack_min[MAX_MARKERS - 1]; + /* Last known minimum (hottest) address */ + /* in stack (or ADDR_LIMIT if unset) */ + /* for markers. */ + +#endif /* PARALLEL_MARK */ + +/* Find stack with the lowest address which overlaps the */ +/* interval [start, limit). */ +/* Return stack bounds in *lo and *hi. If no such stack */ +/* is found, both *hi and *lo will be set to an address */ +/* higher than limit. */ +GC_INNER void GC_get_next_stack(char *start, char *limit, + char **lo, char **hi) +{ + int i; + char * current_min = ADDR_LIMIT; /* Least in-range stack base */ + ptr_t *plast_stack_min = NULL; /* Address of last_stack_min */ + /* field for thread corresponding */ + /* to current_min. */ + GC_thread thread = NULL; /* Either NULL or points to the */ + /* thread's hash table entry */ + /* containing *plast_stack_min. */ + + /* First set current_min, ignoring limit. */ + if (GC_win32_dll_threads) { + LONG my_max = GC_get_max_thread_index(); + + for (i = 0; i <= my_max; i++) { + ptr_t s = (ptr_t)(dll_thread_table[i].stack_base); + + 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; + current_min = s; +# if defined(CPPCHECK) + /* To avoid a warning that thread is always null. */ + thread = (GC_thread)&dll_thread_table[i]; +# endif + } + } + } else { + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread t; + + for (t = GC_threads[i]; t != 0; t = t -> tm.next) { + ptr_t s = t -> stack_base; + + 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. */ + current_min = s; + } + } + } +# ifdef PARALLEL_MARK + for (i = 0; i < GC_markers_m1; ++i) { + ptr_t s = marker_sp[i]; +# ifdef IA64 + /* FIXME: not implemented */ +# endif + 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; + thread = NULL; /* Not a thread's hash table entry. */ + } + } +# endif + } + + *hi = current_min; + if (current_min == ADDR_LIMIT) { + *lo = ADDR_LIMIT; + return; + } + + 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); + /* Keep last_stack_min value unmodified. */ + return; + } +# endif + + 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). */ + *lo = ADDR_LIMIT; + return; + } + + /* Get the minimum address of the found stack by probing its memory */ + /* region starting from the recent known minimum (if set). */ + if (*plast_stack_min == ADDR_LIMIT + || !may_be_in_stack(*plast_stack_min)) { + /* Unsafe to start from last_stack_min value. */ + *lo = GC_get_stack_min(current_min); + } else { + /* Use the recent value to optimize search for min address. */ + *lo = GC_get_stack_min(*plast_stack_min); + } + + /* Remember current stack_min value. */ + if (thread != NULL) { + UNPROTECT_THREAD(thread); + } + *plast_stack_min = *lo; +} + +#ifdef PARALLEL_MARK + +# if defined(GC_PTHREADS) && !defined(GC_PTHREADS_PARAMARK) + /* Use pthread-based parallel mark implementation. */ + + /* Workaround a deadlock in winpthreads-3.0b internals (observed */ + /* with MinGW 32/64). */ +# if !defined(__MINGW32__) +# define GC_PTHREADS_PARAMARK +# endif +# endif + +# if !defined(GC_PTHREADS_PARAMARK) + 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 + +# if defined(GC_PTHREADS) && defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) + static void set_marker_thread_name(unsigned id) + { + /* This code is the same as in pthread_support.c. */ + char name_buf[16]; /* pthread_setname_np may fail for longer names */ + int len = sizeof("GC-marker-") - 1; + + /* Compose the name manually as snprintf may be unavailable or */ + /* "%u directive output may be truncated" warning may occur. */ + BCOPY("GC-marker-", name_buf, len); + if (id >= 10) + name_buf[len++] = (char)('0' + (id / 10) % 10); + name_buf[len] = (char)('0' + id % 10); + name_buf[len + 1] = '\0'; + + if (pthread_setname_np(pthread_self(), name_buf) != 0) + WARN("pthread_setname_np failed\n", 0); + } + +# elif !defined(MSWINCE) + /* A pointer to SetThreadDescription() which is available since */ + /* Windows 10. The function prototype is in processthreadsapi.h. */ + static FARPROC setThreadDescription_fn; + + static void set_marker_thread_name(unsigned id) + { + WCHAR name_buf[16]; + int len = sizeof(L"GC-marker-") / sizeof(WCHAR) - 1; + HRESULT hr; + + if (!setThreadDescription_fn) return; /* missing SetThreadDescription */ + + /* Compose the name manually as swprintf may be unavailable. */ + BCOPY(L"GC-marker-", name_buf, len * sizeof(WCHAR)); + if (id >= 10) + name_buf[len++] = (WCHAR)('0' + (id / 10) % 10); + name_buf[len] = (WCHAR)('0' + id % 10); + name_buf[len + 1] = 0; + + /* Invoke SetThreadDescription(). Cast the function pointer to word */ + /* first to avoid "incompatible function types" compiler warning. */ + hr = (*(HRESULT (WINAPI *)(HANDLE, const WCHAR *)) + (word)setThreadDescription_fn)(GetCurrentThread(), name_buf); + if (FAILED(hr)) + WARN("SetThreadDescription failed\n", 0); + } +# else +# define set_marker_thread_name(id) (void)(id) +# endif + + /* GC_mark_thread() is the same as in pthread_support.c */ +# ifdef GC_PTHREADS_PARAMARK + STATIC void * GC_mark_thread(void * id) +# elif defined(MSWINCE) + STATIC DWORD WINAPI GC_mark_thread(LPVOID id) +# else + STATIC unsigned __stdcall GC_mark_thread(void * id) +# endif + { + word my_mark_no = 0; + + if ((word)id == GC_WORD_MAX) return 0; /* to prevent a compiler warning */ + set_marker_thread_name((unsigned)(word)id); + marker_sp[(word)id] = GC_approx_sp(); +# ifdef IA64 + marker_bsp[(word)id] = GC_save_regs_in_stack(); +# endif +# if !defined(GC_PTHREADS_PARAMARK) + GC_marker_Id[(word)id] = GetCurrentThreadId(); +# endif + + /* Inform GC_start_mark_threads about completion of marker data init. */ + GC_acquire_mark_lock(); + if (0 == --GC_fl_builder_count) /* count may have a negative value */ + GC_notify_all_builder(); + + for (;; ++my_mark_no) { + if (my_mark_no - GC_mark_no > (word)2) { + /* resynchronize if we get far off, e.g. because GC_mark_no */ + /* wrapped. */ + my_mark_no = GC_mark_no; + } +# ifdef DEBUG_THREADS + GC_log_printf("Starting mark helper for mark number %lu\n", + (unsigned long)my_mark_no); +# endif + GC_help_marker(my_mark_no); + } + } + +# 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 */ + + static int available_markers_m1 = 0; + +# ifdef GC_PTHREADS_PARAMARK +# include + +# if defined(GC_ASSERTIONS) && !defined(NUMERIC_THREAD_ID) +# define NUMERIC_THREAD_ID(id) (unsigned long)(word)GC_PTHREAD_PTRVAL(id) + /* Id not guaranteed to be unique. */ +# endif + +# ifdef CAN_HANDLE_FORK + static pthread_cond_t mark_cv; + /* initialized by GC_start_mark_threads_inner */ +# else + static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER; +# endif + + /* GC_start_mark_threads is the same as in pthread_support.c except */ + /* for thread stack that is assumed to be large enough. */ + + GC_INNER void GC_start_mark_threads_inner(void) + { + int i; + pthread_attr_t attr; + pthread_t new_thread; +# ifndef NO_MARKER_SPECIAL_SIGMASK + sigset_t set, oldset; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + ASSERT_CANCEL_DISABLED(); + if (available_markers_m1 <= 0 || GC_parallel) return; + /* Skip if parallel markers disabled or already started. */ + GC_wait_for_gc_completion(TRUE); + +# ifdef CAN_HANDLE_FORK + /* Reset mark_cv state after forking (as in pthread_support.c). */ + { + pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER; + BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv)); + } +# endif + + GC_ASSERT(GC_fl_builder_count == 0); + 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"); + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Apply special signal mask to GC marker threads, and don't drop */ + /* user defined signals by GC marker threads. */ + if (sigfillset(&set) != 0) + ABORT("sigfillset failed"); + if (pthread_sigmask(SIG_BLOCK, &set, &oldset) < 0) { + WARN("pthread_sigmask set failed, no markers started\n", 0); + GC_markers_m1 = 0; + (void)pthread_attr_destroy(&attr); + return; + } +# endif /* !NO_MARKER_SPECIAL_SIGMASK */ + + /* To have proper GC_parallel value in GC_help_marker. */ + GC_markers_m1 = available_markers_m1; + + 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_m1 = i; + break; + } + } + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Restore previous signal mask. */ + if (pthread_sigmask(SIG_SETMASK, &oldset, NULL) < 0) { + WARN("pthread_sigmask restore failed\n", 0); + } +# endif + + (void)pthread_attr_destroy(&attr); + GC_wait_for_markers_init(); + 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; + + /* GC_acquire/release_mark_lock(), GC_wait_builder/marker(), */ + /* GC_wait_for_reclaim(), GC_notify_all_builder/marker() are the same */ + /* as in pthread_support.c except that GC_generic_lock() is not used. */ + +# ifdef LOCK_STATS + volatile AO_t GC_block_count = 0; +# endif + + GC_INNER void GC_acquire_mark_lock(void) + { +# ifdef NUMERIC_THREAD_ID_UNIQUE + GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self())); +# endif + if (pthread_mutex_lock(&mark_mutex) != 0) { + ABORT("pthread_mutex_lock failed"); + } +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_block_count); +# endif + /* GC_generic_lock(&mark_mutex); */ + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_release_mark_lock(void) + { + UNSET_MARK_LOCK_HOLDER; + if (pthread_mutex_unlock(&mark_mutex) != 0) { + ABORT("pthread_mutex_unlock failed"); + } + } + + /* Collector must wait for a freelist builders for 2 reasons: */ + /* 1) Mark bits may still be getting examined without lock. */ + /* 2) Partial free lists referenced only by locals may not be */ + /* scanned correctly, e.g. if they contain "pointer-free" objects, */ + /* since the free-list link may be ignored. */ + STATIC void GC_wait_builder(void) + { + 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); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_wait_for_reclaim(void) + { + GC_acquire_mark_lock(); + while (GC_fl_builder_count > 0) { + GC_wait_builder(); + } + GC_release_mark_lock(); + } + + GC_INNER void GC_notify_all_builder(void) + { + GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self())); + if (pthread_cond_broadcast(&builder_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } + } + + GC_INNER void GC_wait_marker(void) + { + GC_ASSERT(GC_parallel); + 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); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_notify_all_marker(void) + { + GC_ASSERT(GC_parallel); + if (pthread_cond_broadcast(&mark_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } + } + +# else /* ! GC_PTHREADS_PARAMARK */ + +# ifndef MARK_THREAD_STACK_SIZE +# define MARK_THREAD_STACK_SIZE 0 /* default value */ +# endif + + /* mark_mutex_event, builder_cv, mark_cv are initialized in GC_thr_init */ + static HANDLE mark_mutex_event = (HANDLE)0; /* Event with auto-reset. */ + static HANDLE builder_cv = (HANDLE)0; /* Event with manual reset. */ + static HANDLE mark_cv = (HANDLE)0; /* Event with manual reset. */ + + GC_INNER void GC_start_mark_threads_inner(void) + { + int i; + + GC_ASSERT(I_HOLD_LOCK()); + ASSERT_CANCEL_DISABLED(); + if (available_markers_m1 <= 0 || GC_parallel) return; + GC_wait_for_gc_completion(TRUE); + + GC_ASSERT(GC_fl_builder_count == 0); + /* Initialize GC_marker_cv[] fully before starting the */ + /* first helper thread. */ + GC_markers_m1 = available_markers_m1; + 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"); + } + + for (i = 0; i < GC_markers_m1; ++i) { +# if defined(MSWINCE) || defined(MSWIN_XBOX1) + HANDLE handle; + DWORD thread_id; + + marker_last_stack_min[i] = ADDR_LIMIT; + /* There is no _beginthreadex() in WinCE. */ + handle = CreateThread(NULL /* lpsa */, + MARK_THREAD_STACK_SIZE /* ignored */, + GC_mark_thread, (LPVOID)(word)i, + 0 /* fdwCreate */, &thread_id); + if (handle == NULL) { + WARN("Marker thread creation failed\n", 0); + /* The most probable failure reason is "not enough memory". */ + /* Don't try to create other marker threads. */ + break; + } else { + /* It's safe to detach the thread. */ + CloseHandle(handle); + } +# else + GC_uintptr_t handle; + unsigned thread_id; + + marker_last_stack_min[i] = ADDR_LIMIT; + handle = _beginthreadex(NULL /* security_attr */, + MARK_THREAD_STACK_SIZE, GC_mark_thread, + (void *)(word)i, 0 /* flags */, &thread_id); + if (!handle || handle == (GC_uintptr_t)-1L) { + WARN("Marker thread creation failed\n", 0); + /* Don't try to create other marker threads. */ + break; + } else {/* We may detach the thread (if handle is of HANDLE type) */ + /* CloseHandle((HANDLE)handle); */ + } +# endif + } + + /* Adjust GC_markers_m1 (and free unused resources) if failed. */ + while (GC_markers_m1 > i) { + GC_markers_m1--; + CloseHandle(GC_marker_cv[GC_markers_m1]); + } + GC_wait_for_markers_init(); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); + if (i == 0) { + 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 */ + + STATIC /* volatile */ LONG GC_mark_mutex_state = 0; + /* Mutex state: 0 - unlocked, */ + /* 1 - locked and no other waiters, */ + /* -1 - locked and waiters may exist. */ + /* Accessed by InterlockedExchange(). */ + + /* #define LOCK_STATS */ +# ifdef LOCK_STATS + 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()); + if (InterlockedExchange(&GC_mark_mutex_state, 1 /* locked */) != 0) { +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_block_count); +# endif + /* Repeatedly reset the state and wait until acquire the lock. */ + while (InterlockedExchange(&GC_mark_mutex_state, + -1 /* locked_and_has_waiters */) != 0) { + if (WaitForSingleObject(mark_mutex_event, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + } + } +# ifdef LOCK_STATS + else { + (void)AO_fetch_and_add1(&GC_unlocked_count); + } +# endif + + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_release_mark_lock(void) + { + UNSET_MARK_LOCK_HOLDER; + if (InterlockedExchange(&GC_mark_mutex_state, 0 /* unlocked */) < 0) { + /* wake a waiter */ + if (SetEvent(mark_mutex_event) == FALSE) + ABORT("SetEvent failed"); + } + } + + /* In GC_wait_for_reclaim/GC_notify_all_builder() we emulate POSIX */ + /* cond_wait/cond_broadcast() primitives with WinAPI Event object */ + /* (working in "manual reset" mode). This works here because */ + /* GC_notify_all_builder() is always called holding lock on */ + /* mark_mutex and the checked condition (GC_fl_builder_count == 0) */ + /* is the only one for which broadcasting on builder_cv is performed. */ + + GC_INNER void GC_wait_for_reclaim(void) + { + GC_ASSERT(builder_cv != 0); + for (;;) { + GC_acquire_mark_lock(); + if (GC_fl_builder_count == 0) + break; + if (ResetEvent(builder_cv) == FALSE) + ABORT("ResetEvent failed"); + GC_release_mark_lock(); + if (WaitForSingleObject(builder_cv, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + } + GC_release_mark_lock(); + } + + GC_INNER void GC_notify_all_builder(void) + { + 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"); + } + + /* mark_cv is used (for waiting) by a non-helper thread. */ + + GC_INNER void GC_wait_marker(void) + { + HANDLE event = mark_cv; + DWORD thread_id = GetCurrentThreadId(); + int i = GC_markers_m1; + + while (i-- > 0) { + if (GC_marker_Id[i] == thread_id) { + event = GC_marker_cv[i]; + break; + } + } + + if (ResetEvent(event) == FALSE) + ABORT("ResetEvent failed"); + GC_release_mark_lock(); + if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + GC_acquire_mark_lock(); + } + + GC_INNER void GC_notify_all_marker(void) + { + DWORD thread_id = GetCurrentThreadId(); + 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"); + } + } + +# endif /* ! GC_PTHREADS_PARAMARK */ + + static unsigned required_markers_cnt = 0; + /* The default value (0) means the number of */ + /* markers should be selected automatically. */ + +# define START_MARK_THREADS() \ + if (EXPECT(GC_parallel || available_markers_m1 <= 0, TRUE)) {} \ + else GC_start_mark_threads() +#else + +# define START_MARK_THREADS() (void)0 +#endif /* !PARALLEL_MARK */ + + /* We have no DllMain to take care of new threads. Thus we */ + /* must properly intercept thread creation. */ + + typedef struct { + LPTHREAD_START_ROUTINE start; + LPVOID param; + } thread_args; + + STATIC void * GC_CALLBACK GC_win32_start_inner(struct GC_stack_base *sb, + void *arg) + { + void * ret; + LPTHREAD_START_ROUTINE start = ((thread_args *)arg)->start; + LPVOID param = ((thread_args *)arg)->param; + + GC_register_my_thread(sb); /* This waits for an in-progress GC. */ + +# ifdef DEBUG_THREADS + GC_log_printf("thread 0x%lx starting...\n", (long)GetCurrentThreadId()); +# endif + + GC_free(arg); + + /* Clear the thread entry even if we exit with an exception. */ + /* This is probably pointless, since an uncaught exception is */ + /* supposed to result in the process being killed. */ +# if !defined(__GNUC__) && !defined(NO_CRT) + ret = NULL; /* to suppress "might be uninitialized" compiler warning */ + __try +# endif + { + ret = (void *)(word)(*start)(param); + } +# if !defined(__GNUC__) && !defined(NO_CRT) + __finally +# endif + { + GC_unregister_my_thread(); + } + +# ifdef DEBUG_THREADS + GC_log_printf("thread 0x%lx returned from start routine\n", + (long)GetCurrentThreadId()); +# endif + return ret; + } + + STATIC DWORD WINAPI GC_win32_start(LPVOID arg) + { + return (DWORD)(word)GC_call_with_stack_base(GC_win32_start_inner, arg); + } + + GC_API HANDLE WINAPI GC_CreateThread( + LPSECURITY_ATTRIBUTES lpThreadAttributes, + GC_WIN32_SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, DWORD dwCreationFlags, + LPDWORD lpThreadId) + { + if (!EXPECT(parallel_initialized, TRUE)) + GC_init_parallel(); + /* make sure GC is initialized (i.e. main thread is */ + /* attached, tls initialized). */ + +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from 0x%lx\n", + (long)GetCurrentThreadId()); +# endif + if (GC_win32_dll_threads) { + return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, + lpParameter, dwCreationFlags, lpThreadId); + } else { + thread_args *args = + (thread_args *)GC_malloc_uncollectable(sizeof(thread_args)); + /* Handed off to and deallocated by child thread. */ + HANDLE thread_h; + + if (NULL == args) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + /* set up thread arguments */ + args -> start = lpStartAddress; + args -> param = lpParameter; + GC_dirty(args); + REACHABLE_AFTER_DIRTY(lpParameter); + + START_MARK_THREADS(); + set_need_to_lock(); + thread_h = CreateThread(lpThreadAttributes, dwStackSize, GC_win32_start, + args, dwCreationFlags, lpThreadId); + if (thread_h == 0) GC_free(args); + return thread_h; + } + } + + GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread(DWORD dwExitCode) + { + GC_unregister_my_thread(); + ExitThread(dwExitCode); + } + +# if !defined(CYGWIN32) && !defined(MSWINCE) && !defined(MSWIN_XBOX1) \ + && !defined(NO_CRT) + GC_API GC_uintptr_t GC_CALL GC_beginthreadex( + void *security, unsigned stack_size, + unsigned (__stdcall *start_address)(void *), + void *arglist, unsigned initflag, + unsigned *thrdaddr) + { + if (!EXPECT(parallel_initialized, TRUE)) + GC_init_parallel(); + /* make sure GC is initialized (i.e. main thread is */ + /* attached, tls initialized). */ +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from 0x%lx\n", + (long)GetCurrentThreadId()); +# endif + + if (GC_win32_dll_threads) { + return _beginthreadex(security, stack_size, start_address, + arglist, initflag, thrdaddr); + } else { + GC_uintptr_t thread_h; + thread_args *args = + (thread_args *)GC_malloc_uncollectable(sizeof(thread_args)); + /* Handed off to and deallocated by child thread. */ + + if (NULL == args) { + /* MSDN docs say _beginthreadex() returns 0 on error and sets */ + /* errno to either EAGAIN (too many threads) or EINVAL (the */ + /* argument is invalid or the stack size is incorrect), so we */ + /* set errno to EAGAIN on "not enough memory". */ + errno = EAGAIN; + return 0; + } + + /* set up thread arguments */ + args -> start = (LPTHREAD_START_ROUTINE)start_address; + args -> param = arglist; + GC_dirty(args); + REACHABLE_AFTER_DIRTY(arglist); + + START_MARK_THREADS(); + set_need_to_lock(); + thread_h = _beginthreadex(security, stack_size, + (unsigned (__stdcall *)(void *))GC_win32_start, + args, initflag, thrdaddr); + if (thread_h == 0) GC_free(args); + return thread_h; + } + } + + GC_API void GC_CALL GC_endthreadex(unsigned retval) + { + GC_unregister_my_thread(); + _endthreadex(retval); + } +# endif /* !CYGWIN32 && !MSWINCE && !MSWIN_XBOX1 && !NO_CRT */ + +#ifdef GC_WINMAIN_REDIRECT + /* This might be useful on WinCE. Shouldn't be used with GC_DLL. */ + +# if defined(MSWINCE) && defined(UNDER_CE) +# define WINMAIN_LPTSTR LPWSTR +# else +# define WINMAIN_LPTSTR LPSTR +# endif + + /* This is defined in gc.h. */ +# undef WinMain + + /* Defined outside GC by an application. */ + int WINAPI GC_WinMain(HINSTANCE, HINSTANCE, WINMAIN_LPTSTR, int); + + typedef struct { + HINSTANCE hInstance; + HINSTANCE hPrevInstance; + WINMAIN_LPTSTR lpCmdLine; + int nShowCmd; + } main_thread_args; + + static DWORD WINAPI main_thread_start(LPVOID arg) + { + main_thread_args * args = (main_thread_args *) arg; + return (DWORD)GC_WinMain(args->hInstance, args->hPrevInstance, + args->lpCmdLine, args->nShowCmd); + } + + STATIC void *GC_CALLBACK GC_waitForSingleObjectInfinite(void *handle) + { + return (void *)(word)WaitForSingleObject((HANDLE)handle, INFINITE); + } + +# ifndef WINMAIN_THREAD_STACK_SIZE +# define WINMAIN_THREAD_STACK_SIZE 0 /* default value */ +# endif + + int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + WINMAIN_LPTSTR lpCmdLine, int nShowCmd) + { + DWORD exit_code = 1; + + main_thread_args args = { + hInstance, hPrevInstance, lpCmdLine, nShowCmd + }; + HANDLE thread_h; + DWORD thread_id; + + /* initialize everything */ + GC_INIT(); + + /* start the main thread */ + thread_h = GC_CreateThread(NULL /* lpsa */, + WINMAIN_THREAD_STACK_SIZE /* ignored on WinCE */, + main_thread_start, &args, 0 /* fdwCreate */, + &thread_id); + + if (thread_h != NULL) { + if ((DWORD)(word)GC_do_blocking(GC_waitForSingleObjectInfinite, + (void *)thread_h) == WAIT_FAILED) + ABORT("WaitForSingleObject(main_thread) failed"); + GetExitCodeThread (thread_h, &exit_code); + CloseHandle (thread_h); + } else { + ABORT("GC_CreateThread(main_thread) failed"); + } + +# ifdef MSWINCE + GC_deinit(); +# endif + return (int) exit_code; + } + +#endif /* GC_WINMAIN_REDIRECT */ + +GC_API void GC_CALL GC_set_markers_count(unsigned markers GC_ATTR_UNUSED) +{ + /* The same implementation as in pthread_support.c. */ +# ifdef PARALLEL_MARK + required_markers_cnt = markers < MAX_MARKERS ? markers : MAX_MARKERS; +# endif +} + +#ifdef WOW64_THREAD_CONTEXT_WORKAROUND +# ifdef MSWINRT_FLAVOR + /* Available on WinRT but we have to declare it manually. */ + __declspec(dllimport) HMODULE WINAPI GetModuleHandleW(LPCWSTR); +# endif + + static GC_bool is_wow64_process(HMODULE hK32 GC_ATTR_UNUSED) + { + BOOL is_wow64; +# ifdef MSWINRT_FLAVOR + /* Try to use IsWow64Process2 as it handles different WoW64 cases. */ + HMODULE hWow64 = GetModuleHandleW(L"api-ms-win-core-wow64-l1-1-1.dll"); + + if (hWow64) { + FARPROC pfn2 = GetProcAddress(hWow64, "IsWow64Process2"); + USHORT process_machine, native_machine; + + if (pfn2 + && (*(BOOL (WINAPI*)(HANDLE, USHORT*, USHORT*))(word)pfn2)( + GetCurrentProcess(), &process_machine, &native_machine)) + return process_machine != native_machine; + } + if (IsWow64Process(GetCurrentProcess(), &is_wow64)) + return (GC_bool)is_wow64; +# else + if (hK32) { + FARPROC pfn = GetProcAddress(hK32, "IsWow64Process"); + + if (pfn + && (*(BOOL (WINAPI*)(HANDLE, BOOL*))(word)pfn)( + GetCurrentProcess(), &is_wow64)) + return (GC_bool)is_wow64; + } +# endif + return FALSE; /* IsWow64Process failed */ + } +#endif /* WOW64_THREAD_CONTEXT_WORKAROUND */ + +#ifndef DONT_USE_ATEXIT + GC_INNER GC_bool GC_is_main_thread(void) + { + GC_ASSERT(GC_thr_initialized); + return GC_main_thread == GetCurrentThreadId(); + } +#endif /* !DONT_USE_ATEXIT */ + +GC_INNER void GC_thr_init(void) +{ + struct GC_stack_base sb; +# if (!defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) && !defined(MSWINCE) \ + && defined(PARALLEL_MARK)) || defined(WOW64_THREAD_CONTEXT_WORKAROUND) + HMODULE hK32; +# ifdef MSWINRT_FLAVOR + MEMORY_BASIC_INFORMATION memInfo; + + if (VirtualQuery((void*)(word)GetProcAddress, &memInfo, sizeof(memInfo)) + != sizeof(memInfo)) + ABORT("Weird VirtualQuery result"); + hK32 = (HMODULE)memInfo.AllocationBase; +# else + hK32 = GetModuleHandle(TEXT("kernel32.dll")); +# endif +# endif + + 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 +# ifdef WOW64_THREAD_CONTEXT_WORKAROUND + /* Set isWow64 flag. */ + isWow64 = is_wow64_process(hK32); +# endif + /* Add the initial thread, so we can stop it. */ + sb.mem_base = GC_stackbottom; + GC_ASSERT(sb.mem_base != NULL); +# ifdef IA64 + sb.reg_base = GC_register_stackbottom; +# endif + +# if defined(PARALLEL_MARK) + { + char * markers_string = GETENV("GC_MARKERS"); + int markers = required_markers_cnt; + + if (markers_string != NULL) { + markers = atoi(markers_string); + if (markers <= 0 || markers > MAX_MARKERS) { + WARN("Too big or invalid number of mark threads: %" WARN_PRIdPTR + "; using maximum threads\n", (signed_word)markers); + markers = MAX_MARKERS; + } + } else if (0 == markers) { + /* Unless the client sets the desired number of */ + /* parallel markers, it is determined based on the */ + /* number of CPU cores. */ +# ifdef MSWINCE + /* There is no GetProcessAffinityMask() in WinCE. */ + /* GC_sysinfo is already initialized. */ + markers = (int)GC_sysinfo.dwNumberOfProcessors; +# else +# ifdef _WIN64 + DWORD_PTR procMask = 0; + DWORD_PTR sysMask; +# else + DWORD procMask = 0; + DWORD sysMask; +# endif + int ncpu = 0; + if ( +# ifdef __cplusplus + GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask) +# else + /* Cast args to void* for compatibility with some old SDKs. */ + GetProcessAffinityMask(GetCurrentProcess(), + (void *)&procMask, (void *)&sysMask) +# endif + && procMask) { + do { + ncpu++; + } while ((procMask &= procMask - 1) != 0); + } + markers = ncpu; +# endif +# if defined(GC_MIN_MARKERS) && !defined(CPPCHECK) + /* This is primarily for testing on systems without getenv(). */ + if (markers < GC_MIN_MARKERS) + markers = GC_MIN_MARKERS; +# endif + if (markers > MAX_MARKERS) + markers = MAX_MARKERS; /* silently limit the value */ + } + available_markers_m1 = markers - 1; + } + + /* Check whether parallel mode could be enabled. */ + if (GC_win32_dll_threads || available_markers_m1 <= 0) { + /* Disable parallel marking. */ + GC_parallel = FALSE; + GC_COND_LOG_PRINTF( + "Single marker thread, turning off parallel marking\n"); + } else { +# ifndef GC_PTHREADS_PARAMARK + /* Initialize Win32 event objects for parallel marking. */ + mark_mutex_event = CreateEvent(NULL /* attrs */, + FALSE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + builder_cv = CreateEvent(NULL /* attrs */, + TRUE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + mark_cv = CreateEvent(NULL /* attrs */, TRUE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + if (mark_mutex_event == (HANDLE)0 || builder_cv == (HANDLE)0 + || mark_cv == (HANDLE)0) + ABORT("CreateEvent failed"); +# endif +# if !defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) && !defined(MSWINCE) + if (hK32) + setThreadDescription_fn = GetProcAddress(hK32, + "SetThreadDescription"); +# endif + } +# endif /* PARALLEL_MARK */ + + GC_ASSERT(0 == GC_lookup_thread_inner(GC_main_thread)); + GC_register_my_thread_inner(&sb, GC_main_thread); +} + +#ifdef GC_PTHREADS + + struct start_info { + void *(*start_routine)(void *); + void *arg; + sem_t registered; /* 1 ==> in our thread table, but */ + /* parent hasn't yet noticed. */ + int detached; + }; + + GC_API int GC_pthread_join(pthread_t pthread_id, void **retval) + { + 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", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId(), + (void *)GC_PTHREAD_PTRVAL(pthread_id)); +# endif + + /* After the join, thread id may have been recycled. */ + t = GC_lookup_pthread(pthread_id); + result = pthread_join(pthread_id, retval); + if (0 == result) { + LOCK(); + if ((t -> flags & FINISHED) != 0) { + GC_delete_gc_thread_no_free(t); + GC_INTERNAL_FREE(t); + } + UNLOCK(); + } + +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%lx) join with thread %p %s\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId(), + (void *)GC_PTHREAD_PTRVAL(pthread_id), + result != 0 ? "failed" : "succeeded"); +# endif + return result; + } + + /* Cygwin-pthreads calls CreateThread internally, but it's not easily */ + /* interceptable by us..., so intercept pthread_create instead. */ + GC_API int GC_pthread_create(pthread_t *new_thread, + 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) */ + GC_ASSERT(!GC_win32_dll_threads); + + if (sem_init(&si.registered, GC_SEM_INIT_PSHARED, 0) != 0) + ABORT("sem_init failed"); + + si.start_routine = start_routine; + si.arg = arg; + si.detached = 0; + if (attr != NULL + && pthread_attr_getdetachstate(attr, &si.detached) != 0) + ABORT("pthread_attr_getdetachstate failed"); +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from %p(0x%lx)\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId()); +# endif + START_MARK_THREADS(); + set_need_to_lock(); + result = pthread_create(new_thread, attr, GC_pthread_start, &si); + + /* Wait until child has been added to the thread table. */ + /* This also ensures that we hold onto the stack-allocated si */ + /* until the child is done with it. */ + if (0 == result) { + while (0 != sem_wait(&si.registered)) { + if (EINTR != errno) ABORT("sem_wait failed"); + } + } + sem_destroy(&si.registered); + return(result); + } + + STATIC void * GC_CALLBACK GC_pthread_start_inner(struct GC_stack_base *sb, + void * arg) + { + struct start_info * si = (struct start_info *)arg; + void * result; + void *(*start)(void *); + void *start_arg; + DWORD thread_id = GetCurrentThreadId(); + pthread_t pthread_id = pthread_self(); + GC_thread me; + DCL_LOCK_STATE; + +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%x) starting...\n", + (void *)GC_PTHREAD_PTRVAL(pthread_id), (int)thread_id); +# endif + + GC_ASSERT(!GC_win32_dll_threads); + /* If a GC occurs before the thread is registered, that GC will */ + /* ignore this thread. That's fine, since it will block trying to */ + /* acquire the allocation lock, and won't yet hold interesting */ + /* pointers. */ + LOCK(); + /* We register the thread here instead of in the parent, so that */ + /* we don't need to hold the allocation lock during pthread_create. */ + me = GC_register_my_thread_inner(sb, thread_id); + SET_PTHREAD_MAP_CACHE(pthread_id, thread_id); + GC_ASSERT(me != &first_thread); + me -> pthread_id = pthread_id; + if (si->detached) me -> flags |= DETACHED; +# ifdef THREAD_LOCAL_ALLOC + GC_init_thread_local(&me->tlfs); +# endif + UNLOCK(); + + start = si -> start_routine; + start_arg = si -> arg; + sem_post(&(si -> registered)); /* Last action on si. */ + /* OK to deallocate. */ + + pthread_cleanup_push(GC_thread_exit_proc, (void *)me); + result = (*start)(start_arg); + me -> status = result; + GC_dirty(me); + pthread_cleanup_pop(1); + +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%x) returned from start routine\n", + (void *)GC_PTHREAD_PTRVAL(pthread_id), (int)thread_id); +# endif + return(result); + } + + STATIC void * GC_pthread_start(void * arg) + { + return GC_call_with_stack_base(GC_pthread_start_inner, arg); + } + + STATIC void GC_thread_exit_proc(void *arg) + { + GC_thread me = (GC_thread)arg; + DCL_LOCK_STATE; + + GC_ASSERT(!GC_win32_dll_threads); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%lx) called pthread_exit()\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId()); +# 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) { + GC_delete_thread(GetCurrentThreadId()); + } else { + /* 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(); + } + +# ifndef GC_NO_PTHREAD_SIGMASK + /* pthreads-win32 does not support sigmask. */ + /* So, nothing required here... */ + GC_API int GC_pthread_sigmask(int how, const sigset_t *set, + sigset_t *oset) + { + return pthread_sigmask(how, set, oset); + } +# endif /* !GC_NO_PTHREAD_SIGMASK */ + + GC_API int GC_pthread_detach(pthread_t thread) + { + int result; + GC_thread t; + DCL_LOCK_STATE; + + GC_ASSERT(!GC_win32_dll_threads); + t = GC_lookup_pthread(thread); + result = pthread_detach(thread); + if (result == 0) { + LOCK(); + t -> flags |= DETACHED; + /* Here the pthread thread id may have been recycled. */ + if ((t -> flags & FINISHED) != 0) { + GC_delete_gc_thread_no_free(t); + GC_INTERNAL_FREE(t); + } + UNLOCK(); + } + return result; + } + +#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 */ + /* than the main one should be created with an uninitialized */ + /* 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) + { + DWORD thread_id; + + /* Note that GC_use_threads_discovery should be called by the */ + /* client application at start-up to activate automatic thread */ + /* registration (it is the default GC behavior); */ + /* to always have automatic thread registration turned on, the GC */ + /* should be compiled with -D GC_DISCOVER_TASK_THREADS. */ + if (!GC_win32_dll_threads && parallel_initialized) return TRUE; + + switch (reason) { + case DLL_THREAD_ATTACH: +# ifdef PARALLEL_MARK + /* Don't register marker threads. */ + if (GC_parallel) { + /* We could reach here only if parallel_initialized == FALSE. */ + break; + } +# endif + /* FALLTHRU */ + case DLL_PROCESS_ATTACH: + /* This may run with the collector uninitialized. */ + thread_id = GetCurrentThreadId(); + if (parallel_initialized && GC_main_thread != thread_id) { + struct GC_stack_base sb; + /* Don't lock here. */ +# ifdef GC_ASSERTIONS + int sb_result = +# endif + GC_get_stack_base(&sb); + GC_ASSERT(sb_result == GC_SUCCESS); + GC_register_my_thread_inner(&sb, thread_id); + } /* o.w. we already did it during GC_thr_init, called by GC_init */ + break; + + case DLL_THREAD_DETACH: + /* We are hopefully running in the context of the exiting thread. */ + if (GC_win32_dll_threads) { + GC_delete_thread(GetCurrentThreadId()); + } + break; + + case DLL_PROCESS_DETACH: + if (GC_win32_dll_threads) { + int i; + int my_max = (int)GC_get_max_thread_index(); + + for (i = 0; i <= my_max; ++i) { + if (AO_load(&(dll_thread_table[i].tm.in_use))) + GC_delete_gc_thread_no_free(&dll_thread_table[i]); + } + GC_deinit(); + } + break; + } + return TRUE; + } +#endif /* !GC_NO_THREADS_DISCOVERY && !GC_PTHREADS */ + +/* Perform all initializations, including those that */ +/* may require allocation. */ +/* Called without allocation lock. */ +/* Must be called before a second thread is created. */ +GC_INNER void GC_init_parallel(void) +{ +# if defined(THREAD_LOCAL_ALLOC) + GC_thread me; + DCL_LOCK_STATE; +# endif + + if (parallel_initialized) return; + parallel_initialized = TRUE; + /* GC_init() calls us back, so set flag first. */ + + if (!GC_is_initialized) GC_init(); +# if defined(CPPCHECK) && !defined(GC_NO_THREADS_DISCOVERY) + GC_noop1((word)&GC_DllMain); +# endif + if (GC_win32_dll_threads) { + set_need_to_lock(); + /* Cannot intercept thread creation. Hence we don't know if */ + /* other threads exist. However, client is not allowed to */ + /* create other threads before collector initialization. */ + /* Thus it's OK not to lock before this. */ + } + /* Initialize thread local free lists if used. */ +# if defined(THREAD_LOCAL_ALLOC) + LOCK(); + me = GC_lookup_thread_inner(GetCurrentThreadId()); + CHECK_LOOKUP_MY_THREAD(me); + GC_init_thread_local(&me->tlfs); + UNLOCK(); +# endif +} + +#if defined(USE_PTHREAD_LOCKS) + /* Support for pthread locking code. */ + /* pthread_mutex_trylock may not win here, */ + /* due to builtin support for spinning first? */ + + GC_INNER void GC_lock(void) + { + pthread_mutex_lock(&GC_allocate_ml); + } +#endif /* USE_PTHREAD_LOCKS */ + +#if defined(THREAD_LOCAL_ALLOC) + + /* Add thread-local allocation support. VC++ uses __declspec(thread). */ + + /* We must explicitly mark ptrfree and gcj free lists, since the free */ + /* list links wouldn't otherwise be found. We also set them in the */ + /* normal free lists, since that involves touching less memory than if */ + /* we scanned them normally. */ + GC_INNER void GC_mark_thread_local_free_lists(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; 0 != p; p = p -> tm.next) { + if (!KNOWN_FINISHED(p)) { +# ifdef DEBUG_THREADS + GC_log_printf("Marking thread locals for 0x%x\n", (int)p -> id); +# endif + GC_mark_thread_local_fls_for(&(p->tlfs)); + } + } + } + } + +# if defined(GC_ASSERTIONS) + /* Check that all thread-local free-lists are completely marked. */ + /* also check that thread-specific-data structures are marked. */ + void GC_check_tls(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; 0 != p; p = p -> tm.next) { + if (!KNOWN_FINISHED(p)) + GC_check_tls_for(&(p->tlfs)); + } + } +# if defined(USE_CUSTOM_SPECIFIC) + if (GC_thread_key != 0) + GC_check_tsd_marks(GC_thread_key); +# endif + } +# endif /* GC_ASSERTIONS */ + +#endif /* THREAD_LOCAL_ALLOC ... */ + +# ifndef GC_NO_THREAD_REDIRECTS + /* Restore thread calls redirection. */ +# define CreateThread GC_CreateThread +# define ExitThread GC_ExitThread +# undef _beginthreadex +# define _beginthreadex GC_beginthreadex +# undef _endthreadex +# define _endthreadex GC_endthreadex +# endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_WIN32_THREADS */ From e9e4d3359c8288b90439db8d19c0c76246c37a9d Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Mon, 23 Dec 2024 03:05:09 -0700 Subject: [PATCH 02/24] REPO: Rename bdwgc to gc. --- {bdwgc => gc}/.appveyor.yml | 0 {bdwgc => gc}/.gitattributes | 0 {bdwgc => gc}/.gitignore | 0 {bdwgc => gc}/.gitrepo | 0 {bdwgc => gc}/.travis.yml | 0 {bdwgc => gc}/AUTHORS | 0 {bdwgc => gc}/CMakeLists.txt | 0 {bdwgc => gc}/ChangeLog | 0 {bdwgc => gc}/Config.cmake.in | 0 {bdwgc => gc}/Makefile.am | 0 {bdwgc => gc}/Makefile.direct | 0 {bdwgc => gc}/NT_MAKEFILE | 0 {bdwgc => gc}/OS2_MAKEFILE | 0 {bdwgc => gc}/PCR-Makefile | 0 {bdwgc => gc}/README.QUICK | 0 {bdwgc => gc}/README.md | 0 {bdwgc => gc}/SMakefile.amiga | 0 {bdwgc => gc}/WCC_MAKEFILE | 0 {bdwgc => gc}/allchblk.c | 0 {bdwgc => gc}/alloc.c | 0 {bdwgc => gc}/autogen.sh | 0 {bdwgc => gc}/backgraph.c | 0 {bdwgc => gc}/bdw-gc.pc.in | 0 {bdwgc => gc}/blacklst.c | 0 {bdwgc => gc}/build/s60v3/bld.inf | 0 {bdwgc => gc}/build/s60v3/libgc.mmp | 0 {bdwgc => gc}/checksums.c | 0 {bdwgc => gc}/configure.ac | 0 {bdwgc => gc}/cord/cord.am | 0 {bdwgc => gc}/cord/cordbscs.c | 0 {bdwgc => gc}/cord/cordprnt.c | 0 {bdwgc => gc}/cord/cordxtra.c | 0 {bdwgc => gc}/cord/tests/cordtest.c | 0 {bdwgc => gc}/cord/tests/de.c | 0 {bdwgc => gc}/cord/tests/de_cmds.h | 0 {bdwgc => gc}/cord/tests/de_win.c | 0 {bdwgc => gc}/cord/tests/de_win.h | 0 {bdwgc => gc}/cord/tests/de_win.rc | 0 {bdwgc => gc}/darwin_stop_world.c | 0 {bdwgc => gc}/dbg_mlc.c | 0 {bdwgc => gc}/digimars.mak | 0 {bdwgc => gc}/doc/README.DGUX386 | 0 {bdwgc => gc}/doc/README.Mac | 0 {bdwgc => gc}/doc/README.OS2 | 0 {bdwgc => gc}/doc/README.amiga | 0 {bdwgc => gc}/doc/README.arm.cross | 0 {bdwgc => gc}/doc/README.autoconf | 0 {bdwgc => gc}/doc/README.cmake | 0 {bdwgc => gc}/doc/README.cords | 0 {bdwgc => gc}/doc/README.darwin | 0 {bdwgc => gc}/doc/README.emscripten | 0 {bdwgc => gc}/doc/README.environment | 0 {bdwgc => gc}/doc/README.ews4800 | 0 {bdwgc => gc}/doc/README.hp | 0 {bdwgc => gc}/doc/README.linux | 0 {bdwgc => gc}/doc/README.macros | 0 {bdwgc => gc}/doc/README.rs6000 | 0 {bdwgc => gc}/doc/README.sgi | 0 {bdwgc => gc}/doc/README.solaris2 | 0 {bdwgc => gc}/doc/README.symbian | 0 {bdwgc => gc}/doc/README.uts | 0 {bdwgc => gc}/doc/README.win32 | 0 {bdwgc => gc}/doc/README.win64 | 0 {bdwgc => gc}/doc/debugging.md | 0 {bdwgc => gc}/doc/doc.am | 0 {bdwgc => gc}/doc/finalization.md | 0 {bdwgc => gc}/doc/gc.man | 0 {bdwgc => gc}/doc/gcdescr.md | 0 {bdwgc => gc}/doc/gcinterface.md | 0 {bdwgc => gc}/doc/leak.md | 0 {bdwgc => gc}/doc/overview.md | 0 {bdwgc => gc}/doc/porting.md | 0 {bdwgc => gc}/doc/scale.md | 0 {bdwgc => gc}/doc/simple_example.md | 0 {bdwgc => gc}/doc/tree.md | 0 {bdwgc => gc}/dyn_load.c | 0 {bdwgc => gc}/extra/AmigaOS.c | 0 {bdwgc => gc}/extra/MacOS.c | 0 {bdwgc => gc}/extra/Mac_files/MacOS_config.h | 0 {bdwgc => gc}/extra/Mac_files/dataend.c | 0 {bdwgc => gc}/extra/Mac_files/datastart.c | 0 {bdwgc => gc}/extra/gc.c | 0 {bdwgc => gc}/extra/msvc_dbg.c | 0 {bdwgc => gc}/extra/pcr_interface.c | 0 {bdwgc => gc}/extra/real_malloc.c | 0 {bdwgc => gc}/extra/symbian.cpp | 0 {bdwgc => gc}/extra/symbian/global_end.cpp | 0 {bdwgc => gc}/extra/symbian/global_start.cpp | 0 {bdwgc => gc}/extra/symbian/init_global_static_roots.cpp | 0 {bdwgc => gc}/finalize.c | 0 {bdwgc => gc}/fnlz_mlc.c | 0 {bdwgc => gc}/gc_badalc.cc | 0 {bdwgc => gc}/gc_badalc.cpp | 0 {bdwgc => gc}/gc_cpp.cc | 0 {bdwgc => gc}/gc_cpp.cpp | 0 {bdwgc => gc}/gc_dlopen.c | 0 {bdwgc => gc}/gcj_mlc.c | 0 {bdwgc => gc}/headers.c | 0 {bdwgc => gc}/ia64_save_regs_in_stack.s | 0 {bdwgc => gc}/include/cord.h | 0 {bdwgc => gc}/include/cord_pos.h | 0 {bdwgc => gc}/include/ec.h | 0 {bdwgc => gc}/include/extra/gc.h | 0 {bdwgc => gc}/include/extra/gc_cpp.h | 0 {bdwgc => gc}/include/gc.h | 0 {bdwgc => gc}/include/gc_allocator.h | 0 {bdwgc => gc}/include/gc_backptr.h | 0 {bdwgc => gc}/include/gc_config_macros.h | 0 {bdwgc => gc}/include/gc_cpp.h | 0 {bdwgc => gc}/include/gc_disclaim.h | 0 {bdwgc => gc}/include/gc_gcj.h | 0 {bdwgc => gc}/include/gc_inline.h | 0 {bdwgc => gc}/include/gc_mark.h | 0 {bdwgc => gc}/include/gc_pthread_redirects.h | 0 {bdwgc => gc}/include/gc_tiny_fl.h | 0 {bdwgc => gc}/include/gc_typed.h | 0 {bdwgc => gc}/include/gc_version.h | 0 {bdwgc => gc}/include/include.am | 0 {bdwgc => gc}/include/javaxfc.h | 0 {bdwgc => gc}/include/leak_detector.h | 0 {bdwgc => gc}/include/private/darwin_semaphore.h | 0 {bdwgc => gc}/include/private/darwin_stop_world.h | 0 {bdwgc => gc}/include/private/dbg_mlc.h | 0 {bdwgc => gc}/include/private/gc_alloc_ptrs.h | 0 {bdwgc => gc}/include/private/gc_atomic_ops.h | 0 {bdwgc => gc}/include/private/gc_hdrs.h | 0 {bdwgc => gc}/include/private/gc_locks.h | 0 {bdwgc => gc}/include/private/gc_pmark.h | 0 {bdwgc => gc}/include/private/gc_priv.h | 0 {bdwgc => gc}/include/private/gcconfig.h | 0 {bdwgc => gc}/include/private/msvc_dbg.h | 0 {bdwgc => gc}/include/private/pthread_stop_world.h | 0 {bdwgc => gc}/include/private/pthread_support.h | 0 {bdwgc => gc}/include/private/specific.h | 0 {bdwgc => gc}/include/private/thread_local_alloc.h | 0 {bdwgc => gc}/m4/gc_set_version.m4 | 0 {bdwgc => gc}/mach_dep.c | 0 {bdwgc => gc}/malloc.c | 0 {bdwgc => gc}/mallocx.c | 0 {bdwgc => gc}/mark.c | 0 {bdwgc => gc}/mark_rts.c | 0 {bdwgc => gc}/misc.c | 0 {bdwgc => gc}/new_hblk.c | 0 {bdwgc => gc}/obj_map.c | 0 {bdwgc => gc}/os_dep.c | 0 {bdwgc => gc}/pthread_start.c | 0 {bdwgc => gc}/pthread_stop_world.c | 0 {bdwgc => gc}/pthread_support.c | 0 {bdwgc => gc}/ptr_chck.c | 0 {bdwgc => gc}/reclaim.c | 0 {bdwgc => gc}/sparc_mach_dep.S | 0 {bdwgc => gc}/sparc_netbsd_mach_dep.s | 0 {bdwgc => gc}/specific.c | 0 {bdwgc => gc}/tests/disclaim_bench.c | 0 {bdwgc => gc}/tests/disclaim_test.c | 0 {bdwgc => gc}/tests/disclaim_weakmap_test.c | 0 {bdwgc => gc}/tests/huge_test.c | 0 {bdwgc => gc}/tests/initsecondarythread.c | 0 {bdwgc => gc}/tests/leak_test.c | 0 {bdwgc => gc}/tests/middle.c | 0 {bdwgc => gc}/tests/realloc_test.c | 0 {bdwgc => gc}/tests/smash_test.c | 0 {bdwgc => gc}/tests/staticrootslib.c | 0 {bdwgc => gc}/tests/staticrootstest.c | 0 {bdwgc => gc}/tests/subthread_create.c | 0 {bdwgc => gc}/tests/test.c | 0 {bdwgc => gc}/tests/test_atomic_ops.c | 0 {bdwgc => gc}/tests/test_cpp.cc | 0 {bdwgc => gc}/tests/tests.am | 0 {bdwgc => gc}/tests/thread_leak_test.c | 0 {bdwgc => gc}/tests/threadkey_test.c | 0 {bdwgc => gc}/tests/trace_test.c | 0 {bdwgc => gc}/thread_local_alloc.c | 0 {bdwgc => gc}/tools/callprocs.sh | 0 {bdwgc => gc}/tools/if_mach.c | 0 {bdwgc => gc}/tools/if_not_there.c | 0 {bdwgc => gc}/tools/setjmp_t.c | 0 {bdwgc => gc}/tools/threadlibs.c | 0 {bdwgc => gc}/typd_mlc.c | 0 {bdwgc => gc}/win32_threads.c | 0 180 files changed, 0 insertions(+), 0 deletions(-) rename {bdwgc => gc}/.appveyor.yml (100%) rename {bdwgc => gc}/.gitattributes (100%) rename {bdwgc => gc}/.gitignore (100%) rename {bdwgc => gc}/.gitrepo (100%) rename {bdwgc => gc}/.travis.yml (100%) rename {bdwgc => gc}/AUTHORS (100%) rename {bdwgc => gc}/CMakeLists.txt (100%) rename {bdwgc => gc}/ChangeLog (100%) rename {bdwgc => gc}/Config.cmake.in (100%) rename {bdwgc => gc}/Makefile.am (100%) rename {bdwgc => gc}/Makefile.direct (100%) rename {bdwgc => gc}/NT_MAKEFILE (100%) rename {bdwgc => gc}/OS2_MAKEFILE (100%) rename {bdwgc => gc}/PCR-Makefile (100%) rename {bdwgc => gc}/README.QUICK (100%) rename {bdwgc => gc}/README.md (100%) rename {bdwgc => gc}/SMakefile.amiga (100%) rename {bdwgc => gc}/WCC_MAKEFILE (100%) rename {bdwgc => gc}/allchblk.c (100%) rename {bdwgc => gc}/alloc.c (100%) rename {bdwgc => gc}/autogen.sh (100%) mode change 100755 => 100644 rename {bdwgc => gc}/backgraph.c (100%) rename {bdwgc => gc}/bdw-gc.pc.in (100%) rename {bdwgc => gc}/blacklst.c (100%) rename {bdwgc => gc}/build/s60v3/bld.inf (100%) rename {bdwgc => gc}/build/s60v3/libgc.mmp (100%) rename {bdwgc => gc}/checksums.c (100%) rename {bdwgc => gc}/configure.ac (100%) rename {bdwgc => gc}/cord/cord.am (100%) rename {bdwgc => gc}/cord/cordbscs.c (100%) rename {bdwgc => gc}/cord/cordprnt.c (100%) rename {bdwgc => gc}/cord/cordxtra.c (100%) rename {bdwgc => gc}/cord/tests/cordtest.c (100%) rename {bdwgc => gc}/cord/tests/de.c (100%) rename {bdwgc => gc}/cord/tests/de_cmds.h (100%) rename {bdwgc => gc}/cord/tests/de_win.c (100%) rename {bdwgc => gc}/cord/tests/de_win.h (100%) rename {bdwgc => gc}/cord/tests/de_win.rc (100%) rename {bdwgc => gc}/darwin_stop_world.c (100%) rename {bdwgc => gc}/dbg_mlc.c (100%) rename {bdwgc => gc}/digimars.mak (100%) rename {bdwgc => gc}/doc/README.DGUX386 (100%) rename {bdwgc => gc}/doc/README.Mac (100%) rename {bdwgc => gc}/doc/README.OS2 (100%) rename {bdwgc => gc}/doc/README.amiga (100%) rename {bdwgc => gc}/doc/README.arm.cross (100%) rename {bdwgc => gc}/doc/README.autoconf (100%) rename {bdwgc => gc}/doc/README.cmake (100%) rename {bdwgc => gc}/doc/README.cords (100%) rename {bdwgc => gc}/doc/README.darwin (100%) rename {bdwgc => gc}/doc/README.emscripten (100%) rename {bdwgc => gc}/doc/README.environment (100%) rename {bdwgc => gc}/doc/README.ews4800 (100%) rename {bdwgc => gc}/doc/README.hp (100%) rename {bdwgc => gc}/doc/README.linux (100%) rename {bdwgc => gc}/doc/README.macros (100%) rename {bdwgc => gc}/doc/README.rs6000 (100%) rename {bdwgc => gc}/doc/README.sgi (100%) rename {bdwgc => gc}/doc/README.solaris2 (100%) rename {bdwgc => gc}/doc/README.symbian (100%) rename {bdwgc => gc}/doc/README.uts (100%) rename {bdwgc => gc}/doc/README.win32 (100%) rename {bdwgc => gc}/doc/README.win64 (100%) rename {bdwgc => gc}/doc/debugging.md (100%) rename {bdwgc => gc}/doc/doc.am (100%) rename {bdwgc => gc}/doc/finalization.md (100%) rename {bdwgc => gc}/doc/gc.man (100%) rename {bdwgc => gc}/doc/gcdescr.md (100%) rename {bdwgc => gc}/doc/gcinterface.md (100%) rename {bdwgc => gc}/doc/leak.md (100%) rename {bdwgc => gc}/doc/overview.md (100%) rename {bdwgc => gc}/doc/porting.md (100%) rename {bdwgc => gc}/doc/scale.md (100%) rename {bdwgc => gc}/doc/simple_example.md (100%) rename {bdwgc => gc}/doc/tree.md (100%) rename {bdwgc => gc}/dyn_load.c (100%) rename {bdwgc => gc}/extra/AmigaOS.c (100%) rename {bdwgc => gc}/extra/MacOS.c (100%) rename {bdwgc => gc}/extra/Mac_files/MacOS_config.h (100%) rename {bdwgc => gc}/extra/Mac_files/dataend.c (100%) rename {bdwgc => gc}/extra/Mac_files/datastart.c (100%) rename {bdwgc => gc}/extra/gc.c (100%) rename {bdwgc => gc}/extra/msvc_dbg.c (100%) rename {bdwgc => gc}/extra/pcr_interface.c (100%) rename {bdwgc => gc}/extra/real_malloc.c (100%) rename {bdwgc => gc}/extra/symbian.cpp (100%) rename {bdwgc => gc}/extra/symbian/global_end.cpp (100%) rename {bdwgc => gc}/extra/symbian/global_start.cpp (100%) rename {bdwgc => gc}/extra/symbian/init_global_static_roots.cpp (100%) rename {bdwgc => gc}/finalize.c (100%) rename {bdwgc => gc}/fnlz_mlc.c (100%) rename {bdwgc => gc}/gc_badalc.cc (100%) rename {bdwgc => gc}/gc_badalc.cpp (100%) rename {bdwgc => gc}/gc_cpp.cc (100%) rename {bdwgc => gc}/gc_cpp.cpp (100%) rename {bdwgc => gc}/gc_dlopen.c (100%) rename {bdwgc => gc}/gcj_mlc.c (100%) rename {bdwgc => gc}/headers.c (100%) rename {bdwgc => gc}/ia64_save_regs_in_stack.s (100%) rename {bdwgc => gc}/include/cord.h (100%) rename {bdwgc => gc}/include/cord_pos.h (100%) rename {bdwgc => gc}/include/ec.h (100%) rename {bdwgc => gc}/include/extra/gc.h (100%) rename {bdwgc => gc}/include/extra/gc_cpp.h (100%) rename {bdwgc => gc}/include/gc.h (100%) rename {bdwgc => gc}/include/gc_allocator.h (100%) rename {bdwgc => gc}/include/gc_backptr.h (100%) rename {bdwgc => gc}/include/gc_config_macros.h (100%) rename {bdwgc => gc}/include/gc_cpp.h (100%) rename {bdwgc => gc}/include/gc_disclaim.h (100%) rename {bdwgc => gc}/include/gc_gcj.h (100%) rename {bdwgc => gc}/include/gc_inline.h (100%) rename {bdwgc => gc}/include/gc_mark.h (100%) rename {bdwgc => gc}/include/gc_pthread_redirects.h (100%) rename {bdwgc => gc}/include/gc_tiny_fl.h (100%) rename {bdwgc => gc}/include/gc_typed.h (100%) rename {bdwgc => gc}/include/gc_version.h (100%) rename {bdwgc => gc}/include/include.am (100%) rename {bdwgc => gc}/include/javaxfc.h (100%) rename {bdwgc => gc}/include/leak_detector.h (100%) rename {bdwgc => gc}/include/private/darwin_semaphore.h (100%) rename {bdwgc => gc}/include/private/darwin_stop_world.h (100%) rename {bdwgc => gc}/include/private/dbg_mlc.h (100%) rename {bdwgc => gc}/include/private/gc_alloc_ptrs.h (100%) rename {bdwgc => gc}/include/private/gc_atomic_ops.h (100%) rename {bdwgc => gc}/include/private/gc_hdrs.h (100%) rename {bdwgc => gc}/include/private/gc_locks.h (100%) rename {bdwgc => gc}/include/private/gc_pmark.h (100%) rename {bdwgc => gc}/include/private/gc_priv.h (100%) rename {bdwgc => gc}/include/private/gcconfig.h (100%) rename {bdwgc => gc}/include/private/msvc_dbg.h (100%) rename {bdwgc => gc}/include/private/pthread_stop_world.h (100%) rename {bdwgc => gc}/include/private/pthread_support.h (100%) rename {bdwgc => gc}/include/private/specific.h (100%) rename {bdwgc => gc}/include/private/thread_local_alloc.h (100%) rename {bdwgc => gc}/m4/gc_set_version.m4 (100%) rename {bdwgc => gc}/mach_dep.c (100%) rename {bdwgc => gc}/malloc.c (100%) rename {bdwgc => gc}/mallocx.c (100%) rename {bdwgc => gc}/mark.c (100%) rename {bdwgc => gc}/mark_rts.c (100%) rename {bdwgc => gc}/misc.c (100%) rename {bdwgc => gc}/new_hblk.c (100%) rename {bdwgc => gc}/obj_map.c (100%) rename {bdwgc => gc}/os_dep.c (100%) rename {bdwgc => gc}/pthread_start.c (100%) rename {bdwgc => gc}/pthread_stop_world.c (100%) rename {bdwgc => gc}/pthread_support.c (100%) rename {bdwgc => gc}/ptr_chck.c (100%) rename {bdwgc => gc}/reclaim.c (100%) rename {bdwgc => gc}/sparc_mach_dep.S (100%) rename {bdwgc => gc}/sparc_netbsd_mach_dep.s (100%) rename {bdwgc => gc}/specific.c (100%) rename {bdwgc => gc}/tests/disclaim_bench.c (100%) rename {bdwgc => gc}/tests/disclaim_test.c (100%) rename {bdwgc => gc}/tests/disclaim_weakmap_test.c (100%) rename {bdwgc => gc}/tests/huge_test.c (100%) rename {bdwgc => gc}/tests/initsecondarythread.c (100%) rename {bdwgc => gc}/tests/leak_test.c (100%) rename {bdwgc => gc}/tests/middle.c (100%) rename {bdwgc => gc}/tests/realloc_test.c (100%) rename {bdwgc => gc}/tests/smash_test.c (100%) rename {bdwgc => gc}/tests/staticrootslib.c (100%) rename {bdwgc => gc}/tests/staticrootstest.c (100%) rename {bdwgc => gc}/tests/subthread_create.c (100%) rename {bdwgc => gc}/tests/test.c (100%) rename {bdwgc => gc}/tests/test_atomic_ops.c (100%) rename {bdwgc => gc}/tests/test_cpp.cc (100%) rename {bdwgc => gc}/tests/tests.am (100%) rename {bdwgc => gc}/tests/thread_leak_test.c (100%) rename {bdwgc => gc}/tests/threadkey_test.c (100%) rename {bdwgc => gc}/tests/trace_test.c (100%) rename {bdwgc => gc}/thread_local_alloc.c (100%) rename {bdwgc => gc}/tools/callprocs.sh (100%) mode change 100755 => 100644 rename {bdwgc => gc}/tools/if_mach.c (100%) rename {bdwgc => gc}/tools/if_not_there.c (100%) rename {bdwgc => gc}/tools/setjmp_t.c (100%) rename {bdwgc => gc}/tools/threadlibs.c (100%) rename {bdwgc => gc}/typd_mlc.c (100%) rename {bdwgc => gc}/win32_threads.c (100%) diff --git a/bdwgc/.appveyor.yml b/gc/.appveyor.yml similarity index 100% rename from bdwgc/.appveyor.yml rename to gc/.appveyor.yml diff --git a/bdwgc/.gitattributes b/gc/.gitattributes similarity index 100% rename from bdwgc/.gitattributes rename to gc/.gitattributes diff --git a/bdwgc/.gitignore b/gc/.gitignore similarity index 100% rename from bdwgc/.gitignore rename to gc/.gitignore diff --git a/bdwgc/.gitrepo b/gc/.gitrepo similarity index 100% rename from bdwgc/.gitrepo rename to gc/.gitrepo diff --git a/bdwgc/.travis.yml b/gc/.travis.yml similarity index 100% rename from bdwgc/.travis.yml rename to gc/.travis.yml diff --git a/bdwgc/AUTHORS b/gc/AUTHORS similarity index 100% rename from bdwgc/AUTHORS rename to gc/AUTHORS diff --git a/bdwgc/CMakeLists.txt b/gc/CMakeLists.txt similarity index 100% rename from bdwgc/CMakeLists.txt rename to gc/CMakeLists.txt diff --git a/bdwgc/ChangeLog b/gc/ChangeLog similarity index 100% rename from bdwgc/ChangeLog rename to gc/ChangeLog diff --git a/bdwgc/Config.cmake.in b/gc/Config.cmake.in similarity index 100% rename from bdwgc/Config.cmake.in rename to gc/Config.cmake.in diff --git a/bdwgc/Makefile.am b/gc/Makefile.am similarity index 100% rename from bdwgc/Makefile.am rename to gc/Makefile.am diff --git a/bdwgc/Makefile.direct b/gc/Makefile.direct similarity index 100% rename from bdwgc/Makefile.direct rename to gc/Makefile.direct diff --git a/bdwgc/NT_MAKEFILE b/gc/NT_MAKEFILE similarity index 100% rename from bdwgc/NT_MAKEFILE rename to gc/NT_MAKEFILE diff --git a/bdwgc/OS2_MAKEFILE b/gc/OS2_MAKEFILE similarity index 100% rename from bdwgc/OS2_MAKEFILE rename to gc/OS2_MAKEFILE diff --git a/bdwgc/PCR-Makefile b/gc/PCR-Makefile similarity index 100% rename from bdwgc/PCR-Makefile rename to gc/PCR-Makefile diff --git a/bdwgc/README.QUICK b/gc/README.QUICK similarity index 100% rename from bdwgc/README.QUICK rename to gc/README.QUICK diff --git a/bdwgc/README.md b/gc/README.md similarity index 100% rename from bdwgc/README.md rename to gc/README.md diff --git a/bdwgc/SMakefile.amiga b/gc/SMakefile.amiga similarity index 100% rename from bdwgc/SMakefile.amiga rename to gc/SMakefile.amiga diff --git a/bdwgc/WCC_MAKEFILE b/gc/WCC_MAKEFILE similarity index 100% rename from bdwgc/WCC_MAKEFILE rename to gc/WCC_MAKEFILE diff --git a/bdwgc/allchblk.c b/gc/allchblk.c similarity index 100% rename from bdwgc/allchblk.c rename to gc/allchblk.c diff --git a/bdwgc/alloc.c b/gc/alloc.c similarity index 100% rename from bdwgc/alloc.c rename to gc/alloc.c diff --git a/bdwgc/autogen.sh b/gc/autogen.sh old mode 100755 new mode 100644 similarity index 100% rename from bdwgc/autogen.sh rename to gc/autogen.sh diff --git a/bdwgc/backgraph.c b/gc/backgraph.c similarity index 100% rename from bdwgc/backgraph.c rename to gc/backgraph.c diff --git a/bdwgc/bdw-gc.pc.in b/gc/bdw-gc.pc.in similarity index 100% rename from bdwgc/bdw-gc.pc.in rename to gc/bdw-gc.pc.in diff --git a/bdwgc/blacklst.c b/gc/blacklst.c similarity index 100% rename from bdwgc/blacklst.c rename to gc/blacklst.c diff --git a/bdwgc/build/s60v3/bld.inf b/gc/build/s60v3/bld.inf similarity index 100% rename from bdwgc/build/s60v3/bld.inf rename to gc/build/s60v3/bld.inf diff --git a/bdwgc/build/s60v3/libgc.mmp b/gc/build/s60v3/libgc.mmp similarity index 100% rename from bdwgc/build/s60v3/libgc.mmp rename to gc/build/s60v3/libgc.mmp diff --git a/bdwgc/checksums.c b/gc/checksums.c similarity index 100% rename from bdwgc/checksums.c rename to gc/checksums.c diff --git a/bdwgc/configure.ac b/gc/configure.ac similarity index 100% rename from bdwgc/configure.ac rename to gc/configure.ac diff --git a/bdwgc/cord/cord.am b/gc/cord/cord.am similarity index 100% rename from bdwgc/cord/cord.am rename to gc/cord/cord.am diff --git a/bdwgc/cord/cordbscs.c b/gc/cord/cordbscs.c similarity index 100% rename from bdwgc/cord/cordbscs.c rename to gc/cord/cordbscs.c diff --git a/bdwgc/cord/cordprnt.c b/gc/cord/cordprnt.c similarity index 100% rename from bdwgc/cord/cordprnt.c rename to gc/cord/cordprnt.c diff --git a/bdwgc/cord/cordxtra.c b/gc/cord/cordxtra.c similarity index 100% rename from bdwgc/cord/cordxtra.c rename to gc/cord/cordxtra.c diff --git a/bdwgc/cord/tests/cordtest.c b/gc/cord/tests/cordtest.c similarity index 100% rename from bdwgc/cord/tests/cordtest.c rename to gc/cord/tests/cordtest.c diff --git a/bdwgc/cord/tests/de.c b/gc/cord/tests/de.c similarity index 100% rename from bdwgc/cord/tests/de.c rename to gc/cord/tests/de.c diff --git a/bdwgc/cord/tests/de_cmds.h b/gc/cord/tests/de_cmds.h similarity index 100% rename from bdwgc/cord/tests/de_cmds.h rename to gc/cord/tests/de_cmds.h diff --git a/bdwgc/cord/tests/de_win.c b/gc/cord/tests/de_win.c similarity index 100% rename from bdwgc/cord/tests/de_win.c rename to gc/cord/tests/de_win.c diff --git a/bdwgc/cord/tests/de_win.h b/gc/cord/tests/de_win.h similarity index 100% rename from bdwgc/cord/tests/de_win.h rename to gc/cord/tests/de_win.h diff --git a/bdwgc/cord/tests/de_win.rc b/gc/cord/tests/de_win.rc similarity index 100% rename from bdwgc/cord/tests/de_win.rc rename to gc/cord/tests/de_win.rc diff --git a/bdwgc/darwin_stop_world.c b/gc/darwin_stop_world.c similarity index 100% rename from bdwgc/darwin_stop_world.c rename to gc/darwin_stop_world.c diff --git a/bdwgc/dbg_mlc.c b/gc/dbg_mlc.c similarity index 100% rename from bdwgc/dbg_mlc.c rename to gc/dbg_mlc.c diff --git a/bdwgc/digimars.mak b/gc/digimars.mak similarity index 100% rename from bdwgc/digimars.mak rename to gc/digimars.mak diff --git a/bdwgc/doc/README.DGUX386 b/gc/doc/README.DGUX386 similarity index 100% rename from bdwgc/doc/README.DGUX386 rename to gc/doc/README.DGUX386 diff --git a/bdwgc/doc/README.Mac b/gc/doc/README.Mac similarity index 100% rename from bdwgc/doc/README.Mac rename to gc/doc/README.Mac diff --git a/bdwgc/doc/README.OS2 b/gc/doc/README.OS2 similarity index 100% rename from bdwgc/doc/README.OS2 rename to gc/doc/README.OS2 diff --git a/bdwgc/doc/README.amiga b/gc/doc/README.amiga similarity index 100% rename from bdwgc/doc/README.amiga rename to gc/doc/README.amiga diff --git a/bdwgc/doc/README.arm.cross b/gc/doc/README.arm.cross similarity index 100% rename from bdwgc/doc/README.arm.cross rename to gc/doc/README.arm.cross diff --git a/bdwgc/doc/README.autoconf b/gc/doc/README.autoconf similarity index 100% rename from bdwgc/doc/README.autoconf rename to gc/doc/README.autoconf diff --git a/bdwgc/doc/README.cmake b/gc/doc/README.cmake similarity index 100% rename from bdwgc/doc/README.cmake rename to gc/doc/README.cmake diff --git a/bdwgc/doc/README.cords b/gc/doc/README.cords similarity index 100% rename from bdwgc/doc/README.cords rename to gc/doc/README.cords diff --git a/bdwgc/doc/README.darwin b/gc/doc/README.darwin similarity index 100% rename from bdwgc/doc/README.darwin rename to gc/doc/README.darwin diff --git a/bdwgc/doc/README.emscripten b/gc/doc/README.emscripten similarity index 100% rename from bdwgc/doc/README.emscripten rename to gc/doc/README.emscripten diff --git a/bdwgc/doc/README.environment b/gc/doc/README.environment similarity index 100% rename from bdwgc/doc/README.environment rename to gc/doc/README.environment diff --git a/bdwgc/doc/README.ews4800 b/gc/doc/README.ews4800 similarity index 100% rename from bdwgc/doc/README.ews4800 rename to gc/doc/README.ews4800 diff --git a/bdwgc/doc/README.hp b/gc/doc/README.hp similarity index 100% rename from bdwgc/doc/README.hp rename to gc/doc/README.hp diff --git a/bdwgc/doc/README.linux b/gc/doc/README.linux similarity index 100% rename from bdwgc/doc/README.linux rename to gc/doc/README.linux diff --git a/bdwgc/doc/README.macros b/gc/doc/README.macros similarity index 100% rename from bdwgc/doc/README.macros rename to gc/doc/README.macros diff --git a/bdwgc/doc/README.rs6000 b/gc/doc/README.rs6000 similarity index 100% rename from bdwgc/doc/README.rs6000 rename to gc/doc/README.rs6000 diff --git a/bdwgc/doc/README.sgi b/gc/doc/README.sgi similarity index 100% rename from bdwgc/doc/README.sgi rename to gc/doc/README.sgi diff --git a/bdwgc/doc/README.solaris2 b/gc/doc/README.solaris2 similarity index 100% rename from bdwgc/doc/README.solaris2 rename to gc/doc/README.solaris2 diff --git a/bdwgc/doc/README.symbian b/gc/doc/README.symbian similarity index 100% rename from bdwgc/doc/README.symbian rename to gc/doc/README.symbian diff --git a/bdwgc/doc/README.uts b/gc/doc/README.uts similarity index 100% rename from bdwgc/doc/README.uts rename to gc/doc/README.uts diff --git a/bdwgc/doc/README.win32 b/gc/doc/README.win32 similarity index 100% rename from bdwgc/doc/README.win32 rename to gc/doc/README.win32 diff --git a/bdwgc/doc/README.win64 b/gc/doc/README.win64 similarity index 100% rename from bdwgc/doc/README.win64 rename to gc/doc/README.win64 diff --git a/bdwgc/doc/debugging.md b/gc/doc/debugging.md similarity index 100% rename from bdwgc/doc/debugging.md rename to gc/doc/debugging.md diff --git a/bdwgc/doc/doc.am b/gc/doc/doc.am similarity index 100% rename from bdwgc/doc/doc.am rename to gc/doc/doc.am diff --git a/bdwgc/doc/finalization.md b/gc/doc/finalization.md similarity index 100% rename from bdwgc/doc/finalization.md rename to gc/doc/finalization.md diff --git a/bdwgc/doc/gc.man b/gc/doc/gc.man similarity index 100% rename from bdwgc/doc/gc.man rename to gc/doc/gc.man diff --git a/bdwgc/doc/gcdescr.md b/gc/doc/gcdescr.md similarity index 100% rename from bdwgc/doc/gcdescr.md rename to gc/doc/gcdescr.md diff --git a/bdwgc/doc/gcinterface.md b/gc/doc/gcinterface.md similarity index 100% rename from bdwgc/doc/gcinterface.md rename to gc/doc/gcinterface.md diff --git a/bdwgc/doc/leak.md b/gc/doc/leak.md similarity index 100% rename from bdwgc/doc/leak.md rename to gc/doc/leak.md diff --git a/bdwgc/doc/overview.md b/gc/doc/overview.md similarity index 100% rename from bdwgc/doc/overview.md rename to gc/doc/overview.md diff --git a/bdwgc/doc/porting.md b/gc/doc/porting.md similarity index 100% rename from bdwgc/doc/porting.md rename to gc/doc/porting.md diff --git a/bdwgc/doc/scale.md b/gc/doc/scale.md similarity index 100% rename from bdwgc/doc/scale.md rename to gc/doc/scale.md diff --git a/bdwgc/doc/simple_example.md b/gc/doc/simple_example.md similarity index 100% rename from bdwgc/doc/simple_example.md rename to gc/doc/simple_example.md diff --git a/bdwgc/doc/tree.md b/gc/doc/tree.md similarity index 100% rename from bdwgc/doc/tree.md rename to gc/doc/tree.md diff --git a/bdwgc/dyn_load.c b/gc/dyn_load.c similarity index 100% rename from bdwgc/dyn_load.c rename to gc/dyn_load.c diff --git a/bdwgc/extra/AmigaOS.c b/gc/extra/AmigaOS.c similarity index 100% rename from bdwgc/extra/AmigaOS.c rename to gc/extra/AmigaOS.c diff --git a/bdwgc/extra/MacOS.c b/gc/extra/MacOS.c similarity index 100% rename from bdwgc/extra/MacOS.c rename to gc/extra/MacOS.c diff --git a/bdwgc/extra/Mac_files/MacOS_config.h b/gc/extra/Mac_files/MacOS_config.h similarity index 100% rename from bdwgc/extra/Mac_files/MacOS_config.h rename to gc/extra/Mac_files/MacOS_config.h diff --git a/bdwgc/extra/Mac_files/dataend.c b/gc/extra/Mac_files/dataend.c similarity index 100% rename from bdwgc/extra/Mac_files/dataend.c rename to gc/extra/Mac_files/dataend.c diff --git a/bdwgc/extra/Mac_files/datastart.c b/gc/extra/Mac_files/datastart.c similarity index 100% rename from bdwgc/extra/Mac_files/datastart.c rename to gc/extra/Mac_files/datastart.c diff --git a/bdwgc/extra/gc.c b/gc/extra/gc.c similarity index 100% rename from bdwgc/extra/gc.c rename to gc/extra/gc.c diff --git a/bdwgc/extra/msvc_dbg.c b/gc/extra/msvc_dbg.c similarity index 100% rename from bdwgc/extra/msvc_dbg.c rename to gc/extra/msvc_dbg.c diff --git a/bdwgc/extra/pcr_interface.c b/gc/extra/pcr_interface.c similarity index 100% rename from bdwgc/extra/pcr_interface.c rename to gc/extra/pcr_interface.c diff --git a/bdwgc/extra/real_malloc.c b/gc/extra/real_malloc.c similarity index 100% rename from bdwgc/extra/real_malloc.c rename to gc/extra/real_malloc.c diff --git a/bdwgc/extra/symbian.cpp b/gc/extra/symbian.cpp similarity index 100% rename from bdwgc/extra/symbian.cpp rename to gc/extra/symbian.cpp diff --git a/bdwgc/extra/symbian/global_end.cpp b/gc/extra/symbian/global_end.cpp similarity index 100% rename from bdwgc/extra/symbian/global_end.cpp rename to gc/extra/symbian/global_end.cpp diff --git a/bdwgc/extra/symbian/global_start.cpp b/gc/extra/symbian/global_start.cpp similarity index 100% rename from bdwgc/extra/symbian/global_start.cpp rename to gc/extra/symbian/global_start.cpp diff --git a/bdwgc/extra/symbian/init_global_static_roots.cpp b/gc/extra/symbian/init_global_static_roots.cpp similarity index 100% rename from bdwgc/extra/symbian/init_global_static_roots.cpp rename to gc/extra/symbian/init_global_static_roots.cpp diff --git a/bdwgc/finalize.c b/gc/finalize.c similarity index 100% rename from bdwgc/finalize.c rename to gc/finalize.c diff --git a/bdwgc/fnlz_mlc.c b/gc/fnlz_mlc.c similarity index 100% rename from bdwgc/fnlz_mlc.c rename to gc/fnlz_mlc.c diff --git a/bdwgc/gc_badalc.cc b/gc/gc_badalc.cc similarity index 100% rename from bdwgc/gc_badalc.cc rename to gc/gc_badalc.cc diff --git a/bdwgc/gc_badalc.cpp b/gc/gc_badalc.cpp similarity index 100% rename from bdwgc/gc_badalc.cpp rename to gc/gc_badalc.cpp diff --git a/bdwgc/gc_cpp.cc b/gc/gc_cpp.cc similarity index 100% rename from bdwgc/gc_cpp.cc rename to gc/gc_cpp.cc diff --git a/bdwgc/gc_cpp.cpp b/gc/gc_cpp.cpp similarity index 100% rename from bdwgc/gc_cpp.cpp rename to gc/gc_cpp.cpp diff --git a/bdwgc/gc_dlopen.c b/gc/gc_dlopen.c similarity index 100% rename from bdwgc/gc_dlopen.c rename to gc/gc_dlopen.c diff --git a/bdwgc/gcj_mlc.c b/gc/gcj_mlc.c similarity index 100% rename from bdwgc/gcj_mlc.c rename to gc/gcj_mlc.c diff --git a/bdwgc/headers.c b/gc/headers.c similarity index 100% rename from bdwgc/headers.c rename to gc/headers.c diff --git a/bdwgc/ia64_save_regs_in_stack.s b/gc/ia64_save_regs_in_stack.s similarity index 100% rename from bdwgc/ia64_save_regs_in_stack.s rename to gc/ia64_save_regs_in_stack.s diff --git a/bdwgc/include/cord.h b/gc/include/cord.h similarity index 100% rename from bdwgc/include/cord.h rename to gc/include/cord.h diff --git a/bdwgc/include/cord_pos.h b/gc/include/cord_pos.h similarity index 100% rename from bdwgc/include/cord_pos.h rename to gc/include/cord_pos.h diff --git a/bdwgc/include/ec.h b/gc/include/ec.h similarity index 100% rename from bdwgc/include/ec.h rename to gc/include/ec.h diff --git a/bdwgc/include/extra/gc.h b/gc/include/extra/gc.h similarity index 100% rename from bdwgc/include/extra/gc.h rename to gc/include/extra/gc.h diff --git a/bdwgc/include/extra/gc_cpp.h b/gc/include/extra/gc_cpp.h similarity index 100% rename from bdwgc/include/extra/gc_cpp.h rename to gc/include/extra/gc_cpp.h diff --git a/bdwgc/include/gc.h b/gc/include/gc.h similarity index 100% rename from bdwgc/include/gc.h rename to gc/include/gc.h diff --git a/bdwgc/include/gc_allocator.h b/gc/include/gc_allocator.h similarity index 100% rename from bdwgc/include/gc_allocator.h rename to gc/include/gc_allocator.h diff --git a/bdwgc/include/gc_backptr.h b/gc/include/gc_backptr.h similarity index 100% rename from bdwgc/include/gc_backptr.h rename to gc/include/gc_backptr.h diff --git a/bdwgc/include/gc_config_macros.h b/gc/include/gc_config_macros.h similarity index 100% rename from bdwgc/include/gc_config_macros.h rename to gc/include/gc_config_macros.h diff --git a/bdwgc/include/gc_cpp.h b/gc/include/gc_cpp.h similarity index 100% rename from bdwgc/include/gc_cpp.h rename to gc/include/gc_cpp.h diff --git a/bdwgc/include/gc_disclaim.h b/gc/include/gc_disclaim.h similarity index 100% rename from bdwgc/include/gc_disclaim.h rename to gc/include/gc_disclaim.h diff --git a/bdwgc/include/gc_gcj.h b/gc/include/gc_gcj.h similarity index 100% rename from bdwgc/include/gc_gcj.h rename to gc/include/gc_gcj.h diff --git a/bdwgc/include/gc_inline.h b/gc/include/gc_inline.h similarity index 100% rename from bdwgc/include/gc_inline.h rename to gc/include/gc_inline.h diff --git a/bdwgc/include/gc_mark.h b/gc/include/gc_mark.h similarity index 100% rename from bdwgc/include/gc_mark.h rename to gc/include/gc_mark.h diff --git a/bdwgc/include/gc_pthread_redirects.h b/gc/include/gc_pthread_redirects.h similarity index 100% rename from bdwgc/include/gc_pthread_redirects.h rename to gc/include/gc_pthread_redirects.h diff --git a/bdwgc/include/gc_tiny_fl.h b/gc/include/gc_tiny_fl.h similarity index 100% rename from bdwgc/include/gc_tiny_fl.h rename to gc/include/gc_tiny_fl.h diff --git a/bdwgc/include/gc_typed.h b/gc/include/gc_typed.h similarity index 100% rename from bdwgc/include/gc_typed.h rename to gc/include/gc_typed.h diff --git a/bdwgc/include/gc_version.h b/gc/include/gc_version.h similarity index 100% rename from bdwgc/include/gc_version.h rename to gc/include/gc_version.h diff --git a/bdwgc/include/include.am b/gc/include/include.am similarity index 100% rename from bdwgc/include/include.am rename to gc/include/include.am diff --git a/bdwgc/include/javaxfc.h b/gc/include/javaxfc.h similarity index 100% rename from bdwgc/include/javaxfc.h rename to gc/include/javaxfc.h diff --git a/bdwgc/include/leak_detector.h b/gc/include/leak_detector.h similarity index 100% rename from bdwgc/include/leak_detector.h rename to gc/include/leak_detector.h diff --git a/bdwgc/include/private/darwin_semaphore.h b/gc/include/private/darwin_semaphore.h similarity index 100% rename from bdwgc/include/private/darwin_semaphore.h rename to gc/include/private/darwin_semaphore.h diff --git a/bdwgc/include/private/darwin_stop_world.h b/gc/include/private/darwin_stop_world.h similarity index 100% rename from bdwgc/include/private/darwin_stop_world.h rename to gc/include/private/darwin_stop_world.h diff --git a/bdwgc/include/private/dbg_mlc.h b/gc/include/private/dbg_mlc.h similarity index 100% rename from bdwgc/include/private/dbg_mlc.h rename to gc/include/private/dbg_mlc.h diff --git a/bdwgc/include/private/gc_alloc_ptrs.h b/gc/include/private/gc_alloc_ptrs.h similarity index 100% rename from bdwgc/include/private/gc_alloc_ptrs.h rename to gc/include/private/gc_alloc_ptrs.h diff --git a/bdwgc/include/private/gc_atomic_ops.h b/gc/include/private/gc_atomic_ops.h similarity index 100% rename from bdwgc/include/private/gc_atomic_ops.h rename to gc/include/private/gc_atomic_ops.h diff --git a/bdwgc/include/private/gc_hdrs.h b/gc/include/private/gc_hdrs.h similarity index 100% rename from bdwgc/include/private/gc_hdrs.h rename to gc/include/private/gc_hdrs.h diff --git a/bdwgc/include/private/gc_locks.h b/gc/include/private/gc_locks.h similarity index 100% rename from bdwgc/include/private/gc_locks.h rename to gc/include/private/gc_locks.h diff --git a/bdwgc/include/private/gc_pmark.h b/gc/include/private/gc_pmark.h similarity index 100% rename from bdwgc/include/private/gc_pmark.h rename to gc/include/private/gc_pmark.h diff --git a/bdwgc/include/private/gc_priv.h b/gc/include/private/gc_priv.h similarity index 100% rename from bdwgc/include/private/gc_priv.h rename to gc/include/private/gc_priv.h diff --git a/bdwgc/include/private/gcconfig.h b/gc/include/private/gcconfig.h similarity index 100% rename from bdwgc/include/private/gcconfig.h rename to gc/include/private/gcconfig.h diff --git a/bdwgc/include/private/msvc_dbg.h b/gc/include/private/msvc_dbg.h similarity index 100% rename from bdwgc/include/private/msvc_dbg.h rename to gc/include/private/msvc_dbg.h diff --git a/bdwgc/include/private/pthread_stop_world.h b/gc/include/private/pthread_stop_world.h similarity index 100% rename from bdwgc/include/private/pthread_stop_world.h rename to gc/include/private/pthread_stop_world.h diff --git a/bdwgc/include/private/pthread_support.h b/gc/include/private/pthread_support.h similarity index 100% rename from bdwgc/include/private/pthread_support.h rename to gc/include/private/pthread_support.h diff --git a/bdwgc/include/private/specific.h b/gc/include/private/specific.h similarity index 100% rename from bdwgc/include/private/specific.h rename to gc/include/private/specific.h diff --git a/bdwgc/include/private/thread_local_alloc.h b/gc/include/private/thread_local_alloc.h similarity index 100% rename from bdwgc/include/private/thread_local_alloc.h rename to gc/include/private/thread_local_alloc.h diff --git a/bdwgc/m4/gc_set_version.m4 b/gc/m4/gc_set_version.m4 similarity index 100% rename from bdwgc/m4/gc_set_version.m4 rename to gc/m4/gc_set_version.m4 diff --git a/bdwgc/mach_dep.c b/gc/mach_dep.c similarity index 100% rename from bdwgc/mach_dep.c rename to gc/mach_dep.c diff --git a/bdwgc/malloc.c b/gc/malloc.c similarity index 100% rename from bdwgc/malloc.c rename to gc/malloc.c diff --git a/bdwgc/mallocx.c b/gc/mallocx.c similarity index 100% rename from bdwgc/mallocx.c rename to gc/mallocx.c diff --git a/bdwgc/mark.c b/gc/mark.c similarity index 100% rename from bdwgc/mark.c rename to gc/mark.c diff --git a/bdwgc/mark_rts.c b/gc/mark_rts.c similarity index 100% rename from bdwgc/mark_rts.c rename to gc/mark_rts.c diff --git a/bdwgc/misc.c b/gc/misc.c similarity index 100% rename from bdwgc/misc.c rename to gc/misc.c diff --git a/bdwgc/new_hblk.c b/gc/new_hblk.c similarity index 100% rename from bdwgc/new_hblk.c rename to gc/new_hblk.c diff --git a/bdwgc/obj_map.c b/gc/obj_map.c similarity index 100% rename from bdwgc/obj_map.c rename to gc/obj_map.c diff --git a/bdwgc/os_dep.c b/gc/os_dep.c similarity index 100% rename from bdwgc/os_dep.c rename to gc/os_dep.c diff --git a/bdwgc/pthread_start.c b/gc/pthread_start.c similarity index 100% rename from bdwgc/pthread_start.c rename to gc/pthread_start.c diff --git a/bdwgc/pthread_stop_world.c b/gc/pthread_stop_world.c similarity index 100% rename from bdwgc/pthread_stop_world.c rename to gc/pthread_stop_world.c diff --git a/bdwgc/pthread_support.c b/gc/pthread_support.c similarity index 100% rename from bdwgc/pthread_support.c rename to gc/pthread_support.c diff --git a/bdwgc/ptr_chck.c b/gc/ptr_chck.c similarity index 100% rename from bdwgc/ptr_chck.c rename to gc/ptr_chck.c diff --git a/bdwgc/reclaim.c b/gc/reclaim.c similarity index 100% rename from bdwgc/reclaim.c rename to gc/reclaim.c diff --git a/bdwgc/sparc_mach_dep.S b/gc/sparc_mach_dep.S similarity index 100% rename from bdwgc/sparc_mach_dep.S rename to gc/sparc_mach_dep.S diff --git a/bdwgc/sparc_netbsd_mach_dep.s b/gc/sparc_netbsd_mach_dep.s similarity index 100% rename from bdwgc/sparc_netbsd_mach_dep.s rename to gc/sparc_netbsd_mach_dep.s diff --git a/bdwgc/specific.c b/gc/specific.c similarity index 100% rename from bdwgc/specific.c rename to gc/specific.c diff --git a/bdwgc/tests/disclaim_bench.c b/gc/tests/disclaim_bench.c similarity index 100% rename from bdwgc/tests/disclaim_bench.c rename to gc/tests/disclaim_bench.c diff --git a/bdwgc/tests/disclaim_test.c b/gc/tests/disclaim_test.c similarity index 100% rename from bdwgc/tests/disclaim_test.c rename to gc/tests/disclaim_test.c diff --git a/bdwgc/tests/disclaim_weakmap_test.c b/gc/tests/disclaim_weakmap_test.c similarity index 100% rename from bdwgc/tests/disclaim_weakmap_test.c rename to gc/tests/disclaim_weakmap_test.c diff --git a/bdwgc/tests/huge_test.c b/gc/tests/huge_test.c similarity index 100% rename from bdwgc/tests/huge_test.c rename to gc/tests/huge_test.c diff --git a/bdwgc/tests/initsecondarythread.c b/gc/tests/initsecondarythread.c similarity index 100% rename from bdwgc/tests/initsecondarythread.c rename to gc/tests/initsecondarythread.c diff --git a/bdwgc/tests/leak_test.c b/gc/tests/leak_test.c similarity index 100% rename from bdwgc/tests/leak_test.c rename to gc/tests/leak_test.c diff --git a/bdwgc/tests/middle.c b/gc/tests/middle.c similarity index 100% rename from bdwgc/tests/middle.c rename to gc/tests/middle.c diff --git a/bdwgc/tests/realloc_test.c b/gc/tests/realloc_test.c similarity index 100% rename from bdwgc/tests/realloc_test.c rename to gc/tests/realloc_test.c diff --git a/bdwgc/tests/smash_test.c b/gc/tests/smash_test.c similarity index 100% rename from bdwgc/tests/smash_test.c rename to gc/tests/smash_test.c diff --git a/bdwgc/tests/staticrootslib.c b/gc/tests/staticrootslib.c similarity index 100% rename from bdwgc/tests/staticrootslib.c rename to gc/tests/staticrootslib.c diff --git a/bdwgc/tests/staticrootstest.c b/gc/tests/staticrootstest.c similarity index 100% rename from bdwgc/tests/staticrootstest.c rename to gc/tests/staticrootstest.c diff --git a/bdwgc/tests/subthread_create.c b/gc/tests/subthread_create.c similarity index 100% rename from bdwgc/tests/subthread_create.c rename to gc/tests/subthread_create.c diff --git a/bdwgc/tests/test.c b/gc/tests/test.c similarity index 100% rename from bdwgc/tests/test.c rename to gc/tests/test.c diff --git a/bdwgc/tests/test_atomic_ops.c b/gc/tests/test_atomic_ops.c similarity index 100% rename from bdwgc/tests/test_atomic_ops.c rename to gc/tests/test_atomic_ops.c diff --git a/bdwgc/tests/test_cpp.cc b/gc/tests/test_cpp.cc similarity index 100% rename from bdwgc/tests/test_cpp.cc rename to gc/tests/test_cpp.cc diff --git a/bdwgc/tests/tests.am b/gc/tests/tests.am similarity index 100% rename from bdwgc/tests/tests.am rename to gc/tests/tests.am diff --git a/bdwgc/tests/thread_leak_test.c b/gc/tests/thread_leak_test.c similarity index 100% rename from bdwgc/tests/thread_leak_test.c rename to gc/tests/thread_leak_test.c diff --git a/bdwgc/tests/threadkey_test.c b/gc/tests/threadkey_test.c similarity index 100% rename from bdwgc/tests/threadkey_test.c rename to gc/tests/threadkey_test.c diff --git a/bdwgc/tests/trace_test.c b/gc/tests/trace_test.c similarity index 100% rename from bdwgc/tests/trace_test.c rename to gc/tests/trace_test.c diff --git a/bdwgc/thread_local_alloc.c b/gc/thread_local_alloc.c similarity index 100% rename from bdwgc/thread_local_alloc.c rename to gc/thread_local_alloc.c diff --git a/bdwgc/tools/callprocs.sh b/gc/tools/callprocs.sh old mode 100755 new mode 100644 similarity index 100% rename from bdwgc/tools/callprocs.sh rename to gc/tools/callprocs.sh diff --git a/bdwgc/tools/if_mach.c b/gc/tools/if_mach.c similarity index 100% rename from bdwgc/tools/if_mach.c rename to gc/tools/if_mach.c diff --git a/bdwgc/tools/if_not_there.c b/gc/tools/if_not_there.c similarity index 100% rename from bdwgc/tools/if_not_there.c rename to gc/tools/if_not_there.c diff --git a/bdwgc/tools/setjmp_t.c b/gc/tools/setjmp_t.c similarity index 100% rename from bdwgc/tools/setjmp_t.c rename to gc/tools/setjmp_t.c diff --git a/bdwgc/tools/threadlibs.c b/gc/tools/threadlibs.c similarity index 100% rename from bdwgc/tools/threadlibs.c rename to gc/tools/threadlibs.c diff --git a/bdwgc/typd_mlc.c b/gc/typd_mlc.c similarity index 100% rename from bdwgc/typd_mlc.c rename to gc/typd_mlc.c diff --git a/bdwgc/win32_threads.c b/gc/win32_threads.c similarity index 100% rename from bdwgc/win32_threads.c rename to gc/win32_threads.c From 535935c7b9cd0e193281c74916485c1dff4cf867 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Mon, 23 Dec 2024 03:05:25 -0700 Subject: [PATCH 03/24] REPO: Unignore gc files. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bda331f60..9bec32009 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ __pycache__ ### Compressed packages and Garbage Collector -gc-* *.tar.gz *.tgz libatomic* @@ -18,6 +17,8 @@ xasy ### Version info *version.* +!gc/include/gc_version.h +!gc/m4/gc_set_version.m4 !version.texi.in revision.* From 15cc98d1576ac69a5407b1c465acc6f89514860d Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sun, 22 Dec 2024 01:15:03 -0700 Subject: [PATCH 04/24] git subrepo clone --branch=v7.8.2 https://github.com/ivmai/libatomic_ops subrepo: subdir: "libatomic_ops" merged: "4c00f978c" upstream: origin: "https://github.com/ivmai/libatomic_ops" branch: "v7.8.2" commit: "4c00f978c" git-subrepo: version: "0.4.9" origin: "https://github.com/ingydotnet/git-subrepo" commit: "cce3d93" --- libatomic_ops/.appveyor.yml | 63 + libatomic_ops/.gitattributes | 7 + libatomic_ops/.gitignore | 111 + libatomic_ops/.gitrepo | 12 + libatomic_ops/.travis.yml | 719 +++ libatomic_ops/AUTHORS | 60 + libatomic_ops/CMakeLists.txt | 380 ++ libatomic_ops/COPYING | 339 ++ libatomic_ops/ChangeLog | 765 +++ libatomic_ops/Config.cmake.in | 5 + libatomic_ops/LICENSE | 76 + libatomic_ops/Makefile.am | 29 + libatomic_ops/README.md | 130 + libatomic_ops/README_details.txt | 254 + libatomic_ops/README_malloc.txt | 60 + libatomic_ops/README_stack.txt | 78 + libatomic_ops/README_win32.txt | 46 + libatomic_ops/autogen.sh | 10 + libatomic_ops/configure.ac | 261 + libatomic_ops/m4/.gitignore | 3 + .../pkgconfig/atomic_ops-uninstalled.pc.in | 10 + libatomic_ops/pkgconfig/atomic_ops.pc.in | 10 + libatomic_ops/src/Makefile.am | 268 + libatomic_ops/src/Makefile.msft | 72 + libatomic_ops/src/atomic_ops.c | 301 + libatomic_ops/src/atomic_ops.h | 509 ++ libatomic_ops/src/atomic_ops/ao_version.h | 38 + .../src/atomic_ops/generalize-arithm.h | 3408 +++++++++++ .../src/atomic_ops/generalize-arithm.template | 852 +++ .../src/atomic_ops/generalize-small.h | 2640 ++++++++ .../src/atomic_ops/generalize-small.template | 528 ++ libatomic_ops/src/atomic_ops/generalize.h | 729 +++ libatomic_ops/src/atomic_ops/sysdeps/README | 7 + .../sysdeps/all_acquire_release_volatile.h | 30 + .../sysdeps/all_aligned_atomic_load_store.h | 38 + .../sysdeps/all_atomic_load_store.h | 32 + .../atomic_ops/sysdeps/all_atomic_only_load.h | 30 + .../src/atomic_ops/sysdeps/ao_t_is_int.h | 552 ++ .../atomic_ops/sysdeps/ao_t_is_int.template | 92 + .../src/atomic_ops/sysdeps/armcc/arm_v6.h | 264 + .../src/atomic_ops/sysdeps/emul_cas.h | 87 + .../src/atomic_ops/sysdeps/gcc/aarch64.h | 282 + .../src/atomic_ops/sysdeps/gcc/alpha.h | 67 + .../src/atomic_ops/sysdeps/gcc/arm.h | 742 +++ .../src/atomic_ops/sysdeps/gcc/avr32.h | 71 + .../src/atomic_ops/sysdeps/gcc/cris.h | 65 + .../src/atomic_ops/sysdeps/gcc/e2k.h | 28 + .../atomic_ops/sysdeps/gcc/generic-arithm.h | 864 +++ .../sysdeps/gcc/generic-arithm.template | 54 + .../atomic_ops/sysdeps/gcc/generic-small.h | 632 ++ .../sysdeps/gcc/generic-small.template | 158 + .../src/atomic_ops/sysdeps/gcc/generic.h | 247 + .../src/atomic_ops/sysdeps/gcc/hexagon.h | 140 + .../src/atomic_ops/sysdeps/gcc/hppa.h | 94 + .../src/atomic_ops/sysdeps/gcc/ia64.h | 287 + .../src/atomic_ops/sysdeps/gcc/m68k.h | 68 + .../src/atomic_ops/sysdeps/gcc/mips.h | 205 + .../src/atomic_ops/sysdeps/gcc/powerpc.h | 348 ++ .../src/atomic_ops/sysdeps/gcc/riscv.h | 32 + .../src/atomic_ops/sysdeps/gcc/s390.h | 92 + libatomic_ops/src/atomic_ops/sysdeps/gcc/sh.h | 34 + .../src/atomic_ops/sysdeps/gcc/sparc.h | 88 + .../src/atomic_ops/sysdeps/gcc/tile.h | 48 + .../src/atomic_ops/sysdeps/gcc/x86.h | 657 ++ .../src/atomic_ops/sysdeps/generic_pthread.h | 442 ++ .../src/atomic_ops/sysdeps/hpc/hppa.h | 104 + .../src/atomic_ops/sysdeps/hpc/ia64.h | 153 + .../src/atomic_ops/sysdeps/ibmc/powerpc.h | 183 + .../src/atomic_ops/sysdeps/icc/ia64.h | 207 + .../loadstore/acquire_release_volatile.h | 62 + .../acquire_release_volatile.template | 62 + .../sysdeps/loadstore/atomic_load.h | 37 + .../sysdeps/loadstore/atomic_load.template | 37 + .../sysdeps/loadstore/atomic_store.h | 35 + .../sysdeps/loadstore/atomic_store.template | 35 + .../loadstore/char_acquire_release_volatile.h | 62 + .../sysdeps/loadstore/char_atomic_load.h | 37 + .../sysdeps/loadstore/char_atomic_store.h | 35 + .../loadstore/double_atomic_load_store.h | 50 + .../loadstore/int_acquire_release_volatile.h | 62 + .../sysdeps/loadstore/int_atomic_load.h | 37 + .../sysdeps/loadstore/int_atomic_store.h | 35 + .../sysdeps/loadstore/ordered_loads_only.h | 135 + .../loadstore/ordered_loads_only.template | 27 + .../sysdeps/loadstore/ordered_stores_only.h | 135 + .../loadstore/ordered_stores_only.template | 27 + .../short_acquire_release_volatile.h | 62 + .../sysdeps/loadstore/short_atomic_load.h | 37 + .../sysdeps/loadstore/short_atomic_store.h | 35 + .../src/atomic_ops/sysdeps/msftc/arm.h | 112 + .../src/atomic_ops/sysdeps/msftc/arm64.h | 116 + .../atomic_ops/sysdeps/msftc/common32_defs.h | 1197 ++++ .../src/atomic_ops/sysdeps/msftc/x86.h | 156 + .../src/atomic_ops/sysdeps/msftc/x86_64.h | 151 + .../src/atomic_ops/sysdeps/ordered.h | 33 + .../atomic_ops/sysdeps/ordered_except_wr.h | 42 + .../src/atomic_ops/sysdeps/read_ordered.h | 37 + .../atomic_ops/sysdeps/standard_ao_double_t.h | 89 + .../src/atomic_ops/sysdeps/sunc/sparc.S | 5 + .../src/atomic_ops/sysdeps/sunc/sparc.h | 44 + .../src/atomic_ops/sysdeps/sunc/x86.h | 240 + .../sysdeps/test_and_set_t_is_ao_t.h | 36 + .../sysdeps/test_and_set_t_is_char.h | 51 + libatomic_ops/src/atomic_ops_malloc.c | 385 ++ libatomic_ops/src/atomic_ops_malloc.h | 81 + libatomic_ops/src/atomic_ops_stack.c | 380 ++ libatomic_ops/src/atomic_ops_stack.h | 210 + libatomic_ops/src/atomic_ops_sysdeps.S | 9 + libatomic_ops/tests/Makefile.am | 148 + libatomic_ops/tests/list_atomic.template | 132 + libatomic_ops/tests/run_parallel.h | 228 + libatomic_ops/tests/test_atomic.c | 267 + libatomic_ops/tests/test_atomic_include.h | 5364 +++++++++++++++++ .../tests/test_atomic_include.template | 596 ++ libatomic_ops/tests/test_malloc.c | 261 + libatomic_ops/tests/test_stack.c | 402 ++ 116 files changed, 32113 insertions(+) create mode 100644 libatomic_ops/.appveyor.yml create mode 100644 libatomic_ops/.gitattributes create mode 100644 libatomic_ops/.gitignore create mode 100644 libatomic_ops/.gitrepo create mode 100644 libatomic_ops/.travis.yml create mode 100644 libatomic_ops/AUTHORS create mode 100644 libatomic_ops/CMakeLists.txt create mode 100644 libatomic_ops/COPYING create mode 100644 libatomic_ops/ChangeLog create mode 100644 libatomic_ops/Config.cmake.in create mode 100644 libatomic_ops/LICENSE create mode 100644 libatomic_ops/Makefile.am create mode 100644 libatomic_ops/README.md create mode 100644 libatomic_ops/README_details.txt create mode 100644 libatomic_ops/README_malloc.txt create mode 100644 libatomic_ops/README_stack.txt create mode 100644 libatomic_ops/README_win32.txt create mode 100755 libatomic_ops/autogen.sh create mode 100644 libatomic_ops/configure.ac create mode 100644 libatomic_ops/m4/.gitignore create mode 100644 libatomic_ops/pkgconfig/atomic_ops-uninstalled.pc.in create mode 100644 libatomic_ops/pkgconfig/atomic_ops.pc.in create mode 100644 libatomic_ops/src/Makefile.am create mode 100644 libatomic_ops/src/Makefile.msft create mode 100644 libatomic_ops/src/atomic_ops.c create mode 100644 libatomic_ops/src/atomic_ops.h create mode 100644 libatomic_ops/src/atomic_ops/ao_version.h create mode 100644 libatomic_ops/src/atomic_ops/generalize-arithm.h create mode 100644 libatomic_ops/src/atomic_ops/generalize-arithm.template create mode 100644 libatomic_ops/src/atomic_ops/generalize-small.h create mode 100644 libatomic_ops/src/atomic_ops/generalize-small.template create mode 100644 libatomic_ops/src/atomic_ops/generalize.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/README create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/all_acquire_release_volatile.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/all_aligned_atomic_load_store.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/all_atomic_load_store.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/all_atomic_only_load.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/ao_t_is_int.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/ao_t_is_int.template create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/armcc/arm_v6.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/emul_cas.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/aarch64.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/alpha.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/arm.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/avr32.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/cris.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/e2k.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-arithm.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-arithm.template create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-small.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-small.template create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/generic.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/hexagon.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/hppa.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/ia64.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/m68k.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/mips.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/powerpc.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/riscv.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/s390.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/sh.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/sparc.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/tile.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/gcc/x86.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/generic_pthread.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/hpc/hppa.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/hpc/ia64.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/ibmc/powerpc.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/icc/ia64.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.template create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_load.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_load.template create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_store.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_store.template create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_atomic_load.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_atomic_store.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/double_atomic_load_store.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_atomic_load.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_atomic_store.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_loads_only.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_loads_only.template create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_stores_only.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_stores_only.template create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_atomic_load.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_atomic_store.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/msftc/arm.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/msftc/arm64.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/msftc/common32_defs.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/msftc/x86.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/msftc/x86_64.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/ordered.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/ordered_except_wr.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/read_ordered.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/standard_ao_double_t.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/sunc/sparc.S create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/sunc/sparc.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/sunc/x86.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/test_and_set_t_is_ao_t.h create mode 100644 libatomic_ops/src/atomic_ops/sysdeps/test_and_set_t_is_char.h create mode 100644 libatomic_ops/src/atomic_ops_malloc.c create mode 100644 libatomic_ops/src/atomic_ops_malloc.h create mode 100644 libatomic_ops/src/atomic_ops_stack.c create mode 100644 libatomic_ops/src/atomic_ops_stack.h create mode 100644 libatomic_ops/src/atomic_ops_sysdeps.S create mode 100644 libatomic_ops/tests/Makefile.am create mode 100644 libatomic_ops/tests/list_atomic.template create mode 100644 libatomic_ops/tests/run_parallel.h create mode 100644 libatomic_ops/tests/test_atomic.c create mode 100644 libatomic_ops/tests/test_atomic_include.h create mode 100644 libatomic_ops/tests/test_atomic_include.template create mode 100644 libatomic_ops/tests/test_malloc.c create mode 100644 libatomic_ops/tests/test_stack.c diff --git a/libatomic_ops/.appveyor.yml b/libatomic_ops/.appveyor.yml new file mode 100644 index 000000000..509a98156 --- /dev/null +++ b/libatomic_ops/.appveyor.yml @@ -0,0 +1,63 @@ +version: 7.8.x-{build} + +image: +- Visual Studio 2019 + +environment: + STUDIO_VERSION_EDITION: Studio\2019\Community + PLATFORM_TYPE: + CMAKE_CONFIG: Debug + TEST_TARGET: check + matrix: + - TARGET: cmake + CMAKE_OPTIONS: -DBUILD_SHARED_LIBS=ON -Dbuild_tests=ON -Werror=deprecated + - TARGET: cmake + CFLAGS_EXTRA: -DAO_CMPXCHG16B_AVAILABLE + CMAKE_OPTIONS: -Dbuild_tests=ON -Denable_assertions=ON -Dinstall_headers=OFF + - TARGET: cmake + CMAKE_CONFIG: Release + CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -Dbuild_tests=ON -Denable_gpl=OFF + - TARGET: cmake + CMAKE_OPTIONS: -A Win32 -Dbuild_tests=ON -Denable_assertions=ON + - TARGET: cmake + CMAKE_OPTIONS: -A ARM -Denable_assertions=ON -Denable_docs=OFF + - TARGET: cmake + CMAKE_CONFIG: Release + CMAKE_OPTIONS: -A ARM64 -DCMAKE_BUILD_TYPE=MinSizeRel -DBUILD_SHARED_LIBS=ON + - TARGET: nmake + ARCH: x86 + CFLAGS_EXTRA: /J + - TARGET: nmake + ARCH: x86 + CFLAGS_EXTRA: -DNDEBUG + WINSDK_VERSION: 8.1 + - TARGET: nmake + ARCH: x64 + CFLAGS_EXTRA: -DAO_CMPXCHG16B_AVAILABLE + WINSDK_VERSION: 8.1 + - TARGET: nmake + ARCH: x86_x64 + CFLAGS_EXTRA: -DNDEBUG + - TARGET: nmake + ARCH: x86_arm + TEST_TARGET: check-deps + - TARGET: nmake + ARCH: x64_arm64 + TEST_TARGET: check-deps + +clone_depth: 50 + +build_script: +- cmd: if [%TARGET%]==[cmake] ( + mkdir out && cd out + && cmake %CMAKE_OPTIONS% -Denable_werror=ON -DCFLAGS_EXTRA="%CFLAGS_EXTRA%" .. + && cmake --build . --config %CMAKE_CONFIG% --verbose ) +- cmd: if [%TARGET%]==[nmake] ( + "C:\Program Files (x86)\Microsoft Visual %STUDIO_VERSION_EDITION%\VC\Auxiliary\Build\vcvarsall.bat" %ARCH% %PLATFORM_TYPE% %WINSDK_VERSION% + && cd src && nmake -f Makefile.msft clean all CFLAGS_EXTRA="/WX %CFLAGS_EXTRA%" && cd .. ) + +test_script: +- cmd: if [%TARGET%]==[cmake] ( ctest --build-config %CMAKE_CONFIG% -j4 -V ) +- cmd: if [%TARGET%]==[nmake] ( + "C:\Program Files (x86)\Microsoft Visual %STUDIO_VERSION_EDITION%\VC\Auxiliary\Build\vcvarsall.bat" %ARCH% %PLATFORM_TYPE% %WINSDK_VERSION% + && cd src && nmake -f Makefile.msft %TEST_TARGET% CFLAGS_EXTRA="/WX %CFLAGS_EXTRA%" ) diff --git a/libatomic_ops/.gitattributes b/libatomic_ops/.gitattributes new file mode 100644 index 000000000..f8eb4c6e8 --- /dev/null +++ b/libatomic_ops/.gitattributes @@ -0,0 +1,7 @@ +# Git repo attributes. + +# Ensure all text files have normalized (LF) line endings in the repository. +* text=auto + +# Note: "core.eol" configuration variable controls which line endings to use +# for the normalized files in the working directory (the default is native). diff --git a/libatomic_ops/.gitignore b/libatomic_ops/.gitignore new file mode 100644 index 000000000..753409cd7 --- /dev/null +++ b/libatomic_ops/.gitignore @@ -0,0 +1,111 @@ +# Ignored files in libatomic_ops Git repo. + +Makefile + +/pkgconfig/atomic_ops.pc +/pkgconfig/atomic_ops-uninstalled.pc +/autom4te.cache/ +/config.cache +/config.log +/config.status +/libatomic_ops-* + +*.a +*.dll +*.exe +*.exp +*.gcda +*.gch +*.gcno +*.ilk +*.la +*.lib +*.lo +*.o +*.obj +*.pdb +*.so + +/build/ +/out/ + +/src/.deps/ +/src/.dirstamp +/src/.libs/ +/src/config.h +/src/config.h.in~ +/src/stamp-h1 + +/tests/.deps/ +/tests/.dirstamp +/tests/.libs/ +/tests/core +/tests/list_atomic.i +/tests/test_atomic +/tests/test_atomic_generalized +/tests/test_atomic_pthreads +/tests/test_malloc +/tests/test_stack + +# External library (without trailing slash to allow symlinks): +/pthreads-w32* + +# These files are generated by autoreconf: +/aclocal.m4 +/compile +/config.guess +/config.sub +/configure +/configure~ +/depcomp +/install-sh +/missing +/mkinstalldirs +/src/config.h.in +/test-driver +Makefile.in + +# Generated by libtoolize: +/libtool +/ltmain.sh +/m4/*.m4 + +# These files are generated by make check: +/tests/list_atomic.c +/tests/test*.log +/tests/test*.trs + +# These files are generated by CMake: +/*.cmake +/*.dll.embed.manifest +/*.dll.embed.manifest.res +/*.dll.intermediate.manifest +/*.dll.resource.txt +/*.exe.embed.manifest +/*.exe.embed.manifest.res +/*.exe.intermediate.manifest +/*.exe.resource.txt +/*.tlog +/*.vcxproj +/*.vcxproj.filters +/CMakeCache.txt +/CMakeFiles/ +/DartConfiguration.tcl +/Testing/Temporary/ +/atomic_ops.sln +/test_atomic +/test_atomic_generalized +/test_atomic_pthreads +/test_malloc +/test_stack + +# Code analysis tools: +*.c.gcov +*.h.gcov +*.sancov +/.sv*-dir +/cov-int +/coverage.info +/pvs-project.log +/pvs-project.tasks +/strace_out diff --git a/libatomic_ops/.gitrepo b/libatomic_ops/.gitrepo new file mode 100644 index 000000000..46be41659 --- /dev/null +++ b/libatomic_ops/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme +; +[subrepo] + remote = https://github.com/ivmai/libatomic_ops + branch = v7.8.2 + commit = 4c00f978cbafe4eb76929dace4ba8f456e800fec + parent = daf3d0972286f43d706be64b6929cbbf288f964a + method = merge + cmdver = 0.4.9 diff --git a/libatomic_ops/.travis.yml b/libatomic_ops/.travis.yml new file mode 100644 index 000000000..aa118bd74 --- /dev/null +++ b/libatomic_ops/.travis.yml @@ -0,0 +1,719 @@ +language: c +os: linux + +jobs: + include: + - compiler: clang + - compiler: gcc + - os: osx + - env: + - MAKEFILE_TARGET=dist + - addons: + apt: + packages: + - lcov + compiler: gcc + env: + - CONF_OPTIONS="--enable-gcov --enable-shared" + - CC_FOR_CHECK=gcc + - MAKEFILE_TARGET=all + - REPORT_COVERAGE=true + - CFLAGS_EXTRA="-march=native -D DEBUG_RUN_ONE_TEST -D VERBOSE_STACK" + - addons: + apt: + packages: + - cppcheck + dist: jammy + env: + - CPPCHECK_ENABLE="-j16 -q --enable=information,performance,portability,style,warning --suppress=missingIncludeSystem --suppress=unmatchedSuppression" + - MAKEFILE_TARGET=all + - addons: + apt: + packages: + - cppcheck + dist: jammy + env: + - CPPCHECK_ENABLE="-q --enable=unusedFunction -D AO_TEST_EMULATION" + - MAKEFILE_TARGET=all + - compiler: clang + dist: jammy + env: + - CSA_CHECK=true + - MAKEFILE_TARGET=all + - CFLAGS_EXTRA="-D AO_TRACE_MALLOC -D HAVE_MMAP -D VERBOSE_STACK" + - compiler: clang + env: + - CONF_OPTIONS="--disable-gpl" + - arch: arm64 + compiler: clang + - arch: arm64 + compiler: gcc + - arch: arm64 + compiler: clang + env: + - CFLAGS_EXTRA="-O3 -D N_EXPERIMENTS=10" + - CONF_OPTIONS="--enable-assertions" + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: arm64 + compiler: clang-12 + dist: focal + env: + - CFLAGS_EXTRA="-O3" + - CONF_OPTIONS="--enable-assertions" + - TESTS_CUSTOM_RUN=true + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: arm64 + compiler: clang-12 + dist: focal + env: + - CFLAGS_EXTRA="-O3 -D AO_AARCH64_ASM_LOAD_STORE_CAS -D AO_PREFER_BUILTIN_ATOMICS" + - TESTS_CUSTOM_RUN=true + - arch: arm64 + compiler: gcc + dist: jammy + env: + - CFLAGS_EXTRA="-O3 -march=armv8.2-a" + - CONF_OPTIONS="--enable-shared" + - TESTS_CUSTOM_RUN=true + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: arm64 + compiler: clang-12 + dist: focal + env: + - CFLAGS_EXTRA="-fsanitize=address -fno-omit-frame-pointer" + - TESTS_CUSTOM_RUN=true + - addons: + apt: + packages: + - musl-tools + arch: arm64 + compiler: musl-gcc + env: + - CFLAGS_EXTRA="-O3" + - TESTS_CUSTOM_RUN=true + - arch: arm64 + compiler: gcc + env: + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release" + - arch: ppc64le + compiler: clang + - arch: ppc64le + compiler: gcc + - arch: ppc64le + compiler: clang + env: + - CFLAGS_EXTRA="-O3" + - CONF_OPTIONS="--enable-assertions --disable-atomic-intrinsics" + - TESTS_CUSTOM_RUN=true + - arch: ppc64le + compiler: gcc + env: + - CFLAGS_EXTRA="-O3" + - CONF_OPTIONS="--disable-atomic-intrinsics" + - TESTS_CUSTOM_RUN=true + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: ppc64le + compiler: clang-12 + dist: focal + env: + - CFLAGS_EXTRA="-O3" + - CONF_OPTIONS="--enable-assertions --enable-shared" + - TESTS_CUSTOM_RUN=true + - arch: ppc64le + compiler: gcc + dist: jammy + env: + - CFLAGS_EXTRA="-O3 -D NO_TIMES" + - TESTS_CUSTOM_RUN=true + - arch: ppc64le + compiler: clang + env: + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -Denable_assertions=ON -Denable_docs=OFF" + - arch: ppc64le + compiler: gcc + env: + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=MinSizeRel -Denable_assertions=ON -Denable_atomic_intrinsics=OFF" + - arch: s390x + compiler: clang + - arch: s390x + compiler: gcc + - addons: + apt: + packages: + - clang-12 + sources: + - ubuntu-toolchain-r-test + arch: s390x + compiler: clang-12 + dist: focal + env: + - CFLAGS_EXTRA="-O3 -D AO_BL_SIZE=6" + - CONF_OPTIONS="--enable-assertions --enable-shared" + - TESTS_CUSTOM_RUN=true + - arch: s390x + compiler: gcc + dist: bionic + env: + - CFLAGS_EXTRA="-O3" + - TESTS_CUSTOM_RUN=true + - os: freebsd + env: + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release -Denable_gpl=OFF" + - CMAKE_BUILD_OPTIONS="--verbose" + - compiler: clang + dist: jammy + env: + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Debug -Denable_atomic_intrinsics=OFF" + - compiler: gcc + env: + - CMAKE_OPTIONS="-DBUILD_SHARED_LIBS=ON -Dinstall_headers=OFF" + - compiler: clang + env: + - CFLAGS_EXTRA="-O3 -march=native -funsigned-char" + - CONF_OPTIONS="--enable-assertions" + - compiler: gcc + env: + - CFLAGS_EXTRA="-O3 -march=native" + - CONF_OPTIONS="--enable-assertions" + - os: osx + env: + - CFLAGS_EXTRA="-O3 -march=native -D AO_USE_ALMOST_LOCK_FREE" + - CONF_OPTIONS="--enable-assertions" + - compiler: clang + env: + - CFLAGS_EXTRA="-march=native -std=c11" + - compiler: clang + dist: jammy + env: + - CFLAGS_EXTRA="-O3 -march=native -std=c11 -D AO_BL_SIZE=4 -D DEFAULT_NTHREADS=32" + - compiler: gcc + dist: jammy + env: + - CFLAGS_EXTRA="-march=native -D _FORTIFY_SOURCE=2 -std=c89" + - addons: + apt: + packages: + - gcc-multilib + compiler: clang + dist: jammy + env: + - CFLAGS_EXTRA="-m32" + - CONF_OPTIONS="--enable-assertions" + - addons: + apt: + packages: + - gcc-multilib + compiler: clang + env: + - CFLAGS_EXTRA="-m32 -O3 -march=native" + - addons: + apt: + packages: + - gcc-multilib + compiler: clang + env: + - CFLAGS_EXTRA="-m32 -march=native" + - CONF_OPTIONS="--disable-atomic-intrinsics" + - addons: + apt: + packages: + - gcc-multilib + compiler: gcc + dist: jammy + env: + - CFLAGS_EXTRA="-m32 -march=native -funsigned-char -D AO_USE_ALMOST_LOCK_FREE" + - CONF_OPTIONS="--enable-assertions" + - addons: + apt: + packages: + - gcc-multilib + compiler: gcc + env: + - CFLAGS_EXTRA="-m32 -march=native" + - CONF_OPTIONS="--disable-atomic-intrinsics" + - os: osx + env: + - CFLAGS_EXTRA="-m32 -march=native -D _FORTIFY_SOURCE=2" + - CONF_OPTIONS="--enable-assertions" + - compiler: gcc + env: + - CFLAGS_EXTRA="-D AO_GENERALIZE_ASM_BOOL_CAS -D AO_USE_NANOSLEEP -D AO_USE_NO_SIGNALS" + - CONF_OPTIONS="--enable-assertions" + - compiler: clang + env: + - CFLAGS_EXTRA="-D AO_STACK_PREFER_CAS_DOUBLE -D AO_USE_PTHREAD_DEFS" + - CONF_OPTIONS="--enable-assertions" + - compiler: clang + env: + - CFLAGS_EXTRA="-D DONT_USE_MMAP -O3" + - CONF_OPTIONS="--enable-assertions --enable-shared" + - addons: + apt: + packages: + - clang-14 + compiler: clang-14 + dist: jammy + env: + - CFLAGS_EXTRA="-O3 -march=native" + - compiler: gcc + dist: jammy + env: + - CFLAGS_EXTRA="-O3 -march=native" + - addons: + apt: + packages: + - gcc-9 + - gcc-9-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-9 + env: + - CFLAGS_EXTRA="-m32 -O3 -march=native" + - addons: + apt: + packages: + - gcc-9 + - gcc-9-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-9 + env: + - CFLAGS_EXTRA="-mx32 -march=native -D _FORTIFY_SOURCE=2" + - CONF_OPTIONS="--enable-assertions --enable-shared" + - addons: + apt: + packages: + - gcc-9 + - gcc-9-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-9 + env: + - CFLAGS_EXTRA="-mx32 -march=native" + - CONF_OPTIONS="--disable-atomic-intrinsics --disable-docs" + - addons: + apt: + packages: + - clang-14 + compiler: clang-14 + dist: jammy + env: + - CFLAGS_EXTRA="-fsanitize=address -D AO_USE_ALMOST_LOCK_FREE -fno-omit-frame-pointer" + - TESTS_CUSTOM_RUN=true + - addons: + apt: + packages: + - clang-14 + compiler: clang-14 + dist: jammy + env: + - CFLAGS_EXTRA="-fsanitize=address -march=native -fno-common -fno-omit-frame-pointer" + - CONF_OPTIONS="--enable-assertions" + - compiler: gcc + env: + - CFLAGS_EXTRA="-fsanitize=address -fno-omit-frame-pointer -D AO_USE_ALMOST_LOCK_FREE -D USE_STANDARD_MALLOC" + - CONF_OPTIONS="--enable-assertions" + - addons: + apt: + packages: + - gcc-9 + - gcc-9-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-9 + env: + - CFLAGS_EXTRA="-fsanitize=address -m32 -march=native -fno-omit-frame-pointer" + - LDFLAGS="-fuse-ld=gold" + - os: osx + env: + - CFLAGS_EXTRA="-fsanitize=address -m32 -fno-omit-frame-pointer" + - addons: + apt: + packages: + - clang-14 + compiler: clang-14 + dist: jammy + env: + - CFLAGS_EXTRA="-fsanitize=memory,undefined -march=native -fno-omit-frame-pointer" + - TESTS_CUSTOM_RUN=true + - compiler: clang + env: + - CFLAGS_EXTRA="-fsanitize=thread -D AO_USE_ALMOST_LOCK_FREE -fno-omit-frame-pointer" + - addons: + apt: + packages: + - clang-14 + compiler: clang-14 + dist: jammy + env: + - CFLAGS_EXTRA="-fsanitize=thread -march=native -fno-omit-frame-pointer" + - CONF_OPTIONS="--enable-assertions" + - compiler: clang + env: + - CONF_OPTIONS="--disable-atomic-intrinsics" + - CFLAGS_EXTRA="-march=native" + - compiler: clang + dist: jammy + env: + - CFLAGS_EXTRA="-x c++ -march=native -D VERBOSE_STACK" + - CONF_OPTIONS="--enable-assertions" + - MAKEFILE_TARGET=all + - compiler: gcc + dist: jammy + env: + - CC_FOR_CHECK=g++ + - MAKEFILE_TARGET=all + - addons: + apt: + packages: + - musl-tools + compiler: musl-gcc + dist: jammy + env: + - CFLAGS_EXTRA="-march=native" + - CONF_OPTIONS="--enable-assertions" + - addons: + apt: + packages: + - gcc-mingw-w64 + compiler: x86_64-w64-mingw32-gcc + dist: jammy + env: + - CONF_OPTIONS="--host=x86_64-w64-mingw32 --enable-shared --disable-static" + - MAKEFILE_TARGET=all + - addons: + apt: + packages: + - gcc-mingw-w64 + compiler: i686-w64-mingw32-gcc + dist: jammy + env: + - CONF_OPTIONS="--host=i686-w64-mingw32" + - MAKEFILE_TARGET=all + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=aarch64-linux + - CFLAGS_EXTRA="-mabi=ilp32" + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=alpha-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=arm-linux-gnueabi + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=arm-linux-gnueabi + - CFLAGS_EXTRA="-D AO_DISABLE_GCC_ATOMICS" + - addons: + apt: + packages: + - gcc-4.6 + - gcc-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-4.6 + env: + - CROSS_GCC_VER=4.2.4 + - NOLIBC_ARCH_ABI=avr32-linux + - NOLIBC_DELIM=_ + - CFLAGS_EXTRA="-fno-strict-aliasing" + - addons: + apt: + packages: + - gcc-4.6 + - gcc-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-4.6 + env: + - CROSS_GCC_VER=4.6.3 + - NOLIBC_ARCH_ABI=cris-linux + - NOLIBC_DELIM=_ + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=hppa-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=ia64-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=m68k-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=mips-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=mips-linux + - CFLAGS_EXTRA="-D AO_DISABLE_GCC_ATOMICS" + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=mips64-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=nios2-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=powerpc-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=powerpc-linux + - CFLAGS_EXTRA="-D AO_DISABLE_GCC_ATOMICS" + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=powerpc64-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=riscv32-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=riscv64-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=s390-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=sh2-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=sh4-linux + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=sparc-linux + - CFLAGS_EXTRA="-D AO_NO_SPARC_V9" + - addons: + apt: + packages: + - gcc-multilib + env: + - CROSS_GCC_VER=8.1.0 + - NOLIBC_ARCH_ABI=sparc64-linux + - addons: + apt: + packages: + - gcc-4.6 + - gcc-multilib + sources: + - ubuntu-toolchain-r-test + compiler: gcc-4.6 + env: + - CROSS_GCC_VER=4.6.2 + - NOLIBC_ARCH_ABI=tilegx-linux + - NOLIBC_DELIM=_ + - dist: focal + env: + - MAKEFILE_TARGET=distcheck + - AUTOCONF_VER=2.71 + - AUTOMAKE_VER=1.16.5 + - LIBTOOL_VER=2.4.7 + - M4_VER=1.4.19 + +before_install: +- if [[ "$CROSS_GCC_VER" != "" ]]; then + if [[ "$NOLIBC_DELIM" == "" ]]; then NOLIBC_DELIM=-; fi; + BUILD_ARCH=x86_64; + KERNEL_ORG_PUB_SITE=https://www.kernel.org/pub; + TAR_FOLDER_URL=$KERNEL_ORG_PUB_SITE/tools/crosstool/files/bin/$BUILD_ARCH/$CROSS_GCC_VER; + TARFILE=$BUILD_ARCH-gcc-$CROSS_GCC_VER-nolibc$NOLIBC_DELIM$NOLIBC_ARCH_ABI.tar.xz; + wget --no-check-certificate -O - $TAR_FOLDER_URL/$TARFILE | tar xf - --xz --directory ~; + CROSS_CC=~/gcc-$CROSS_GCC_VER-nolibc/$NOLIBC_ARCH_ABI/bin/$NOLIBC_ARCH_ABI-gcc; + export C_INCLUDE_PATH=/usr/include; + MAKEFILE_TARGET=check-nolink; + fi +- if [[ "$AUTOCONF_VER" != "" || "$AUTOMAKE_VER" != "" || "$LIBTOOL_VER" != "" || "$M4_VER" != "" ]]; then + GNUTOOLS_ROOT=`pwd`/../gnu-tools; + export PATH=$GNUTOOLS_ROOT/bin:$PATH; + GNU_DOWNLOAD_SITE=https://ftp.gnu.org/gnu; + fi +- if [[ "$M4_VER" != "" ]]; then + M4_XZ_URL=$GNU_DOWNLOAD_SITE/m4/m4-$M4_VER.tar.xz; + wget -O - $M4_XZ_URL | tar xf - --xz --directory ~; + (cd ~/m4-$M4_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$LIBTOOL_VER" != "" ]]; then + LIBTOOL_XZ_URL=$GNU_DOWNLOAD_SITE/libtool/libtool-$LIBTOOL_VER.tar.xz; + wget -O - $LIBTOOL_XZ_URL | tar xf - --xz --directory ~; + (cd ~/libtool-$LIBTOOL_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$AUTOCONF_VER" != "" ]]; then + AUTOCONF_XZ_URL=$GNU_DOWNLOAD_SITE/autoconf/autoconf-$AUTOCONF_VER.tar.xz; + wget -O - $AUTOCONF_XZ_URL | tar xf - --xz --directory ~; + (cd ~/autoconf-$AUTOCONF_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$AUTOMAKE_VER" != "" ]]; then + AUTOMAKE_XZ_URL=$GNU_DOWNLOAD_SITE/automake/automake-$AUTOMAKE_VER.tar.xz; + wget -O - $AUTOMAKE_XZ_URL | tar xf - --xz --directory ~; + (cd ~/automake-$AUTOMAKE_VER && ./configure --prefix=$GNUTOOLS_ROOT && make -j && make install); + fi +- if [[ "$MAKEFILE_TARGET" == "dist"* ]]; then + autoconf --version; + automake --version; + m4 --version; + libtool --version || true; + fi +- if [[ "$CMAKE_OPTIONS" != "" ]]; then + cmake --version; + fi +- if [[ "$MAKEFILE_TARGET" == "" ]]; then MAKEFILE_TARGET=check; fi + +install: +- if [[ "$CMAKE_OPTIONS" == "" ]]; then + ./autogen.sh; + fi +- if [[ "$REPORT_COVERAGE" == true ]]; then gem install coveralls-lcov; fi + +script: +- if [[ "$CMAKE_OPTIONS" == "" && "$COVERITY_SCAN_BRANCH" != 1 ]]; then + ./configure $CONF_OPTIONS --enable-werror; + fi +- if [[ "$CSA_CHECK" != true && "$CMAKE_OPTIONS" == "" + && "$CPPCHECK_ENABLE" == "" && "$COVERITY_SCAN_BRANCH" != 1 ]]; then + cat src/config.h; + fi +- if [[ "$CROSS_GCC_VER" != "" ]]; then CC=$CROSS_CC; fi +- if [[ "$CMAKE_OPTIONS" == "" && "$COVERITY_SCAN_BRANCH" != 1 ]]; then + make -j $MAKEFILE_TARGET CC=$CC CFLAGS_EXTRA="$CFLAGS_EXTRA" + LDFLAGS="$LDFLAGS"; + fi +- if [[ "$CMAKE_OPTIONS" != "" ]]; then + cmake $CMAKE_OPTIONS -Dbuild_tests=ON -Denable_werror=ON -Werror=dev . && + cmake --build . $CMAKE_BUILD_OPTIONS; + fi +- if [[ "$CMAKE_OPTIONS" != "" ]]; then + ctest -j4 -V; + fi +- if [[ "$CC_FOR_CHECK" != "" ]]; then + make check CC=$CC_FOR_CHECK CFLAGS_EXTRA="$CFLAGS_EXTRA"; + fi +- if [ -f tests/test_atomic.log ]; then cat tests/test_atomic*.log; fi +- if [ -f tests/test_stack.log ]; then cat tests/test_stack.log; fi +- if [[ "$CSA_CHECK" == true ]]; then + ${CC} --analyze -Xanalyzer -analyzer-output=text -Werror -I src + $CFLAGS_EXTRA tests/*.c src/*.c; + fi +- if [[ "$CPPCHECK_ENABLE" != "" ]]; then + cppcheck -f --error-exitcode=2 -U AO_API -D CPPCHECK -I src + $CPPCHECK_ENABLE src/*.c tests/test_malloc.c tests/test_stack.c; + fi +- if [[ "$TESTS_CUSTOM_RUN" == true ]]; then + ASAN_OPTIONS="detect_leaks=1" UBSAN_OPTIONS="halt_on_error=1" + make -C tests check-without-test-driver; + fi + +after_success: +- if [[ "$REPORT_COVERAGE" == true ]]; then + lcov --capture --directory src --directory tests --output-file coverage.info; + lcov --remove coverage.info '/usr/*' 'tests/*' --output-file coverage.info; + lcov --list coverage.info; + coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info; + bash <(curl -s https://codecov.io/bash); + fi + +deploy: + provider: releases + edge: true + file: libatomic_ops-*.tar.gz + file_glob: true + on: + condition: $MAKEFILE_TARGET = distcheck + repo: ivmai/libatomic_ops + tags: true diff --git a/libatomic_ops/AUTHORS b/libatomic_ops/AUTHORS new file mode 100644 index 000000000..0077812ec --- /dev/null +++ b/libatomic_ops/AUTHORS @@ -0,0 +1,60 @@ +Originally written by Hans Boehm, with some platform-dependent code +imported from the Boehm-Demers-Weiser GC, where it was contributed +by many others. +Currently maintained by Ivan Maidanski. + +Alexey Pavlov +Andreas Tobler +Andrew Agno +Andy Li +Bradley Smith +Bruce Mitchener +Carlos O'Donell +Chris Metcalf +Daniel Grayson +David Mosberger +Doug Lea +Earl Chew +Emmanuel Stapf +Fabrizio Fabbri +Frank Schaefer +Frederic Recoules +George Koehler +Gilles Talis +Gregory Farnum +H.J. Lu +Hans Boehm +Hans-Peter Nilsson +Ian Wienand +Ivan Maidanski +James Cowgill +Jean Girardet +Jeremy Huddleston +Jim Marshall +Joerg Wagner +Linas Vepstas +Luca Barbato +Kochin Chang +Maged Michael +Manuel Serrano +Marek Vasut +Max Horn +Michael Hope +Mikael Urankar +Patrick Marlier +Pavel Raiskup +Petter Urkedal +Philipp Zambelli +Ranko Zivojnovic +Roger Hoover +Sebastian Siewior +Shea Levy +Steve Capper +Takashi Yoshii +Tautvydas Zilys +Thiemo Seufer +Thorsten Glaser +Tobias Leich +Tony Mantler +YunQiang Su +Yvan Roux diff --git a/libatomic_ops/CMakeLists.txt b/libatomic_ops/CMakeLists.txt new file mode 100644 index 000000000..498d28a28 --- /dev/null +++ b/libatomic_ops/CMakeLists.txt @@ -0,0 +1,380 @@ +# +# Copyright (c) 2021-2022 Ivan Maidanski +## +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +## + +cmake_minimum_required(VERSION 3.5) + +set(PACKAGE_VERSION 7.8.2) +# Version must match that in AC_INIT of configure.ac and that in README. +# Version must conform to: [0-9]+[.][0-9]+[.][0-9]+ + +# Info (current:revision:age) for the Libtool versioning system. +# These values should match those in src/Makefile.am. +set(LIBATOMIC_OPS_VER_INFO 3:0:2) +set(LIBATOMIC_OPS_GPL_VER_INFO 3:1:2) + +project(libatomic_ops C) + +if (POLICY CMP0057) + # Required for CheckLinkerFlag, at least. + cmake_policy(SET CMP0057 NEW) +endif() + +include(CheckCCompilerFlag) +include(CheckFunctionExists) +include(CMakePackageConfigHelpers) +include(CTest) +include(GNUInstallDirs) + +if (NOT (${CMAKE_VERSION} VERSION_LESS "3.18.0")) + include(CheckLinkerFlag) +endif() + +# Customize the build by passing "-D=ON|OFF" in the command line. +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) +option(build_tests "Build tests" OFF) +option(enable_assertions "Enable assertion checking" OFF) +option(enable_werror "Treat warnings as errors" OFF) +option(enable_atomic_intrinsics "Use GCC atomic intrinsics" ON) +option(enable_docs "Build and install documentation" ON) +option(enable_gpl "Build atomic_ops_gpl library" ON) +option(install_headers "Install header and pkg-config metadata files" ON) + +# Override the default build type to RelWithDebInfo (this instructs cmake to +# pass -O2 -g -DNDEBUG options to the compiler). +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE + STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY + STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel") +endif() + +# Convert VER_INFO values to [SO]VERSION ones. +if (BUILD_SHARED_LIBS) + # atomic_ops: + string(REGEX REPLACE "(.+):.+:.+" "\\1" ao_cur ${LIBATOMIC_OPS_VER_INFO}) + string(REGEX REPLACE ".+:(.+):.+" "\\1" ao_rev ${LIBATOMIC_OPS_VER_INFO}) + string(REGEX REPLACE ".+:.+:(.+)$" "\\1" ao_age ${LIBATOMIC_OPS_VER_INFO}) + math(EXPR AO_SOVERSION "${ao_cur} - ${ao_age}") + set(AO_VERSION_PROP "${AO_SOVERSION}.${ao_age}.${ao_rev}") + message(STATUS "AO_VERSION_PROP = ${AO_VERSION_PROP}") + # atomic_ops_gpl: + string(REGEX REPLACE "(.+):.+:.+" "\\1" ao_gpl_cur + ${LIBATOMIC_OPS_GPL_VER_INFO}) + string(REGEX REPLACE ".+:(.+):.+" "\\1" ao_gpl_rev + ${LIBATOMIC_OPS_GPL_VER_INFO}) + string(REGEX REPLACE ".+:.+:(.+)$" "\\1" ao_gpl_age + ${LIBATOMIC_OPS_GPL_VER_INFO}) + math(EXPR AO_GPL_SOVERSION "${ao_gpl_cur} - ${ao_gpl_age}") + set(AO_GPL_VERSION_PROP "${AO_GPL_SOVERSION}.${ao_gpl_age}.${ao_gpl_rev}") + message(STATUS "AO_GPL_VERSION_PROP = ${AO_GPL_VERSION_PROP}") +endif(BUILD_SHARED_LIBS) + +# Output all warnings. +if (MSVC) + # All warnings but ignoring "conditional expression is constant" ones. + add_compile_options(/W4 /wd4127) +else() + # TODO: add -[W]pedantic -Wno-long-long + add_compile_options(-Wall -Wextra) +endif() + +find_package(Threads REQUIRED) +message(STATUS "Thread library: ${CMAKE_THREAD_LIBS_INIT}") +include_directories(${Threads_INCLUDE_DIR}) +set(THREADDLLIBS_LIST ${CMAKE_THREAD_LIBS_INIT}) + +if (CMAKE_USE_PTHREADS_INIT) + # Required define if using POSIX threads. + add_compile_options(-D_REENTRANT) +else() + # No pthreads library available. + add_compile_options(-DAO_NO_PTHREADS) +endif() + +if (enable_assertions) + # In case NDEBUG macro is defined e.g. by cmake -DCMAKE_BUILD_TYPE=Release. + add_compile_options(-UNDEBUG) +else() + # Define to disable assertion checking. + add_compile_options(-DNDEBUG) +endif() + +if (NOT enable_atomic_intrinsics) + # Define to avoid GCC atomic intrinsics even if available. + add_compile_options(-DAO_DISABLE_GCC_ATOMICS) +endif() + +# AO API symbols export control. +if (BUILD_SHARED_LIBS) + add_compile_options(-DAO_DLL) +endif() + +if (enable_werror) + if (MSVC) + add_compile_options(/WX) + else() + add_compile_options(-Werror) + endif() +endif(enable_werror) + +# Extra user-defined flags to pass to the C compiler. +if (DEFINED CFLAGS_EXTRA) + separate_arguments(CFLAGS_EXTRA_LIST UNIX_COMMAND "${CFLAGS_EXTRA}") + add_compile_options(${CFLAGS_EXTRA_LIST}) +endif() + +set(SRC src/atomic_ops.c) + +if (CMAKE_C_COMPILER_ID STREQUAL "SunPro") + # SunCC compiler on SunOS (Solaris). + set(SRC ${SRC} src/atomic_ops_sysdeps.S) +endif() + +add_library(atomic_ops ${SRC}) +target_link_libraries(atomic_ops PRIVATE ${THREADDLLIBS_LIST}) +target_include_directories(atomic_ops + PUBLIC "$" + INTERFACE "$") + +if (enable_gpl) + set(AO_GPL_SRC src/atomic_ops_malloc.c src/atomic_ops_stack.c) + add_library(atomic_ops_gpl ${AO_GPL_SRC}) + check_function_exists(mmap HAVE_MMAP) + if (HAVE_MMAP) + target_compile_definitions(atomic_ops_gpl PRIVATE HAVE_MMAP) + endif() + target_link_libraries(atomic_ops_gpl PRIVATE atomic_ops) + target_include_directories(atomic_ops_gpl + PUBLIC "$" + INTERFACE "$") + if (BUILD_SHARED_LIBS) + set_property(TARGET atomic_ops_gpl PROPERTY VERSION ${AO_GPL_VERSION_PROP}) + set_property(TARGET atomic_ops_gpl PROPERTY SOVERSION ${AO_GPL_SOVERSION}) + endif() + install(TARGETS atomic_ops_gpl EXPORT Atomic_opsTargets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") +endif(enable_gpl) + +if (BUILD_SHARED_LIBS) + if (NOT (${CMAKE_SYSTEM_NAME} MATCHES "BSD")) + if (${CMAKE_VERSION} VERSION_LESS "3.18.0") + set(WL_NO_UNDEFINED_OPT "-Wl,--no-undefined") + check_c_compiler_flag(${WL_NO_UNDEFINED_OPT} HAVE_FLAG_WL_NO_UNDEFINED) + else() + set(WL_NO_UNDEFINED_OPT "LINKER:--no-undefined") + check_linker_flag(C "${WL_NO_UNDEFINED_OPT}" HAVE_FLAG_WL_NO_UNDEFINED) + endif() + if (HAVE_FLAG_WL_NO_UNDEFINED) + # Declare that the libraries do not refer to external symbols. + if (${CMAKE_VERSION} VERSION_LESS "3.13.0") + target_link_libraries(atomic_ops PRIVATE ${WL_NO_UNDEFINED_OPT}) + if (enable_gpl) + target_link_libraries(atomic_ops_gpl PRIVATE ${WL_NO_UNDEFINED_OPT}) + endif(enable_gpl) + else() + target_link_options(atomic_ops PRIVATE ${WL_NO_UNDEFINED_OPT}) + if (enable_gpl) + target_link_options(atomic_ops_gpl PRIVATE ${WL_NO_UNDEFINED_OPT}) + endif(enable_gpl) + endif() + endif(HAVE_FLAG_WL_NO_UNDEFINED) + endif() + set_property(TARGET atomic_ops PROPERTY VERSION ${AO_VERSION_PROP}) + set_property(TARGET atomic_ops PROPERTY SOVERSION ${AO_SOVERSION}) +endif(BUILD_SHARED_LIBS) + +install(TARGETS atomic_ops EXPORT Atomic_opsTargets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + +if (install_headers) + install(FILES src/atomic_ops.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + if (enable_gpl) + install(FILES src/atomic_ops_malloc.h + src/atomic_ops_stack.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + endif() + + install(FILES src/atomic_ops/ao_version.h + src/atomic_ops/generalize-arithm.h + src/atomic_ops/generalize-small.h + src/atomic_ops/generalize.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops") + + install(FILES src/atomic_ops/sysdeps/all_acquire_release_volatile.h + src/atomic_ops/sysdeps/all_aligned_atomic_load_store.h + src/atomic_ops/sysdeps/all_atomic_load_store.h + src/atomic_ops/sysdeps/all_atomic_only_load.h + src/atomic_ops/sysdeps/ao_t_is_int.h + src/atomic_ops/sysdeps/emul_cas.h + src/atomic_ops/sysdeps/generic_pthread.h + src/atomic_ops/sysdeps/ordered.h + src/atomic_ops/sysdeps/ordered_except_wr.h + src/atomic_ops/sysdeps/read_ordered.h + src/atomic_ops/sysdeps/standard_ao_double_t.h + src/atomic_ops/sysdeps/test_and_set_t_is_ao_t.h + src/atomic_ops/sysdeps/test_and_set_t_is_char.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps") + + install(FILES src/atomic_ops/sysdeps/armcc/arm_v6.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps/armcc") + install(FILES src/atomic_ops/sysdeps/gcc/aarch64.h + src/atomic_ops/sysdeps/gcc/alpha.h + src/atomic_ops/sysdeps/gcc/arm.h + src/atomic_ops/sysdeps/gcc/avr32.h + src/atomic_ops/sysdeps/gcc/cris.h + src/atomic_ops/sysdeps/gcc/e2k.h + src/atomic_ops/sysdeps/gcc/generic-arithm.h + src/atomic_ops/sysdeps/gcc/generic-small.h + src/atomic_ops/sysdeps/gcc/generic.h + src/atomic_ops/sysdeps/gcc/hexagon.h + src/atomic_ops/sysdeps/gcc/hppa.h + src/atomic_ops/sysdeps/gcc/ia64.h + src/atomic_ops/sysdeps/gcc/m68k.h + src/atomic_ops/sysdeps/gcc/mips.h + src/atomic_ops/sysdeps/gcc/powerpc.h + src/atomic_ops/sysdeps/gcc/riscv.h + src/atomic_ops/sysdeps/gcc/s390.h + src/atomic_ops/sysdeps/gcc/sh.h + src/atomic_ops/sysdeps/gcc/sparc.h + src/atomic_ops/sysdeps/gcc/tile.h + src/atomic_ops/sysdeps/gcc/x86.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps/gcc") + + install(FILES src/atomic_ops/sysdeps/hpc/hppa.h + src/atomic_ops/sysdeps/hpc/ia64.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps/hpc") + install(FILES src/atomic_ops/sysdeps/ibmc/powerpc.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps/ibmc") + install(FILES src/atomic_ops/sysdeps/icc/ia64.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps/icc") + + install(FILES src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.h + src/atomic_ops/sysdeps/loadstore/atomic_load.h + src/atomic_ops/sysdeps/loadstore/atomic_store.h + src/atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h + src/atomic_ops/sysdeps/loadstore/char_atomic_load.h + src/atomic_ops/sysdeps/loadstore/char_atomic_store.h + src/atomic_ops/sysdeps/loadstore/double_atomic_load_store.h + src/atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h + src/atomic_ops/sysdeps/loadstore/int_atomic_load.h + src/atomic_ops/sysdeps/loadstore/int_atomic_store.h + src/atomic_ops/sysdeps/loadstore/ordered_loads_only.h + src/atomic_ops/sysdeps/loadstore/ordered_stores_only.h + src/atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h + src/atomic_ops/sysdeps/loadstore/short_atomic_load.h + src/atomic_ops/sysdeps/loadstore/short_atomic_store.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps/loadstore") + + install(FILES src/atomic_ops/sysdeps/msftc/arm.h + src/atomic_ops/sysdeps/msftc/arm64.h + src/atomic_ops/sysdeps/msftc/common32_defs.h + src/atomic_ops/sysdeps/msftc/x86.h + src/atomic_ops/sysdeps/msftc/x86_64.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps/msftc") + install(FILES src/atomic_ops/sysdeps/sunc/sparc.h + src/atomic_ops/sysdeps/sunc/x86.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/atomic_ops/sysdeps/sunc") + + # Provide pkg-config metadata. + set(prefix "${CMAKE_INSTALL_PREFIX}") + set(exec_prefix \${prefix}) + set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}") + set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}") + string(REPLACE ";" " " THREADDLLIBS "${THREADDLLIBS_LIST}") + # PACKAGE_VERSION is defined above. + configure_file(pkgconfig/atomic_ops.pc.in pkgconfig/atomic_ops.pc @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/atomic_ops.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + # TODO: handle atomic_ops-uninstalled.pc.in +endif(install_headers) + +if (build_tests) + add_executable(test_atomic tests/test_atomic.c) + target_link_libraries(test_atomic PRIVATE atomic_ops ${THREADDLLIBS_LIST}) + add_test(NAME test_atomic COMMAND test_atomic) + + add_executable(test_atomic_generalized tests/test_atomic.c) + target_compile_definitions(test_atomic_generalized + PRIVATE AO_PREFER_GENERALIZED AO_TEST_EMULATION) + target_link_libraries(test_atomic_generalized + PRIVATE atomic_ops ${THREADDLLIBS_LIST}) + add_test(NAME test_atomic_generalized COMMAND test_atomic_generalized) + + if (CMAKE_USE_PTHREADS_INIT) + add_executable(test_atomic_pthreads tests/test_atomic.c) + target_compile_definitions(test_atomic_pthreads + PRIVATE AO_USE_PTHREAD_DEFS) + target_link_libraries(test_atomic_pthreads + PRIVATE atomic_ops ${THREADDLLIBS_LIST}) + add_test(NAME test_atomic_pthreads COMMAND test_atomic_pthreads) + endif() + + if (enable_gpl) + add_executable(test_stack tests/test_stack.c) + target_link_libraries(test_stack + PRIVATE atomic_ops atomic_ops_gpl ${THREADDLLIBS_LIST}) + add_test(NAME test_stack COMMAND test_stack) + + add_executable(test_malloc tests/test_malloc.c) + target_link_libraries(test_malloc + PRIVATE atomic_ops atomic_ops_gpl ${THREADDLLIBS_LIST}) + add_test(NAME test_malloc COMMAND test_malloc) + endif() +endif(build_tests) + +if (enable_docs) + install(FILES AUTHORS ChangeLog LICENSE README.md + README_details.txt README_win32.txt + DESTINATION "${CMAKE_INSTALL_DOCDIR}") + if (enable_gpl) + install(FILES COPYING README_malloc.txt README_stack.txt + DESTINATION "${CMAKE_INSTALL_DOCDIR}") + endif() +endif(enable_docs) + +# CMake config/targets files. +install(EXPORT Atomic_opsTargets FILE Atomic_opsTargets.cmake + NAMESPACE Atomic_ops:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/atomic_ops") + +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/Atomic_opsConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/atomic_ops" + NO_SET_AND_CHECK_MACRO) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/Atomic_opsConfigVersion.cmake" + VERSION "${PACKAGE_VERSION}" COMPATIBILITY AnyNewerVersion) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Atomic_opsConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/Atomic_opsConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/atomic_ops") + +export(EXPORT Atomic_opsTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/Atomic_opsTargets.cmake") diff --git a/libatomic_ops/COPYING b/libatomic_ops/COPYING new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/libatomic_ops/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/libatomic_ops/ChangeLog b/libatomic_ops/ChangeLog new file mode 100644 index 000000000..6c03d7141 --- /dev/null +++ b/libatomic_ops/ChangeLog @@ -0,0 +1,765 @@ + +== [7.8.2] 2023-12-15 == + +* Eliminate 'atomic_thread_fence is unsupported with tsan' gcc-11 warning +* Eliminate 'comparing signed/unsigned values' lcc warning in add_chunk_as +* Fix 'undefined reference to AO_pt_lock' if configure is using clang-16 +* Fix 'undefined reference to __atomic_load/store/cas_16' Mingw64-gcc error +* Fix 'undefined reference' linker errors if shared build on OpenBSD (CMake) +* Fix get_chunk for case of mmap area is located before AO_initial_heap +* Fix typo in AO_HAVE_compare_and_swap_double name in atomic_ops_stack.h +* Fix typo in comment of run_one_test of test_stack +* Fix typos in comments of atomic_ops_malloc.c and atomic_ops_stack.c/h +* Update cmake minimum required version to 3.5 + + +== [7.8.0] 2023-03-26 == + +* Add AUTHORS file to the list of installed documentation +* Add goal to Makefile.msft to build all tests but not execute them +* Allocate marks[] dynamically and report all found errors in test_stack +* Always export stack_init/push_release/pop_acquire from atomic_ops_gpl +* Always use 'mfence' for nop_full if target CPU supports SSE2 (MS VC/x86) +* Avoid 'cast increases required alignment' warnings in atomic_ops_malloc.c +* Avoid breaking strict-aliasing rules in test_malloc and test_stack +* Avoid code duplication in AO_stack_push_explicit_aux_release +* Better document test_stack internals +* Build atomic_ops.lib by Makefile.msft (MS VC) +* Build test object and executable files in tests folder (MS VC) +* Define AO_stack_t uniformly +* Define double_compare_and_swap_full if MS VS 2017+ (x86) +* Do not expose AO_REAL_HEAD/NEXT_PTR implementation in header by default +* Document config macros in README_win32 and remove them from configure +* Eliminate 'function is never used' cppcheck warning for AO_stack_init +* Enforce most strict level of compiler warnings (MS VC) +* Ensure atomic_ops.c global symbols are always declared as extern 'C' +* Explicitly outline symbols exported in AO shared libraries with AO_API +* Hide AO_free_list symbol +* Implement AO_stack_init using memset +* Implement AO_test_and_set using InterlockedExchange8 (MS VC) +* Implement and/or/xor for AO_t, short and int types (MS VC) +* Implement nf/acq/rel variants of primitives on Windows RT (MS VC) +* Mention MIT near core library licensing terms in LICENSE file +* Move all README_*.txt and license files to the top folder +* Move all non-double intrinsic-based primitives to msftc/common32_defs.h +* Move gcc-4/alpha workaround outside AO_stack_pop_explicit_aux_acquire +* New AO_stack_is_lock_free API function +* New configure option (--disable-gpl) to skip building of libatomic_ops_gpl +* Print message of almost-lock-free implementation in test_stack if used +* Refine LICENSE and README about code parts covered by MIT and GPL-2.0 +* Refine copyright terms in GPL source files +* Reformat atomic_ops_stack.c/h files +* Remove 'lib' prefix for atomic_ops_gpl.lib in Makefile.msft +* Remove redundant assert in AO_stack_pop_explicit_aux_acquire +* Remove redundant cast to AO_t in lock-free AO_stack_pop_acquire +* Rename LICENSING.txt to LICENSE file +* Rename VERBOSE macro to VERBOSE_STACK in test_stack (refactoring) +* Rename fetch_and_add to fetch_then_add in test_stack (refactoring) +* Replace obsolete AC_HELP_STRING with AS_HELP_STRING in configure +* Replace obsolete AC_TRY_COMPILE with AC_COMPILE_IFELSE in configure +* Split test_stack main into several functions (refactoring) +* Support Elbrus 2000 (gcc/e2k) +* Support build with CMake +* Support double-wide CAS on armv7+ and UWP/arm64 (MS VC) +* Support test_atomic with MS build w/o the need to run GNU make first +* Update autotools for release tarball preparation (ac-2.71, m4-1.4.19) +* Use GCC atomic intrinsics for SPARC +* Use builtin_expect in AO_stack_push_explicit_aux_release + + +== [7.6.16] 2023-12-15 == + +* Eliminate 'atomic_thread_fence is unsupported with tsan' gcc-11 warning +* Eliminate 'comparing signed/unsigned values' lcc warning in add_chunk_as +* Fix 'undefined reference to AO_pt_lock' if configure is using clang-16 +* Fix 'undefined reference to __atomic_load/store/cas_16' Mingw64-gcc error +* Fix get_chunk for case of mmap area is located before AO_initial_heap +* Fix typo in AO_HAVE_compare_and_swap_double name in atomic_ops_stack.h +* Fix typos in comments of atomic_ops_malloc.c and atomic_ops_stack.c/h + + +== [7.6.14] 2022-08-25 == + +* Add note to README that AO malloc code has same license as AO stack +* Adjust/reformat content of LICENSING.txt +* Avoid AO_stack_t to cross CPU cache line boundary +* Do not assume 'ordered except earlier write' for UWP/arm64 +* Do not name GCC intrinsics as C11 ones in ChangeLog and configure +* Eliminate '-pedantic is not option that controls warnings' GCC-6.3 message +* Ensure result of AO_test_and_set is always AO_TS_CLEAR or AO_TS_SET +* Fix 'AO_malloc redefinition' MS VC warning caused by attributes mismatch +* Fix 'use of undeclared SIG_BLOCK' Clang error if -std=c89 on Cygwin +* Fix AO_compare_and_swap_full asm code for clang on sparc +* Fix a typo in comment of AO_stack_push_explicit_aux_release +* Fix code indentation in main() of test_stack.c +* Refine AO_UNIPROCESSOR macro description in configure +* Remove outdated comment about unsupported Win64 in atomic_ops_stack.h +* Repeat black list check on CAS fail in stack_push_explicit_aux_release + + +== [7.6.12] 2021-09-13 == + +* Allow to generalize bool-CAS for sparc (gcc) +* Declare argument of AO_load_next with const in atomic_ops_stack +* Describe double_compare_and_swap operation in README_details +* Document CAS operations better in README_details +* Fix gcc/sunc x86 AO_compare_double_and_swap_double missing side effect +* Fix library name in README_details +* Fix link fail caused by missing GCC char/short atomic primitives on riscv64 +* Fix size of local variable passed to cas[x] (gcc/sparc) +* Implement fetch-CAS for sparc (gcc) +* Refactor gcc x86 memory constraints +* Refine and reformat description of size prefix in README_details +* Remove outdated notes in README_details +* Replace x86 setz instruction by asm flag output operand (gcc) +* Support MSYS host (configure) +* Turn off compare_double_and_swap_double_full PIC hack for GCC 5+ (x86) +* Update README_win32 to match Makefile.msft +* Use GCC atomic intrinsics for s390x (clang 8.0+ and gcc 5.4+) +* Use __alignof__ instead of sizeof in atomic variable alignment assertions +* Workaround assertion violation in AO_load/store on m68k + + +== [7.6.10] 2019-03-01 == + +* Eliminate 'my_chunk_ptr-AO_initial_heap out of bounds' cppcheck warning +* Fix 'AO_*_TS_T is not defined' compiler warnings (GCC-8) +* Fix 'duplicate symbol' error for test_malloc/stack with static libs (OS X) +* Workaround 'argument to function assert is always 1' cppcheck warnings + + +== [7.6.8] 2018-12-11 == + +* Eliminate 'casting signed to bigger unsigned int' CSA warning (test_stack) +* Eliminate 'redundant blank line at start/end of block' CodeFactor warning +* Fix 'Cannot implement CAS_full on this architecture' build error (nios2) +* Fix a typo in arm_v6.h +* Support aarch64-ilp32 (GCC) and UWP/arm64 (MS VC) targets +* Undefine AO_ARM_HAVE_* private macros after their usage +* Use standalone private macro to guard against AO_GCC_BARRIER redefinition +* Workaround 'condition my_chunk_ptr is always false' cppcheck false positive + + +== [7.6.6] 2018-08-07 == + +* COPYING: sync with FSF's gpl-2.0.txt +* Fix 'undefined reference to __atomic_load/store/cas_16' error (gcc-7/x64) +* Fix a typo in the overview section of README +* Fix comments style in configure.ac and Makefile.am +* Update copyright information in README and some header files + + +== [7.6.4] 2018-03-27 == + +* Add RISC-V support +* Convert atomic_ops_malloc.c and tests to valid C++ code +* Eliminate 'function is never used' cppcheck warning for load_before_cas +* Eliminate 'using argument that points at uninitialized var' cppcheck error +* Fix 'AO_pt_lock undefined' error if cross-compiling manually (MinGW) +* Fix public headers inclusion from clients C++ code +* Remove gcc/nios2.h file (include gcc/generic.h directly for nios2) +* Support MIPS rel6 + + +== [7.6.2] 2017-12-24 == + +* Allow to alter DEFAULT/MAX_NTHREADS values in test_malloc/stack +* Allow to select almost-non-blocking stack implementation explicitly +* Annotate AO_malloc with 'alloc_size' and 'malloc' attributes +* Avoid misleading 'AO_t undefined' error if wrong atomic_ops.h included +* Define AO_TS_SET to 1 (true) if GCC atomic_test_and_set is used +* Disable workaround in stack_pop_acquire that was needed for ancient Clang +* Do not define AO_GCC_FORCE_HAVE_CAS for Clang 3.8+ (Aarch64) +* Do not disallow to define double_load using built-in atomics (Aarch64) +* Do not expose AO_GCC_FORCE_HAVE_CAS macro to client code (GCC) +* Do not install documentation if configure --disable-docs (new option) +* Do not produce .tar.bz2 distribution file (configure) +* Eliminate '-pedantic is not an option that controls warnings' GCC message +* Eliminate data race in cons() of test_malloc +* Eliminate GCC-5 ASan global-buffer-overflow false positive for AO_stack_bl +* Fill in allocated memory with values depending on thread id (test_malloc) +* Fix 'bad register name %sil' assembler error (GCC-4.4/x86) +* Fix 'unknown attribute no_sanitize' compiler warning for GCC +* Fix AO_malloc for sizes near CHUNK_SIZE +* Fix memory leak in test_malloc +* Fix test failures for Clang-3.8 and older (Aarch64) +* Fix test_stack failure if AO_PREFER_BUILTIN_ATOMICS (GCC/Aarch64) +* Fix typo in AO_REAL_NEXT_PTR comment +* Increase the default number of threads to 16 in test_malloc/stack +* Mark unallocated/freed memory as inaccessible using ASan functionality +* New macro (DONT_USE_MMAP) to support testing as if mmap() is unavailable +* New macro to select stack implementation based on CAS-double +* Place no_sanitize attributes in a GCC-compliant way +* Prevent too long run of test_atomic_generalized (especially with TSan) +* Simplify '#if' expressions in gcc/x86.h (code refactoring) +* Test smallest allocation of large type (test_malloc) +* Use __builtin_expect in atomic_ops_malloc +* Use built-in atomics for load/store/CAS for Clang by default (Aarch64) +* Use double-word atomic intrinsics for recent Clang versions (gcc/x86.h) +* Use GCC atomic intrinsics for Hexagon (clang 3.9+) +* Use generalized double-wide load/store if AO_PREFER_GENERALIZED (Aarch64) +* Workaround 'unused result' code defects in atomic_ops.c, list_atomic +* Workaround Thread Sanitizer (TSan) false positive warnings +Also, includes 7.4.8 changes + + +== [7.6.0] 2017-05-19 == + +* Add *_and/or/xor* and *_[fetch_]compare_and_swap* tests to test_atomic +* Add asm-based and/or/xor implementation for char/short/int (gcc/x86) +* Add asm-based char/short/int CAS implementation for gcc/x86[_64] +* Add configure '--disable-atomic-intrinsics' option +* Add dd_acquire_read case to test_atomic +* Add initial nios2 architecture support +* Add Makefile target (check-nolink) to compile all source without linking +* Add Makefile target to run all tests without test-driver +* Add test_atomic_generalized to Makefile and Makefile.msft +* Allow alternate CC (CROSS_CC) for AC_TRY_COMPILE (configure) +* Always define word-wide CAS for x86 (MS VC++ 8+) +* Avoid atomic_compare_exchange_n if no __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n +* Avoid extra nop_full in stack_pop_acquire if atomic intrinsics used (x86) +* Basic support of TILE-Gx and TILEPro CPUs +* Code refactoring of int-wide primitives in gcc/x86.h +* Define AO_TS_SET as __GCC_ATOMIC_TEST_AND_SET_TRUEVAL if applicable +* Define CLANG/GNUC_PREREQ macros to check gcc/clang minimum version +* Do not define print_list() unless used (tests) +* Eliminate 'condition sizeof(long)>4 is always true' cppcheck style warning +* Eliminate 'ISO C90 does not support long long' compiler pedantic warning +* Eliminate 'scope of variable can be reduced' cppcheck warnings +* Eliminate redundant lwsync 2nd call in CAS_full on fail (gcc/PowerPC) +* Fix 'unknown attribute no_sanitize' compiler warning (clang prior to v3.8) +* Fix 'variable new value is never used' cppcheck style warning +* Fix missing double_compare_and_swap_dd_acquire_read +* Fix reporting about missing and/or/xor_dd_acquire_read (test_atomic) +* Hide AO_locks symbol +* Implement AO_CLEAR using atomic intrinsic (GCC) +* Implement CAS_acquire/release/full using __atomic_compare_exchange_n (gcc) +* Implement char and/or/xor and short CAS for msftc ARM and X86[_64] +* Implement char CAS and char/short add for msftc X86[_64] (VS 2013+) +* Implement compiler_barrier using __atomic_signal_fence (GCC) +* Implement int CAS/inc/dec for msftc/x86_64 +* Implement short inc/dec directly for msftc ARM and X86[_64] +* Initial ibmc/powerpc (xlc) support +* New configure option (--enable-werror) to treat warnings as compiler errors +* New macro AO_PREFER_BUILTIN_ATOMICS to rely on GCC atomics fully (AArch64) +* Refine AO_nop_write comment for ARM big.LITTLE architecture +* Refine configure messages when checking for compiler options +* Refine documentation about _full memory ordering suffix +* Refine README how to build the library source from the repository +* Relax shareability domain for dmb st in AO_nop_write (ARM/AArch64) +* Remove redundant include windows.h from headers (msftc/x86[_64]) +* Remove spaces at EOLn in asm code +* Report gcc/clang pedantic warnings (configure) +* Support NaCl/arm +* Suppress 'ISO C does not support __int128 type' GCC/Clang pedantic warning +* Test store/CAS emulation explicitly +* Update shared libraries version info to 2:0:1 +* Use GCC atomic intrinsics for PowerPC 32/64 (GCC 4.8+ and clang 3.8+) +* Use GCC atomic intrinsics for x86, x64, ARM, MIPS (gcc 4.9+, clang 3.5+) +* Use generalized double-wide load/store if AO_PREFER_GENERALIZED (gcc/x86) +* Workaround '#error' cppcheck error messages +* Workaround 'condition always true', 'unused stored value' cppcheck warnings +* Workaround 'function is never used' cppcheck style warnings +* Workaround 'obsolescent ftime called' cppcheck style warning (POSIX) +* Workaround 'overflow in pointer subtraction' cppcheck warning +* Workaround 'shifting 32-bit value by 32 bits undefined' cppcheck warning +* Workaround 'uninitialized memory use' code analyzer false warning (tests) +* Workaround 'uninitialized variable' cppcheck error in hpc/hppa.h +* Workaround 'value of macro is unknown' cppcheck information messages +* Workaround a bug in double-wide intrinsics of Clang/x64 with ASan enabled +* Workaround MSan warning about uninitialized data read by generalized store +Also, includes 7.4.6 changes + + +== [7.4.20] 2023-12-15 == + +* Eliminate 'comparing signed/unsigned values' lcc warning in add_chunk_as +* Fix get_chunk for case of mmap area is located before AO_initial_heap +* Fix typo in AO_HAVE_compare_and_swap_double name in atomic_ops_stack.h +* Fix typos in comments of atomic_ops_malloc.c and atomic_ops_stack.c/h + + +== [7.4.18] 2022-08-25 == + +* Avoid AO_stack_t to cross CPU cache line boundary +* Do not assume 'ordered except earlier write' for UWP/arm64 +* Ensure result of AO_test_and_set is always AO_TS_CLEAR or AO_TS_SET +* Fix 'use of undeclared SIG_BLOCK' Clang error if -std=c89 on Cygwin +* Fix a typo in comment of AO_stack_push_explicit_aux_release +* Fix code indentation in main of test_stack.c +* Remove outdated comment about unsupported Win64 in atomic_ops_stack.h +* Repeat black list check on CAS fail in stack_push_explicit_aux_release + + +== [7.4.16] 2021-09-13 == + +* Fix gcc/sunc x86 AO_compare_double_and_swap_double missing side effect +* Fix library name in README_details +* Fix size of local variable passed to cas[x] (gcc/sparc) +* Workaround assertion violation in AO_load/store on m68k + + +== [7.4.14] 2019-03-01 == + +* Fix 'AO_*_TS_T is not defined' compiler warnings (GCC-8) + + +== [7.4.12] 2018-12-11 == + +* COPYING: sync with FSF's gpl-2.0.txt +* Fix a typo in arm_v6.h +* Fix a typo in the overview section of README +* Support ILP32 in AArch64 assembly routines (GCC) +* Support UWP/arm64 target + + +== [7.4.10] 2017-12-22 == + +* Fix AO_malloc for sizes near CHUNK_SIZE +* Fix memory leak in test_malloc +* Fix typo in AO_REAL_NEXT_PTR comment + + +== [7.4.8] 2017-10-18 == + +* Fix 'missing select.h', 'undefined sigprocmask' compiler errors (Hexagon) +* Update README about bugs reporting and new releases notification +* Workaround misspelling in GCC to detect ARMv6KZ platform + + +== [7.4.6] 2017-05-18 == + +* Add assertion that double-wide CAS target is aligned (msftc/x86[_64]) +* Add configure --enable-gcov option (enable code coverage analysis) +* Code refactoring of gcc/powerpc.h to avoid code duplication +* Eliminate 'cast to long from void*' compiler warning in test_atomic +* Eliminate 'implicit declaration of close' warning in 'strict ANSI' mode +* Eliminate 'missing braces around initializer' gcc warning (hppa) +* Eliminate 'printf format specifies type void*' GCC pedantic warnings +* Eliminate 'value shift followed by expansion' false code defect warning +* Enable limited testing in Makefile.msft without Cygwin +* Fix (delete) comment for AO_and_full (x86) +* Fix block_all_signals compilation in 'strict ANSI' mode +* Fix missing .exe for test filenames in Makefile (MinGW) +* Fix missing printed value names (test_stack) +* Implement fetch-CAS for s390[x] (gcc) +* Move libraries version info to the beginning of Makefile.am +* Refine documentation in Makefile.msft how to run all tests (MS VC) +* Refine README about library downloading +* Rename doc/README.txt to doc/README_details.txt +* Support AIX/ppc (gcc) +* Support CFLAGS_EXTRA to pass extra user-defined compiler flags (make) +* Support n32 ABI for mips64 +* Update shared libraries version info for 7.4.6+ (to 1:4:0) +* Use 'inline code' format for commands in README.md +* Use LLD and SCD instructions on mips64 +* Workaround 'resource leak' false positives in AO_malloc, add_elements +* Workaround 'uninitialized memory use' MemorySanitizer warning (test_atomic) +Also, includes 7.2h changes + + +== [7.4.4] 2016-05-24 == + +* Eliminate 'signed-to-unsigned value extension' compiler warning in malloc +* Eliminate 'variable set but not used' Cppcheck warnings in test_stack +* Fix GCC 5.x compatibility for AArch64 double-wide primitives +* Fix makefile preventing AO_pause undefined in libatomic_ops_gpl +* Fix missing casts to match printf format specifier in test_atomic +* Fix missing output folder on making auto-generated test files (Automake) +* Fix typo in configure.ac (in description of AO_ASM_X64_AVAILABLE) +* Minor fix of code alignment in mips AO_compare_and_swap +* Remove TODO file +* Restore contribution info in ChangeLog for authors not listed in git log +Also, includes 7.2g changes + + +== [7.4.2] 2014-05-02 == + +* Fix a typo in doc/README.txt (remove redundant "an" article) +* Update emails/links due to project site transition +Also, includes 7.2f changes + + +== [7.4.0] 2013-11-17 == + +* Add and/or/xor entries to list_atomic (tests) +* Add char/short/int/AO_double_t and dd_acquire cases to list_atomic (tests) +* Add compile-time assertion for size of 'standard' AO_double_t +* Add double_store pthread-based implementation and tests +* Add generalized CAS primitives of char/short/int size +* Add generalized atomic and/or/xor operations for char/short/int types +* Add generalized fetch_and_add_acquire/release (for ARMv6+) +* Add generic implementation of double_load primitives +* Add information about AO_ASSUME_VISTA to README_win32 +* Add internal header containing only char/short/int/AO_t atomic loads +* Add load/store primitives generalization based on CAS +* Add lock-based implementation of char/short/int_fetch_compare_and_swap +* Add makefile rule to test list_atomic.template syntax (tests) +* Add missing 'const' in aligned-atomic XSIZE_load implementation +* Add missing double_compare_and_swap to generalization +* Add missing generalization of no-barrier CAS in template +* Add negative double-CAS test cases to test_atomic_include (tests) +* Add test_stack to Makefile.msft (tests) +* Adjust fprintf arguments type matching specifier in test_stack (tests) +* Adjust included filenames in atomic_ops_malloc and test_stack +* Adjust quotes in echo command of Makefile.msft (Win32) +* Always use 'mfence' for nop_full if target CPU supports SSE2 (gcc/x86) +* Better document configure THREADDLLIBS variable +* Cast away volatile on dereference in CAS-based generalization primitives +* Change policy regarding version numbers ("micro" part instead of "alpha") +* Convert README to Markdown format +* Define AO_NO_PTHREADS in configure if no pthreads (Win32 and VxWorks) +* Define AO_int_X operations for ARM and avr32 +* Define double-wide ordered loads/stores for x86 +* Define int_and/or/xor primitives in ao_t_is_int header +* Define nop_full as compiler barrier for pre-ARMv6 single-core case +* Do not duplicate BUILT_SOURCES entries in nobase_private_HEADERS (Makefile) +* Do not include standard_ao_double_t.h where double-CAS is unimplemented +* Do not report absence of meaningless nop, load and store in test_atomic +* Do not use deprecated AO_T and AO_TS_T (tests) +* Eliminate 'missing initializer' warning for AO_stack_t value initializer +* Eliminate 64-bit compiler warnings in atomic_ops_malloc +* Eliminate arithmetic shifts in double-CAS (gcc/arm, msftc/x86) +* Eliminate warning for fetch_and_add argument in test_atomic_include (tests) +* Enable Makefile.msft for Win64 +* Enable build using toolchain without pthreads +* Enable double_compare_and_swap for non-cpp code (msftc/x86.h) +* Enable generalization of all variants of CAS via fetch_compare_and_swap +* Enable test_stack for pthreads-w32 and Win32 with native threads +* Fix generalized AO_char/short_compare_and_swap args (missing 'unsigned') +* Fix makefile sed rule for list_atomic (tests) +* Fix missing abort() usage in atomic_ops_malloc and tests on WinCE +* Generalize compare_double_and_swap_double using double_compare_and_swap +* Generalize double_load/store for x86_64 (GCC) +* Generate ao_t_is_int, 'loadstore' headers from templates +* Generate generalized AO_t load/store/fetch_and_add primitives from template +* Generate ordered_loads/stores_only headers from templates +* Group all X_acquire_release_volatile.h and X_[aligned_]atomic_load_store.h +* Implement and/or/xor, AO_double_load for ARM +* Implement atomic store using direct write by default on ARMv6+ +* Implement char/short/int-wide primitives using GCC built-in atomic/sync +* Implement char/short/int_fetch_and_add for msftc/x86[_64] (Win32) +* Implement char/short_fetch_and_add, char/short_load for ARMv6+ (GCC) +* Implement char/short_store primitives at aligned addresses for ARM +* Implement compare_double_and_swap_double for SunCC/x86 +* Implement double_load/store based on guaranteed x86 access atomicity +* Implement double_store for ARMv7 using LDREXD/STREXD +* Implement load/store via simple LDR/STR for ARMv6+ (msftc) +* Implement nop_full/write using 'dmb' instruction if available (gcc/arm) +* Improve debug printing in test_stack (tests) +* Log messages to stdout instead of stderr (tests) +* Make AO_ASSUME_VISTA also enables Win98 code in msftc/x86.h (Win32) +* Minimize gcc/generic-arithm template by factoring out barriers +* Move 'unsigned' keyword to XCTYPE in generalize-small template +* Move default compiler options to CFLAGS in Makefile.msft (Win32) +* Move definitions of ordered loads/stores to inner separate headers +* Move gcc-generic AO_t-wide primitives to generic-small/arithm headers +* Move generalized arithmetical primitives to 'generalize-arithm' template +* Optimize AO_spin manually to minimize compiler influence on its duration +* Parameterize list_atomic template with XSIZE (tests) +* Perform only few list reversals in test_malloc if AO based on pthreads +* Put autogen.sh to 'dist' package (Automake) +* Remote duplicate definition of test_and_set_acquire in generalize.h +* Remove X_aligned_atomic_load_store headers and template +* Remove duplicate AO_spin and AO_pause definition in atomic_ops_stack +* Remove gcc/x86_64.h eliminating code duplication of gcc/x86.h +* Remove nested AO_USE_PTHREAD_DEFS macro check in atomic_ops.h (gcc/arm) +* Remove redundant 'cc' clobber for LDREXD instruction (gcc/arm) +* Remove store_full from msftc/arm.h in favor of generalized primitive +* Remove sunc/x86_64.h eliminating code duplication of sunc/x86.h +* Remove unsafe emulation-based implementation of double CAS (SunCC/x86_64) +* Remove useless 'perror' call in run_parallel.h (tests) +* Reorder AO_double_t union elements for AO_DOUBLE_T_INITIALIZER portability +* Replace atomic_load_store.template with atomic_load and atomic_store ones +* Replace some FIXME items with TODO in atomic_ops.c and sysdeps headers +* Specify fetch_and_add/sub1 result as unused in test_atomic (tests) +* Support AArch64 (64-bit ARM) target (GCC) +* Support ARMv8 target (gcc/arm) +* Test double_compare_and_swap in test_atomic (tests) +* Use AO_ prefix for internal functions in arm_v6.h, hppa.h +* Use __atomic GCC built-in to implement generic double-wide CAS +* Use built-in __sync CAS for double-CAS if AO_USE_SYNC_CAS_BUILTIN for x86 +* Workaround GCC 4.4.3 warning reported for 'val' of list_atomic.c (tests) +Also, includes 7.2e changes + + +== [7.3alpha2] 2012-05-11 == + +* Add '-no-undefined' to LDFLAGS in src/Makefile.am +* Add AO_and, AO_xor atomic operations +* Add AO_fetch_compare_and_swap primitives +* Add and fill in AUTHORS, TODO files +* Add autogen.sh file +* Adjust AO_..._H macros in public headers +* Code refactoring of gcc/arm.h by introducing AO_ARM_HAVE_x macros +* Define AO macros for libatomic_ops version identification +* Do not define NDEBUG if '--enable-assertions' passed to configure +* Eliminate compiler warnings in various functions and macros +* Generalize AO_compare_and_swap primitives via AO_fetch_compare_and_swap +* Generalize acquire/release/full CAS primitives for MIPS +* Implement fetch_and_add, test_and_set primitives for MIPS +* Improve Makefile for MS VC++; pass '-W3' option to MS compiler +* Include ao_t_is_int.h from atomic_ops.h after first generalization pass +* Merge all Makefile.am files in src tree +* Minor code refactoring of atomic_ops.c, generic_pthread.h +* Minor configure build improvements (e.g., ensure proper autoconf version) +* Place only major per-release changes description to ChangeLog (this file) +* Recognize AO_PREFER_GENERALIZED macro to favor generalization over assembly +* Remove all auto-generated files except for generalize-small.h from the repo +* Remove duplicate doc/COPYING and empty NEWS files +* Replace atomic_ops_malloc static mmap-related empty functions with macros +* Replace pointer relational comparisons with non-pointer ones +* Require autoconf 2.61 instead of v2.64 +* Show extra compiler warnings (GCC only) +* Turn off AO primitives inlining if AO_NO_INLINE defined +* Use __builtin_expect in CAS failure loop condition checks (GCC only) +Also, includes 7.2 changes + + +== [7.2l] 2023-12-15 == + +* Fix get_chunk for case of mmap area is located before AO_initial_heap +* Fix typo in AO_HAVE_compare_and_swap_double name in atomic_ops_stack.h +* Fix typos in comments of atomic_ops_malloc.c and atomic_ops_stack.c/h + + +== [7.2k] 2022-08-24 == + +* Avoid AO_stack_t to cross CPU cache line boundary +* Fix a typo in comment of AO_stack_push_explicit_aux_release +* Fix code indentation in main of test_stack.c +* Remove outdated comment about unsupported Win64 in atomic_ops_stack.h +* Repeat black list check on CAS fail in stack_push_explicit_aux_release + + +== [7.2j] 2021-09-12 == + +* Fix a typo in arm_v6.h +* Fix asm constraints of primitives in sunc/x86_64.h +* Fix gcc/sunc x86 AO_compare_double_and_swap_double missing side effect +* Fix library name in README details +* Fix size of local variable passed to cas[x] (gcc/sparc) +* Workaround assertion violation in AO_load/store on m68k + + +== [7.2i] 2017-12-21 == + +* Fix 'missing select.h', 'undefined sigprocmask' compiler errors (Hexagon) +* Fix AO_malloc for sizes near CHUNK_SIZE +* Fix typo in AO_REAL_NEXT_PTR comment + + +== [7.2h] 2017-05-17 == + +* Add 'clean' target to Makefile.msft +* Enable Makefile.msft for Win64 +* Exclude 'check' from nmake all (Makefile.msft) +* Fix 'Cannot implement CAS_full on this architecture' build error (cris) +* Fix 'doc' files installation folder +* Fix (improve) AO_REQUIRE_CAS description in README +* Fix AO_SIZE_MAX definition (Linux/musl-gcc) +* Fix assertions style in test_atomic_include +* Fix size value wrap around in AO_malloc_large +* Fix test_atomic failure caused unaligned AO_double_t access (x86) +* Fix type of general AO_TS_INITIALIZER +* Fix typo in comments in gcc/arm.h +* Fix typos in 'error' pragma messages +* Workaround test_stack failure on AIX/ppc + + +== [7.2g] 2016-05-23 == + +* Add disclaimer to README to favor C11/C++14 atomics over libatomic_ops use +* Regenerate configure files using official libtool release (v2.4.2) +* Remove inclusion of acquire_release_volatile.h on MIPS +* Remove obsolete information from README about C++0x standard future +* Update links due to project site transition + + +== [7.2f] 2014-05-02 == + +* Fix a typo in doc/README.txt (remove redundant "an" article) +* Regenerate configure files by new automake (v1.14.1), libtool (v2.4.2.418) + + +== [7.2e] 2013-11-10 == + +* Fix (remove) invalid include of read_ordered.h for ARM +* Fix AM_CONFIG_HEADER in configure for autoconf-2.69-1 +* Fix AO_pause sleep delay for particular argument values (Win32) +* Fix ARMv7 LDREXD/STREXD double-wide operand specification (GCC/Clang) +* Fix LDREXD/STREXD use for pre-Clang3.3/arm +* Fix README regarding _acquire_read barrier +* Fix XSIZE_load/store definition order in generalize-small template +* Fix asm constraint of CAS memory operand for gcc/alpha, clang-3.1/mips +* Fix asm constraints of primitives in sunc/x86.h +* Fix cmpxchg16b-based compare_double_and_swap_double for SunCC/x86_64 +* Fix compare_double_and_swap_double and double_ptr_storage for gcc/x32 +* Fix compare_double_and_swap_double for clang3.0/x86 in PIC mode +* Fix compare_double_and_swap_double_full definition condition in emul_cas +* Fix generalize-small template adding missed CAS-based fetch_and_add +* Fix generalized fetch_and_add function +* Fix missing compiler barrier in nop_full for uniprocessor ARM +* Fix ordered_except_wr header inclusion for s390 +* Fix return type of AO_int_X primitives defined in ao_t_is_int header +* Fix return type of char/short/int_load_read() in read_ordered.h +* Fix template-based headers regeneration order in src/Makefile +* Fix typos in ao_t_is_int, atomic_ops.h, generalize.h, msftc/arm.h comments +* Fix variable type to match printf format specifier in test_stack +* Fix visibility and initial value of 'dummy' variable in atomic_ops_stack +* Terminate tests with abort after error reported + + +== [7.2d] 2012-08-09 == + +* Fix AO_compare_double_and_swap_double_full for gcc-4.2.1/x86 in PIC mode +* Fix AO_compiler_barrier missing parentheses +* Fix missing 'unsigned' for generalized AO_char/short_fetch_and_add result + + +== [7.2] 2012-05-11 == + +* Add atomic_ops.pc.in and atomic_ops-uninstalled.pc.in to pkgconfig folder +* Define and use AO_PTRDIFF_T in tests for casts between pointer and integer +* Fix AO_compare_and_swap return type for s390 and PowerPC +* Fix AO_compare_double_and_swap_double_full for gcc/x86 (PIC mode) +* Fix AO_stack_push_release to workaround bug in clang-1.1/x86 compiler +* Fix AO_test_and_setXX in tests/list_atomic.template +* Fix AO_test_and_set_full (gcc/x86[_64].h) to work-around a bug in LLVM v2.7 +* Fix AO_test_and_set_full on m68k +* Fix __ARM_ARCH_5__ macro handling for Android NDK (ARMv7) +* Fix configure for Cygwin, mingw-w64/32 +* Fix configure to define __PIC__ macro explicitly if needed (GCC) +* Fix double_ptr_storage definition for GCC pre-v4 (x86_64) +* Fix for x32 by removing 'q' suffix in x86-64 instructions +* Fix generalization for IA-64 (regarding AO_or, AO_..._read/write primitives) +* Fix generalized AO__fetch_and_add() return type +* Fix test_atomic_include for the case of missing CAS primitive +* Fix test_malloc - allocate less memory in case of missing mmap +* Implement the basic atomic primitives for the hexagon CPU + + +== [7.2alpha6] 2011-06-14 == + +* Add missing AO_HAVE_ macros +* Add support of avr32 CPU +* Better support of various models of ARM +* Disable AO_compare_double_and_swap_double_full for SunCC x86 as not working +* Enable ARM Thumb-2 mode +* Fix AO_test_and_set_full for SunCC (x86) +* Fix bugs in tests +* Fix clobbers in AO_compare_and_swap_full (x86.h) +* Fix typos in identifiers and comments +* Improve AO_sync for PowerPC +* Improve make scripts (configure.ac) +* Make get_mmaped() in atomic_ops_malloc.c more portable +* Support Intel compiler +* Support NaCl target +* Suppress compiler warnings in various places +* Test more predefined macros (ARM, PowerPC) +* Use assembly code only for MS VC if available (x86_64) +* Use built-in __sync_bool_compare_and_swap if available (x86_64) +* Workaround bugs in LLVM GCC and SunCC regarding XCHG (x86, x86_64) + + +== [7.2alpha4] 2009-12-02 == + +* Fix typos in comments, identifiers and documentation +* Implement AO_compare_and_swap_full for SPARC +* Refine ARM-specific code +* Refine code and comments for MS VC +* Regenerate make scripts +* Share common code for all 32-bit CPUs (MS VC) +* Support DigitalMars and Watcom compilers +* Support MS VC for ARM (WinCE) +* Support SH CPU +* Support win32-pthreads +* Support x86 and x86_64 for SunCC compiler + + +== [7.2alpha2] 2009-05-27 == + +* Add MIPS support +* Add better support for m68k +* Add "const" to first parameter of load calls +* Add parentheses around address argument for various macros +* Add some platform-specific documentation to INSTALL +* Add untested 64-bit support for PowerPC +* Fix AO_compare_and_swap_double_acquire +* Fix AO_int_fetch_and_add_full (x86_64) +* Fix comments +* Fix s390 include paths +* Fix use of lwz instruction (PowerPC) +* Refine clobbers (PowerPC) +* Remove outdated info about Windows support in README +* Replace K&R-style function definition with ANSI C one +* add AO_compare_double_and_swap_double for ARMv6 +* gcc/powerpc.h: Consider __NO_LWSYNC__ + + +== [7.1] 2008-02-11 == + +* Add test_and_set, AO_double_compare_and_swap generalizations +* Conditionally add compare_double_and_swap_double (x86) +* Conditionally add compare_double_and_swap_double (x86) +* Fix AO_compare_double_and_swap_double_full (x86) for PIC mode +* Fix AO_load_acquire for PowerPC +* Fix double-width CAS (x86) +* Refine README (add more warnings about data dependencies) +* Refine double_ptr_storage type definition +* Support ARMv6+ in GCC +* Support ArmCC compiler +* Use _InterlockedExchangeAdd for MS VC (x86) + + +== [7.0] 2007-06-28 == + +* Add 64-bit version of AO_load_acquire for PowerPC (by Luca Barbato) +* Add support of x86 and x86_64 for MS VC +* Do not assume that "mfence" is always present (x86.h) +* Fix ARM AO_test_and_set_full +* Include windows.h (MS VC) +* Update README to reflect C++0x effort + + +== [1.2] 2006-07-11 == + +* Add prototypes to suppress compiler warnings +* Add simple VxWorks support +* Fix InterlockedCompareExchange proto usage +* Fix typos (ia64) +* Include all_acquire_release_volatile.h and all_atomic_load_store.h (ia64) +* Initial support for 64-bit targets +* Use "=q" for AO_test_and_set_full (x86) +* Use inline assembler to generate "mfence" and byte sized XCHG +* Use new intrinsics available in MSVC 2003 and MSVC 2005 + + +== [1.1] 2005-09-27 == + +* Add and use read_ordered.h +* Change function naming from "byte" to "char" +* Fix AO_test_and_set for ARM; define AO_CAN_EMUL_CAS + + +== [1.0] 2005-03-21 == + +* Add atomic_ops primitives for different sized data +* Add compare_double_and_swap_double and compare_and_swap_double +* Add gcc/cris.h (originally comes from Hans-Peter Nilsson) +* Add gcc/m68k.h (contributed by Tony Mantler) +* Add gcc/powerpc.h (with help of Maged Michael, Doug Lea, Roger Hoover) +* Add initial support for atomic_ops for VC++/Windows/X86 and HP/UX +* Add minimal support for the Sun SPARC compiler +* Add support for platforms that require out-of-line assembly code +* Add support of int-wide operations on platforms with int-sized pointers +* Added libatomic_ops_gpl library with support for lock-free stack and malloc +* Change atomic_ops include file structure +* Change most platforms to use byte-wide test-and-set locations +* Define AO_CLEAR, __ldcw[_align] macros in gcc/hppa.h (by Carlos O'Donell) +* Fix various bugs +* Install under "atomic_ops" instead of "ao" +* Remove compiler_barrier workaround for gcc 3.4+ +* Renamed various types to end in _t +* Replace AO_HAVE_NOP_FULL with AO_HAVE_nop_full (by Ranko Zivojnovic) +* Use autoconf, automake diff --git a/libatomic_ops/Config.cmake.in b/libatomic_ops/Config.cmake.in new file mode 100644 index 000000000..034b456ce --- /dev/null +++ b/libatomic_ops/Config.cmake.in @@ -0,0 +1,5 @@ +# The Atomic_ops CMake configuration file. + +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/Atomic_opsTargets.cmake") +check_required_components(libatomic_ops) diff --git a/libatomic_ops/LICENSE b/libatomic_ops/LICENSE new file mode 100644 index 000000000..aff4d002d --- /dev/null +++ b/libatomic_ops/LICENSE @@ -0,0 +1,76 @@ +MIT License (core library) / GPL-2.0 (gpl extension library) + +Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. +Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. +Copyright (c) 1999-2011 Hewlett-Packard Development Company, L.P. +Copyright (c) 2005, 2007 Thiemo Seufer +Copyright (c) 2007 by NEC LE-IT. All rights reserved. +Copyright (c) 2008-2022 Ivan Maidanski +Copyright (c) 2009 Bradley Smith +Copyright (c) 2009 by Takashi Yoshii. All rights reserved. + + +Our intent is to make it easy to use libatomic_ops, in both free and +proprietary software. Hence most of code (core library) that we expect to +be linked into a client application is covered by a MIT or MIT-style license. + +However, a few library routines (the gpl extension library) are covered by +the GNU General Public License. These are put into a separate library, +libatomic_ops_gpl.a file. + +Most of the test code is covered by the GNU General Public License too. + + +The low-level (core) part of the library (libatomic_ops.a) is mostly covered +by the MIT license: + +---------------------------------------- + +Copyright (c) ... + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------- + + +Some files in the atomic_ops/sysdeps directory (part of core library) were +inherited in part from the Boehm-Demers-Weiser conservative garbage collector, +and are covered by its license, which is similar in spirit to MIT license: + +-------------------------------- + +Copyright (c) ... + +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 files are covered by the GNU General Public License. (See file +"COPYING".) This applies only to the test code and the atomic_ops_gpl +portion of the library. Thus, atomic_ops_gpl should generally not be +linked into proprietary code. (This distinction was motivated by patent +considerations.) diff --git a/libatomic_ops/Makefile.am b/libatomic_ops/Makefile.am new file mode 100644 index 000000000..fbb40231f --- /dev/null +++ b/libatomic_ops/Makefile.am @@ -0,0 +1,29 @@ +SUBDIRS = src tests + +ACLOCAL_AMFLAGS = -I m4 + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = pkgconfig/atomic_ops.pc +noinst_DATA = pkgconfig/atomic_ops-uninstalled.pc + +# Installed documentation. +if ENABLE_DOCS +dist_doc_DATA = AUTHORS ChangeLog LICENSE README.md README_details.txt \ + README_win32.txt +if ENABLE_GPL +dist_doc_DATA += COPYING README_malloc.txt README_stack.txt +endif +endif + +EXTRA_DIST = autogen.sh CMakeLists.txt Config.cmake.in + +## TODO: After migration to autoconf-1.13+, remove check-nolink definition +## from this Makefile.am and add AM_EXTRA_RECURSIVE_TARGETS([check-nolink]) +## back to configure.ac file. +.PHONY: check-nolink check-nolink-local +check-nolink: check-nolink-local + $(MAKE) --directory tests $(AM_MAKEFLAGS) check-nolink-local + +check-nolink-local: all + +#distclean-local: diff --git a/libatomic_ops/README.md b/libatomic_ops/README.md new file mode 100644 index 000000000..1e96a70bc --- /dev/null +++ b/libatomic_ops/README.md @@ -0,0 +1,130 @@ +# The atomic_ops library (`libatomic_ops`) + +IN NEW CODE, PLEASE USE C11 OR C++14 STANDARD ATOMICS INSTEAD OF THE CORE +LIBRARY IN THIS PACKAGE. + +This is version 7.8.2 of libatomic_ops. + +License: [MIT](LICENSE) for core library / [GPL-2.0](COPYING) for gpl +extension. + + +## Download + +You might find a more recent/stable version on the +[Download](https://github.com/ivmai/libatomic_ops/wiki/Download) page, or +[BDWGC site](http://www.hboehm.info/gc/). + +Also, the latest bug fixes and new features are available in the +[development repository](https://github.com/ivmai/libatomic_ops). + + +## Overview + +This package provides semi-portable access to hardware-provided +atomic memory update operations on a number of architectures. These might +allow you to write code: + +* That does more interesting things in signal handlers. + +* Makes more effective use of multiprocessors by allowing you to write + clever lock-free code. Note that such code is very difficult to get + right, and will unavoidably be less portable than lock-based code. It + is also not always faster than lock-based code. But it may occasionally + be a large performance win. + +* To experiment with new and much better thread programming paradigms, etc. + +Please see other README files for the details: + +* [README_details.txt](README_details.txt) - details about atomic_ops.h + +* [README_malloc.txt](README_malloc.txt) - a simple almost-lock-free malloc + implementation (part of libatomic_ops_gpl) + +* [README_stack.txt](README_stack.txt) - an almost lock-free LIFO linked + lists (stack) implementation (part of libatomic_ops_gpl) + + +## Installation and Usage + +The configuration and build scripts for this package were generated by +Automake/Autoconf. `./configure; make; sudo make install` in this +directory should work. For a more customized build, see the output of +`./configure --help`. To build it from the development repository, +`./autogen.sh` should be executed first. + +Alternatively, CMake could be use to build this package, e.g. +`cmake . && cmake --build .` in this directory should work. + +Note that much of the content of this library is in the header files. +However, two small libraries are built and installed: + +* `libatomic_ops.a` is a support (core) library, which is not needed on some + platforms. This is intended to be usable, under some mild restrictions, + in free or proprietary code, as are all the header files. + See [LICENSE](LICENSE) for more details about the licensing. + +* `libatomic_ops_gpl.a` is a so called gpl extension library containing some + higher level facilities. This code is covered by the GPL. The contents + correspond to the headers `atomic_ops_malloc.h` and `atomic_ops_stack.h`. + Not built and not installed if `--disable-gpl` option is passed to + `configure` (or if `-Denable_gpl=OFF` option is passed to `cmake` if the + latter is used to build the package). The licensing details are given in + [COPYING](COPYING) and [LICENSE](LICENSE) files. + + +## Platform Specific Notes + +Win32/64: src/Makefile.msft contains a very simple Makefile for building +and running tests and building the gpl library. The core `libatomic_ops` +implementation is entirely in header files (libatomic_ops.lib is built anyway +to match that of the configure-based build process, but libatomic_ops.lib has +only the implementation of the internal AO_pause() used by the gpl library). +More information is provided in [README_win32.txt](README_win32.txt) file. + +HP-UX/PA-RISC: `aCC -Ae` won't work as a C compiler, since it doesn't support +inline assembly code. Use cc. + + +## Feedback, Contribution, Questions and Notifications + +Please address bug reports and new feature ideas to +[GitHub issues](https://github.com/ivmai/libatomic_ops/issues). Before the +submission please check that it has not been done yet by someone else. + +If you want to contribute, submit +a [pull request](https://github.com/ivmai/libatomic_ops/pulls) to GitHub. + +If you need help, use +[Stack Overflow](https://stackoverflow.com/questions/tagged/atomic-ops). +Older questions on the site can be found by +[this query](https://stackoverflow.com/search?q=atomic_ops). +Older technical discussions are also available in `bdwgc` mailing list +archive - it can be downloaded as a +[compressed file](https://github.com/ivmai/bdwgc/files/1038163/bdwgc-mailing-list-archive-2017_04.tar.gz) +or browsed at [Narkive](http://bdwgc.opendylan.narkive.com) (please search +for _atomic_ keyword). + +To get new release announcements, subscribe to +[RSS feed](https://github.com/ivmai/libatomic_ops/releases.atom). +(To receive the notifications by email, a 3rd-party free service like +[IFTTT RSS Feed](https://ifttt.com/feed) can be setup.) +To be notified on all issues, please +[watch](https://github.com/ivmai/libatomic_ops/watchers) the project on +GitHub. + + +## Copyright & Warranty, Contributors + +Please be aware of the dual nature of the license of libatomic_ops: + +* the core part (implementing semi-portable access to hardware-provided + atomic memory operations) is released under MIT license + +* the gpl extension (almost lock-free malloc and stack implementations) and + the tests are released under GPL-2.0 license + +The exact licensing information is provided in [LICENSE](LICENSE) file. + +The library contributors are listed in [AUTHORS](AUTHORS) file. diff --git a/libatomic_ops/README_details.txt b/libatomic_ops/README_details.txt new file mode 100644 index 000000000..43f2531e8 --- /dev/null +++ b/libatomic_ops/README_details.txt @@ -0,0 +1,254 @@ +Usage: + +0) If possible, do this on a multiprocessor, especially if you are planning +on modifying or enhancing the package. It will work on a uniprocessor, +but the tests are much more likely to pass in the presence of serious problems. + +1) Type ./configure --prefix=; make; make check +in the directory containing unpacked source. The usual GNU build machinery +is used, except that only static, but position-independent, libraries +are normally built. On Windows, follow README_win32.txt to use +src/Makefile.msft instead of the above sequence. + +Alternatively, the libraries could be built with CMake, even for Windows, +like this: +> mkdir out +> cd out +> cmake -Dbuild_tests=ON .. +> cmake --build . --config Release +> ctest --build-config Release + +The available options in the CMake script to customize the build is roughly +the same as those in the configure one, please see the exact option list in +CMakeLists.txt file. + +2) Applications should include atomic_ops.h. Nearly all operations +are implemented by header files included from it. It is sometimes +necessary, and always recommended to also link against libatomic_ops.a. +To use the almost non-blocking stack or malloc implementations, +see the corresponding README files, and also link against libatomic_ops_gpl.a +before linking against libatomic_ops.a. + +OVERVIEW: +Atomic_ops.h defines a large collection of operations, each one of which is +a combination of an (optional) atomic memory operation, and a memory barrier. +Also defines associated feature-test macros to determine whether a particular +operation is available on the current target hardware (either directly or +by synthesis). This is an attempt to replace various existing files with +similar goals, since they usually do not handle differences in memory +barrier styles with sufficient generality. + +If this is included after defining AO_REQUIRE_CAS, then the package makes +an attempt to emulate [fetch_]compare_and_swap* (single-width) in a way that, +at least on Linux, should still be async-signal-safe. As a result, most +other atomic operations may then be defined using the compare-and-swap +emulation. This emulation is slow, since it needs to disable signals. +And it needs to block in case of contention. If you care about performance +on a platform that can't directly provide compare-and-swap, there are +probably better alternatives. But this allows easy ports to some such +platforms (e.g. PA_RISC). The option is ignored if compare-and-swap +can be implemented directly. + +If atomic_ops.h is included after defining AO_USE_PTHREAD_DEFS, then all +atomic operations will be emulated with pthread locking. This is NOT +async-signal-safe. And it is slow. It is intended primarily for debugging +of the atomic_ops package itself. + +Note that the implementation reflects our understanding of real processor +behavior. This occasionally diverges from the documented behavior. (E.g. +the documented X86 behavior seems to be weak enough that it is impractical +to use. Current real implementations appear to be much better behaved.) +We of course are in no position to guarantee that future processors +(even HPs) will continue to behave this way, though we hope they will. + +Corrections/additions for other platforms are greatly appreciated. + +OPERATIONS: + +Most operations handle values of type AO_t, which are unsigned integers +whose size matches that of pointers on the given architecture. Additionally, +on most supported architectures the operations are also implemented to handle +smaller integers types; such operations are indicated by the appropriate size +prefix: +- char_... Operates on unsigned char values; +- short_... Operates on unsigned short values; +- int_... Operates on unsigned int values. + +The notable exception is AO_test_and_set operating only on AO_TS_t, which is +whatever size the hardware supports with good performance. In some cases this +is the length of a cache line, in some other cases it is a byte. In many +cases AO_TS_t is equivalent to AO_t. + +The defined operations are all of the form AO_[](). + +The component specifies an atomic memory operation. It may be +one of the following, where the corresponding argument and result types +are also specified: + +void nop() + No atomic operation. The barrier may still be useful. +AO_t load(const volatile AO_t * addr) + Atomic load of *addr. +void store(volatile AO_t * addr, AO_t new_val) + Atomically store new_val to *addr. +AO_t fetch_and_add(volatile AO_t *addr, AO_t incr) + Atomically add incr to *addr, and return the original value of *addr. +AO_t fetch_and_add1(volatile AO_t *addr) + Equivalent to AO_fetch_and_add(addr, 1). +AO_t fetch_and_sub1(volatile AO_t *addr) + Equivalent to AO_fetch_and_add(addr, (AO_t)(-1)). +void and(volatile AO_t *addr, AO_t value) + Atomically 'and' value into *addr. +void or(volatile AO_t *addr, AO_t value) + Atomically 'or' value into *addr. +void xor(volatile AO_t *addr, AO_t value) + Atomically 'xor' value into *addr. +int compare_and_swap(volatile AO_t * addr, AO_t old_val, AO_t new_val) + Atomically compare *addr to old_val, and replace *addr by new_val + if the first comparison succeeds; returns nonzero if the comparison + succeeded and *addr was updated; cannot fail spuriously. +AO_t fetch_compare_and_swap(volatile AO_t * addr, AO_t old_val, AO_t new_val) + Atomically compare *addr to old_val, and replace *addr by new_val + if the first comparison succeeds; returns the original value of *addr; + cannot fail spuriously. +AO_TS_VAL_t test_and_set(volatile AO_TS_t * addr) + Atomically read the binary value at *addr, and set it. AO_TS_VAL_t + is an enumeration type which includes two values AO_TS_SET and + AO_TS_CLEAR. An AO_TS_t location is capable of holding an + AO_TS_VAL_t, but may be much larger, as dictated by hardware + constraints. Test_and_set logically sets the value to AO_TS_SET. + It may be reset to AO_TS_CLEAR with the AO_CLEAR(AO_TS_t *) macro. + AO_TS_t locations should be initialized to AO_TS_INITIALIZER. + The values of AO_TS_SET and AO_TS_CLEAR are hardware dependent. + (On PA-RISC, AO_TS_SET is zero!) + +Test_and_set is a more limited version of compare_and_swap. Its only +advantage is that it is more easily implementable on some hardware. It +should thus be used if only binary test-and-set functionality is needed. + +If available, we also provide compare_and_swap operations that operate +on wider values. Since standard data types for double width values +may not be available, these explicitly take pairs of arguments for the +new and/or old value. Unfortunately, there are two common variants, +neither of which can easily and efficiently emulate the other. +The first performs a comparison against the entire value being replaced, +where the second replaces a double-width replacement, but performs +a single-width comparison: + +int compare_double_and_swap_double(volatile AO_double_t * addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2); + +int compare_and_swap_double(volatile AO_double_t * addr, + AO_t old_val1, + AO_t new_val1, AO_t new_val2); + +where AO_double_t is a structure containing AO_val1 and AO_val2 fields, +both of type AO_t. For compare_and_swap_double, we compare against +the val1 field. AO_double_t exists only if AO_HAVE_double_t +is defined. If this type is available then the following operation is +provided for convenience, fully equivalent to compare_double_and_swap_double: + +int double_compare_and_swap(volatile AO_double_t * addr, + AO_double_t old_val, AO_double_t new_val) + +Please note that AO_double_t (and AO_stack_t) variables should be properly +aligned (8-byte alignment on 32-bit targets, 16-byte alignment on 64-bit ones) +otherwise the behavior of a double-wide atomic primitive might be undefined +(or an assertion violation might occur) if such a misaligned variable is +passed (as a reference) to the primitive. Global and static variables should +already have proper alignment automatically but automatic variables (i.e. +located on the stack) might be misaligned because the stack might be +word-aligned (e.g. 4-byte stack alignment is the default one for x86). +Luckily, stack-allocated AO variables operated atomically are used rarely +in practice. + +ORDERING CONSTRAINTS: + +Each operation name also includes a suffix that specifies the associated +ordering semantics. The ordering constraint limits reordering of this +operation with respect to other atomic operations and ordinary memory +references. The current implementation assumes that all memory references +are to ordinary cacheable memory; the ordering guarantee is with respect +to other threads or processes, not I/O devices. (Whether or not this +distinction is important is platform-dependent.) + +Ordering suffixes are one of the following: + +: No memory barrier. A plain AO_nop() really does nothing. +_release: Earlier operations must become visible to other threads + before the atomic operation. +_acquire: Later operations must become visible after this operation. +_read: Subsequent reads must become visible after reads included in + the atomic operation or preceding it. Rarely useful for clients? +_write: Earlier writes become visible before writes during or after + the atomic operation. Rarely useful for clients? +_full: The associated operation is ordered with respect to both earlier and + later memory ops. If the associated operation is nop, then this orders + all earlier memory operations with respect to subsequent ones. + AO_store_full or AO_nop_full are the normal ways to force a store + to be ordered with respect to a later load. +_release_write: Ordered with respect to earlier writes. This is + normally implemented as either a _write or _release + barrier. +_acquire_read: Ordered with respect to later reads. This is + normally implemented as either a _read or _acquire barrier. +_dd_acquire_read: Ordered with respect to later reads that are data + dependent on this one. This is needed on + a pointer read, which is later dereferenced to read a + second value, with the expectation that the second + read is ordered after the first one. On most architectures, + this is equivalent to no barrier. (This is very + hard to define precisely. It should probably be avoided. + A major problem is that optimizers tend to try to + eliminate dependencies from the generated code, since + dependencies force the hardware to execute the code + serially.) + +We assume that if a store is data-dependent on a previous load, then +the two are always implicitly ordered. + +It is possible to test whether AO_[] is available on the +target platform by checking whether AO_HAVE_[] is defined +as a macro. + +Note that we generally don't implement operations that are either +meaningless (e.g. AO_nop_acquire, AO_nop_release) or which appear to +have no clear use (e.g. AO_load_release, AO_store_acquire, AO_load_write, +AO_store_read). On some platforms (e.g. PA-RISC) many operations +will remain undefined unless AO_REQUIRE_CAS is defined before including +the package. + +When typed in the package build directory, the following command +will print operations that are unimplemented on the platform: + +make test_atomic; ./test_atomic + +The following command generates a file "list_atomic.i" containing the +macro expansions of all implemented operations on the platform: + +make list_atomic.i + +Known issues include: + +We should be more precise in defining the semantics of the ordering +constraints, and if and how we can guarantee sequential consistency. + +Dd_acquire_read is very hard or impossible to define in a way that cannot +be invalidated by reasonably standard compiler transformations. + +Example: + +If you want to initialize an object, and then "publish" a pointer to it +in a global location p, such that other threads reading the new value of +p are guaranteed to see an initialized object, it suffices to use +AO_release_write(p, ...) to write the pointer to the object, and to +retrieve it in other threads with AO_acquire_read(p). + +Platform notes: + +All X86: We quietly assume 486 or better. + +Gcc on x86: +Define AO_USE_PENTIUM4_INSTRS to use the Pentium 4 mfence instruction. +Currently this is appears to be of marginal benefit. diff --git a/libatomic_ops/README_malloc.txt b/libatomic_ops/README_malloc.txt new file mode 100644 index 000000000..7f741f2fc --- /dev/null +++ b/libatomic_ops/README_malloc.txt @@ -0,0 +1,60 @@ +The atomic_ops_gpl includes a simple almost-lock-free malloc implementation. + +Note that the AO malloc implementation is licensed under the GPL, unlike the +lower level routines. + +This is intended as a safe way to allocate memory from a signal handler, +or to allocate memory in the context of a library that does not know what +thread library it will be used with. In either case locking is impossible. + +Note that the operations are only guaranteed to be 1-lock-free, i.e. a +single blocked thread will not prevent progress, but multiple blocked +threads may. To safely use these operations in a signal handler, +the handler should be non-reentrant, i.e. it should not be interruptable +by another handler using these operations. Furthermore use outside +of signal handlers in a multithreaded application should be protected +by a lock, so that at most one invocation may be interrupted by a signal. +The header will define the macro "AO_MALLOC_IS_LOCK_FREE" on platforms +on which malloc is completely lock-free, and hence these restrictions +do not apply. + +In the presence of threads, but absence of contention, the time performance +of this package should be as good, or slightly better than, most system +malloc implementations. Its space performance +is theoretically optimal (to within a constant factor), but probably +quite poor in practice. In particular, no attempt is made to +coalesce free small memory blocks. Something like Doug Lea's malloc is +likely to use significantly less memory for complex applications. + +Performance on platforms without an efficient compare-and-swap implementation +will be poor. + +This package was not designed for processor-scalability in the face of +high allocation rates. If all threads happen to allocate different-sized +objects, you might get lucky. Otherwise expect contention and false-sharing +problems. If this is an issue, something like Maged Michael's algorithm +(PLDI 2004) would be technically a far better choice. If you are concerned +only with scalability, and not signal-safety, you might also consider +using Hoard instead. We have seen a factor of 3 to 4 slowdown from the +standard glibc malloc implementation with contention, even when the +performance without contention was faster. (To make the implementation +more scalable, one would need to replicate at least the free list headers, +so that concurrent access is possible without cache conflicts.) + +Unfortunately there is no portable async-signal-safe way to obtain large +chunks of memory from the OS. Based on reading of the source code, +mmap-based allocation appears safe under Linux, and probably BSD variants. +It is probably unsafe for operating systems built on Mach, such as +Apple's Darwin. Without use of mmap, the allocator is +limited to a fixed size, statically preallocated heap (2MB by default), +and will fail to allocate objects above a certain size (just under 64K +by default). Use of mmap to circumvent these limitations requires an +explicit call. + +The entire interface to the AO_malloc package currently consists of: + +#include /* includes atomic_ops.h */ + +void *AO_malloc(size_t sz); +void AO_free(void *p); +void AO_malloc_enable_mmap(void); diff --git a/libatomic_ops/README_stack.txt b/libatomic_ops/README_stack.txt new file mode 100644 index 000000000..a3c8e00cd --- /dev/null +++ b/libatomic_ops/README_stack.txt @@ -0,0 +1,78 @@ +Note that the AO_stack implementation is licensed under the GPL, +unlike the lower level routines. + +The header file atomic_ops_stack.h defines a linked stack abstraction. +Stacks may be accessed by multiple concurrent threads. The implementation +is 1-lock-free, i.e. it will continue to make progress if at most one +thread becomes inactive while operating on the data structure. + +(The implementation can be built to be N-lock-free for any given N. But that +seems to rarely be useful, especially since larger N involve some slowdown.) + +This makes it safe to access these data structures from non-reentrant +signal handlers, provided at most one non-signal-handler thread is +accessing the data structure at once. This latter condition can be +ensured by acquiring an ordinary lock around the non-handler accesses +to the data structure. + +For details see: + +Hans-J. Boehm, "An Almost Non-Blocking Stack", PODC 2004, +http://portal.acm.org/citation.cfm?doid=1011767.1011774 +(This is not exactly the implementation described there, since the +interface was cleaned up in the interim. But it should perform +very similarly.) + +We use a fully lock-free implementation when the underlying hardware +makes that less expensive, i.e. when we have a double-wide compare-and-swap +operation available. (The fully lock-free implementation uses an AO_t- +sized version count, and assumes it does not wrap during the time any +given operation is active. This seems reasonably safe on 32-bit hardware, +and very safe on 64-bit hardware.) If a fully lock-free implementation, +AO_stack_is_lock_free() returns 1 (also, the macro AO_STACK_IS_LOCK_FREE is +defined in this case but its usage by client is deprecated). + +The implementation is interesting only because it allows reuse of +existing nodes. This is necessary, for example, to implement a memory +allocator. + +Since we want to leave the precise stack node type up to the client, +we insist only that each stack node contains a link field of type AO_t. +When a new node is pushed on the stack, the push operation expects to be +passed the pointer to this link field, which will then be overwritten by +this link field. Similarly, the pop operation returns a pointer to the +link field of the object that previously was on the top of the stack. + +The cleanest way to use these routines is probably to define the stack node +type with an initial AO_t link field, so that the conversion between the +link-field pointer and the stack element pointer is just a compile-time +cast. But other possibilities exist. (This would be cleaner in C++ with +templates.) + +A stack is represented by an AO_stack_t structure. (This is normally +2 or 3 times the size of a pointer.) It may be statically initialized +by setting it to AO_STACK_INITIALIZER, or dynamically initialized to +an empty stack with AO_stack_init. There are only three operations for +accessing stacks: + +void AO_stack_init(AO_stack_t *list); +void AO_stack_push_release(AO_stack_t *list, AO_t *new_element); +AO_t * AO_stack_pop_acquire(volatile AO_stack_t *list); + +We require that the objects pushed as list elements remain addressable +as long as any push or pop operation are in progress. (It is OK for an object +to be "popped" off a stack and "deallocated" with a concurrent "pop" on +the same stack still in progress, but only if "deallocation" leaves the +object addressable. The second "pop" may still read the object, but +the value it reads will not matter.) + +We require that the headers (AO_stack objects) remain allocated and +valid as long as any operations on them are still in-flight. + +We also provide macros AO_REAL_HEAD_PTR that converts an AO_stack_t +to a pointer to the link field in the next element, and AO_REAL_NEXT_PTR +that converts a link field to a real, dereferencable, pointer to the link field +in the next element. This is intended only for debugging, or to traverse +the list after modification has ceased. There is otherwise no guarantee that +walking a stack using this macro will produce any kind of consistent +picture of the data structure. diff --git a/libatomic_ops/README_win32.txt b/libatomic_ops/README_win32.txt new file mode 100644 index 000000000..40735e786 --- /dev/null +++ b/libatomic_ops/README_win32.txt @@ -0,0 +1,46 @@ +Most of the atomic_ops functionality is available under Win32 with +the Microsoft tools, but the build process is somewhat different from +that on Linux/Unix platforms. + +To build and test the package: +1) Make sure the Microsoft command-line tools (e.g. nmake) are available. +2) Go to the src directory in the distribution and run +"nmake -f Makefile.msft check". This should build atomic_ops.lib and +atomic_ops_gpl.lib, and execute some tests. +Alternatively, CMake could be used (e.g., see how to in README_details.txt). + +To compile applications, you will need to retain or copy the following +pieces from the resulting src directory contents: + "atomic_ops.h" - Header file defining low-level primitives. This + includes files from the following folder. + "atomic_ops" - Subdirectory containing implementation header files. + The atomic_ops.h implementation is entirely in the + header files in Win32. + "atomic_ops.lib" - Library containing implementation of AO_pause() + defined in atomic_ops.c (AO_pause is needed for + the almost lock-free stack implementation). + "atomic_ops_stack.h" - Header file describing almost lock-free stack. + "atomic_ops_malloc.h" - Header file describing almost lock-free malloc. + "atomic_ops_gpl.lib" - Library containing implementation of the + above two. + +Note that atomic_ops_gpl.lib is covered by the GNU General Public License, +while the top 3 of these pieces allow use in proprietary code. + +There are several macros a client could use to configure the build with the +Microsoft tools (except for AO_CMPXCHG16B_AVAILABLE one, others should be +rarely needed in practice): +* AO_ASM_X64_AVAILABLE - inline assembly available (only x86_64) +* AO_ASSUME_VISTA - assume Windows Server 2003, Vista or later target (only + x86, implied if Visual Studio 2015 or older) +* AO_CMPXCHG16B_AVAILABLE - assume target is not old AMD Opteron chip (only + x86_64) +* AO_OLD_STYLE_INTERLOCKED_COMPARE_EXCHANGE - assume ancient MS VS Win32 + headers (only arm and x86) +* AO_PREFER_GENERALIZED - prefer generalized definitions to direct + assembly-based ones +* AO_UNIPROCESSOR - assume single-core target (only arm) +* AO_USE_INTERLOCKED_INTRINSICS - assume Win32 _Interlocked* primitives + available as intrinsics (only arm) +* AO_USE_PENTIUM4_INSTRS - use mfence instruction instead of xchg (only x86, + implied if SSE2 is available) diff --git a/libatomic_ops/autogen.sh b/libatomic_ops/autogen.sh new file mode 100755 index 000000000..98b91da93 --- /dev/null +++ b/libatomic_ops/autogen.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +# This script creates (or regenerates) configure (as well as aclocal.m4, +# config.h.in, Makefile.in, etc.) missing in the source repository. + +autoreconf -i + +echo +echo "Ready to run './configure'." diff --git a/libatomic_ops/configure.ac b/libatomic_ops/configure.ac new file mode 100644 index 000000000..f70921f89 --- /dev/null +++ b/libatomic_ops/configure.ac @@ -0,0 +1,261 @@ +# Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2009-2021 Ivan Maidanski +# +# 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. + +dnl Process this file with autoconf to produce configure. + +AC_INIT([libatomic_ops],[7.8.2],https://github.com/ivmai/libatomic_ops/issues) + +AC_PREREQ(2.61) +AC_CANONICAL_TARGET([]) +AC_CONFIG_SRCDIR(src/atomic_ops.c) +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE([foreign nostdinc]) +AM_MAINTAINER_MODE + +AC_CONFIG_HEADERS([src/config.h]) + +dnl Checks for programs. +AM_PROG_CC_C_O +AM_PROG_AS +AC_PROG_INSTALL +LT_INIT([disable-shared]) + +dnl Checks for functions. +AC_FUNC_MMAP + +# Determine PIC flag. +need_asm=false +PICFLAG= +AC_MSG_CHECKING(for PIC compiler flag) +if test "$GCC" = yes; then + old_CC="$CC" + if test -n "$CROSS_CC"; then + CC="$CROSS_CC" + fi + + case "$host" in + *-*-cygwin* | *-*-mingw* | *-*-msys*) + # Cygwin and Mingw[-w32/64] do not need -fPIC. + AC_MSG_RESULT([not needed]) + ;; + *) + AC_MSG_RESULT(-fPIC) + PICFLAG=-fPIC + AC_MSG_CHECKING(whether -fPIC compiler option causes __PIC__ definition) + # Workaround: at least GCC 3.4.6 (Solaris) does not define this macro. + old_CFLAGS="$CFLAGS" + CFLAGS="$PICFLAG $CFLAGS" + AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([ + #ifndef __PIC__ + # error + #endif + ])], [ac_cv_pic_macro=yes], [ac_cv_pic_macro=no]) + CFLAGS="$old_CFLAGS" + AC_MSG_RESULT($ac_cv_pic_macro) + AS_IF([test "$ac_cv_pic_macro" = yes], [], + [PICFLAG="-D__PIC__=1 $PICFLAG"]) + ;; + esac + + # Output all warnings. + AC_MSG_CHECKING([whether compiler supports -Wextra]) + old_CFLAGS="$CFLAGS" + CFLAGS="-Wextra $CFLAGS" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([])], + [ac_cv_cc_wextra=yes], [ac_cv_cc_wextra=no]) + CFLAGS="$old_CFLAGS" + AC_MSG_RESULT($ac_cv_cc_wextra) + AS_IF([test "$ac_cv_cc_wextra" = yes], [WEXTRA="-Wextra"], [WEXTRA="-W"]) + AC_MSG_CHECKING([whether compiler supports -Wpedantic]) + CFLAGS="-Wpedantic -Wno-long-long $CFLAGS" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +extern int quiet; + ])], [ac_cv_cc_pedantic=yes], [ac_cv_cc_pedantic=no]) + CFLAGS="$old_CFLAGS" + AC_MSG_RESULT($ac_cv_cc_pedantic) + WPEDANTIC= + AS_IF([test "$ac_cv_cc_pedantic" = yes], + [WPEDANTIC="-Wpedantic -Wno-long-long"]) + CFLAGS="-Wall $WEXTRA $WPEDANTIC $CFLAGS" + + CC="$old_CC" +else + case "$host" in + *-*-hpux*) + AC_MSG_RESULT([+Z]) + PICFLAG="+Z" + CFLAGS="+O2 -mt $CFLAGS" + ;; + *-*-solaris*) + AC_MSG_RESULT(-Kpic) + PICFLAG=-Kpic + CFLAGS="-O $CFLAGS" + need_asm=true + ;; + *-*-linux*) + AC_MSG_RESULT(-fPIC) + PICFLAG=-fPIC + # Any Linux compiler had better be gcc compatible. + ;; + *) + AC_MSG_RESULT([none]) + ;; + esac +fi + +AC_ARG_ENABLE(assertions, + [AS_HELP_STRING([--enable-assertions], [Assertion checking])]) +if test "$enable_assertions" != yes; then + AC_DEFINE([NDEBUG], 1, [Define to disable assertion checking.]) +fi + +AC_ARG_ENABLE(atomic-intrinsics, + [AS_HELP_STRING([--disable-atomic-intrinsics], + [Do not use GCC atomic intrinsics])]) +if test "$enable_atomic_intrinsics" = no; then + AC_DEFINE([AO_DISABLE_GCC_ATOMICS], 1, + [Define to avoid GCC atomic intrinsics even if available.]) +fi + +AC_ARG_ENABLE(gcov, AS_HELP_STRING([--enable-gcov], + [Turn on code coverage analysis])) +if test "$enable_gcov" = "yes"; then + CFLAGS="$CFLAGS --coverage" + # Turn off code optimization to get accurate line numbers. + CFLAGS=`echo "$CFLAGS" | sed -e 's/-O\(1\|2\|3\|4\|s\|fast\)\?//g'` +fi + +AC_ARG_ENABLE(gpl, + [AS_HELP_STRING([--disable-gpl], + [Do not build atomic_ops_gpl library])]) +AM_CONDITIONAL(ENABLE_GPL, test x$enable_gpl != xno) + +AC_ARG_ENABLE(docs, + [AS_HELP_STRING([--disable-docs], + [Do not build and install documentation])]) +AM_CONDITIONAL(ENABLE_DOCS, test x$enable_docs != xno) + +AC_SUBST(PICFLAG) +AC_SUBST(DEFS) + +dnl Extra user-defined C flags. +AC_SUBST([CFLAGS_EXTRA]) + +AH_TEMPLATE([_PTHREADS], [Indicates the use of pthreads (NetBSD).]) + +AH_TEMPLATE([AO_USE_NANOSLEEP], + [Use nanosleep() instead of select() (only if atomic operations \ + are emulated)]) +AH_TEMPLATE([AO_USE_NO_SIGNALS], + [Do not block signals in compare_and_swap (only if atomic operations \ + are emulated)]) +AH_TEMPLATE([AO_USE_WIN32_PTHREADS], + [Use Win32 Sleep() instead of select() (only if atomic operations \ + are emulated)]) +AH_TEMPLATE([AO_TRACE_MALLOC], [Trace AO_malloc/free calls (for debug only)]) + +dnl These macros are tested in public headers. +AH_TEMPLATE([AO_GENERALIZE_ASM_BOOL_CAS], + [Force compare_and_swap definition via fetch_compare_and_swap]) +AH_TEMPLATE([AO_PREFER_GENERALIZED], + [Prefer generalized definitions to direct assembly-based ones]) +AH_TEMPLATE([AO_USE_PTHREAD_DEFS], + [Emulate atomic operations via slow and async-signal-unsafe \ + pthread locking]) +AH_TEMPLATE([AO_CMPXCHG16B_AVAILABLE], + [Assume target is not old AMD Opteron chip (only x86_64)]) +AH_TEMPLATE([AO_FORCE_USE_SWP], + [Force test_and_set to use SWP instruction instead of LDREX/STREX \ + (only arm v6+)]) +AH_TEMPLATE([AO_NO_SPARC_V9], [Assume target is not sparc v9+ (only sparc)]) +AH_TEMPLATE([AO_UNIPROCESSOR], + [Assume single-core target (only arm v6+ or GCC intrinsics)]) +AH_TEMPLATE([AO_USE_PENTIUM4_INSTRS], + [Use Pentium 4 'mfence' instruction (only x86)]) +AH_TEMPLATE([AO_USE_SYNC_CAS_BUILTIN], + [Prefer GCC built-in CAS intrinsics in favor of inline assembly \ + (only gcc/x86, gcc/x86_64)]) +AH_TEMPLATE([AO_WEAK_DOUBLE_CAS_EMULATION], + [Emulate double-width CAS via pthread locking in case of no hardware \ + support (only gcc/x86_64, the emulation is unsafe)]) +AH_TEMPLATE([AO_PREFER_BUILTIN_ATOMICS], + [Prefer GCC atomic intrinsics over assembly-based implementation \ + even in case of inefficient implementation (do not use assembly for \ + any atomic_ops primitive if the atomic intrinsics are available)]) + +AC_DEFINE(_REENTRANT, 1, [Required define if using POSIX threads.]) + +# Libraries needed to support threads (if any). +have_pthreads=false +AC_CHECK_LIB(pthread, pthread_self, have_pthreads=true) +if test x$have_pthreads = xtrue; then + THREADDLLIBS=-lpthread + case "$host" in + *-*-netbsd*) + # Indicates the use of pthreads. + AC_DEFINE(_PTHREADS) + ;; + *-*-openbsd* | *-*-kfreebsd*-gnu | *-*-dgux*) + THREADDLLIBS=-pthread + ;; + *-*-cygwin* | *-*-darwin*) + # Cygwin does not have a real libpthread, so Libtool cannot link + # against it. + THREADDLLIBS= + ;; + *-*-mingw* | *-*-msys*) + # Use Win32 threads for tests anyway. + THREADDLLIBS= + # Skip test_atomic_pthreads. + have_pthreads=false + ;; + esac +else + AC_DEFINE([AO_NO_PTHREADS], 1, [No pthreads library available]) + # Assume VxWorks or Win32. + THREADDLLIBS= +fi +AC_SUBST(THREADDLLIBS) + +# AO API symbols export control. +# Compile with AO_DLL defined unless building static libraries. +if test x$enable_shared = xyes -a x$enable_static = xno; then + CFLAGS="-DAO_DLL $CFLAGS" +fi + +# Turn compiler warnings into errors, if requested. +# Note: this check is placed after AC_CHECK_LIB(pthread) to workaround +# a missing void in pthread_self declaration generated by autoconf. +AC_ARG_ENABLE(werror, [AS_HELP_STRING([--enable-werror], + [Pass -Werror to the C compiler])]) +if test "$enable_werror" = yes -a "$GCC" = yes; then + CFLAGS="-Werror $CFLAGS" +fi + +AM_CONDITIONAL(ENABLE_SHARED, test x$enable_shared = xyes) +AM_CONDITIONAL(HAVE_PTHREAD_H, test x$have_pthreads = xtrue) +AM_CONDITIONAL(NEED_ASM, test x$need_asm = xtrue) + +AC_CONFIG_FILES([ + Makefile + src/Makefile + tests/Makefile + pkgconfig/atomic_ops.pc + pkgconfig/atomic_ops-uninstalled.pc ]) + +AC_CONFIG_COMMANDS([default],[[]],[[ +PICFLAG="${PICFLAG}" +CC="${CC}" +DEFS="${DEFS}" +]]) +AC_OUTPUT diff --git a/libatomic_ops/m4/.gitignore b/libatomic_ops/m4/.gitignore new file mode 100644 index 000000000..4902d2ea4 --- /dev/null +++ b/libatomic_ops/m4/.gitignore @@ -0,0 +1,3 @@ +# Place holder to keep this directory in the Git repository. +* +!.gitignore diff --git a/libatomic_ops/pkgconfig/atomic_ops-uninstalled.pc.in b/libatomic_ops/pkgconfig/atomic_ops-uninstalled.pc.in new file mode 100644 index 000000000..3618f75b9 --- /dev/null +++ b/libatomic_ops/pkgconfig/atomic_ops-uninstalled.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +top_builddir=@abs_top_builddir@ +top_srcdir=@abs_top_srcdir@ + +Name: The atomic_ops library (uninstalled) +Description: Atomic memory update operations +Version: @PACKAGE_VERSION@ +Libs: ${top_builddir}/src/libatomic_ops.la +Cflags: -I${top_builddir}/src -I${top_srcdir}/src diff --git a/libatomic_ops/pkgconfig/atomic_ops.pc.in b/libatomic_ops/pkgconfig/atomic_ops.pc.in new file mode 100644 index 000000000..73cc499c3 --- /dev/null +++ b/libatomic_ops/pkgconfig/atomic_ops.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: The atomic_ops library +Description: Atomic memory update operations portable implementation +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -latomic_ops +Cflags: -I${includedir} diff --git a/libatomic_ops/src/Makefile.am b/libatomic_ops/src/Makefile.am new file mode 100644 index 000000000..43daa5ddd --- /dev/null +++ b/libatomic_ops/src/Makefile.am @@ -0,0 +1,268 @@ + +# Info (current:revision:age) for the Libtool versioning system. +# These numbers should be updated at most once just before the release, +# and, optionally, at most once during the development (after the release). +LIBATOMIC_OPS_VER_INFO = 3:0:2 +LIBATOMIC_OPS_GPL_VER_INFO = 3:1:2 + +AM_CFLAGS=@PICFLAG@ +AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src + +CFLAGS += $(CFLAGS_EXTRA) + +include_HEADERS = atomic_ops.h +lib_LTLIBRARIES = libatomic_ops.la +if NEED_ASM +libatomic_ops_la_SOURCES = atomic_ops.c atomic_ops_sysdeps.S +else +libatomic_ops_la_SOURCES = atomic_ops.c +endif +libatomic_ops_la_LDFLAGS = -version-info $(LIBATOMIC_OPS_VER_INFO) \ + -no-undefined + +if ENABLE_GPL +include_HEADERS += atomic_ops_malloc.h atomic_ops_stack.h +lib_LTLIBRARIES += libatomic_ops_gpl.la +libatomic_ops_gpl_la_SOURCES = atomic_ops_stack.c atomic_ops_malloc.c +libatomic_ops_gpl_la_LDFLAGS = -version-info $(LIBATOMIC_OPS_GPL_VER_INFO) \ + -no-undefined +libatomic_ops_gpl_la_LIBADD = libatomic_ops.la +endif + +EXTRA_DIST = Makefile.msft atomic_ops/sysdeps/README \ + atomic_ops/generalize-arithm.template \ + atomic_ops/generalize-small.template \ + atomic_ops/sysdeps/ao_t_is_int.template \ + atomic_ops/sysdeps/gcc/generic-arithm.template \ + atomic_ops/sysdeps/gcc/generic-small.template \ + atomic_ops/sysdeps/loadstore/acquire_release_volatile.template \ + atomic_ops/sysdeps/loadstore/atomic_load.template \ + atomic_ops/sysdeps/loadstore/atomic_store.template \ + atomic_ops/sysdeps/loadstore/ordered_loads_only.template \ + atomic_ops/sysdeps/loadstore/ordered_stores_only.template \ + atomic_ops/sysdeps/sunc/sparc.S + +BUILT_SOURCES = atomic_ops/generalize-arithm.h \ + atomic_ops/generalize-small.h \ + atomic_ops/sysdeps/ao_t_is_int.h \ + atomic_ops/sysdeps/gcc/generic-arithm.h \ + atomic_ops/sysdeps/gcc/generic-small.h \ + atomic_ops/sysdeps/loadstore/acquire_release_volatile.h \ + atomic_ops/sysdeps/loadstore/atomic_load.h \ + atomic_ops/sysdeps/loadstore/atomic_store.h \ + atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h \ + atomic_ops/sysdeps/loadstore/char_atomic_load.h \ + atomic_ops/sysdeps/loadstore/char_atomic_store.h \ + atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h \ + atomic_ops/sysdeps/loadstore/int_atomic_load.h \ + atomic_ops/sysdeps/loadstore/int_atomic_store.h \ + atomic_ops/sysdeps/loadstore/ordered_loads_only.h \ + atomic_ops/sysdeps/loadstore/ordered_stores_only.h \ + atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h \ + atomic_ops/sysdeps/loadstore/short_atomic_load.h \ + atomic_ops/sysdeps/loadstore/short_atomic_store.h + +#Private Headers +privatedir=${includedir}/ +nobase_private_HEADERS = atomic_ops/ao_version.h \ + atomic_ops/generalize.h \ + $(BUILT_SOURCES) \ + \ + atomic_ops/sysdeps/all_acquire_release_volatile.h \ + atomic_ops/sysdeps/all_aligned_atomic_load_store.h \ + atomic_ops/sysdeps/all_atomic_load_store.h \ + atomic_ops/sysdeps/all_atomic_only_load.h \ + atomic_ops/sysdeps/emul_cas.h \ + atomic_ops/sysdeps/generic_pthread.h \ + atomic_ops/sysdeps/ordered.h \ + atomic_ops/sysdeps/ordered_except_wr.h \ + atomic_ops/sysdeps/read_ordered.h \ + atomic_ops/sysdeps/standard_ao_double_t.h \ + atomic_ops/sysdeps/test_and_set_t_is_ao_t.h \ + atomic_ops/sysdeps/test_and_set_t_is_char.h \ + \ + atomic_ops/sysdeps/armcc/arm_v6.h \ + \ + atomic_ops/sysdeps/gcc/aarch64.h \ + atomic_ops/sysdeps/gcc/alpha.h \ + atomic_ops/sysdeps/gcc/arm.h \ + atomic_ops/sysdeps/gcc/avr32.h \ + atomic_ops/sysdeps/gcc/cris.h \ + atomic_ops/sysdeps/gcc/e2k.h \ + atomic_ops/sysdeps/gcc/generic.h \ + atomic_ops/sysdeps/gcc/hexagon.h \ + atomic_ops/sysdeps/gcc/hppa.h \ + atomic_ops/sysdeps/gcc/ia64.h \ + atomic_ops/sysdeps/gcc/m68k.h \ + atomic_ops/sysdeps/gcc/mips.h \ + atomic_ops/sysdeps/gcc/powerpc.h \ + atomic_ops/sysdeps/gcc/riscv.h \ + atomic_ops/sysdeps/gcc/s390.h \ + atomic_ops/sysdeps/gcc/sh.h \ + atomic_ops/sysdeps/gcc/sparc.h \ + atomic_ops/sysdeps/gcc/tile.h \ + atomic_ops/sysdeps/gcc/x86.h \ + \ + atomic_ops/sysdeps/hpc/hppa.h \ + atomic_ops/sysdeps/hpc/ia64.h \ + \ + atomic_ops/sysdeps/ibmc/powerpc.h \ + \ + atomic_ops/sysdeps/icc/ia64.h \ + \ + atomic_ops/sysdeps/loadstore/double_atomic_load_store.h \ + \ + atomic_ops/sysdeps/msftc/arm.h \ + atomic_ops/sysdeps/msftc/arm64.h \ + atomic_ops/sysdeps/msftc/common32_defs.h \ + atomic_ops/sysdeps/msftc/x86.h \ + atomic_ops/sysdeps/msftc/x86_64.h \ + \ + atomic_ops/sysdeps/sunc/sparc.h \ + atomic_ops/sysdeps/sunc/x86.h + +atomic_ops/generalize-small.h: atomic_ops/generalize-small.template + mkdir -p `dirname $@` + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g $? >> $@ + +atomic_ops/generalize-arithm.h: atomic_ops/generalize-arithm.template + mkdir -p `dirname $@` + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + +atomic_ops/sysdeps/ao_t_is_int.h: atomic_ops/sysdeps/ao_t_is_int.template + mkdir -p `dirname $@` + sed -e s:_XBAR::g $? > $@ + sed -e s:XBAR:full:g $? >> $@ + sed -e s:XBAR:acquire:g $? >> $@ + sed -e s:XBAR:release:g $? >> $@ + sed -e s:XBAR:write:g $? >> $@ + sed -e s:XBAR:read:g $? >> $@ + +atomic_ops/sysdeps/gcc/generic-arithm.h: \ + atomic_ops/sysdeps/gcc/generic-arithm.template + mkdir -p `dirname $@` + sed -e s:_XBAR::g -e s:XGCCBAR:RELAXED:g \ + -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + sed -e s:_XBAR::g -e s:XGCCBAR:RELAXED:g \ + -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:_XBAR::g -e s:XGCCBAR:RELAXED:g \ + -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:_XBAR::g -e s:XGCCBAR:RELAXED:g \ + -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + sed -e s:XBAR:acquire:g -e s:XGCCBAR:ACQUIRE:g \ + -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? >> $@ + sed -e s:XBAR:acquire:g -e s:XGCCBAR:ACQUIRE:g \ + -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:XBAR:acquire:g -e s:XGCCBAR:ACQUIRE:g \ + -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:XBAR:acquire:g -e s:XGCCBAR:ACQUIRE:g \ + -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + sed -e s:XBAR:release:g -e s:XGCCBAR:RELEASE:g \ + -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? >> $@ + sed -e s:XBAR:release:g -e s:XGCCBAR:RELEASE:g \ + -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:XBAR:release:g -e s:XGCCBAR:RELEASE:g \ + -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:XBAR:release:g -e s:XGCCBAR:RELEASE:g \ + -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + sed -e s:XBAR:full:g -e s:XGCCBAR:SEQ_CST:g \ + -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? >> $@ + sed -e s:XBAR:full:g -e s:XGCCBAR:SEQ_CST:g \ + -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:XBAR:full:g -e s:XGCCBAR:SEQ_CST:g \ + -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:XBAR:full:g -e s:XGCCBAR:SEQ_CST:g \ + -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + +atomic_ops/sysdeps/gcc/generic-small.h: \ + atomic_ops/sysdeps/gcc/generic-small.template + mkdir -p `dirname $@` + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + +atomic_ops/sysdeps/loadstore/ordered_loads_only.h: \ + atomic_ops/sysdeps/loadstore/ordered_loads_only.template + mkdir -p `dirname $@` + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g $? >> $@ + +atomic_ops/sysdeps/loadstore/ordered_stores_only.h: \ + atomic_ops/sysdeps/loadstore/ordered_stores_only.template + mkdir -p `dirname $@` + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g $? >> $@ + +atomic_ops/sysdeps/loadstore/acquire_release_volatile.h: \ + atomic_ops/sysdeps/loadstore/acquire_release_volatile.template + mkdir -p `dirname $@` + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? > $@ + +atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h: \ + atomic_ops/sysdeps/loadstore/acquire_release_volatile.template + mkdir -p `dirname $@` + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + +atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h: \ + atomic_ops/sysdeps/loadstore/acquire_release_volatile.template + mkdir -p `dirname $@` + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? > $@ + +atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h: \ + atomic_ops/sysdeps/loadstore/acquire_release_volatile.template + mkdir -p `dirname $@` + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? > $@ + +atomic_ops/sysdeps/loadstore/atomic_load.h: \ + atomic_ops/sysdeps/loadstore/atomic_load.template + mkdir -p `dirname $@` + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? > $@ + +atomic_ops/sysdeps/loadstore/char_atomic_load.h: \ + atomic_ops/sysdeps/loadstore/atomic_load.template + mkdir -p `dirname $@` + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + +atomic_ops/sysdeps/loadstore/int_atomic_load.h: \ + atomic_ops/sysdeps/loadstore/atomic_load.template + mkdir -p `dirname $@` + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? > $@ + +atomic_ops/sysdeps/loadstore/short_atomic_load.h: \ + atomic_ops/sysdeps/loadstore/atomic_load.template + mkdir -p `dirname $@` + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? > $@ + +atomic_ops/sysdeps/loadstore/atomic_store.h: \ + atomic_ops/sysdeps/loadstore/atomic_store.template + mkdir -p `dirname $@` + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g $? > $@ + +atomic_ops/sysdeps/loadstore/char_atomic_store.h: \ + atomic_ops/sysdeps/loadstore/atomic_store.template + mkdir -p `dirname $@` + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g $? > $@ + +atomic_ops/sysdeps/loadstore/int_atomic_store.h: \ + atomic_ops/sysdeps/loadstore/atomic_store.template + mkdir -p `dirname $@` + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g $? > $@ + +atomic_ops/sysdeps/loadstore/short_atomic_store.h: \ + atomic_ops/sysdeps/loadstore/atomic_store.template + mkdir -p `dirname $@` + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g $? > $@ diff --git a/libatomic_ops/src/Makefile.msft b/libatomic_ops/src/Makefile.msft new file mode 100644 index 000000000..c684a72bc --- /dev/null +++ b/libatomic_ops/src/Makefile.msft @@ -0,0 +1,72 @@ +# +# Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. +# +# The really trivial win32/VC++ Makefile. Note that atomic_ops.c defines +# only AO_pause (used by atomic_ops_stack). +# And we rely on a pre-built test_atomic_include.h and generalize-small.h, +# since we can't rely on sed. To run all the tests, type: +# nmake -f Makefile.msft check +# To install, copy atomic_ops.h and the atomic_ops/... tree to your favorite +# include directory. + +#!include + +CFLAGS_EXTRA= +CFLAGS=-O2 -W4 $(CFLAGS_EXTRA) + +LIB_OBJS=atomic_ops.obj +LIB_GPL_OBJS=atomic_ops_malloc.obj atomic_ops_stack.obj + +all-disable-gpl: atomic_ops.lib + +all: all-disable-gpl atomic_ops_gpl.lib + +atomic_ops.obj: + cl $(CFLAGS) -c atomic_ops.c + +atomic_ops_stack.obj: + cl $(CFLAGS) -c atomic_ops_stack.c + +atomic_ops_malloc.obj: + cl $(CFLAGS) /wd4127 -c atomic_ops_malloc.c + +atomic_ops.lib: $(LIB_OBJS) + lib /out:atomic_ops.lib $(LIB_OBJS) + +atomic_ops_gpl.lib: $(LIB_GPL_OBJS) + lib /out:atomic_ops_gpl.lib $(LIB_GPL_OBJS) + +..\tests\test_atomic: ..\tests\test_atomic.c ..\tests\test_atomic_include.h + cl $(CFLAGS) -I. ..\tests\test_atomic.c \ + /Fo..\tests\test_atomic /Fe..\tests\test_atomic + +..\tests\test_atomic_generalized: ..\tests\test_atomic.c \ + ..\tests\test_atomic_include.h + cl $(CFLAGS) -DAO_PREFER_GENERALIZED -I. ..\tests\test_atomic.c \ + /Fo..\tests\test_atomic_generalized \ + /Fe..\tests\test_atomic_generalized + +..\tests\test_malloc: ..\tests\test_malloc.c atomic_ops.lib atomic_ops_gpl.lib + cl $(CFLAGS) -I. ..\tests\test_malloc.c /Fo..\tests\test_malloc \ + /Fe..\tests\test_malloc atomic_ops.lib atomic_ops_gpl.lib + +..\tests\test_stack: ..\tests\test_stack.c atomic_ops.lib atomic_ops_gpl.lib + cl $(CFLAGS) -I. ..\tests\test_stack.c /Fo..\tests\test_stack \ + /Fe..\tests\test_stack atomic_ops.lib atomic_ops_gpl.lib + +check-deps: check-noautogen-deps ..\tests\test_atomic \ + ..\tests\test_atomic_generalized + +check: check-deps check-noautogen + @echo "The following will print some 'Missing ...' messages" + ..\tests\test_atomic + ..\tests\test_atomic_generalized + +check-noautogen-deps: ..\tests\test_malloc ..\tests\test_stack + +check-noautogen: check-noautogen-deps + ..\tests\test_malloc + ..\tests\test_stack + +clean: + del *.obj atomic_ops*.lib ..\tests\*.obj ..\tests\*.exe diff --git a/libatomic_ops/src/atomic_ops.c b/libatomic_ops/src/atomic_ops.c new file mode 100644 index 000000000..e1dfdb79e --- /dev/null +++ b/libatomic_ops/src/atomic_ops.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Initialized data and out-of-line functions to support atomic_ops.h + * go here. Currently this is needed only for pthread-based atomics + * emulation, or for compare-and-swap emulation. + * Pthreads emulation isn't useful on a native Windows platform, and + * cas emulation is not needed. Thus we skip this on Windows. + */ + +#if defined(HAVE_CONFIG_H) +# include "config.h" +#endif + +#if (defined(__hexagon__) || defined(__native_client__)) \ + && !defined(AO_USE_NO_SIGNALS) && !defined(AO_USE_NANOSLEEP) + /* Hexagon QuRT does not have sigprocmask (but Hexagon does not need */ + /* emulation, so it is OK not to bother about signals blocking). */ + /* Since NaCl is not recognized by configure yet, we do it here. */ +# define AO_USE_NO_SIGNALS +# define AO_USE_NANOSLEEP +#endif + +#if defined(AO_USE_WIN32_PTHREADS) && !defined(AO_USE_NO_SIGNALS) +# define AO_USE_NO_SIGNALS +#endif + +#if (defined(__CYGWIN__) || defined(__GLIBC__) || defined(__GNU__) \ + || defined(__linux__)) \ + && !defined(AO_USE_NO_SIGNALS) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE 1 +#endif + +#ifndef AO_BUILD +# define AO_BUILD +#endif + +#undef AO_REQUIRE_CAS +#include "atomic_ops.h" /* Without cas emulation! */ + +#ifdef __cplusplus + extern "C" { +#endif + +AO_API void AO_pause(int); /* defined below */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__) \ + || defined(AO_USE_NO_SIGNALS) + +#ifndef AO_NO_PTHREADS +# include +#endif + +#ifndef AO_USE_NO_SIGNALS +# include +#endif + +#ifdef AO_USE_NANOSLEEP + /* This requires _POSIX_TIMERS feature. */ +# include +# include +#elif defined(AO_USE_WIN32_PTHREADS) +# include /* for Sleep() */ +#elif defined(_HPUX_SOURCE) +# include +#else +# include +#endif + +#ifndef AO_HAVE_double_t +# include "atomic_ops/sysdeps/standard_ao_double_t.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +AO_API AO_t AO_fetch_compare_and_swap_emulation(volatile AO_t *addr, + AO_t old_val, AO_t new_val); + +AO_API int +AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2); + +AO_API void AO_store_full_emulation(volatile AO_t *addr, AO_t val); + +/* Lock for pthreads-based implementation. */ +#ifndef AO_NO_PTHREADS + AO_API pthread_mutex_t AO_pt_lock; +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#ifndef AO_NO_PTHREADS + pthread_mutex_t AO_pt_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +/* + * Out of line compare-and-swap emulation based on test and set. + * + * We use a small table of locks for different compare_and_swap locations. + * Before we update perform a compare-and-swap, we grab the corresponding + * lock. Different locations may hash to the same lock, but since we + * never acquire more than one lock at a time, this can't deadlock. + * We explicitly disable signals while we perform this operation. + * + * TODO: Probably also support emulation based on Lamport + * locks, since we may not have test_and_set either. + */ +#define AO_HASH_SIZE 16 + +#define AO_HASH(x) (((unsigned long)(x) >> 12) & (AO_HASH_SIZE-1)) + +static AO_TS_t AO_locks[AO_HASH_SIZE] = { + AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, + AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, + AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, + AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, +}; + +static void lock_ool(volatile AO_TS_t *l) +{ + int i = 0; + + while (AO_test_and_set_acquire(l) == AO_TS_SET) + AO_pause(++i); +} + +AO_INLINE void lock(volatile AO_TS_t *l) +{ + if (AO_EXPECT_FALSE(AO_test_and_set_acquire(l) == AO_TS_SET)) + lock_ool(l); +} + +AO_INLINE void unlock(volatile AO_TS_t *l) +{ + AO_CLEAR(l); +} + +#ifndef AO_USE_NO_SIGNALS + static sigset_t all_sigs; + static volatile AO_t initialized = 0; + static volatile AO_TS_t init_lock = AO_TS_INITIALIZER; + + AO_INLINE void block_all_signals(sigset_t *old_sigs_ptr) + { + if (AO_EXPECT_FALSE(!AO_load_acquire(&initialized))) + { + lock(&init_lock); + if (!initialized) + sigfillset(&all_sigs); + unlock(&init_lock); + AO_store_release(&initialized, 1); + } + sigprocmask(SIG_BLOCK, &all_sigs, old_sigs_ptr); + /* Neither sigprocmask nor pthread_sigmask is 100% */ + /* guaranteed to work here. Sigprocmask is not */ + /* guaranteed be thread safe, and pthread_sigmask */ + /* is not async-signal-safe. Under linuxthreads, */ + /* sigprocmask may block some pthreads-internal */ + /* signals. So long as we do that for short periods, */ + /* we should be OK. */ + } +#endif /* !AO_USE_NO_SIGNALS */ + +AO_API AO_t AO_fetch_compare_and_swap_emulation(volatile AO_t *addr, + AO_t old_val, AO_t new_val) +{ + AO_TS_t *my_lock = AO_locks + AO_HASH(addr); + AO_t fetched_val; + +# ifndef AO_USE_NO_SIGNALS + sigset_t old_sigs; + block_all_signals(&old_sigs); +# endif + lock(my_lock); + fetched_val = *addr; + if (fetched_val == old_val) + *addr = new_val; + unlock(my_lock); +# ifndef AO_USE_NO_SIGNALS + sigprocmask(SIG_SETMASK, &old_sigs, NULL); +# endif + return fetched_val; +} + +AO_API int +AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) +{ + AO_TS_t *my_lock = AO_locks + AO_HASH(addr); + int result; + +# ifndef AO_USE_NO_SIGNALS + sigset_t old_sigs; + block_all_signals(&old_sigs); +# endif + lock(my_lock); + if (addr -> AO_val1 == old_val1 && addr -> AO_val2 == old_val2) + { + addr -> AO_val1 = new_val1; + addr -> AO_val2 = new_val2; + result = 1; + } + else + result = 0; + unlock(my_lock); +# ifndef AO_USE_NO_SIGNALS + sigprocmask(SIG_SETMASK, &old_sigs, NULL); +# endif + return result; +} + +AO_API void AO_store_full_emulation(volatile AO_t *addr, AO_t val) +{ + AO_TS_t *my_lock = AO_locks + AO_HASH(addr); + lock(my_lock); + *addr = val; + unlock(my_lock); +} + +#else /* Non-posix platform */ + +# include + +# define AO_USE_WIN32_PTHREADS + /* define to use Sleep() */ + + extern int AO_non_posix_implementation_is_entirely_in_headers; + +#endif + +static AO_t spin_dummy = 1; + +/* Spin for 2**n units. */ +static void AO_spin(int n) +{ + AO_t j = AO_load(&spin_dummy); + int i = 2 << n; + + while (i-- > 0) + j += (j - 1) << 2; + /* Given 'spin_dummy' is initialized to 1, j is 1 after the loop. */ + AO_store(&spin_dummy, j); +} + +AO_API void AO_pause(int n) +{ + if (n < 12) + AO_spin(n); + else + { +# ifdef AO_USE_NANOSLEEP + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = n > 28 ? 100000L * 1000 : 1L << (n - 2); + nanosleep(&ts, 0); +# elif defined(AO_USE_WIN32_PTHREADS) + Sleep(n > 28 ? 100 /* millis */ + : n < 22 ? 1 : (DWORD)1 << (n - 22)); +# else + struct timeval tv; + /* Short async-signal-safe sleep. */ + int usec = n > 28 ? 100000 : 1 << (n - 12); + /* Use an intermediate variable (of int type) to avoid */ + /* "shift followed by widening conversion" warning. */ + + tv.tv_sec = 0; + tv.tv_usec = usec; + (void)select(0, 0, 0, 0, &tv); +# endif + } +} diff --git a/libatomic_ops/src/atomic_ops.h b/libatomic_ops/src/atomic_ops.h new file mode 100644 index 000000000..a2545a372 --- /dev/null +++ b/libatomic_ops/src/atomic_ops.h @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef AO_ATOMIC_OPS_H +#define AO_ATOMIC_OPS_H + +#include "atomic_ops/ao_version.h" + /* Define version numbers here to allow */ + /* test on build machines for cross-builds. */ + +#include +#include + +/* We define various atomic operations on memory in a */ +/* machine-specific way. Unfortunately, this is complicated */ +/* by the fact that these may or may not be combined with */ +/* various memory barriers. Thus the actual operations we */ +/* define have the form AO__, for all */ +/* plausible combinations of and . */ +/* This of course results in a mild combinatorial explosion. */ +/* To deal with it, we try to generate derived */ +/* definitions for as many of the combinations as we can, as */ +/* automatically as possible. */ +/* */ +/* Our assumption throughout is that the programmer will */ +/* specify the least demanding operation and memory barrier */ +/* that will guarantee correctness for the implementation. */ +/* Our job is to find the least expensive way to implement it */ +/* on the applicable hardware. In many cases that will */ +/* involve, for example, a stronger memory barrier, or a */ +/* combination of hardware primitives. */ +/* */ +/* Conventions: */ +/* "plain" atomic operations are not guaranteed to include */ +/* a barrier. The suffix in the name specifies the barrier */ +/* type. Suffixes are: */ +/* _release: Earlier operations may not be delayed past it. */ +/* _acquire: Later operations may not move ahead of it. */ +/* _read: Subsequent reads must follow this operation and */ +/* preceding reads. */ +/* _write: Earlier writes precede both this operation and */ +/* later writes. */ +/* _full: Ordered with respect to both earlier and later memory */ +/* operations. */ +/* _release_write: Ordered with respect to earlier writes. */ +/* _acquire_read: Ordered with respect to later reads. */ +/* */ +/* Currently we try to define the following atomic memory */ +/* operations, in combination with the above barriers: */ +/* AO_nop */ +/* AO_load */ +/* AO_store */ +/* AO_test_and_set (binary) */ +/* AO_fetch_and_add */ +/* AO_fetch_and_add1 */ +/* AO_fetch_and_sub1 */ +/* AO_and */ +/* AO_or */ +/* AO_xor */ +/* AO_compare_and_swap */ +/* AO_fetch_compare_and_swap */ +/* */ +/* Note that atomicity guarantees are valid only if both */ +/* readers and writers use AO_ operations to access the */ +/* shared value, while ordering constraints are intended to */ +/* apply all memory operations. If a location can potentially */ +/* be accessed simultaneously from multiple threads, and one of */ +/* those accesses may be a write access, then all such */ +/* accesses to that location should be through AO_ primitives. */ +/* However if AO_ operations enforce sufficient ordering to */ +/* ensure that a location x cannot be accessed concurrently, */ +/* or can only be read concurrently, then x can be accessed */ +/* via ordinary references and assignments. */ +/* */ +/* AO_compare_and_swap takes an address and an expected old */ +/* value and a new value, and returns an int. Non-zero result */ +/* indicates that it succeeded. */ +/* AO_fetch_compare_and_swap takes an address and an expected */ +/* old value and a new value, and returns the real old value. */ +/* The operation succeeded if and only if the expected old */ +/* value matches the old value returned. */ +/* */ +/* Test_and_set takes an address, atomically replaces it by */ +/* AO_TS_SET, and returns the prior value. */ +/* An AO_TS_t location can be reset with the */ +/* AO_CLEAR macro, which normally uses AO_store_release. */ +/* AO_fetch_and_add takes an address and an AO_t increment */ +/* value. The AO_fetch_and_add1 and AO_fetch_and_sub1 variants */ +/* are provided, since they allow faster implementations on */ +/* some hardware. AO_and, AO_or, AO_xor do atomically and, or, */ +/* xor (respectively) an AO_t value into a memory location, */ +/* but do not provide access to the original. */ +/* */ +/* We expect this list to grow slowly over time. */ +/* */ +/* Note that AO_nop_full is a full memory barrier. */ +/* */ +/* Note that if some data is initialized with */ +/* data.x = ...; data.y = ...; ... */ +/* AO_store_release_write(&data_is_initialized, 1) */ +/* then data is guaranteed to be initialized after the test */ +/* if (AO_load_acquire_read(&data_is_initialized)) ... */ +/* succeeds. Furthermore, this should generate near-optimal */ +/* code on all common platforms. */ +/* */ +/* All operations operate on unsigned AO_t, which */ +/* is the natural word size, and usually unsigned long. */ +/* It is possible to check whether a particular operation op */ +/* is available on a particular platform by checking whether */ +/* AO_HAVE_op is defined. We make heavy use of these macros */ +/* internally. */ + +/* The rest of this file basically has three sections: */ +/* */ +/* Some utility and default definitions. */ +/* */ +/* The architecture dependent section: */ +/* This defines atomic operations that have direct hardware */ +/* support on a particular platform, mostly by including the */ +/* appropriate compiler- and hardware-dependent file. */ +/* */ +/* The synthesis section: */ +/* This tries to define other atomic operations in terms of */ +/* those that are explicitly available on the platform. */ +/* This section is hardware independent. */ +/* We make no attempt to synthesize operations in ways that */ +/* effectively introduce locks, except for the debugging/demo */ +/* pthread-based implementation at the beginning. A more */ +/* realistic implementation that falls back to locks could be */ +/* added as a higher layer. But that would sacrifice */ +/* usability from signal handlers. */ +/* The synthesis section is implemented almost entirely in */ +/* atomic_ops/generalize.h. */ + +/* Some common defaults. Overridden for some architectures. */ +#define AO_t size_t + +/* The test_and_set primitive returns an AO_TS_VAL_t value. */ +/* AO_TS_t is the type of an in-memory test-and-set location. */ + +#define AO_TS_INITIALIZER ((AO_TS_t)AO_TS_CLEAR) + +/* Convenient internal macro to test version of GCC. */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +# define AO_GNUC_PREREQ(major, minor) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((major) << 16) + (minor)) +#else +# define AO_GNUC_PREREQ(major, minor) 0 /* false */ +#endif + +/* Convenient internal macro to test version of Clang. */ +#if defined(__clang__) && defined(__clang_major__) +# define AO_CLANG_PREREQ(major, minor) \ + ((__clang_major__ << 16) + __clang_minor__ >= ((major) << 16) + (minor)) +#else +# define AO_CLANG_PREREQ(major, minor) 0 /* false */ +#endif + +/* Platform-dependent stuff: */ +#if (defined(__GNUC__) || defined(_MSC_VER) || defined(__INTEL_COMPILER) \ + || defined(__DMC__) || defined(__WATCOMC__)) && !defined(AO_NO_INLINE) +# define AO_INLINE static __inline +#elif defined(__sun) && !defined(AO_NO_INLINE) +# define AO_INLINE static inline +#else +# define AO_INLINE static +#endif + +#if AO_GNUC_PREREQ(3, 0) && !defined(LINT2) +# define AO_EXPECT_FALSE(expr) __builtin_expect(expr, 0) + /* Equivalent to (expr) but predict that usually (expr) == 0. */ +#else +# define AO_EXPECT_FALSE(expr) (expr) +#endif /* !__GNUC__ */ + +#if defined(__has_feature) + /* __has_feature() is supported. */ +# if __has_feature(address_sanitizer) +# define AO_ADDRESS_SANITIZER +# endif +# if __has_feature(memory_sanitizer) +# define AO_MEMORY_SANITIZER +# endif +# if __has_feature(thread_sanitizer) +# define AO_THREAD_SANITIZER +# endif +#else +# ifdef __SANITIZE_ADDRESS__ + /* GCC v4.8+ */ +# define AO_ADDRESS_SANITIZER +# endif +#endif /* !__has_feature */ + +#ifndef AO_ATTR_NO_SANITIZE_MEMORY +# ifndef AO_MEMORY_SANITIZER +# define AO_ATTR_NO_SANITIZE_MEMORY /* empty */ +# elif AO_CLANG_PREREQ(3, 8) +# define AO_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# else +# define AO_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +# endif +#endif /* !AO_ATTR_NO_SANITIZE_MEMORY */ + +#ifndef AO_ATTR_NO_SANITIZE_THREAD +# ifndef AO_THREAD_SANITIZER +# define AO_ATTR_NO_SANITIZE_THREAD /* empty */ +# elif AO_CLANG_PREREQ(3, 8) +# define AO_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread"))) +# else +# define AO_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) +# endif +#endif /* !AO_ATTR_NO_SANITIZE_THREAD */ + +#if (AO_GNUC_PREREQ(7, 5) || __STDC_VERSION__ >= 201112L) && !defined(LINT2) +# define AO_ALIGNOF_SUPPORTED 1 +#endif + +#if defined(AO_DLL) && !defined(AO_API) +# ifdef AO_BUILD +# if defined(__CEGCC__) || (defined(__MINGW32__) && !defined(__cplusplus)) +# define AO_API __declspec(dllexport) +# elif defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__) \ + || defined(__DMC__) || defined(__MINGW32__) || defined(__WATCOMC__) +# define AO_API extern __declspec(dllexport) +# endif +# else +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CEGCC__) \ + || defined(__CYGWIN__) || defined(__DMC__) +# define AO_API __declspec(dllimport) +# elif defined(__MINGW32_DELAY_LOAD__) +# define AO_API __declspec(dllexport) +# elif defined(__MINGW32__) || defined(__WATCOMC__) +# define AO_API extern __declspec(dllimport) +# endif +# endif +#endif /* AO_DLL */ + +#ifndef AO_API +# define AO_API extern +#endif + +#ifdef AO_ALIGNOF_SUPPORTED +# define AO_ASSERT_ADDR_ALIGNED(addr) \ + assert(((size_t)(addr) & (__alignof__(*(addr)) - 1)) == 0) +#else +# define AO_ASSERT_ADDR_ALIGNED(addr) \ + assert(((size_t)(addr) & (sizeof(*(addr)) - 1)) == 0) +#endif /* !AO_ALIGNOF_SUPPORTED */ + +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +# define AO_compiler_barrier() __asm__ __volatile__("" : : : "memory") +#elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \ + || defined(__WATCOMC__) +# if defined(_AMD64_) || defined(_M_X64) || _MSC_VER >= 1400 +# if defined(_WIN32_WCE) +/* # include */ +# elif defined(_MSC_VER) +# include +# endif +# pragma intrinsic(_ReadWriteBarrier) +# define AO_compiler_barrier() _ReadWriteBarrier() + /* We assume this does not generate a fence instruction. */ + /* The documentation is a bit unclear. */ +# else +# define AO_compiler_barrier() __asm { } + /* The preceding implementation may be preferable here too. */ + /* But the documentation warns about VC++ 2003 and earlier. */ +# endif +#elif defined(__INTEL_COMPILER) +# define AO_compiler_barrier() __memory_barrier() + /* FIXME: Too strong? IA64-only? */ +#elif defined(_HPUX_SOURCE) +# if defined(__ia64) +# include +# define AO_compiler_barrier() _Asm_sched_fence() +# else + /* FIXME - We do not know how to do this. This is a guess. */ + /* And probably a bad one. */ + static volatile int AO_barrier_dummy; +# define AO_compiler_barrier() (void)(AO_barrier_dummy = AO_barrier_dummy) +# endif +#else + /* We conjecture that the following usually gives us the right */ + /* semantics or an error. */ +# define AO_compiler_barrier() asm("") +#endif + +#if defined(AO_USE_PTHREAD_DEFS) +# include "atomic_ops/sysdeps/generic_pthread.h" +#endif /* AO_USE_PTHREAD_DEFS */ + +#if (defined(__CC_ARM) || defined(__ARMCC__)) && !defined(__GNUC__) \ + && !defined(AO_USE_PTHREAD_DEFS) +# include "atomic_ops/sysdeps/armcc/arm_v6.h" +# define AO_GENERALIZE_TWICE +#endif + +#if defined(__GNUC__) && !defined(AO_USE_PTHREAD_DEFS) \ + && !defined(__INTEL_COMPILER) +# if defined(__i386__) + /* We don't define AO_USE_SYNC_CAS_BUILTIN for x86 here because */ + /* it might require specifying additional options (like -march) */ + /* or additional link libraries (if -march is not specified). */ +# include "atomic_ops/sysdeps/gcc/x86.h" +# elif defined(__x86_64__) +# if AO_GNUC_PREREQ(4, 2) && !defined(AO_USE_SYNC_CAS_BUILTIN) + /* It is safe to use __sync CAS built-in on this architecture. */ +# define AO_USE_SYNC_CAS_BUILTIN +# endif +# include "atomic_ops/sysdeps/gcc/x86.h" +# elif defined(__ia64__) +# include "atomic_ops/sysdeps/gcc/ia64.h" +# define AO_GENERALIZE_TWICE +# elif defined(__hppa__) +# include "atomic_ops/sysdeps/gcc/hppa.h" +# define AO_CAN_EMUL_CAS +# elif defined(__alpha__) +# include "atomic_ops/sysdeps/gcc/alpha.h" +# define AO_GENERALIZE_TWICE +# elif defined(__s390__) +# include "atomic_ops/sysdeps/gcc/s390.h" +# elif defined(__sparc__) +# include "atomic_ops/sysdeps/gcc/sparc.h" +# define AO_CAN_EMUL_CAS +# elif defined(__m68k__) +# include "atomic_ops/sysdeps/gcc/m68k.h" +# elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \ + || defined(__powerpc64__) || defined(__ppc64__) || defined(_ARCH_PPC) +# include "atomic_ops/sysdeps/gcc/powerpc.h" +# elif defined(__aarch64__) +# include "atomic_ops/sysdeps/gcc/aarch64.h" +# define AO_CAN_EMUL_CAS +# elif defined(__arm__) +# include "atomic_ops/sysdeps/gcc/arm.h" +# define AO_CAN_EMUL_CAS +# elif defined(__cris__) || defined(CRIS) +# include "atomic_ops/sysdeps/gcc/cris.h" +# define AO_CAN_EMUL_CAS +# define AO_GENERALIZE_TWICE +# elif defined(__mips__) +# include "atomic_ops/sysdeps/gcc/mips.h" +# elif defined(__sh__) || defined(SH4) +# include "atomic_ops/sysdeps/gcc/sh.h" +# define AO_CAN_EMUL_CAS +# elif defined(__avr32__) +# include "atomic_ops/sysdeps/gcc/avr32.h" +# elif defined(__e2k__) +# include "atomic_ops/sysdeps/gcc/e2k.h" +# elif defined(__hexagon__) +# include "atomic_ops/sysdeps/gcc/hexagon.h" +# elif defined(__nios2__) +# include "atomic_ops/sysdeps/gcc/generic.h" +# define AO_CAN_EMUL_CAS +# elif defined(__riscv) +# include "atomic_ops/sysdeps/gcc/riscv.h" +# elif defined(__tile__) +# include "atomic_ops/sysdeps/gcc/tile.h" +# else /* etc. */ +# include "atomic_ops/sysdeps/gcc/generic.h" +# endif +#endif /* __GNUC__ && !AO_USE_PTHREAD_DEFS */ + +#if (defined(__IBMC__) || defined(__IBMCPP__)) && !defined(__GNUC__) \ + && !defined(AO_USE_PTHREAD_DEFS) +# if defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) \ + || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) \ + || defined(_ARCH_PWR) +# include "atomic_ops/sysdeps/ibmc/powerpc.h" +# define AO_GENERALIZE_TWICE +# endif +#endif + +#if defined(__INTEL_COMPILER) && !defined(AO_USE_PTHREAD_DEFS) +# if defined(__ia64__) +# include "atomic_ops/sysdeps/icc/ia64.h" +# define AO_GENERALIZE_TWICE +# endif +# if defined(__GNUC__) + /* Intel Compiler in GCC compatible mode */ +# if defined(__i386__) +# include "atomic_ops/sysdeps/gcc/x86.h" +# endif /* __i386__ */ +# if defined(__x86_64__) +# if (__INTEL_COMPILER > 1110) && !defined(AO_USE_SYNC_CAS_BUILTIN) +# define AO_USE_SYNC_CAS_BUILTIN +# endif +# include "atomic_ops/sysdeps/gcc/x86.h" +# endif /* __x86_64__ */ +# endif +#endif + +#if defined(_HPUX_SOURCE) && !defined(__GNUC__) && !defined(AO_USE_PTHREAD_DEFS) +# if defined(__ia64) +# include "atomic_ops/sysdeps/hpc/ia64.h" +# define AO_GENERALIZE_TWICE +# else +# include "atomic_ops/sysdeps/hpc/hppa.h" +# define AO_CAN_EMUL_CAS +# endif +#endif + +#if defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \ + || (defined(__WATCOMC__) && defined(__NT__)) +# if defined(_AMD64_) || defined(_M_X64) +# include "atomic_ops/sysdeps/msftc/x86_64.h" +# elif defined(_M_ARM64) +# include "atomic_ops/sysdeps/msftc/arm64.h" +# elif defined(_M_IX86) || defined(x86) +# include "atomic_ops/sysdeps/msftc/x86.h" +# elif defined(_M_ARM) || defined(ARM) || defined(_ARM_) +# include "atomic_ops/sysdeps/msftc/arm.h" +# define AO_GENERALIZE_TWICE +# endif +#endif + +#if defined(__sun) && !defined(__GNUC__) && !defined(AO_USE_PTHREAD_DEFS) + /* Note: use -DAO_USE_PTHREAD_DEFS if Sun CC does not handle inline asm. */ +# if defined(__i386) || defined(__x86_64) || defined(__amd64) +# include "atomic_ops/sysdeps/sunc/x86.h" +# endif +#endif + +#if !defined(__GNUC__) && (defined(sparc) || defined(__sparc)) \ + && !defined(AO_USE_PTHREAD_DEFS) +# include "atomic_ops/sysdeps/sunc/sparc.h" +# define AO_CAN_EMUL_CAS +#endif + +#if (defined(AO_REQUIRE_CAS) && !defined(AO_HAVE_compare_and_swap) \ + && !defined(AO_HAVE_fetch_compare_and_swap) \ + && !defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_fetch_compare_and_swap_full) \ + && !defined(AO_HAVE_compare_and_swap_acquire) \ + && !defined(AO_HAVE_fetch_compare_and_swap_acquire)) || defined(CPPCHECK) +# if defined(AO_CAN_EMUL_CAS) +# include "atomic_ops/sysdeps/emul_cas.h" +# elif !defined(CPPCHECK) +# error Cannot implement AO_compare_and_swap_full on this architecture. +# endif +#endif /* AO_REQUIRE_CAS && !AO_HAVE_compare_and_swap ... */ + +/* The most common way to clear a test-and-set location */ +/* at the end of a critical section. */ +#if defined(AO_AO_TS_T) && !defined(AO_HAVE_CLEAR) +# define AO_CLEAR(addr) AO_store_release((AO_TS_t *)(addr), AO_TS_CLEAR) +# define AO_HAVE_CLEAR +#endif +#if defined(AO_CHAR_TS_T) && !defined(AO_HAVE_CLEAR) +# define AO_CLEAR(addr) AO_char_store_release((AO_TS_t *)(addr), AO_TS_CLEAR) +# define AO_HAVE_CLEAR +#endif + +/* The generalization section. */ +#if !defined(AO_GENERALIZE_TWICE) && defined(AO_CAN_EMUL_CAS) \ + && !defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_fetch_compare_and_swap_full) +# define AO_GENERALIZE_TWICE +#endif + +/* Theoretically we should repeatedly include atomic_ops/generalize.h. */ +/* In fact, we observe that this converges after a small fixed number */ +/* of iterations, usually one. */ +#include "atomic_ops/generalize.h" + +#if !defined(AO_GENERALIZE_TWICE) \ + && defined(AO_HAVE_compare_double_and_swap_double) \ + && (!defined(AO_HAVE_double_load) || !defined(AO_HAVE_double_store)) +# define AO_GENERALIZE_TWICE +#endif + +#ifdef AO_T_IS_INT + /* Included after the first generalization pass. */ +# include "atomic_ops/sysdeps/ao_t_is_int.h" +# ifndef AO_GENERALIZE_TWICE + /* Always generalize again. */ +# define AO_GENERALIZE_TWICE +# endif +#endif /* AO_T_IS_INT */ + +#ifdef AO_GENERALIZE_TWICE +# include "atomic_ops/generalize.h" +#endif + +/* For compatibility with version 0.4 and earlier */ +#define AO_TS_T AO_TS_t +#define AO_T AO_t +#define AO_TS_VAL AO_TS_VAL_t + +#endif /* !AO_ATOMIC_OPS_H */ diff --git a/libatomic_ops/src/atomic_ops/ao_version.h b/libatomic_ops/src/atomic_ops/ao_version.h new file mode 100644 index 000000000..913e62a34 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/ao_version.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2003-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2011-2018 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef AO_ATOMIC_OPS_H +# error This file should not be included directly. +#endif + +/* The policy regarding version numbers: development code has odd */ +/* "minor" number (and "micro" part is 0); when development is finished */ +/* and a release is prepared, "minor" number is incremented (keeping */ +/* "micro" number still zero), whenever a defect is fixed a new release */ +/* is prepared incrementing "micro" part to odd value (the most stable */ +/* release has the biggest "micro" number). */ + +/* The version here should match that in configure.ac and README. */ +#define AO_VERSION_MAJOR 7 +#define AO_VERSION_MINOR 8 +#define AO_VERSION_MICRO 2 /* 7.8.2 */ diff --git a/libatomic_ops/src/atomic_ops/generalize-arithm.h b/libatomic_ops/src/atomic_ops/generalize-arithm.h new file mode 100644 index 000000000..515f52e9e --- /dev/null +++ b/libatomic_ops/src/atomic_ops/generalize-arithm.h @@ -0,0 +1,3408 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* char_compare_and_swap (based on fetch_compare_and_swap) */ +#if defined(AO_HAVE_char_fetch_compare_and_swap_full) \ + && !defined(AO_HAVE_char_compare_and_swap_full) + AO_INLINE int + AO_char_compare_and_swap_full(volatile unsigned/**/char *addr, unsigned/**/char old_val, + unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap_full(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_char_compare_and_swap_full +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap_acquire) \ + && !defined(AO_HAVE_char_compare_and_swap_acquire) + AO_INLINE int + AO_char_compare_and_swap_acquire(volatile unsigned/**/char *addr, unsigned/**/char old_val, + unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap_acquire(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_char_compare_and_swap_acquire +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap_release) \ + && !defined(AO_HAVE_char_compare_and_swap_release) + AO_INLINE int + AO_char_compare_and_swap_release(volatile unsigned/**/char *addr, unsigned/**/char old_val, + unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap_release(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_char_compare_and_swap_release +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap_write) \ + && !defined(AO_HAVE_char_compare_and_swap_write) + AO_INLINE int + AO_char_compare_and_swap_write(volatile unsigned/**/char *addr, unsigned/**/char old_val, + unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap_write(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_char_compare_and_swap_write +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap_read) \ + && !defined(AO_HAVE_char_compare_and_swap_read) + AO_INLINE int + AO_char_compare_and_swap_read(volatile unsigned/**/char *addr, unsigned/**/char old_val, + unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap_read(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_char_compare_and_swap_read +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap) \ + && !defined(AO_HAVE_char_compare_and_swap) + AO_INLINE int + AO_char_compare_and_swap(volatile unsigned/**/char *addr, unsigned/**/char old_val, + unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap(addr, old_val, new_val) == old_val; + } +# define AO_HAVE_char_compare_and_swap +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap_release_write) \ + && !defined(AO_HAVE_char_compare_and_swap_release_write) + AO_INLINE int + AO_char_compare_and_swap_release_write(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap_release_write(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_char_compare_and_swap_release_write +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap_acquire_read) \ + && !defined(AO_HAVE_char_compare_and_swap_acquire_read) + AO_INLINE int + AO_char_compare_and_swap_acquire_read(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_char_compare_and_swap_acquire_read +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap_dd_acquire_read) \ + && !defined(AO_HAVE_char_compare_and_swap_dd_acquire_read) + AO_INLINE int + AO_char_compare_and_swap_dd_acquire_read(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + return AO_char_fetch_compare_and_swap_dd_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_char_compare_and_swap_dd_acquire_read +#endif + +/* char_fetch_and_add */ +/* We first try to implement fetch_and_add variants in terms of the */ +/* corresponding compare_and_swap variants to minimize adding barriers. */ +#if defined(AO_HAVE_char_compare_and_swap_full) \ + && !defined(AO_HAVE_char_fetch_and_add_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/char + AO_char_fetch_and_add_full(volatile unsigned/**/char *addr, unsigned/**/char incr) + { + unsigned/**/char old; + + do + { + old = *(unsigned/**/char *)addr; + } + while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_full(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_char_fetch_and_add_full +#endif + +#if defined(AO_HAVE_char_compare_and_swap_acquire) \ + && !defined(AO_HAVE_char_fetch_and_add_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/char + AO_char_fetch_and_add_acquire(volatile unsigned/**/char *addr, unsigned/**/char incr) + { + unsigned/**/char old; + + do + { + old = *(unsigned/**/char *)addr; + } + while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_acquire(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_char_fetch_and_add_acquire +#endif + +#if defined(AO_HAVE_char_compare_and_swap_release) \ + && !defined(AO_HAVE_char_fetch_and_add_release) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/char + AO_char_fetch_and_add_release(volatile unsigned/**/char *addr, unsigned/**/char incr) + { + unsigned/**/char old; + + do + { + old = *(unsigned/**/char *)addr; + } + while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_release(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_char_fetch_and_add_release +#endif + +#if defined(AO_HAVE_char_compare_and_swap) \ + && !defined(AO_HAVE_char_fetch_and_add) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/char + AO_char_fetch_and_add(volatile unsigned/**/char *addr, unsigned/**/char incr) + { + unsigned/**/char old; + + do + { + old = *(unsigned/**/char *)addr; + } + while (AO_EXPECT_FALSE(!AO_char_compare_and_swap(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_char_fetch_and_add +#endif + +#if defined(AO_HAVE_char_fetch_and_add_full) +# if !defined(AO_HAVE_char_fetch_and_add_release) +# define AO_char_fetch_and_add_release(addr, val) \ + AO_char_fetch_and_add_full(addr, val) +# define AO_HAVE_char_fetch_and_add_release +# endif +# if !defined(AO_HAVE_char_fetch_and_add_acquire) +# define AO_char_fetch_and_add_acquire(addr, val) \ + AO_char_fetch_and_add_full(addr, val) +# define AO_HAVE_char_fetch_and_add_acquire +# endif +# if !defined(AO_HAVE_char_fetch_and_add_write) +# define AO_char_fetch_and_add_write(addr, val) \ + AO_char_fetch_and_add_full(addr, val) +# define AO_HAVE_char_fetch_and_add_write +# endif +# if !defined(AO_HAVE_char_fetch_and_add_read) +# define AO_char_fetch_and_add_read(addr, val) \ + AO_char_fetch_and_add_full(addr, val) +# define AO_HAVE_char_fetch_and_add_read +# endif +#endif /* AO_HAVE_char_fetch_and_add_full */ + +#if defined(AO_HAVE_char_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_fetch_and_add_acquire) + AO_INLINE unsigned/**/char + AO_char_fetch_and_add_acquire(volatile unsigned/**/char *addr, unsigned/**/char incr) + { + unsigned/**/char result = AO_char_fetch_and_add(addr, incr); + AO_nop_full(); + return result; + } +# define AO_HAVE_char_fetch_and_add_acquire +#endif +#if defined(AO_HAVE_char_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_fetch_and_add_release) +# define AO_char_fetch_and_add_release(addr, incr) \ + (AO_nop_full(), AO_char_fetch_and_add(addr, incr)) +# define AO_HAVE_char_fetch_and_add_release +#endif + +#if !defined(AO_HAVE_char_fetch_and_add) \ + && defined(AO_HAVE_char_fetch_and_add_release) +# define AO_char_fetch_and_add(addr, val) \ + AO_char_fetch_and_add_release(addr, val) +# define AO_HAVE_char_fetch_and_add +#endif +#if !defined(AO_HAVE_char_fetch_and_add) \ + && defined(AO_HAVE_char_fetch_and_add_acquire) +# define AO_char_fetch_and_add(addr, val) \ + AO_char_fetch_and_add_acquire(addr, val) +# define AO_HAVE_char_fetch_and_add +#endif +#if !defined(AO_HAVE_char_fetch_and_add) \ + && defined(AO_HAVE_char_fetch_and_add_write) +# define AO_char_fetch_and_add(addr, val) \ + AO_char_fetch_and_add_write(addr, val) +# define AO_HAVE_char_fetch_and_add +#endif +#if !defined(AO_HAVE_char_fetch_and_add) \ + && defined(AO_HAVE_char_fetch_and_add_read) +# define AO_char_fetch_and_add(addr, val) \ + AO_char_fetch_and_add_read(addr, val) +# define AO_HAVE_char_fetch_and_add +#endif + +#if defined(AO_HAVE_char_fetch_and_add_acquire) \ + && defined(AO_HAVE_nop_full) && !defined(AO_HAVE_char_fetch_and_add_full) +# define AO_char_fetch_and_add_full(addr, val) \ + (AO_nop_full(), AO_char_fetch_and_add_acquire(addr, val)) +# define AO_HAVE_char_fetch_and_add_full +#endif + +#if !defined(AO_HAVE_char_fetch_and_add_release_write) \ + && defined(AO_HAVE_char_fetch_and_add_write) +# define AO_char_fetch_and_add_release_write(addr, val) \ + AO_char_fetch_and_add_write(addr, val) +# define AO_HAVE_char_fetch_and_add_release_write +#endif +#if !defined(AO_HAVE_char_fetch_and_add_release_write) \ + && defined(AO_HAVE_char_fetch_and_add_release) +# define AO_char_fetch_and_add_release_write(addr, val) \ + AO_char_fetch_and_add_release(addr, val) +# define AO_HAVE_char_fetch_and_add_release_write +#endif + +#if !defined(AO_HAVE_char_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_char_fetch_and_add_read) +# define AO_char_fetch_and_add_acquire_read(addr, val) \ + AO_char_fetch_and_add_read(addr, val) +# define AO_HAVE_char_fetch_and_add_acquire_read +#endif +#if !defined(AO_HAVE_char_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_char_fetch_and_add_acquire) +# define AO_char_fetch_and_add_acquire_read(addr, val) \ + AO_char_fetch_and_add_acquire(addr, val) +# define AO_HAVE_char_fetch_and_add_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_char_fetch_and_add_acquire_read) +# define AO_char_fetch_and_add_dd_acquire_read(addr, val) \ + AO_char_fetch_and_add_acquire_read(addr, val) +# define AO_HAVE_char_fetch_and_add_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_char_fetch_and_add) +# define AO_char_fetch_and_add_dd_acquire_read(addr, val) \ + AO_char_fetch_and_add(addr, val) +# define AO_HAVE_char_fetch_and_add_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* char_fetch_and_add1 */ +#if defined(AO_HAVE_char_fetch_and_add_full) \ + && !defined(AO_HAVE_char_fetch_and_add1_full) +# define AO_char_fetch_and_add1_full(addr) \ + AO_char_fetch_and_add_full(addr, 1) +# define AO_HAVE_char_fetch_and_add1_full +#endif +#if defined(AO_HAVE_char_fetch_and_add_release) \ + && !defined(AO_HAVE_char_fetch_and_add1_release) +# define AO_char_fetch_and_add1_release(addr) \ + AO_char_fetch_and_add_release(addr, 1) +# define AO_HAVE_char_fetch_and_add1_release +#endif +#if defined(AO_HAVE_char_fetch_and_add_acquire) \ + && !defined(AO_HAVE_char_fetch_and_add1_acquire) +# define AO_char_fetch_and_add1_acquire(addr) \ + AO_char_fetch_and_add_acquire(addr, 1) +# define AO_HAVE_char_fetch_and_add1_acquire +#endif +#if defined(AO_HAVE_char_fetch_and_add_write) \ + && !defined(AO_HAVE_char_fetch_and_add1_write) +# define AO_char_fetch_and_add1_write(addr) \ + AO_char_fetch_and_add_write(addr, 1) +# define AO_HAVE_char_fetch_and_add1_write +#endif +#if defined(AO_HAVE_char_fetch_and_add_read) \ + && !defined(AO_HAVE_char_fetch_and_add1_read) +# define AO_char_fetch_and_add1_read(addr) \ + AO_char_fetch_and_add_read(addr, 1) +# define AO_HAVE_char_fetch_and_add1_read +#endif +#if defined(AO_HAVE_char_fetch_and_add_release_write) \ + && !defined(AO_HAVE_char_fetch_and_add1_release_write) +# define AO_char_fetch_and_add1_release_write(addr) \ + AO_char_fetch_and_add_release_write(addr, 1) +# define AO_HAVE_char_fetch_and_add1_release_write +#endif +#if defined(AO_HAVE_char_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_char_fetch_and_add1_acquire_read) +# define AO_char_fetch_and_add1_acquire_read(addr) \ + AO_char_fetch_and_add_acquire_read(addr, 1) +# define AO_HAVE_char_fetch_and_add1_acquire_read +#endif +#if defined(AO_HAVE_char_fetch_and_add) \ + && !defined(AO_HAVE_char_fetch_and_add1) +# define AO_char_fetch_and_add1(addr) AO_char_fetch_and_add(addr, 1) +# define AO_HAVE_char_fetch_and_add1 +#endif + +#if defined(AO_HAVE_char_fetch_and_add1_full) +# if !defined(AO_HAVE_char_fetch_and_add1_release) +# define AO_char_fetch_and_add1_release(addr) \ + AO_char_fetch_and_add1_full(addr) +# define AO_HAVE_char_fetch_and_add1_release +# endif +# if !defined(AO_HAVE_char_fetch_and_add1_acquire) +# define AO_char_fetch_and_add1_acquire(addr) \ + AO_char_fetch_and_add1_full(addr) +# define AO_HAVE_char_fetch_and_add1_acquire +# endif +# if !defined(AO_HAVE_char_fetch_and_add1_write) +# define AO_char_fetch_and_add1_write(addr) \ + AO_char_fetch_and_add1_full(addr) +# define AO_HAVE_char_fetch_and_add1_write +# endif +# if !defined(AO_HAVE_char_fetch_and_add1_read) +# define AO_char_fetch_and_add1_read(addr) \ + AO_char_fetch_and_add1_full(addr) +# define AO_HAVE_char_fetch_and_add1_read +# endif +#endif /* AO_HAVE_char_fetch_and_add1_full */ + +#if !defined(AO_HAVE_char_fetch_and_add1) \ + && defined(AO_HAVE_char_fetch_and_add1_release) +# define AO_char_fetch_and_add1(addr) AO_char_fetch_and_add1_release(addr) +# define AO_HAVE_char_fetch_and_add1 +#endif +#if !defined(AO_HAVE_char_fetch_and_add1) \ + && defined(AO_HAVE_char_fetch_and_add1_acquire) +# define AO_char_fetch_and_add1(addr) AO_char_fetch_and_add1_acquire(addr) +# define AO_HAVE_char_fetch_and_add1 +#endif +#if !defined(AO_HAVE_char_fetch_and_add1) \ + && defined(AO_HAVE_char_fetch_and_add1_write) +# define AO_char_fetch_and_add1(addr) AO_char_fetch_and_add1_write(addr) +# define AO_HAVE_char_fetch_and_add1 +#endif +#if !defined(AO_HAVE_char_fetch_and_add1) \ + && defined(AO_HAVE_char_fetch_and_add1_read) +# define AO_char_fetch_and_add1(addr) AO_char_fetch_and_add1_read(addr) +# define AO_HAVE_char_fetch_and_add1 +#endif + +#if defined(AO_HAVE_char_fetch_and_add1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_fetch_and_add1_full) +# define AO_char_fetch_and_add1_full(addr) \ + (AO_nop_full(), AO_char_fetch_and_add1_acquire(addr)) +# define AO_HAVE_char_fetch_and_add1_full +#endif + +#if !defined(AO_HAVE_char_fetch_and_add1_release_write) \ + && defined(AO_HAVE_char_fetch_and_add1_write) +# define AO_char_fetch_and_add1_release_write(addr) \ + AO_char_fetch_and_add1_write(addr) +# define AO_HAVE_char_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_char_fetch_and_add1_release_write) \ + && defined(AO_HAVE_char_fetch_and_add1_release) +# define AO_char_fetch_and_add1_release_write(addr) \ + AO_char_fetch_and_add1_release(addr) +# define AO_HAVE_char_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_char_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_char_fetch_and_add1_read) +# define AO_char_fetch_and_add1_acquire_read(addr) \ + AO_char_fetch_and_add1_read(addr) +# define AO_HAVE_char_fetch_and_add1_acquire_read +#endif +#if !defined(AO_HAVE_char_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_char_fetch_and_add1_acquire) +# define AO_char_fetch_and_add1_acquire_read(addr) \ + AO_char_fetch_and_add1_acquire(addr) +# define AO_HAVE_char_fetch_and_add1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_char_fetch_and_add1_acquire_read) +# define AO_char_fetch_and_add1_dd_acquire_read(addr) \ + AO_char_fetch_and_add1_acquire_read(addr) +# define AO_HAVE_char_fetch_and_add1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_char_fetch_and_add1) +# define AO_char_fetch_and_add1_dd_acquire_read(addr) \ + AO_char_fetch_and_add1(addr) +# define AO_HAVE_char_fetch_and_add1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* char_fetch_and_sub1 */ +#if defined(AO_HAVE_char_fetch_and_add_full) \ + && !defined(AO_HAVE_char_fetch_and_sub1_full) +# define AO_char_fetch_and_sub1_full(addr) \ + AO_char_fetch_and_add_full(addr, (unsigned/**/char)(-1)) +# define AO_HAVE_char_fetch_and_sub1_full +#endif +#if defined(AO_HAVE_char_fetch_and_add_release) \ + && !defined(AO_HAVE_char_fetch_and_sub1_release) +# define AO_char_fetch_and_sub1_release(addr) \ + AO_char_fetch_and_add_release(addr, (unsigned/**/char)(-1)) +# define AO_HAVE_char_fetch_and_sub1_release +#endif +#if defined(AO_HAVE_char_fetch_and_add_acquire) \ + && !defined(AO_HAVE_char_fetch_and_sub1_acquire) +# define AO_char_fetch_and_sub1_acquire(addr) \ + AO_char_fetch_and_add_acquire(addr, (unsigned/**/char)(-1)) +# define AO_HAVE_char_fetch_and_sub1_acquire +#endif +#if defined(AO_HAVE_char_fetch_and_add_write) \ + && !defined(AO_HAVE_char_fetch_and_sub1_write) +# define AO_char_fetch_and_sub1_write(addr) \ + AO_char_fetch_and_add_write(addr, (unsigned/**/char)(-1)) +# define AO_HAVE_char_fetch_and_sub1_write +#endif +#if defined(AO_HAVE_char_fetch_and_add_read) \ + && !defined(AO_HAVE_char_fetch_and_sub1_read) +# define AO_char_fetch_and_sub1_read(addr) \ + AO_char_fetch_and_add_read(addr, (unsigned/**/char)(-1)) +# define AO_HAVE_char_fetch_and_sub1_read +#endif +#if defined(AO_HAVE_char_fetch_and_add_release_write) \ + && !defined(AO_HAVE_char_fetch_and_sub1_release_write) +# define AO_char_fetch_and_sub1_release_write(addr) \ + AO_char_fetch_and_add_release_write(addr, (unsigned/**/char)(-1)) +# define AO_HAVE_char_fetch_and_sub1_release_write +#endif +#if defined(AO_HAVE_char_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_char_fetch_and_sub1_acquire_read) +# define AO_char_fetch_and_sub1_acquire_read(addr) \ + AO_char_fetch_and_add_acquire_read(addr, (unsigned/**/char)(-1)) +# define AO_HAVE_char_fetch_and_sub1_acquire_read +#endif +#if defined(AO_HAVE_char_fetch_and_add) \ + && !defined(AO_HAVE_char_fetch_and_sub1) +# define AO_char_fetch_and_sub1(addr) \ + AO_char_fetch_and_add(addr, (unsigned/**/char)(-1)) +# define AO_HAVE_char_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_char_fetch_and_sub1_full) +# if !defined(AO_HAVE_char_fetch_and_sub1_release) +# define AO_char_fetch_and_sub1_release(addr) \ + AO_char_fetch_and_sub1_full(addr) +# define AO_HAVE_char_fetch_and_sub1_release +# endif +# if !defined(AO_HAVE_char_fetch_and_sub1_acquire) +# define AO_char_fetch_and_sub1_acquire(addr) \ + AO_char_fetch_and_sub1_full(addr) +# define AO_HAVE_char_fetch_and_sub1_acquire +# endif +# if !defined(AO_HAVE_char_fetch_and_sub1_write) +# define AO_char_fetch_and_sub1_write(addr) \ + AO_char_fetch_and_sub1_full(addr) +# define AO_HAVE_char_fetch_and_sub1_write +# endif +# if !defined(AO_HAVE_char_fetch_and_sub1_read) +# define AO_char_fetch_and_sub1_read(addr) \ + AO_char_fetch_and_sub1_full(addr) +# define AO_HAVE_char_fetch_and_sub1_read +# endif +#endif /* AO_HAVE_char_fetch_and_sub1_full */ + +#if !defined(AO_HAVE_char_fetch_and_sub1) \ + && defined(AO_HAVE_char_fetch_and_sub1_release) +# define AO_char_fetch_and_sub1(addr) AO_char_fetch_and_sub1_release(addr) +# define AO_HAVE_char_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_char_fetch_and_sub1) \ + && defined(AO_HAVE_char_fetch_and_sub1_acquire) +# define AO_char_fetch_and_sub1(addr) AO_char_fetch_and_sub1_acquire(addr) +# define AO_HAVE_char_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_char_fetch_and_sub1) \ + && defined(AO_HAVE_char_fetch_and_sub1_write) +# define AO_char_fetch_and_sub1(addr) AO_char_fetch_and_sub1_write(addr) +# define AO_HAVE_char_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_char_fetch_and_sub1) \ + && defined(AO_HAVE_char_fetch_and_sub1_read) +# define AO_char_fetch_and_sub1(addr) AO_char_fetch_and_sub1_read(addr) +# define AO_HAVE_char_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_char_fetch_and_sub1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_fetch_and_sub1_full) +# define AO_char_fetch_and_sub1_full(addr) \ + (AO_nop_full(), AO_char_fetch_and_sub1_acquire(addr)) +# define AO_HAVE_char_fetch_and_sub1_full +#endif + +#if !defined(AO_HAVE_char_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_char_fetch_and_sub1_write) +# define AO_char_fetch_and_sub1_release_write(addr) \ + AO_char_fetch_and_sub1_write(addr) +# define AO_HAVE_char_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_char_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_char_fetch_and_sub1_release) +# define AO_char_fetch_and_sub1_release_write(addr) \ + AO_char_fetch_and_sub1_release(addr) +# define AO_HAVE_char_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_char_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_char_fetch_and_sub1_read) +# define AO_char_fetch_and_sub1_acquire_read(addr) \ + AO_char_fetch_and_sub1_read(addr) +# define AO_HAVE_char_fetch_and_sub1_acquire_read +#endif +#if !defined(AO_HAVE_char_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_char_fetch_and_sub1_acquire) +# define AO_char_fetch_and_sub1_acquire_read(addr) \ + AO_char_fetch_and_sub1_acquire(addr) +# define AO_HAVE_char_fetch_and_sub1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_char_fetch_and_sub1_acquire_read) +# define AO_char_fetch_and_sub1_dd_acquire_read(addr) \ + AO_char_fetch_and_sub1_acquire_read(addr) +# define AO_HAVE_char_fetch_and_sub1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_char_fetch_and_sub1) +# define AO_char_fetch_and_sub1_dd_acquire_read(addr) \ + AO_char_fetch_and_sub1(addr) +# define AO_HAVE_char_fetch_and_sub1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* char_and */ +#if defined(AO_HAVE_char_compare_and_swap_full) \ + && !defined(AO_HAVE_char_and_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_char_and_full(volatile unsigned/**/char *addr, unsigned/**/char value) + { + unsigned/**/char old; + + do + { + old = *(unsigned/**/char *)addr; + } + while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_full(addr, old, + old & value))); + } +# define AO_HAVE_char_and_full +#endif + +#if defined(AO_HAVE_char_and_full) +# if !defined(AO_HAVE_char_and_release) +# define AO_char_and_release(addr, val) AO_char_and_full(addr, val) +# define AO_HAVE_char_and_release +# endif +# if !defined(AO_HAVE_char_and_acquire) +# define AO_char_and_acquire(addr, val) AO_char_and_full(addr, val) +# define AO_HAVE_char_and_acquire +# endif +# if !defined(AO_HAVE_char_and_write) +# define AO_char_and_write(addr, val) AO_char_and_full(addr, val) +# define AO_HAVE_char_and_write +# endif +# if !defined(AO_HAVE_char_and_read) +# define AO_char_and_read(addr, val) AO_char_and_full(addr, val) +# define AO_HAVE_char_and_read +# endif +#endif /* AO_HAVE_char_and_full */ + +#if !defined(AO_HAVE_char_and) && defined(AO_HAVE_char_and_release) +# define AO_char_and(addr, val) AO_char_and_release(addr, val) +# define AO_HAVE_char_and +#endif +#if !defined(AO_HAVE_char_and) && defined(AO_HAVE_char_and_acquire) +# define AO_char_and(addr, val) AO_char_and_acquire(addr, val) +# define AO_HAVE_char_and +#endif +#if !defined(AO_HAVE_char_and) && defined(AO_HAVE_char_and_write) +# define AO_char_and(addr, val) AO_char_and_write(addr, val) +# define AO_HAVE_char_and +#endif +#if !defined(AO_HAVE_char_and) && defined(AO_HAVE_char_and_read) +# define AO_char_and(addr, val) AO_char_and_read(addr, val) +# define AO_HAVE_char_and +#endif + +#if defined(AO_HAVE_char_and_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_and_full) +# define AO_char_and_full(addr, val) \ + (AO_nop_full(), AO_char_and_acquire(addr, val)) +# define AO_HAVE_char_and_full +#endif + +#if !defined(AO_HAVE_char_and_release_write) \ + && defined(AO_HAVE_char_and_write) +# define AO_char_and_release_write(addr, val) AO_char_and_write(addr, val) +# define AO_HAVE_char_and_release_write +#endif +#if !defined(AO_HAVE_char_and_release_write) \ + && defined(AO_HAVE_char_and_release) +# define AO_char_and_release_write(addr, val) AO_char_and_release(addr, val) +# define AO_HAVE_char_and_release_write +#endif +#if !defined(AO_HAVE_char_and_acquire_read) \ + && defined(AO_HAVE_char_and_read) +# define AO_char_and_acquire_read(addr, val) AO_char_and_read(addr, val) +# define AO_HAVE_char_and_acquire_read +#endif +#if !defined(AO_HAVE_char_and_acquire_read) \ + && defined(AO_HAVE_char_and_acquire) +# define AO_char_and_acquire_read(addr, val) AO_char_and_acquire(addr, val) +# define AO_HAVE_char_and_acquire_read +#endif + +/* char_or */ +#if defined(AO_HAVE_char_compare_and_swap_full) \ + && !defined(AO_HAVE_char_or_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_char_or_full(volatile unsigned/**/char *addr, unsigned/**/char value) + { + unsigned/**/char old; + + do + { + old = *(unsigned/**/char *)addr; + } + while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_full(addr, old, + old | value))); + } +# define AO_HAVE_char_or_full +#endif + +#if defined(AO_HAVE_char_or_full) +# if !defined(AO_HAVE_char_or_release) +# define AO_char_or_release(addr, val) AO_char_or_full(addr, val) +# define AO_HAVE_char_or_release +# endif +# if !defined(AO_HAVE_char_or_acquire) +# define AO_char_or_acquire(addr, val) AO_char_or_full(addr, val) +# define AO_HAVE_char_or_acquire +# endif +# if !defined(AO_HAVE_char_or_write) +# define AO_char_or_write(addr, val) AO_char_or_full(addr, val) +# define AO_HAVE_char_or_write +# endif +# if !defined(AO_HAVE_char_or_read) +# define AO_char_or_read(addr, val) AO_char_or_full(addr, val) +# define AO_HAVE_char_or_read +# endif +#endif /* AO_HAVE_char_or_full */ + +#if !defined(AO_HAVE_char_or) && defined(AO_HAVE_char_or_release) +# define AO_char_or(addr, val) AO_char_or_release(addr, val) +# define AO_HAVE_char_or +#endif +#if !defined(AO_HAVE_char_or) && defined(AO_HAVE_char_or_acquire) +# define AO_char_or(addr, val) AO_char_or_acquire(addr, val) +# define AO_HAVE_char_or +#endif +#if !defined(AO_HAVE_char_or) && defined(AO_HAVE_char_or_write) +# define AO_char_or(addr, val) AO_char_or_write(addr, val) +# define AO_HAVE_char_or +#endif +#if !defined(AO_HAVE_char_or) && defined(AO_HAVE_char_or_read) +# define AO_char_or(addr, val) AO_char_or_read(addr, val) +# define AO_HAVE_char_or +#endif + +#if defined(AO_HAVE_char_or_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_or_full) +# define AO_char_or_full(addr, val) \ + (AO_nop_full(), AO_char_or_acquire(addr, val)) +# define AO_HAVE_char_or_full +#endif + +#if !defined(AO_HAVE_char_or_release_write) \ + && defined(AO_HAVE_char_or_write) +# define AO_char_or_release_write(addr, val) AO_char_or_write(addr, val) +# define AO_HAVE_char_or_release_write +#endif +#if !defined(AO_HAVE_char_or_release_write) \ + && defined(AO_HAVE_char_or_release) +# define AO_char_or_release_write(addr, val) AO_char_or_release(addr, val) +# define AO_HAVE_char_or_release_write +#endif +#if !defined(AO_HAVE_char_or_acquire_read) && defined(AO_HAVE_char_or_read) +# define AO_char_or_acquire_read(addr, val) AO_char_or_read(addr, val) +# define AO_HAVE_char_or_acquire_read +#endif +#if !defined(AO_HAVE_char_or_acquire_read) \ + && defined(AO_HAVE_char_or_acquire) +# define AO_char_or_acquire_read(addr, val) AO_char_or_acquire(addr, val) +# define AO_HAVE_char_or_acquire_read +#endif + +/* char_xor */ +#if defined(AO_HAVE_char_compare_and_swap_full) \ + && !defined(AO_HAVE_char_xor_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_char_xor_full(volatile unsigned/**/char *addr, unsigned/**/char value) + { + unsigned/**/char old; + + do + { + old = *(unsigned/**/char *)addr; + } + while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_full(addr, old, + old ^ value))); + } +# define AO_HAVE_char_xor_full +#endif + +#if defined(AO_HAVE_char_xor_full) +# if !defined(AO_HAVE_char_xor_release) +# define AO_char_xor_release(addr, val) AO_char_xor_full(addr, val) +# define AO_HAVE_char_xor_release +# endif +# if !defined(AO_HAVE_char_xor_acquire) +# define AO_char_xor_acquire(addr, val) AO_char_xor_full(addr, val) +# define AO_HAVE_char_xor_acquire +# endif +# if !defined(AO_HAVE_char_xor_write) +# define AO_char_xor_write(addr, val) AO_char_xor_full(addr, val) +# define AO_HAVE_char_xor_write +# endif +# if !defined(AO_HAVE_char_xor_read) +# define AO_char_xor_read(addr, val) AO_char_xor_full(addr, val) +# define AO_HAVE_char_xor_read +# endif +#endif /* AO_HAVE_char_xor_full */ + +#if !defined(AO_HAVE_char_xor) && defined(AO_HAVE_char_xor_release) +# define AO_char_xor(addr, val) AO_char_xor_release(addr, val) +# define AO_HAVE_char_xor +#endif +#if !defined(AO_HAVE_char_xor) && defined(AO_HAVE_char_xor_acquire) +# define AO_char_xor(addr, val) AO_char_xor_acquire(addr, val) +# define AO_HAVE_char_xor +#endif +#if !defined(AO_HAVE_char_xor) && defined(AO_HAVE_char_xor_write) +# define AO_char_xor(addr, val) AO_char_xor_write(addr, val) +# define AO_HAVE_char_xor +#endif +#if !defined(AO_HAVE_char_xor) && defined(AO_HAVE_char_xor_read) +# define AO_char_xor(addr, val) AO_char_xor_read(addr, val) +# define AO_HAVE_char_xor +#endif + +#if defined(AO_HAVE_char_xor_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_xor_full) +# define AO_char_xor_full(addr, val) \ + (AO_nop_full(), AO_char_xor_acquire(addr, val)) +# define AO_HAVE_char_xor_full +#endif + +#if !defined(AO_HAVE_char_xor_release_write) \ + && defined(AO_HAVE_char_xor_write) +# define AO_char_xor_release_write(addr, val) AO_char_xor_write(addr, val) +# define AO_HAVE_char_xor_release_write +#endif +#if !defined(AO_HAVE_char_xor_release_write) \ + && defined(AO_HAVE_char_xor_release) +# define AO_char_xor_release_write(addr, val) AO_char_xor_release(addr, val) +# define AO_HAVE_char_xor_release_write +#endif +#if !defined(AO_HAVE_char_xor_acquire_read) \ + && defined(AO_HAVE_char_xor_read) +# define AO_char_xor_acquire_read(addr, val) AO_char_xor_read(addr, val) +# define AO_HAVE_char_xor_acquire_read +#endif +#if !defined(AO_HAVE_char_xor_acquire_read) \ + && defined(AO_HAVE_char_xor_acquire) +# define AO_char_xor_acquire_read(addr, val) AO_char_xor_acquire(addr, val) +# define AO_HAVE_char_xor_acquire_read +#endif + +/* char_and/or/xor_dd_acquire_read are meaningless. */ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* short_compare_and_swap (based on fetch_compare_and_swap) */ +#if defined(AO_HAVE_short_fetch_compare_and_swap_full) \ + && !defined(AO_HAVE_short_compare_and_swap_full) + AO_INLINE int + AO_short_compare_and_swap_full(volatile unsigned/**/short *addr, unsigned/**/short old_val, + unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap_full(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_short_compare_and_swap_full +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap_acquire) \ + && !defined(AO_HAVE_short_compare_and_swap_acquire) + AO_INLINE int + AO_short_compare_and_swap_acquire(volatile unsigned/**/short *addr, unsigned/**/short old_val, + unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap_acquire(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_short_compare_and_swap_acquire +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap_release) \ + && !defined(AO_HAVE_short_compare_and_swap_release) + AO_INLINE int + AO_short_compare_and_swap_release(volatile unsigned/**/short *addr, unsigned/**/short old_val, + unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap_release(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_short_compare_and_swap_release +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap_write) \ + && !defined(AO_HAVE_short_compare_and_swap_write) + AO_INLINE int + AO_short_compare_and_swap_write(volatile unsigned/**/short *addr, unsigned/**/short old_val, + unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap_write(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_short_compare_and_swap_write +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap_read) \ + && !defined(AO_HAVE_short_compare_and_swap_read) + AO_INLINE int + AO_short_compare_and_swap_read(volatile unsigned/**/short *addr, unsigned/**/short old_val, + unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap_read(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_short_compare_and_swap_read +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap) \ + && !defined(AO_HAVE_short_compare_and_swap) + AO_INLINE int + AO_short_compare_and_swap(volatile unsigned/**/short *addr, unsigned/**/short old_val, + unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap(addr, old_val, new_val) == old_val; + } +# define AO_HAVE_short_compare_and_swap +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap_release_write) \ + && !defined(AO_HAVE_short_compare_and_swap_release_write) + AO_INLINE int + AO_short_compare_and_swap_release_write(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap_release_write(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_short_compare_and_swap_release_write +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap_acquire_read) \ + && !defined(AO_HAVE_short_compare_and_swap_acquire_read) + AO_INLINE int + AO_short_compare_and_swap_acquire_read(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_short_compare_and_swap_acquire_read +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap_dd_acquire_read) \ + && !defined(AO_HAVE_short_compare_and_swap_dd_acquire_read) + AO_INLINE int + AO_short_compare_and_swap_dd_acquire_read(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + return AO_short_fetch_compare_and_swap_dd_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_short_compare_and_swap_dd_acquire_read +#endif + +/* short_fetch_and_add */ +/* We first try to implement fetch_and_add variants in terms of the */ +/* corresponding compare_and_swap variants to minimize adding barriers. */ +#if defined(AO_HAVE_short_compare_and_swap_full) \ + && !defined(AO_HAVE_short_fetch_and_add_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/short + AO_short_fetch_and_add_full(volatile unsigned/**/short *addr, unsigned/**/short incr) + { + unsigned/**/short old; + + do + { + old = *(unsigned/**/short *)addr; + } + while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_full(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_short_fetch_and_add_full +#endif + +#if defined(AO_HAVE_short_compare_and_swap_acquire) \ + && !defined(AO_HAVE_short_fetch_and_add_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/short + AO_short_fetch_and_add_acquire(volatile unsigned/**/short *addr, unsigned/**/short incr) + { + unsigned/**/short old; + + do + { + old = *(unsigned/**/short *)addr; + } + while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_acquire(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_short_fetch_and_add_acquire +#endif + +#if defined(AO_HAVE_short_compare_and_swap_release) \ + && !defined(AO_HAVE_short_fetch_and_add_release) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/short + AO_short_fetch_and_add_release(volatile unsigned/**/short *addr, unsigned/**/short incr) + { + unsigned/**/short old; + + do + { + old = *(unsigned/**/short *)addr; + } + while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_release(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_short_fetch_and_add_release +#endif + +#if defined(AO_HAVE_short_compare_and_swap) \ + && !defined(AO_HAVE_short_fetch_and_add) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/short + AO_short_fetch_and_add(volatile unsigned/**/short *addr, unsigned/**/short incr) + { + unsigned/**/short old; + + do + { + old = *(unsigned/**/short *)addr; + } + while (AO_EXPECT_FALSE(!AO_short_compare_and_swap(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_short_fetch_and_add +#endif + +#if defined(AO_HAVE_short_fetch_and_add_full) +# if !defined(AO_HAVE_short_fetch_and_add_release) +# define AO_short_fetch_and_add_release(addr, val) \ + AO_short_fetch_and_add_full(addr, val) +# define AO_HAVE_short_fetch_and_add_release +# endif +# if !defined(AO_HAVE_short_fetch_and_add_acquire) +# define AO_short_fetch_and_add_acquire(addr, val) \ + AO_short_fetch_and_add_full(addr, val) +# define AO_HAVE_short_fetch_and_add_acquire +# endif +# if !defined(AO_HAVE_short_fetch_and_add_write) +# define AO_short_fetch_and_add_write(addr, val) \ + AO_short_fetch_and_add_full(addr, val) +# define AO_HAVE_short_fetch_and_add_write +# endif +# if !defined(AO_HAVE_short_fetch_and_add_read) +# define AO_short_fetch_and_add_read(addr, val) \ + AO_short_fetch_and_add_full(addr, val) +# define AO_HAVE_short_fetch_and_add_read +# endif +#endif /* AO_HAVE_short_fetch_and_add_full */ + +#if defined(AO_HAVE_short_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_fetch_and_add_acquire) + AO_INLINE unsigned/**/short + AO_short_fetch_and_add_acquire(volatile unsigned/**/short *addr, unsigned/**/short incr) + { + unsigned/**/short result = AO_short_fetch_and_add(addr, incr); + AO_nop_full(); + return result; + } +# define AO_HAVE_short_fetch_and_add_acquire +#endif +#if defined(AO_HAVE_short_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_fetch_and_add_release) +# define AO_short_fetch_and_add_release(addr, incr) \ + (AO_nop_full(), AO_short_fetch_and_add(addr, incr)) +# define AO_HAVE_short_fetch_and_add_release +#endif + +#if !defined(AO_HAVE_short_fetch_and_add) \ + && defined(AO_HAVE_short_fetch_and_add_release) +# define AO_short_fetch_and_add(addr, val) \ + AO_short_fetch_and_add_release(addr, val) +# define AO_HAVE_short_fetch_and_add +#endif +#if !defined(AO_HAVE_short_fetch_and_add) \ + && defined(AO_HAVE_short_fetch_and_add_acquire) +# define AO_short_fetch_and_add(addr, val) \ + AO_short_fetch_and_add_acquire(addr, val) +# define AO_HAVE_short_fetch_and_add +#endif +#if !defined(AO_HAVE_short_fetch_and_add) \ + && defined(AO_HAVE_short_fetch_and_add_write) +# define AO_short_fetch_and_add(addr, val) \ + AO_short_fetch_and_add_write(addr, val) +# define AO_HAVE_short_fetch_and_add +#endif +#if !defined(AO_HAVE_short_fetch_and_add) \ + && defined(AO_HAVE_short_fetch_and_add_read) +# define AO_short_fetch_and_add(addr, val) \ + AO_short_fetch_and_add_read(addr, val) +# define AO_HAVE_short_fetch_and_add +#endif + +#if defined(AO_HAVE_short_fetch_and_add_acquire) \ + && defined(AO_HAVE_nop_full) && !defined(AO_HAVE_short_fetch_and_add_full) +# define AO_short_fetch_and_add_full(addr, val) \ + (AO_nop_full(), AO_short_fetch_and_add_acquire(addr, val)) +# define AO_HAVE_short_fetch_and_add_full +#endif + +#if !defined(AO_HAVE_short_fetch_and_add_release_write) \ + && defined(AO_HAVE_short_fetch_and_add_write) +# define AO_short_fetch_and_add_release_write(addr, val) \ + AO_short_fetch_and_add_write(addr, val) +# define AO_HAVE_short_fetch_and_add_release_write +#endif +#if !defined(AO_HAVE_short_fetch_and_add_release_write) \ + && defined(AO_HAVE_short_fetch_and_add_release) +# define AO_short_fetch_and_add_release_write(addr, val) \ + AO_short_fetch_and_add_release(addr, val) +# define AO_HAVE_short_fetch_and_add_release_write +#endif + +#if !defined(AO_HAVE_short_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_short_fetch_and_add_read) +# define AO_short_fetch_and_add_acquire_read(addr, val) \ + AO_short_fetch_and_add_read(addr, val) +# define AO_HAVE_short_fetch_and_add_acquire_read +#endif +#if !defined(AO_HAVE_short_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_short_fetch_and_add_acquire) +# define AO_short_fetch_and_add_acquire_read(addr, val) \ + AO_short_fetch_and_add_acquire(addr, val) +# define AO_HAVE_short_fetch_and_add_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_short_fetch_and_add_acquire_read) +# define AO_short_fetch_and_add_dd_acquire_read(addr, val) \ + AO_short_fetch_and_add_acquire_read(addr, val) +# define AO_HAVE_short_fetch_and_add_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_short_fetch_and_add) +# define AO_short_fetch_and_add_dd_acquire_read(addr, val) \ + AO_short_fetch_and_add(addr, val) +# define AO_HAVE_short_fetch_and_add_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* short_fetch_and_add1 */ +#if defined(AO_HAVE_short_fetch_and_add_full) \ + && !defined(AO_HAVE_short_fetch_and_add1_full) +# define AO_short_fetch_and_add1_full(addr) \ + AO_short_fetch_and_add_full(addr, 1) +# define AO_HAVE_short_fetch_and_add1_full +#endif +#if defined(AO_HAVE_short_fetch_and_add_release) \ + && !defined(AO_HAVE_short_fetch_and_add1_release) +# define AO_short_fetch_and_add1_release(addr) \ + AO_short_fetch_and_add_release(addr, 1) +# define AO_HAVE_short_fetch_and_add1_release +#endif +#if defined(AO_HAVE_short_fetch_and_add_acquire) \ + && !defined(AO_HAVE_short_fetch_and_add1_acquire) +# define AO_short_fetch_and_add1_acquire(addr) \ + AO_short_fetch_and_add_acquire(addr, 1) +# define AO_HAVE_short_fetch_and_add1_acquire +#endif +#if defined(AO_HAVE_short_fetch_and_add_write) \ + && !defined(AO_HAVE_short_fetch_and_add1_write) +# define AO_short_fetch_and_add1_write(addr) \ + AO_short_fetch_and_add_write(addr, 1) +# define AO_HAVE_short_fetch_and_add1_write +#endif +#if defined(AO_HAVE_short_fetch_and_add_read) \ + && !defined(AO_HAVE_short_fetch_and_add1_read) +# define AO_short_fetch_and_add1_read(addr) \ + AO_short_fetch_and_add_read(addr, 1) +# define AO_HAVE_short_fetch_and_add1_read +#endif +#if defined(AO_HAVE_short_fetch_and_add_release_write) \ + && !defined(AO_HAVE_short_fetch_and_add1_release_write) +# define AO_short_fetch_and_add1_release_write(addr) \ + AO_short_fetch_and_add_release_write(addr, 1) +# define AO_HAVE_short_fetch_and_add1_release_write +#endif +#if defined(AO_HAVE_short_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_short_fetch_and_add1_acquire_read) +# define AO_short_fetch_and_add1_acquire_read(addr) \ + AO_short_fetch_and_add_acquire_read(addr, 1) +# define AO_HAVE_short_fetch_and_add1_acquire_read +#endif +#if defined(AO_HAVE_short_fetch_and_add) \ + && !defined(AO_HAVE_short_fetch_and_add1) +# define AO_short_fetch_and_add1(addr) AO_short_fetch_and_add(addr, 1) +# define AO_HAVE_short_fetch_and_add1 +#endif + +#if defined(AO_HAVE_short_fetch_and_add1_full) +# if !defined(AO_HAVE_short_fetch_and_add1_release) +# define AO_short_fetch_and_add1_release(addr) \ + AO_short_fetch_and_add1_full(addr) +# define AO_HAVE_short_fetch_and_add1_release +# endif +# if !defined(AO_HAVE_short_fetch_and_add1_acquire) +# define AO_short_fetch_and_add1_acquire(addr) \ + AO_short_fetch_and_add1_full(addr) +# define AO_HAVE_short_fetch_and_add1_acquire +# endif +# if !defined(AO_HAVE_short_fetch_and_add1_write) +# define AO_short_fetch_and_add1_write(addr) \ + AO_short_fetch_and_add1_full(addr) +# define AO_HAVE_short_fetch_and_add1_write +# endif +# if !defined(AO_HAVE_short_fetch_and_add1_read) +# define AO_short_fetch_and_add1_read(addr) \ + AO_short_fetch_and_add1_full(addr) +# define AO_HAVE_short_fetch_and_add1_read +# endif +#endif /* AO_HAVE_short_fetch_and_add1_full */ + +#if !defined(AO_HAVE_short_fetch_and_add1) \ + && defined(AO_HAVE_short_fetch_and_add1_release) +# define AO_short_fetch_and_add1(addr) AO_short_fetch_and_add1_release(addr) +# define AO_HAVE_short_fetch_and_add1 +#endif +#if !defined(AO_HAVE_short_fetch_and_add1) \ + && defined(AO_HAVE_short_fetch_and_add1_acquire) +# define AO_short_fetch_and_add1(addr) AO_short_fetch_and_add1_acquire(addr) +# define AO_HAVE_short_fetch_and_add1 +#endif +#if !defined(AO_HAVE_short_fetch_and_add1) \ + && defined(AO_HAVE_short_fetch_and_add1_write) +# define AO_short_fetch_and_add1(addr) AO_short_fetch_and_add1_write(addr) +# define AO_HAVE_short_fetch_and_add1 +#endif +#if !defined(AO_HAVE_short_fetch_and_add1) \ + && defined(AO_HAVE_short_fetch_and_add1_read) +# define AO_short_fetch_and_add1(addr) AO_short_fetch_and_add1_read(addr) +# define AO_HAVE_short_fetch_and_add1 +#endif + +#if defined(AO_HAVE_short_fetch_and_add1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_fetch_and_add1_full) +# define AO_short_fetch_and_add1_full(addr) \ + (AO_nop_full(), AO_short_fetch_and_add1_acquire(addr)) +# define AO_HAVE_short_fetch_and_add1_full +#endif + +#if !defined(AO_HAVE_short_fetch_and_add1_release_write) \ + && defined(AO_HAVE_short_fetch_and_add1_write) +# define AO_short_fetch_and_add1_release_write(addr) \ + AO_short_fetch_and_add1_write(addr) +# define AO_HAVE_short_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_short_fetch_and_add1_release_write) \ + && defined(AO_HAVE_short_fetch_and_add1_release) +# define AO_short_fetch_and_add1_release_write(addr) \ + AO_short_fetch_and_add1_release(addr) +# define AO_HAVE_short_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_short_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_short_fetch_and_add1_read) +# define AO_short_fetch_and_add1_acquire_read(addr) \ + AO_short_fetch_and_add1_read(addr) +# define AO_HAVE_short_fetch_and_add1_acquire_read +#endif +#if !defined(AO_HAVE_short_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_short_fetch_and_add1_acquire) +# define AO_short_fetch_and_add1_acquire_read(addr) \ + AO_short_fetch_and_add1_acquire(addr) +# define AO_HAVE_short_fetch_and_add1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_short_fetch_and_add1_acquire_read) +# define AO_short_fetch_and_add1_dd_acquire_read(addr) \ + AO_short_fetch_and_add1_acquire_read(addr) +# define AO_HAVE_short_fetch_and_add1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_short_fetch_and_add1) +# define AO_short_fetch_and_add1_dd_acquire_read(addr) \ + AO_short_fetch_and_add1(addr) +# define AO_HAVE_short_fetch_and_add1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* short_fetch_and_sub1 */ +#if defined(AO_HAVE_short_fetch_and_add_full) \ + && !defined(AO_HAVE_short_fetch_and_sub1_full) +# define AO_short_fetch_and_sub1_full(addr) \ + AO_short_fetch_and_add_full(addr, (unsigned/**/short)(-1)) +# define AO_HAVE_short_fetch_and_sub1_full +#endif +#if defined(AO_HAVE_short_fetch_and_add_release) \ + && !defined(AO_HAVE_short_fetch_and_sub1_release) +# define AO_short_fetch_and_sub1_release(addr) \ + AO_short_fetch_and_add_release(addr, (unsigned/**/short)(-1)) +# define AO_HAVE_short_fetch_and_sub1_release +#endif +#if defined(AO_HAVE_short_fetch_and_add_acquire) \ + && !defined(AO_HAVE_short_fetch_and_sub1_acquire) +# define AO_short_fetch_and_sub1_acquire(addr) \ + AO_short_fetch_and_add_acquire(addr, (unsigned/**/short)(-1)) +# define AO_HAVE_short_fetch_and_sub1_acquire +#endif +#if defined(AO_HAVE_short_fetch_and_add_write) \ + && !defined(AO_HAVE_short_fetch_and_sub1_write) +# define AO_short_fetch_and_sub1_write(addr) \ + AO_short_fetch_and_add_write(addr, (unsigned/**/short)(-1)) +# define AO_HAVE_short_fetch_and_sub1_write +#endif +#if defined(AO_HAVE_short_fetch_and_add_read) \ + && !defined(AO_HAVE_short_fetch_and_sub1_read) +# define AO_short_fetch_and_sub1_read(addr) \ + AO_short_fetch_and_add_read(addr, (unsigned/**/short)(-1)) +# define AO_HAVE_short_fetch_and_sub1_read +#endif +#if defined(AO_HAVE_short_fetch_and_add_release_write) \ + && !defined(AO_HAVE_short_fetch_and_sub1_release_write) +# define AO_short_fetch_and_sub1_release_write(addr) \ + AO_short_fetch_and_add_release_write(addr, (unsigned/**/short)(-1)) +# define AO_HAVE_short_fetch_and_sub1_release_write +#endif +#if defined(AO_HAVE_short_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_short_fetch_and_sub1_acquire_read) +# define AO_short_fetch_and_sub1_acquire_read(addr) \ + AO_short_fetch_and_add_acquire_read(addr, (unsigned/**/short)(-1)) +# define AO_HAVE_short_fetch_and_sub1_acquire_read +#endif +#if defined(AO_HAVE_short_fetch_and_add) \ + && !defined(AO_HAVE_short_fetch_and_sub1) +# define AO_short_fetch_and_sub1(addr) \ + AO_short_fetch_and_add(addr, (unsigned/**/short)(-1)) +# define AO_HAVE_short_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_short_fetch_and_sub1_full) +# if !defined(AO_HAVE_short_fetch_and_sub1_release) +# define AO_short_fetch_and_sub1_release(addr) \ + AO_short_fetch_and_sub1_full(addr) +# define AO_HAVE_short_fetch_and_sub1_release +# endif +# if !defined(AO_HAVE_short_fetch_and_sub1_acquire) +# define AO_short_fetch_and_sub1_acquire(addr) \ + AO_short_fetch_and_sub1_full(addr) +# define AO_HAVE_short_fetch_and_sub1_acquire +# endif +# if !defined(AO_HAVE_short_fetch_and_sub1_write) +# define AO_short_fetch_and_sub1_write(addr) \ + AO_short_fetch_and_sub1_full(addr) +# define AO_HAVE_short_fetch_and_sub1_write +# endif +# if !defined(AO_HAVE_short_fetch_and_sub1_read) +# define AO_short_fetch_and_sub1_read(addr) \ + AO_short_fetch_and_sub1_full(addr) +# define AO_HAVE_short_fetch_and_sub1_read +# endif +#endif /* AO_HAVE_short_fetch_and_sub1_full */ + +#if !defined(AO_HAVE_short_fetch_and_sub1) \ + && defined(AO_HAVE_short_fetch_and_sub1_release) +# define AO_short_fetch_and_sub1(addr) AO_short_fetch_and_sub1_release(addr) +# define AO_HAVE_short_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_short_fetch_and_sub1) \ + && defined(AO_HAVE_short_fetch_and_sub1_acquire) +# define AO_short_fetch_and_sub1(addr) AO_short_fetch_and_sub1_acquire(addr) +# define AO_HAVE_short_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_short_fetch_and_sub1) \ + && defined(AO_HAVE_short_fetch_and_sub1_write) +# define AO_short_fetch_and_sub1(addr) AO_short_fetch_and_sub1_write(addr) +# define AO_HAVE_short_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_short_fetch_and_sub1) \ + && defined(AO_HAVE_short_fetch_and_sub1_read) +# define AO_short_fetch_and_sub1(addr) AO_short_fetch_and_sub1_read(addr) +# define AO_HAVE_short_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_short_fetch_and_sub1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_fetch_and_sub1_full) +# define AO_short_fetch_and_sub1_full(addr) \ + (AO_nop_full(), AO_short_fetch_and_sub1_acquire(addr)) +# define AO_HAVE_short_fetch_and_sub1_full +#endif + +#if !defined(AO_HAVE_short_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_short_fetch_and_sub1_write) +# define AO_short_fetch_and_sub1_release_write(addr) \ + AO_short_fetch_and_sub1_write(addr) +# define AO_HAVE_short_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_short_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_short_fetch_and_sub1_release) +# define AO_short_fetch_and_sub1_release_write(addr) \ + AO_short_fetch_and_sub1_release(addr) +# define AO_HAVE_short_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_short_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_short_fetch_and_sub1_read) +# define AO_short_fetch_and_sub1_acquire_read(addr) \ + AO_short_fetch_and_sub1_read(addr) +# define AO_HAVE_short_fetch_and_sub1_acquire_read +#endif +#if !defined(AO_HAVE_short_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_short_fetch_and_sub1_acquire) +# define AO_short_fetch_and_sub1_acquire_read(addr) \ + AO_short_fetch_and_sub1_acquire(addr) +# define AO_HAVE_short_fetch_and_sub1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_short_fetch_and_sub1_acquire_read) +# define AO_short_fetch_and_sub1_dd_acquire_read(addr) \ + AO_short_fetch_and_sub1_acquire_read(addr) +# define AO_HAVE_short_fetch_and_sub1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_short_fetch_and_sub1) +# define AO_short_fetch_and_sub1_dd_acquire_read(addr) \ + AO_short_fetch_and_sub1(addr) +# define AO_HAVE_short_fetch_and_sub1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* short_and */ +#if defined(AO_HAVE_short_compare_and_swap_full) \ + && !defined(AO_HAVE_short_and_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_short_and_full(volatile unsigned/**/short *addr, unsigned/**/short value) + { + unsigned/**/short old; + + do + { + old = *(unsigned/**/short *)addr; + } + while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_full(addr, old, + old & value))); + } +# define AO_HAVE_short_and_full +#endif + +#if defined(AO_HAVE_short_and_full) +# if !defined(AO_HAVE_short_and_release) +# define AO_short_and_release(addr, val) AO_short_and_full(addr, val) +# define AO_HAVE_short_and_release +# endif +# if !defined(AO_HAVE_short_and_acquire) +# define AO_short_and_acquire(addr, val) AO_short_and_full(addr, val) +# define AO_HAVE_short_and_acquire +# endif +# if !defined(AO_HAVE_short_and_write) +# define AO_short_and_write(addr, val) AO_short_and_full(addr, val) +# define AO_HAVE_short_and_write +# endif +# if !defined(AO_HAVE_short_and_read) +# define AO_short_and_read(addr, val) AO_short_and_full(addr, val) +# define AO_HAVE_short_and_read +# endif +#endif /* AO_HAVE_short_and_full */ + +#if !defined(AO_HAVE_short_and) && defined(AO_HAVE_short_and_release) +# define AO_short_and(addr, val) AO_short_and_release(addr, val) +# define AO_HAVE_short_and +#endif +#if !defined(AO_HAVE_short_and) && defined(AO_HAVE_short_and_acquire) +# define AO_short_and(addr, val) AO_short_and_acquire(addr, val) +# define AO_HAVE_short_and +#endif +#if !defined(AO_HAVE_short_and) && defined(AO_HAVE_short_and_write) +# define AO_short_and(addr, val) AO_short_and_write(addr, val) +# define AO_HAVE_short_and +#endif +#if !defined(AO_HAVE_short_and) && defined(AO_HAVE_short_and_read) +# define AO_short_and(addr, val) AO_short_and_read(addr, val) +# define AO_HAVE_short_and +#endif + +#if defined(AO_HAVE_short_and_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_and_full) +# define AO_short_and_full(addr, val) \ + (AO_nop_full(), AO_short_and_acquire(addr, val)) +# define AO_HAVE_short_and_full +#endif + +#if !defined(AO_HAVE_short_and_release_write) \ + && defined(AO_HAVE_short_and_write) +# define AO_short_and_release_write(addr, val) AO_short_and_write(addr, val) +# define AO_HAVE_short_and_release_write +#endif +#if !defined(AO_HAVE_short_and_release_write) \ + && defined(AO_HAVE_short_and_release) +# define AO_short_and_release_write(addr, val) AO_short_and_release(addr, val) +# define AO_HAVE_short_and_release_write +#endif +#if !defined(AO_HAVE_short_and_acquire_read) \ + && defined(AO_HAVE_short_and_read) +# define AO_short_and_acquire_read(addr, val) AO_short_and_read(addr, val) +# define AO_HAVE_short_and_acquire_read +#endif +#if !defined(AO_HAVE_short_and_acquire_read) \ + && defined(AO_HAVE_short_and_acquire) +# define AO_short_and_acquire_read(addr, val) AO_short_and_acquire(addr, val) +# define AO_HAVE_short_and_acquire_read +#endif + +/* short_or */ +#if defined(AO_HAVE_short_compare_and_swap_full) \ + && !defined(AO_HAVE_short_or_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_short_or_full(volatile unsigned/**/short *addr, unsigned/**/short value) + { + unsigned/**/short old; + + do + { + old = *(unsigned/**/short *)addr; + } + while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_full(addr, old, + old | value))); + } +# define AO_HAVE_short_or_full +#endif + +#if defined(AO_HAVE_short_or_full) +# if !defined(AO_HAVE_short_or_release) +# define AO_short_or_release(addr, val) AO_short_or_full(addr, val) +# define AO_HAVE_short_or_release +# endif +# if !defined(AO_HAVE_short_or_acquire) +# define AO_short_or_acquire(addr, val) AO_short_or_full(addr, val) +# define AO_HAVE_short_or_acquire +# endif +# if !defined(AO_HAVE_short_or_write) +# define AO_short_or_write(addr, val) AO_short_or_full(addr, val) +# define AO_HAVE_short_or_write +# endif +# if !defined(AO_HAVE_short_or_read) +# define AO_short_or_read(addr, val) AO_short_or_full(addr, val) +# define AO_HAVE_short_or_read +# endif +#endif /* AO_HAVE_short_or_full */ + +#if !defined(AO_HAVE_short_or) && defined(AO_HAVE_short_or_release) +# define AO_short_or(addr, val) AO_short_or_release(addr, val) +# define AO_HAVE_short_or +#endif +#if !defined(AO_HAVE_short_or) && defined(AO_HAVE_short_or_acquire) +# define AO_short_or(addr, val) AO_short_or_acquire(addr, val) +# define AO_HAVE_short_or +#endif +#if !defined(AO_HAVE_short_or) && defined(AO_HAVE_short_or_write) +# define AO_short_or(addr, val) AO_short_or_write(addr, val) +# define AO_HAVE_short_or +#endif +#if !defined(AO_HAVE_short_or) && defined(AO_HAVE_short_or_read) +# define AO_short_or(addr, val) AO_short_or_read(addr, val) +# define AO_HAVE_short_or +#endif + +#if defined(AO_HAVE_short_or_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_or_full) +# define AO_short_or_full(addr, val) \ + (AO_nop_full(), AO_short_or_acquire(addr, val)) +# define AO_HAVE_short_or_full +#endif + +#if !defined(AO_HAVE_short_or_release_write) \ + && defined(AO_HAVE_short_or_write) +# define AO_short_or_release_write(addr, val) AO_short_or_write(addr, val) +# define AO_HAVE_short_or_release_write +#endif +#if !defined(AO_HAVE_short_or_release_write) \ + && defined(AO_HAVE_short_or_release) +# define AO_short_or_release_write(addr, val) AO_short_or_release(addr, val) +# define AO_HAVE_short_or_release_write +#endif +#if !defined(AO_HAVE_short_or_acquire_read) && defined(AO_HAVE_short_or_read) +# define AO_short_or_acquire_read(addr, val) AO_short_or_read(addr, val) +# define AO_HAVE_short_or_acquire_read +#endif +#if !defined(AO_HAVE_short_or_acquire_read) \ + && defined(AO_HAVE_short_or_acquire) +# define AO_short_or_acquire_read(addr, val) AO_short_or_acquire(addr, val) +# define AO_HAVE_short_or_acquire_read +#endif + +/* short_xor */ +#if defined(AO_HAVE_short_compare_and_swap_full) \ + && !defined(AO_HAVE_short_xor_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_short_xor_full(volatile unsigned/**/short *addr, unsigned/**/short value) + { + unsigned/**/short old; + + do + { + old = *(unsigned/**/short *)addr; + } + while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_full(addr, old, + old ^ value))); + } +# define AO_HAVE_short_xor_full +#endif + +#if defined(AO_HAVE_short_xor_full) +# if !defined(AO_HAVE_short_xor_release) +# define AO_short_xor_release(addr, val) AO_short_xor_full(addr, val) +# define AO_HAVE_short_xor_release +# endif +# if !defined(AO_HAVE_short_xor_acquire) +# define AO_short_xor_acquire(addr, val) AO_short_xor_full(addr, val) +# define AO_HAVE_short_xor_acquire +# endif +# if !defined(AO_HAVE_short_xor_write) +# define AO_short_xor_write(addr, val) AO_short_xor_full(addr, val) +# define AO_HAVE_short_xor_write +# endif +# if !defined(AO_HAVE_short_xor_read) +# define AO_short_xor_read(addr, val) AO_short_xor_full(addr, val) +# define AO_HAVE_short_xor_read +# endif +#endif /* AO_HAVE_short_xor_full */ + +#if !defined(AO_HAVE_short_xor) && defined(AO_HAVE_short_xor_release) +# define AO_short_xor(addr, val) AO_short_xor_release(addr, val) +# define AO_HAVE_short_xor +#endif +#if !defined(AO_HAVE_short_xor) && defined(AO_HAVE_short_xor_acquire) +# define AO_short_xor(addr, val) AO_short_xor_acquire(addr, val) +# define AO_HAVE_short_xor +#endif +#if !defined(AO_HAVE_short_xor) && defined(AO_HAVE_short_xor_write) +# define AO_short_xor(addr, val) AO_short_xor_write(addr, val) +# define AO_HAVE_short_xor +#endif +#if !defined(AO_HAVE_short_xor) && defined(AO_HAVE_short_xor_read) +# define AO_short_xor(addr, val) AO_short_xor_read(addr, val) +# define AO_HAVE_short_xor +#endif + +#if defined(AO_HAVE_short_xor_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_xor_full) +# define AO_short_xor_full(addr, val) \ + (AO_nop_full(), AO_short_xor_acquire(addr, val)) +# define AO_HAVE_short_xor_full +#endif + +#if !defined(AO_HAVE_short_xor_release_write) \ + && defined(AO_HAVE_short_xor_write) +# define AO_short_xor_release_write(addr, val) AO_short_xor_write(addr, val) +# define AO_HAVE_short_xor_release_write +#endif +#if !defined(AO_HAVE_short_xor_release_write) \ + && defined(AO_HAVE_short_xor_release) +# define AO_short_xor_release_write(addr, val) AO_short_xor_release(addr, val) +# define AO_HAVE_short_xor_release_write +#endif +#if !defined(AO_HAVE_short_xor_acquire_read) \ + && defined(AO_HAVE_short_xor_read) +# define AO_short_xor_acquire_read(addr, val) AO_short_xor_read(addr, val) +# define AO_HAVE_short_xor_acquire_read +#endif +#if !defined(AO_HAVE_short_xor_acquire_read) \ + && defined(AO_HAVE_short_xor_acquire) +# define AO_short_xor_acquire_read(addr, val) AO_short_xor_acquire(addr, val) +# define AO_HAVE_short_xor_acquire_read +#endif + +/* short_and/or/xor_dd_acquire_read are meaningless. */ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* int_compare_and_swap (based on fetch_compare_and_swap) */ +#if defined(AO_HAVE_int_fetch_compare_and_swap_full) \ + && !defined(AO_HAVE_int_compare_and_swap_full) + AO_INLINE int + AO_int_compare_and_swap_full(volatile unsigned *addr, unsigned old_val, + unsigned new_val) + { + return AO_int_fetch_compare_and_swap_full(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_int_compare_and_swap_full +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap_acquire) \ + && !defined(AO_HAVE_int_compare_and_swap_acquire) + AO_INLINE int + AO_int_compare_and_swap_acquire(volatile unsigned *addr, unsigned old_val, + unsigned new_val) + { + return AO_int_fetch_compare_and_swap_acquire(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_int_compare_and_swap_acquire +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap_release) \ + && !defined(AO_HAVE_int_compare_and_swap_release) + AO_INLINE int + AO_int_compare_and_swap_release(volatile unsigned *addr, unsigned old_val, + unsigned new_val) + { + return AO_int_fetch_compare_and_swap_release(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_int_compare_and_swap_release +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap_write) \ + && !defined(AO_HAVE_int_compare_and_swap_write) + AO_INLINE int + AO_int_compare_and_swap_write(volatile unsigned *addr, unsigned old_val, + unsigned new_val) + { + return AO_int_fetch_compare_and_swap_write(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_int_compare_and_swap_write +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap_read) \ + && !defined(AO_HAVE_int_compare_and_swap_read) + AO_INLINE int + AO_int_compare_and_swap_read(volatile unsigned *addr, unsigned old_val, + unsigned new_val) + { + return AO_int_fetch_compare_and_swap_read(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_int_compare_and_swap_read +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap) \ + && !defined(AO_HAVE_int_compare_and_swap) + AO_INLINE int + AO_int_compare_and_swap(volatile unsigned *addr, unsigned old_val, + unsigned new_val) + { + return AO_int_fetch_compare_and_swap(addr, old_val, new_val) == old_val; + } +# define AO_HAVE_int_compare_and_swap +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap_release_write) \ + && !defined(AO_HAVE_int_compare_and_swap_release_write) + AO_INLINE int + AO_int_compare_and_swap_release_write(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + return AO_int_fetch_compare_and_swap_release_write(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_int_compare_and_swap_release_write +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap_acquire_read) \ + && !defined(AO_HAVE_int_compare_and_swap_acquire_read) + AO_INLINE int + AO_int_compare_and_swap_acquire_read(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + return AO_int_fetch_compare_and_swap_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_int_compare_and_swap_acquire_read +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap_dd_acquire_read) \ + && !defined(AO_HAVE_int_compare_and_swap_dd_acquire_read) + AO_INLINE int + AO_int_compare_and_swap_dd_acquire_read(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + return AO_int_fetch_compare_and_swap_dd_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_int_compare_and_swap_dd_acquire_read +#endif + +/* int_fetch_and_add */ +/* We first try to implement fetch_and_add variants in terms of the */ +/* corresponding compare_and_swap variants to minimize adding barriers. */ +#if defined(AO_HAVE_int_compare_and_swap_full) \ + && !defined(AO_HAVE_int_fetch_and_add_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned + AO_int_fetch_and_add_full(volatile unsigned *addr, unsigned incr) + { + unsigned old; + + do + { + old = *(unsigned *)addr; + } + while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_full(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_int_fetch_and_add_full +#endif + +#if defined(AO_HAVE_int_compare_and_swap_acquire) \ + && !defined(AO_HAVE_int_fetch_and_add_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned + AO_int_fetch_and_add_acquire(volatile unsigned *addr, unsigned incr) + { + unsigned old; + + do + { + old = *(unsigned *)addr; + } + while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_acquire(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_int_fetch_and_add_acquire +#endif + +#if defined(AO_HAVE_int_compare_and_swap_release) \ + && !defined(AO_HAVE_int_fetch_and_add_release) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned + AO_int_fetch_and_add_release(volatile unsigned *addr, unsigned incr) + { + unsigned old; + + do + { + old = *(unsigned *)addr; + } + while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_release(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_int_fetch_and_add_release +#endif + +#if defined(AO_HAVE_int_compare_and_swap) \ + && !defined(AO_HAVE_int_fetch_and_add) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned + AO_int_fetch_and_add(volatile unsigned *addr, unsigned incr) + { + unsigned old; + + do + { + old = *(unsigned *)addr; + } + while (AO_EXPECT_FALSE(!AO_int_compare_and_swap(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_int_fetch_and_add +#endif + +#if defined(AO_HAVE_int_fetch_and_add_full) +# if !defined(AO_HAVE_int_fetch_and_add_release) +# define AO_int_fetch_and_add_release(addr, val) \ + AO_int_fetch_and_add_full(addr, val) +# define AO_HAVE_int_fetch_and_add_release +# endif +# if !defined(AO_HAVE_int_fetch_and_add_acquire) +# define AO_int_fetch_and_add_acquire(addr, val) \ + AO_int_fetch_and_add_full(addr, val) +# define AO_HAVE_int_fetch_and_add_acquire +# endif +# if !defined(AO_HAVE_int_fetch_and_add_write) +# define AO_int_fetch_and_add_write(addr, val) \ + AO_int_fetch_and_add_full(addr, val) +# define AO_HAVE_int_fetch_and_add_write +# endif +# if !defined(AO_HAVE_int_fetch_and_add_read) +# define AO_int_fetch_and_add_read(addr, val) \ + AO_int_fetch_and_add_full(addr, val) +# define AO_HAVE_int_fetch_and_add_read +# endif +#endif /* AO_HAVE_int_fetch_and_add_full */ + +#if defined(AO_HAVE_int_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_fetch_and_add_acquire) + AO_INLINE unsigned + AO_int_fetch_and_add_acquire(volatile unsigned *addr, unsigned incr) + { + unsigned result = AO_int_fetch_and_add(addr, incr); + AO_nop_full(); + return result; + } +# define AO_HAVE_int_fetch_and_add_acquire +#endif +#if defined(AO_HAVE_int_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_fetch_and_add_release) +# define AO_int_fetch_and_add_release(addr, incr) \ + (AO_nop_full(), AO_int_fetch_and_add(addr, incr)) +# define AO_HAVE_int_fetch_and_add_release +#endif + +#if !defined(AO_HAVE_int_fetch_and_add) \ + && defined(AO_HAVE_int_fetch_and_add_release) +# define AO_int_fetch_and_add(addr, val) \ + AO_int_fetch_and_add_release(addr, val) +# define AO_HAVE_int_fetch_and_add +#endif +#if !defined(AO_HAVE_int_fetch_and_add) \ + && defined(AO_HAVE_int_fetch_and_add_acquire) +# define AO_int_fetch_and_add(addr, val) \ + AO_int_fetch_and_add_acquire(addr, val) +# define AO_HAVE_int_fetch_and_add +#endif +#if !defined(AO_HAVE_int_fetch_and_add) \ + && defined(AO_HAVE_int_fetch_and_add_write) +# define AO_int_fetch_and_add(addr, val) \ + AO_int_fetch_and_add_write(addr, val) +# define AO_HAVE_int_fetch_and_add +#endif +#if !defined(AO_HAVE_int_fetch_and_add) \ + && defined(AO_HAVE_int_fetch_and_add_read) +# define AO_int_fetch_and_add(addr, val) \ + AO_int_fetch_and_add_read(addr, val) +# define AO_HAVE_int_fetch_and_add +#endif + +#if defined(AO_HAVE_int_fetch_and_add_acquire) \ + && defined(AO_HAVE_nop_full) && !defined(AO_HAVE_int_fetch_and_add_full) +# define AO_int_fetch_and_add_full(addr, val) \ + (AO_nop_full(), AO_int_fetch_and_add_acquire(addr, val)) +# define AO_HAVE_int_fetch_and_add_full +#endif + +#if !defined(AO_HAVE_int_fetch_and_add_release_write) \ + && defined(AO_HAVE_int_fetch_and_add_write) +# define AO_int_fetch_and_add_release_write(addr, val) \ + AO_int_fetch_and_add_write(addr, val) +# define AO_HAVE_int_fetch_and_add_release_write +#endif +#if !defined(AO_HAVE_int_fetch_and_add_release_write) \ + && defined(AO_HAVE_int_fetch_and_add_release) +# define AO_int_fetch_and_add_release_write(addr, val) \ + AO_int_fetch_and_add_release(addr, val) +# define AO_HAVE_int_fetch_and_add_release_write +#endif + +#if !defined(AO_HAVE_int_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_int_fetch_and_add_read) +# define AO_int_fetch_and_add_acquire_read(addr, val) \ + AO_int_fetch_and_add_read(addr, val) +# define AO_HAVE_int_fetch_and_add_acquire_read +#endif +#if !defined(AO_HAVE_int_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_int_fetch_and_add_acquire) +# define AO_int_fetch_and_add_acquire_read(addr, val) \ + AO_int_fetch_and_add_acquire(addr, val) +# define AO_HAVE_int_fetch_and_add_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_int_fetch_and_add_acquire_read) +# define AO_int_fetch_and_add_dd_acquire_read(addr, val) \ + AO_int_fetch_and_add_acquire_read(addr, val) +# define AO_HAVE_int_fetch_and_add_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_int_fetch_and_add) +# define AO_int_fetch_and_add_dd_acquire_read(addr, val) \ + AO_int_fetch_and_add(addr, val) +# define AO_HAVE_int_fetch_and_add_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* int_fetch_and_add1 */ +#if defined(AO_HAVE_int_fetch_and_add_full) \ + && !defined(AO_HAVE_int_fetch_and_add1_full) +# define AO_int_fetch_and_add1_full(addr) \ + AO_int_fetch_and_add_full(addr, 1) +# define AO_HAVE_int_fetch_and_add1_full +#endif +#if defined(AO_HAVE_int_fetch_and_add_release) \ + && !defined(AO_HAVE_int_fetch_and_add1_release) +# define AO_int_fetch_and_add1_release(addr) \ + AO_int_fetch_and_add_release(addr, 1) +# define AO_HAVE_int_fetch_and_add1_release +#endif +#if defined(AO_HAVE_int_fetch_and_add_acquire) \ + && !defined(AO_HAVE_int_fetch_and_add1_acquire) +# define AO_int_fetch_and_add1_acquire(addr) \ + AO_int_fetch_and_add_acquire(addr, 1) +# define AO_HAVE_int_fetch_and_add1_acquire +#endif +#if defined(AO_HAVE_int_fetch_and_add_write) \ + && !defined(AO_HAVE_int_fetch_and_add1_write) +# define AO_int_fetch_and_add1_write(addr) \ + AO_int_fetch_and_add_write(addr, 1) +# define AO_HAVE_int_fetch_and_add1_write +#endif +#if defined(AO_HAVE_int_fetch_and_add_read) \ + && !defined(AO_HAVE_int_fetch_and_add1_read) +# define AO_int_fetch_and_add1_read(addr) \ + AO_int_fetch_and_add_read(addr, 1) +# define AO_HAVE_int_fetch_and_add1_read +#endif +#if defined(AO_HAVE_int_fetch_and_add_release_write) \ + && !defined(AO_HAVE_int_fetch_and_add1_release_write) +# define AO_int_fetch_and_add1_release_write(addr) \ + AO_int_fetch_and_add_release_write(addr, 1) +# define AO_HAVE_int_fetch_and_add1_release_write +#endif +#if defined(AO_HAVE_int_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_int_fetch_and_add1_acquire_read) +# define AO_int_fetch_and_add1_acquire_read(addr) \ + AO_int_fetch_and_add_acquire_read(addr, 1) +# define AO_HAVE_int_fetch_and_add1_acquire_read +#endif +#if defined(AO_HAVE_int_fetch_and_add) \ + && !defined(AO_HAVE_int_fetch_and_add1) +# define AO_int_fetch_and_add1(addr) AO_int_fetch_and_add(addr, 1) +# define AO_HAVE_int_fetch_and_add1 +#endif + +#if defined(AO_HAVE_int_fetch_and_add1_full) +# if !defined(AO_HAVE_int_fetch_and_add1_release) +# define AO_int_fetch_and_add1_release(addr) \ + AO_int_fetch_and_add1_full(addr) +# define AO_HAVE_int_fetch_and_add1_release +# endif +# if !defined(AO_HAVE_int_fetch_and_add1_acquire) +# define AO_int_fetch_and_add1_acquire(addr) \ + AO_int_fetch_and_add1_full(addr) +# define AO_HAVE_int_fetch_and_add1_acquire +# endif +# if !defined(AO_HAVE_int_fetch_and_add1_write) +# define AO_int_fetch_and_add1_write(addr) \ + AO_int_fetch_and_add1_full(addr) +# define AO_HAVE_int_fetch_and_add1_write +# endif +# if !defined(AO_HAVE_int_fetch_and_add1_read) +# define AO_int_fetch_and_add1_read(addr) \ + AO_int_fetch_and_add1_full(addr) +# define AO_HAVE_int_fetch_and_add1_read +# endif +#endif /* AO_HAVE_int_fetch_and_add1_full */ + +#if !defined(AO_HAVE_int_fetch_and_add1) \ + && defined(AO_HAVE_int_fetch_and_add1_release) +# define AO_int_fetch_and_add1(addr) AO_int_fetch_and_add1_release(addr) +# define AO_HAVE_int_fetch_and_add1 +#endif +#if !defined(AO_HAVE_int_fetch_and_add1) \ + && defined(AO_HAVE_int_fetch_and_add1_acquire) +# define AO_int_fetch_and_add1(addr) AO_int_fetch_and_add1_acquire(addr) +# define AO_HAVE_int_fetch_and_add1 +#endif +#if !defined(AO_HAVE_int_fetch_and_add1) \ + && defined(AO_HAVE_int_fetch_and_add1_write) +# define AO_int_fetch_and_add1(addr) AO_int_fetch_and_add1_write(addr) +# define AO_HAVE_int_fetch_and_add1 +#endif +#if !defined(AO_HAVE_int_fetch_and_add1) \ + && defined(AO_HAVE_int_fetch_and_add1_read) +# define AO_int_fetch_and_add1(addr) AO_int_fetch_and_add1_read(addr) +# define AO_HAVE_int_fetch_and_add1 +#endif + +#if defined(AO_HAVE_int_fetch_and_add1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_fetch_and_add1_full) +# define AO_int_fetch_and_add1_full(addr) \ + (AO_nop_full(), AO_int_fetch_and_add1_acquire(addr)) +# define AO_HAVE_int_fetch_and_add1_full +#endif + +#if !defined(AO_HAVE_int_fetch_and_add1_release_write) \ + && defined(AO_HAVE_int_fetch_and_add1_write) +# define AO_int_fetch_and_add1_release_write(addr) \ + AO_int_fetch_and_add1_write(addr) +# define AO_HAVE_int_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_int_fetch_and_add1_release_write) \ + && defined(AO_HAVE_int_fetch_and_add1_release) +# define AO_int_fetch_and_add1_release_write(addr) \ + AO_int_fetch_and_add1_release(addr) +# define AO_HAVE_int_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_int_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_int_fetch_and_add1_read) +# define AO_int_fetch_and_add1_acquire_read(addr) \ + AO_int_fetch_and_add1_read(addr) +# define AO_HAVE_int_fetch_and_add1_acquire_read +#endif +#if !defined(AO_HAVE_int_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_int_fetch_and_add1_acquire) +# define AO_int_fetch_and_add1_acquire_read(addr) \ + AO_int_fetch_and_add1_acquire(addr) +# define AO_HAVE_int_fetch_and_add1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_int_fetch_and_add1_acquire_read) +# define AO_int_fetch_and_add1_dd_acquire_read(addr) \ + AO_int_fetch_and_add1_acquire_read(addr) +# define AO_HAVE_int_fetch_and_add1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_int_fetch_and_add1) +# define AO_int_fetch_and_add1_dd_acquire_read(addr) \ + AO_int_fetch_and_add1(addr) +# define AO_HAVE_int_fetch_and_add1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* int_fetch_and_sub1 */ +#if defined(AO_HAVE_int_fetch_and_add_full) \ + && !defined(AO_HAVE_int_fetch_and_sub1_full) +# define AO_int_fetch_and_sub1_full(addr) \ + AO_int_fetch_and_add_full(addr, (unsigned)(-1)) +# define AO_HAVE_int_fetch_and_sub1_full +#endif +#if defined(AO_HAVE_int_fetch_and_add_release) \ + && !defined(AO_HAVE_int_fetch_and_sub1_release) +# define AO_int_fetch_and_sub1_release(addr) \ + AO_int_fetch_and_add_release(addr, (unsigned)(-1)) +# define AO_HAVE_int_fetch_and_sub1_release +#endif +#if defined(AO_HAVE_int_fetch_and_add_acquire) \ + && !defined(AO_HAVE_int_fetch_and_sub1_acquire) +# define AO_int_fetch_and_sub1_acquire(addr) \ + AO_int_fetch_and_add_acquire(addr, (unsigned)(-1)) +# define AO_HAVE_int_fetch_and_sub1_acquire +#endif +#if defined(AO_HAVE_int_fetch_and_add_write) \ + && !defined(AO_HAVE_int_fetch_and_sub1_write) +# define AO_int_fetch_and_sub1_write(addr) \ + AO_int_fetch_and_add_write(addr, (unsigned)(-1)) +# define AO_HAVE_int_fetch_and_sub1_write +#endif +#if defined(AO_HAVE_int_fetch_and_add_read) \ + && !defined(AO_HAVE_int_fetch_and_sub1_read) +# define AO_int_fetch_and_sub1_read(addr) \ + AO_int_fetch_and_add_read(addr, (unsigned)(-1)) +# define AO_HAVE_int_fetch_and_sub1_read +#endif +#if defined(AO_HAVE_int_fetch_and_add_release_write) \ + && !defined(AO_HAVE_int_fetch_and_sub1_release_write) +# define AO_int_fetch_and_sub1_release_write(addr) \ + AO_int_fetch_and_add_release_write(addr, (unsigned)(-1)) +# define AO_HAVE_int_fetch_and_sub1_release_write +#endif +#if defined(AO_HAVE_int_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_int_fetch_and_sub1_acquire_read) +# define AO_int_fetch_and_sub1_acquire_read(addr) \ + AO_int_fetch_and_add_acquire_read(addr, (unsigned)(-1)) +# define AO_HAVE_int_fetch_and_sub1_acquire_read +#endif +#if defined(AO_HAVE_int_fetch_and_add) \ + && !defined(AO_HAVE_int_fetch_and_sub1) +# define AO_int_fetch_and_sub1(addr) \ + AO_int_fetch_and_add(addr, (unsigned)(-1)) +# define AO_HAVE_int_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_int_fetch_and_sub1_full) +# if !defined(AO_HAVE_int_fetch_and_sub1_release) +# define AO_int_fetch_and_sub1_release(addr) \ + AO_int_fetch_and_sub1_full(addr) +# define AO_HAVE_int_fetch_and_sub1_release +# endif +# if !defined(AO_HAVE_int_fetch_and_sub1_acquire) +# define AO_int_fetch_and_sub1_acquire(addr) \ + AO_int_fetch_and_sub1_full(addr) +# define AO_HAVE_int_fetch_and_sub1_acquire +# endif +# if !defined(AO_HAVE_int_fetch_and_sub1_write) +# define AO_int_fetch_and_sub1_write(addr) \ + AO_int_fetch_and_sub1_full(addr) +# define AO_HAVE_int_fetch_and_sub1_write +# endif +# if !defined(AO_HAVE_int_fetch_and_sub1_read) +# define AO_int_fetch_and_sub1_read(addr) \ + AO_int_fetch_and_sub1_full(addr) +# define AO_HAVE_int_fetch_and_sub1_read +# endif +#endif /* AO_HAVE_int_fetch_and_sub1_full */ + +#if !defined(AO_HAVE_int_fetch_and_sub1) \ + && defined(AO_HAVE_int_fetch_and_sub1_release) +# define AO_int_fetch_and_sub1(addr) AO_int_fetch_and_sub1_release(addr) +# define AO_HAVE_int_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_int_fetch_and_sub1) \ + && defined(AO_HAVE_int_fetch_and_sub1_acquire) +# define AO_int_fetch_and_sub1(addr) AO_int_fetch_and_sub1_acquire(addr) +# define AO_HAVE_int_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_int_fetch_and_sub1) \ + && defined(AO_HAVE_int_fetch_and_sub1_write) +# define AO_int_fetch_and_sub1(addr) AO_int_fetch_and_sub1_write(addr) +# define AO_HAVE_int_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_int_fetch_and_sub1) \ + && defined(AO_HAVE_int_fetch_and_sub1_read) +# define AO_int_fetch_and_sub1(addr) AO_int_fetch_and_sub1_read(addr) +# define AO_HAVE_int_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_int_fetch_and_sub1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_fetch_and_sub1_full) +# define AO_int_fetch_and_sub1_full(addr) \ + (AO_nop_full(), AO_int_fetch_and_sub1_acquire(addr)) +# define AO_HAVE_int_fetch_and_sub1_full +#endif + +#if !defined(AO_HAVE_int_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_int_fetch_and_sub1_write) +# define AO_int_fetch_and_sub1_release_write(addr) \ + AO_int_fetch_and_sub1_write(addr) +# define AO_HAVE_int_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_int_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_int_fetch_and_sub1_release) +# define AO_int_fetch_and_sub1_release_write(addr) \ + AO_int_fetch_and_sub1_release(addr) +# define AO_HAVE_int_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_int_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_int_fetch_and_sub1_read) +# define AO_int_fetch_and_sub1_acquire_read(addr) \ + AO_int_fetch_and_sub1_read(addr) +# define AO_HAVE_int_fetch_and_sub1_acquire_read +#endif +#if !defined(AO_HAVE_int_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_int_fetch_and_sub1_acquire) +# define AO_int_fetch_and_sub1_acquire_read(addr) \ + AO_int_fetch_and_sub1_acquire(addr) +# define AO_HAVE_int_fetch_and_sub1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_int_fetch_and_sub1_acquire_read) +# define AO_int_fetch_and_sub1_dd_acquire_read(addr) \ + AO_int_fetch_and_sub1_acquire_read(addr) +# define AO_HAVE_int_fetch_and_sub1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_int_fetch_and_sub1) +# define AO_int_fetch_and_sub1_dd_acquire_read(addr) \ + AO_int_fetch_and_sub1(addr) +# define AO_HAVE_int_fetch_and_sub1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* int_and */ +#if defined(AO_HAVE_int_compare_and_swap_full) \ + && !defined(AO_HAVE_int_and_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_int_and_full(volatile unsigned *addr, unsigned value) + { + unsigned old; + + do + { + old = *(unsigned *)addr; + } + while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_full(addr, old, + old & value))); + } +# define AO_HAVE_int_and_full +#endif + +#if defined(AO_HAVE_int_and_full) +# if !defined(AO_HAVE_int_and_release) +# define AO_int_and_release(addr, val) AO_int_and_full(addr, val) +# define AO_HAVE_int_and_release +# endif +# if !defined(AO_HAVE_int_and_acquire) +# define AO_int_and_acquire(addr, val) AO_int_and_full(addr, val) +# define AO_HAVE_int_and_acquire +# endif +# if !defined(AO_HAVE_int_and_write) +# define AO_int_and_write(addr, val) AO_int_and_full(addr, val) +# define AO_HAVE_int_and_write +# endif +# if !defined(AO_HAVE_int_and_read) +# define AO_int_and_read(addr, val) AO_int_and_full(addr, val) +# define AO_HAVE_int_and_read +# endif +#endif /* AO_HAVE_int_and_full */ + +#if !defined(AO_HAVE_int_and) && defined(AO_HAVE_int_and_release) +# define AO_int_and(addr, val) AO_int_and_release(addr, val) +# define AO_HAVE_int_and +#endif +#if !defined(AO_HAVE_int_and) && defined(AO_HAVE_int_and_acquire) +# define AO_int_and(addr, val) AO_int_and_acquire(addr, val) +# define AO_HAVE_int_and +#endif +#if !defined(AO_HAVE_int_and) && defined(AO_HAVE_int_and_write) +# define AO_int_and(addr, val) AO_int_and_write(addr, val) +# define AO_HAVE_int_and +#endif +#if !defined(AO_HAVE_int_and) && defined(AO_HAVE_int_and_read) +# define AO_int_and(addr, val) AO_int_and_read(addr, val) +# define AO_HAVE_int_and +#endif + +#if defined(AO_HAVE_int_and_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_and_full) +# define AO_int_and_full(addr, val) \ + (AO_nop_full(), AO_int_and_acquire(addr, val)) +# define AO_HAVE_int_and_full +#endif + +#if !defined(AO_HAVE_int_and_release_write) \ + && defined(AO_HAVE_int_and_write) +# define AO_int_and_release_write(addr, val) AO_int_and_write(addr, val) +# define AO_HAVE_int_and_release_write +#endif +#if !defined(AO_HAVE_int_and_release_write) \ + && defined(AO_HAVE_int_and_release) +# define AO_int_and_release_write(addr, val) AO_int_and_release(addr, val) +# define AO_HAVE_int_and_release_write +#endif +#if !defined(AO_HAVE_int_and_acquire_read) \ + && defined(AO_HAVE_int_and_read) +# define AO_int_and_acquire_read(addr, val) AO_int_and_read(addr, val) +# define AO_HAVE_int_and_acquire_read +#endif +#if !defined(AO_HAVE_int_and_acquire_read) \ + && defined(AO_HAVE_int_and_acquire) +# define AO_int_and_acquire_read(addr, val) AO_int_and_acquire(addr, val) +# define AO_HAVE_int_and_acquire_read +#endif + +/* int_or */ +#if defined(AO_HAVE_int_compare_and_swap_full) \ + && !defined(AO_HAVE_int_or_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_int_or_full(volatile unsigned *addr, unsigned value) + { + unsigned old; + + do + { + old = *(unsigned *)addr; + } + while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_full(addr, old, + old | value))); + } +# define AO_HAVE_int_or_full +#endif + +#if defined(AO_HAVE_int_or_full) +# if !defined(AO_HAVE_int_or_release) +# define AO_int_or_release(addr, val) AO_int_or_full(addr, val) +# define AO_HAVE_int_or_release +# endif +# if !defined(AO_HAVE_int_or_acquire) +# define AO_int_or_acquire(addr, val) AO_int_or_full(addr, val) +# define AO_HAVE_int_or_acquire +# endif +# if !defined(AO_HAVE_int_or_write) +# define AO_int_or_write(addr, val) AO_int_or_full(addr, val) +# define AO_HAVE_int_or_write +# endif +# if !defined(AO_HAVE_int_or_read) +# define AO_int_or_read(addr, val) AO_int_or_full(addr, val) +# define AO_HAVE_int_or_read +# endif +#endif /* AO_HAVE_int_or_full */ + +#if !defined(AO_HAVE_int_or) && defined(AO_HAVE_int_or_release) +# define AO_int_or(addr, val) AO_int_or_release(addr, val) +# define AO_HAVE_int_or +#endif +#if !defined(AO_HAVE_int_or) && defined(AO_HAVE_int_or_acquire) +# define AO_int_or(addr, val) AO_int_or_acquire(addr, val) +# define AO_HAVE_int_or +#endif +#if !defined(AO_HAVE_int_or) && defined(AO_HAVE_int_or_write) +# define AO_int_or(addr, val) AO_int_or_write(addr, val) +# define AO_HAVE_int_or +#endif +#if !defined(AO_HAVE_int_or) && defined(AO_HAVE_int_or_read) +# define AO_int_or(addr, val) AO_int_or_read(addr, val) +# define AO_HAVE_int_or +#endif + +#if defined(AO_HAVE_int_or_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_or_full) +# define AO_int_or_full(addr, val) \ + (AO_nop_full(), AO_int_or_acquire(addr, val)) +# define AO_HAVE_int_or_full +#endif + +#if !defined(AO_HAVE_int_or_release_write) \ + && defined(AO_HAVE_int_or_write) +# define AO_int_or_release_write(addr, val) AO_int_or_write(addr, val) +# define AO_HAVE_int_or_release_write +#endif +#if !defined(AO_HAVE_int_or_release_write) \ + && defined(AO_HAVE_int_or_release) +# define AO_int_or_release_write(addr, val) AO_int_or_release(addr, val) +# define AO_HAVE_int_or_release_write +#endif +#if !defined(AO_HAVE_int_or_acquire_read) && defined(AO_HAVE_int_or_read) +# define AO_int_or_acquire_read(addr, val) AO_int_or_read(addr, val) +# define AO_HAVE_int_or_acquire_read +#endif +#if !defined(AO_HAVE_int_or_acquire_read) \ + && defined(AO_HAVE_int_or_acquire) +# define AO_int_or_acquire_read(addr, val) AO_int_or_acquire(addr, val) +# define AO_HAVE_int_or_acquire_read +#endif + +/* int_xor */ +#if defined(AO_HAVE_int_compare_and_swap_full) \ + && !defined(AO_HAVE_int_xor_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_int_xor_full(volatile unsigned *addr, unsigned value) + { + unsigned old; + + do + { + old = *(unsigned *)addr; + } + while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_full(addr, old, + old ^ value))); + } +# define AO_HAVE_int_xor_full +#endif + +#if defined(AO_HAVE_int_xor_full) +# if !defined(AO_HAVE_int_xor_release) +# define AO_int_xor_release(addr, val) AO_int_xor_full(addr, val) +# define AO_HAVE_int_xor_release +# endif +# if !defined(AO_HAVE_int_xor_acquire) +# define AO_int_xor_acquire(addr, val) AO_int_xor_full(addr, val) +# define AO_HAVE_int_xor_acquire +# endif +# if !defined(AO_HAVE_int_xor_write) +# define AO_int_xor_write(addr, val) AO_int_xor_full(addr, val) +# define AO_HAVE_int_xor_write +# endif +# if !defined(AO_HAVE_int_xor_read) +# define AO_int_xor_read(addr, val) AO_int_xor_full(addr, val) +# define AO_HAVE_int_xor_read +# endif +#endif /* AO_HAVE_int_xor_full */ + +#if !defined(AO_HAVE_int_xor) && defined(AO_HAVE_int_xor_release) +# define AO_int_xor(addr, val) AO_int_xor_release(addr, val) +# define AO_HAVE_int_xor +#endif +#if !defined(AO_HAVE_int_xor) && defined(AO_HAVE_int_xor_acquire) +# define AO_int_xor(addr, val) AO_int_xor_acquire(addr, val) +# define AO_HAVE_int_xor +#endif +#if !defined(AO_HAVE_int_xor) && defined(AO_HAVE_int_xor_write) +# define AO_int_xor(addr, val) AO_int_xor_write(addr, val) +# define AO_HAVE_int_xor +#endif +#if !defined(AO_HAVE_int_xor) && defined(AO_HAVE_int_xor_read) +# define AO_int_xor(addr, val) AO_int_xor_read(addr, val) +# define AO_HAVE_int_xor +#endif + +#if defined(AO_HAVE_int_xor_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_xor_full) +# define AO_int_xor_full(addr, val) \ + (AO_nop_full(), AO_int_xor_acquire(addr, val)) +# define AO_HAVE_int_xor_full +#endif + +#if !defined(AO_HAVE_int_xor_release_write) \ + && defined(AO_HAVE_int_xor_write) +# define AO_int_xor_release_write(addr, val) AO_int_xor_write(addr, val) +# define AO_HAVE_int_xor_release_write +#endif +#if !defined(AO_HAVE_int_xor_release_write) \ + && defined(AO_HAVE_int_xor_release) +# define AO_int_xor_release_write(addr, val) AO_int_xor_release(addr, val) +# define AO_HAVE_int_xor_release_write +#endif +#if !defined(AO_HAVE_int_xor_acquire_read) \ + && defined(AO_HAVE_int_xor_read) +# define AO_int_xor_acquire_read(addr, val) AO_int_xor_read(addr, val) +# define AO_HAVE_int_xor_acquire_read +#endif +#if !defined(AO_HAVE_int_xor_acquire_read) \ + && defined(AO_HAVE_int_xor_acquire) +# define AO_int_xor_acquire_read(addr, val) AO_int_xor_acquire(addr, val) +# define AO_HAVE_int_xor_acquire_read +#endif + +/* int_and/or/xor_dd_acquire_read are meaningless. */ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* compare_and_swap (based on fetch_compare_and_swap) */ +#if defined(AO_HAVE_fetch_compare_and_swap_full) \ + && !defined(AO_HAVE_compare_and_swap_full) + AO_INLINE int + AO_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return AO_fetch_compare_and_swap_full(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_compare_and_swap_full +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_acquire) \ + && !defined(AO_HAVE_compare_and_swap_acquire) + AO_INLINE int + AO_compare_and_swap_acquire(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return AO_fetch_compare_and_swap_acquire(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_compare_and_swap_acquire +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_release) \ + && !defined(AO_HAVE_compare_and_swap_release) + AO_INLINE int + AO_compare_and_swap_release(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return AO_fetch_compare_and_swap_release(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_compare_and_swap_release +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_write) \ + && !defined(AO_HAVE_compare_and_swap_write) + AO_INLINE int + AO_compare_and_swap_write(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return AO_fetch_compare_and_swap_write(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_compare_and_swap_write +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_read) \ + && !defined(AO_HAVE_compare_and_swap_read) + AO_INLINE int + AO_compare_and_swap_read(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return AO_fetch_compare_and_swap_read(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_compare_and_swap_read +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap) \ + && !defined(AO_HAVE_compare_and_swap) + AO_INLINE int + AO_compare_and_swap(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return AO_fetch_compare_and_swap(addr, old_val, new_val) == old_val; + } +# define AO_HAVE_compare_and_swap +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_release_write) \ + && !defined(AO_HAVE_compare_and_swap_release_write) + AO_INLINE int + AO_compare_and_swap_release_write(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + return AO_fetch_compare_and_swap_release_write(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_compare_and_swap_release_write +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_acquire_read) \ + && !defined(AO_HAVE_compare_and_swap_acquire_read) + AO_INLINE int + AO_compare_and_swap_acquire_read(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + return AO_fetch_compare_and_swap_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_compare_and_swap_acquire_read +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_dd_acquire_read) \ + && !defined(AO_HAVE_compare_and_swap_dd_acquire_read) + AO_INLINE int + AO_compare_and_swap_dd_acquire_read(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + return AO_fetch_compare_and_swap_dd_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_compare_and_swap_dd_acquire_read +#endif + +/* fetch_and_add */ +/* We first try to implement fetch_and_add variants in terms of the */ +/* corresponding compare_and_swap variants to minimize adding barriers. */ +#if defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_fetch_and_add_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_t + AO_fetch_and_add_full(volatile AO_t *addr, AO_t incr) + { + AO_t old; + + do + { + old = *(AO_t *)addr; + } + while (AO_EXPECT_FALSE(!AO_compare_and_swap_full(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_fetch_and_add_full +#endif + +#if defined(AO_HAVE_compare_and_swap_acquire) \ + && !defined(AO_HAVE_fetch_and_add_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_t + AO_fetch_and_add_acquire(volatile AO_t *addr, AO_t incr) + { + AO_t old; + + do + { + old = *(AO_t *)addr; + } + while (AO_EXPECT_FALSE(!AO_compare_and_swap_acquire(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_fetch_and_add_acquire +#endif + +#if defined(AO_HAVE_compare_and_swap_release) \ + && !defined(AO_HAVE_fetch_and_add_release) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_t + AO_fetch_and_add_release(volatile AO_t *addr, AO_t incr) + { + AO_t old; + + do + { + old = *(AO_t *)addr; + } + while (AO_EXPECT_FALSE(!AO_compare_and_swap_release(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_fetch_and_add_release +#endif + +#if defined(AO_HAVE_compare_and_swap) \ + && !defined(AO_HAVE_fetch_and_add) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_t + AO_fetch_and_add(volatile AO_t *addr, AO_t incr) + { + AO_t old; + + do + { + old = *(AO_t *)addr; + } + while (AO_EXPECT_FALSE(!AO_compare_and_swap(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_fetch_and_add +#endif + +#if defined(AO_HAVE_fetch_and_add_full) +# if !defined(AO_HAVE_fetch_and_add_release) +# define AO_fetch_and_add_release(addr, val) \ + AO_fetch_and_add_full(addr, val) +# define AO_HAVE_fetch_and_add_release +# endif +# if !defined(AO_HAVE_fetch_and_add_acquire) +# define AO_fetch_and_add_acquire(addr, val) \ + AO_fetch_and_add_full(addr, val) +# define AO_HAVE_fetch_and_add_acquire +# endif +# if !defined(AO_HAVE_fetch_and_add_write) +# define AO_fetch_and_add_write(addr, val) \ + AO_fetch_and_add_full(addr, val) +# define AO_HAVE_fetch_and_add_write +# endif +# if !defined(AO_HAVE_fetch_and_add_read) +# define AO_fetch_and_add_read(addr, val) \ + AO_fetch_and_add_full(addr, val) +# define AO_HAVE_fetch_and_add_read +# endif +#endif /* AO_HAVE_fetch_and_add_full */ + +#if defined(AO_HAVE_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_fetch_and_add_acquire) + AO_INLINE AO_t + AO_fetch_and_add_acquire(volatile AO_t *addr, AO_t incr) + { + AO_t result = AO_fetch_and_add(addr, incr); + AO_nop_full(); + return result; + } +# define AO_HAVE_fetch_and_add_acquire +#endif +#if defined(AO_HAVE_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_fetch_and_add_release) +# define AO_fetch_and_add_release(addr, incr) \ + (AO_nop_full(), AO_fetch_and_add(addr, incr)) +# define AO_HAVE_fetch_and_add_release +#endif + +#if !defined(AO_HAVE_fetch_and_add) \ + && defined(AO_HAVE_fetch_and_add_release) +# define AO_fetch_and_add(addr, val) \ + AO_fetch_and_add_release(addr, val) +# define AO_HAVE_fetch_and_add +#endif +#if !defined(AO_HAVE_fetch_and_add) \ + && defined(AO_HAVE_fetch_and_add_acquire) +# define AO_fetch_and_add(addr, val) \ + AO_fetch_and_add_acquire(addr, val) +# define AO_HAVE_fetch_and_add +#endif +#if !defined(AO_HAVE_fetch_and_add) \ + && defined(AO_HAVE_fetch_and_add_write) +# define AO_fetch_and_add(addr, val) \ + AO_fetch_and_add_write(addr, val) +# define AO_HAVE_fetch_and_add +#endif +#if !defined(AO_HAVE_fetch_and_add) \ + && defined(AO_HAVE_fetch_and_add_read) +# define AO_fetch_and_add(addr, val) \ + AO_fetch_and_add_read(addr, val) +# define AO_HAVE_fetch_and_add +#endif + +#if defined(AO_HAVE_fetch_and_add_acquire) \ + && defined(AO_HAVE_nop_full) && !defined(AO_HAVE_fetch_and_add_full) +# define AO_fetch_and_add_full(addr, val) \ + (AO_nop_full(), AO_fetch_and_add_acquire(addr, val)) +# define AO_HAVE_fetch_and_add_full +#endif + +#if !defined(AO_HAVE_fetch_and_add_release_write) \ + && defined(AO_HAVE_fetch_and_add_write) +# define AO_fetch_and_add_release_write(addr, val) \ + AO_fetch_and_add_write(addr, val) +# define AO_HAVE_fetch_and_add_release_write +#endif +#if !defined(AO_HAVE_fetch_and_add_release_write) \ + && defined(AO_HAVE_fetch_and_add_release) +# define AO_fetch_and_add_release_write(addr, val) \ + AO_fetch_and_add_release(addr, val) +# define AO_HAVE_fetch_and_add_release_write +#endif + +#if !defined(AO_HAVE_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_fetch_and_add_read) +# define AO_fetch_and_add_acquire_read(addr, val) \ + AO_fetch_and_add_read(addr, val) +# define AO_HAVE_fetch_and_add_acquire_read +#endif +#if !defined(AO_HAVE_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_fetch_and_add_acquire) +# define AO_fetch_and_add_acquire_read(addr, val) \ + AO_fetch_and_add_acquire(addr, val) +# define AO_HAVE_fetch_and_add_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_fetch_and_add_acquire_read) +# define AO_fetch_and_add_dd_acquire_read(addr, val) \ + AO_fetch_and_add_acquire_read(addr, val) +# define AO_HAVE_fetch_and_add_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_fetch_and_add) +# define AO_fetch_and_add_dd_acquire_read(addr, val) \ + AO_fetch_and_add(addr, val) +# define AO_HAVE_fetch_and_add_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* fetch_and_add1 */ +#if defined(AO_HAVE_fetch_and_add_full) \ + && !defined(AO_HAVE_fetch_and_add1_full) +# define AO_fetch_and_add1_full(addr) \ + AO_fetch_and_add_full(addr, 1) +# define AO_HAVE_fetch_and_add1_full +#endif +#if defined(AO_HAVE_fetch_and_add_release) \ + && !defined(AO_HAVE_fetch_and_add1_release) +# define AO_fetch_and_add1_release(addr) \ + AO_fetch_and_add_release(addr, 1) +# define AO_HAVE_fetch_and_add1_release +#endif +#if defined(AO_HAVE_fetch_and_add_acquire) \ + && !defined(AO_HAVE_fetch_and_add1_acquire) +# define AO_fetch_and_add1_acquire(addr) \ + AO_fetch_and_add_acquire(addr, 1) +# define AO_HAVE_fetch_and_add1_acquire +#endif +#if defined(AO_HAVE_fetch_and_add_write) \ + && !defined(AO_HAVE_fetch_and_add1_write) +# define AO_fetch_and_add1_write(addr) \ + AO_fetch_and_add_write(addr, 1) +# define AO_HAVE_fetch_and_add1_write +#endif +#if defined(AO_HAVE_fetch_and_add_read) \ + && !defined(AO_HAVE_fetch_and_add1_read) +# define AO_fetch_and_add1_read(addr) \ + AO_fetch_and_add_read(addr, 1) +# define AO_HAVE_fetch_and_add1_read +#endif +#if defined(AO_HAVE_fetch_and_add_release_write) \ + && !defined(AO_HAVE_fetch_and_add1_release_write) +# define AO_fetch_and_add1_release_write(addr) \ + AO_fetch_and_add_release_write(addr, 1) +# define AO_HAVE_fetch_and_add1_release_write +#endif +#if defined(AO_HAVE_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_fetch_and_add1_acquire_read) +# define AO_fetch_and_add1_acquire_read(addr) \ + AO_fetch_and_add_acquire_read(addr, 1) +# define AO_HAVE_fetch_and_add1_acquire_read +#endif +#if defined(AO_HAVE_fetch_and_add) \ + && !defined(AO_HAVE_fetch_and_add1) +# define AO_fetch_and_add1(addr) AO_fetch_and_add(addr, 1) +# define AO_HAVE_fetch_and_add1 +#endif + +#if defined(AO_HAVE_fetch_and_add1_full) +# if !defined(AO_HAVE_fetch_and_add1_release) +# define AO_fetch_and_add1_release(addr) \ + AO_fetch_and_add1_full(addr) +# define AO_HAVE_fetch_and_add1_release +# endif +# if !defined(AO_HAVE_fetch_and_add1_acquire) +# define AO_fetch_and_add1_acquire(addr) \ + AO_fetch_and_add1_full(addr) +# define AO_HAVE_fetch_and_add1_acquire +# endif +# if !defined(AO_HAVE_fetch_and_add1_write) +# define AO_fetch_and_add1_write(addr) \ + AO_fetch_and_add1_full(addr) +# define AO_HAVE_fetch_and_add1_write +# endif +# if !defined(AO_HAVE_fetch_and_add1_read) +# define AO_fetch_and_add1_read(addr) \ + AO_fetch_and_add1_full(addr) +# define AO_HAVE_fetch_and_add1_read +# endif +#endif /* AO_HAVE_fetch_and_add1_full */ + +#if !defined(AO_HAVE_fetch_and_add1) \ + && defined(AO_HAVE_fetch_and_add1_release) +# define AO_fetch_and_add1(addr) AO_fetch_and_add1_release(addr) +# define AO_HAVE_fetch_and_add1 +#endif +#if !defined(AO_HAVE_fetch_and_add1) \ + && defined(AO_HAVE_fetch_and_add1_acquire) +# define AO_fetch_and_add1(addr) AO_fetch_and_add1_acquire(addr) +# define AO_HAVE_fetch_and_add1 +#endif +#if !defined(AO_HAVE_fetch_and_add1) \ + && defined(AO_HAVE_fetch_and_add1_write) +# define AO_fetch_and_add1(addr) AO_fetch_and_add1_write(addr) +# define AO_HAVE_fetch_and_add1 +#endif +#if !defined(AO_HAVE_fetch_and_add1) \ + && defined(AO_HAVE_fetch_and_add1_read) +# define AO_fetch_and_add1(addr) AO_fetch_and_add1_read(addr) +# define AO_HAVE_fetch_and_add1 +#endif + +#if defined(AO_HAVE_fetch_and_add1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_fetch_and_add1_full) +# define AO_fetch_and_add1_full(addr) \ + (AO_nop_full(), AO_fetch_and_add1_acquire(addr)) +# define AO_HAVE_fetch_and_add1_full +#endif + +#if !defined(AO_HAVE_fetch_and_add1_release_write) \ + && defined(AO_HAVE_fetch_and_add1_write) +# define AO_fetch_and_add1_release_write(addr) \ + AO_fetch_and_add1_write(addr) +# define AO_HAVE_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_fetch_and_add1_release_write) \ + && defined(AO_HAVE_fetch_and_add1_release) +# define AO_fetch_and_add1_release_write(addr) \ + AO_fetch_and_add1_release(addr) +# define AO_HAVE_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_fetch_and_add1_read) +# define AO_fetch_and_add1_acquire_read(addr) \ + AO_fetch_and_add1_read(addr) +# define AO_HAVE_fetch_and_add1_acquire_read +#endif +#if !defined(AO_HAVE_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_fetch_and_add1_acquire) +# define AO_fetch_and_add1_acquire_read(addr) \ + AO_fetch_and_add1_acquire(addr) +# define AO_HAVE_fetch_and_add1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_fetch_and_add1_acquire_read) +# define AO_fetch_and_add1_dd_acquire_read(addr) \ + AO_fetch_and_add1_acquire_read(addr) +# define AO_HAVE_fetch_and_add1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_fetch_and_add1) +# define AO_fetch_and_add1_dd_acquire_read(addr) \ + AO_fetch_and_add1(addr) +# define AO_HAVE_fetch_and_add1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* fetch_and_sub1 */ +#if defined(AO_HAVE_fetch_and_add_full) \ + && !defined(AO_HAVE_fetch_and_sub1_full) +# define AO_fetch_and_sub1_full(addr) \ + AO_fetch_and_add_full(addr, (AO_t)(-1)) +# define AO_HAVE_fetch_and_sub1_full +#endif +#if defined(AO_HAVE_fetch_and_add_release) \ + && !defined(AO_HAVE_fetch_and_sub1_release) +# define AO_fetch_and_sub1_release(addr) \ + AO_fetch_and_add_release(addr, (AO_t)(-1)) +# define AO_HAVE_fetch_and_sub1_release +#endif +#if defined(AO_HAVE_fetch_and_add_acquire) \ + && !defined(AO_HAVE_fetch_and_sub1_acquire) +# define AO_fetch_and_sub1_acquire(addr) \ + AO_fetch_and_add_acquire(addr, (AO_t)(-1)) +# define AO_HAVE_fetch_and_sub1_acquire +#endif +#if defined(AO_HAVE_fetch_and_add_write) \ + && !defined(AO_HAVE_fetch_and_sub1_write) +# define AO_fetch_and_sub1_write(addr) \ + AO_fetch_and_add_write(addr, (AO_t)(-1)) +# define AO_HAVE_fetch_and_sub1_write +#endif +#if defined(AO_HAVE_fetch_and_add_read) \ + && !defined(AO_HAVE_fetch_and_sub1_read) +# define AO_fetch_and_sub1_read(addr) \ + AO_fetch_and_add_read(addr, (AO_t)(-1)) +# define AO_HAVE_fetch_and_sub1_read +#endif +#if defined(AO_HAVE_fetch_and_add_release_write) \ + && !defined(AO_HAVE_fetch_and_sub1_release_write) +# define AO_fetch_and_sub1_release_write(addr) \ + AO_fetch_and_add_release_write(addr, (AO_t)(-1)) +# define AO_HAVE_fetch_and_sub1_release_write +#endif +#if defined(AO_HAVE_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_fetch_and_sub1_acquire_read) +# define AO_fetch_and_sub1_acquire_read(addr) \ + AO_fetch_and_add_acquire_read(addr, (AO_t)(-1)) +# define AO_HAVE_fetch_and_sub1_acquire_read +#endif +#if defined(AO_HAVE_fetch_and_add) \ + && !defined(AO_HAVE_fetch_and_sub1) +# define AO_fetch_and_sub1(addr) \ + AO_fetch_and_add(addr, (AO_t)(-1)) +# define AO_HAVE_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_fetch_and_sub1_full) +# if !defined(AO_HAVE_fetch_and_sub1_release) +# define AO_fetch_and_sub1_release(addr) \ + AO_fetch_and_sub1_full(addr) +# define AO_HAVE_fetch_and_sub1_release +# endif +# if !defined(AO_HAVE_fetch_and_sub1_acquire) +# define AO_fetch_and_sub1_acquire(addr) \ + AO_fetch_and_sub1_full(addr) +# define AO_HAVE_fetch_and_sub1_acquire +# endif +# if !defined(AO_HAVE_fetch_and_sub1_write) +# define AO_fetch_and_sub1_write(addr) \ + AO_fetch_and_sub1_full(addr) +# define AO_HAVE_fetch_and_sub1_write +# endif +# if !defined(AO_HAVE_fetch_and_sub1_read) +# define AO_fetch_and_sub1_read(addr) \ + AO_fetch_and_sub1_full(addr) +# define AO_HAVE_fetch_and_sub1_read +# endif +#endif /* AO_HAVE_fetch_and_sub1_full */ + +#if !defined(AO_HAVE_fetch_and_sub1) \ + && defined(AO_HAVE_fetch_and_sub1_release) +# define AO_fetch_and_sub1(addr) AO_fetch_and_sub1_release(addr) +# define AO_HAVE_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_fetch_and_sub1) \ + && defined(AO_HAVE_fetch_and_sub1_acquire) +# define AO_fetch_and_sub1(addr) AO_fetch_and_sub1_acquire(addr) +# define AO_HAVE_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_fetch_and_sub1) \ + && defined(AO_HAVE_fetch_and_sub1_write) +# define AO_fetch_and_sub1(addr) AO_fetch_and_sub1_write(addr) +# define AO_HAVE_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_fetch_and_sub1) \ + && defined(AO_HAVE_fetch_and_sub1_read) +# define AO_fetch_and_sub1(addr) AO_fetch_and_sub1_read(addr) +# define AO_HAVE_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_fetch_and_sub1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_fetch_and_sub1_full) +# define AO_fetch_and_sub1_full(addr) \ + (AO_nop_full(), AO_fetch_and_sub1_acquire(addr)) +# define AO_HAVE_fetch_and_sub1_full +#endif + +#if !defined(AO_HAVE_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_fetch_and_sub1_write) +# define AO_fetch_and_sub1_release_write(addr) \ + AO_fetch_and_sub1_write(addr) +# define AO_HAVE_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_fetch_and_sub1_release) +# define AO_fetch_and_sub1_release_write(addr) \ + AO_fetch_and_sub1_release(addr) +# define AO_HAVE_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_fetch_and_sub1_read) +# define AO_fetch_and_sub1_acquire_read(addr) \ + AO_fetch_and_sub1_read(addr) +# define AO_HAVE_fetch_and_sub1_acquire_read +#endif +#if !defined(AO_HAVE_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_fetch_and_sub1_acquire) +# define AO_fetch_and_sub1_acquire_read(addr) \ + AO_fetch_and_sub1_acquire(addr) +# define AO_HAVE_fetch_and_sub1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_fetch_and_sub1_acquire_read) +# define AO_fetch_and_sub1_dd_acquire_read(addr) \ + AO_fetch_and_sub1_acquire_read(addr) +# define AO_HAVE_fetch_and_sub1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_fetch_and_sub1) +# define AO_fetch_and_sub1_dd_acquire_read(addr) \ + AO_fetch_and_sub1(addr) +# define AO_HAVE_fetch_and_sub1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* and */ +#if defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_and_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_and_full(volatile AO_t *addr, AO_t value) + { + AO_t old; + + do + { + old = *(AO_t *)addr; + } + while (AO_EXPECT_FALSE(!AO_compare_and_swap_full(addr, old, + old & value))); + } +# define AO_HAVE_and_full +#endif + +#if defined(AO_HAVE_and_full) +# if !defined(AO_HAVE_and_release) +# define AO_and_release(addr, val) AO_and_full(addr, val) +# define AO_HAVE_and_release +# endif +# if !defined(AO_HAVE_and_acquire) +# define AO_and_acquire(addr, val) AO_and_full(addr, val) +# define AO_HAVE_and_acquire +# endif +# if !defined(AO_HAVE_and_write) +# define AO_and_write(addr, val) AO_and_full(addr, val) +# define AO_HAVE_and_write +# endif +# if !defined(AO_HAVE_and_read) +# define AO_and_read(addr, val) AO_and_full(addr, val) +# define AO_HAVE_and_read +# endif +#endif /* AO_HAVE_and_full */ + +#if !defined(AO_HAVE_and) && defined(AO_HAVE_and_release) +# define AO_and(addr, val) AO_and_release(addr, val) +# define AO_HAVE_and +#endif +#if !defined(AO_HAVE_and) && defined(AO_HAVE_and_acquire) +# define AO_and(addr, val) AO_and_acquire(addr, val) +# define AO_HAVE_and +#endif +#if !defined(AO_HAVE_and) && defined(AO_HAVE_and_write) +# define AO_and(addr, val) AO_and_write(addr, val) +# define AO_HAVE_and +#endif +#if !defined(AO_HAVE_and) && defined(AO_HAVE_and_read) +# define AO_and(addr, val) AO_and_read(addr, val) +# define AO_HAVE_and +#endif + +#if defined(AO_HAVE_and_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_and_full) +# define AO_and_full(addr, val) \ + (AO_nop_full(), AO_and_acquire(addr, val)) +# define AO_HAVE_and_full +#endif + +#if !defined(AO_HAVE_and_release_write) \ + && defined(AO_HAVE_and_write) +# define AO_and_release_write(addr, val) AO_and_write(addr, val) +# define AO_HAVE_and_release_write +#endif +#if !defined(AO_HAVE_and_release_write) \ + && defined(AO_HAVE_and_release) +# define AO_and_release_write(addr, val) AO_and_release(addr, val) +# define AO_HAVE_and_release_write +#endif +#if !defined(AO_HAVE_and_acquire_read) \ + && defined(AO_HAVE_and_read) +# define AO_and_acquire_read(addr, val) AO_and_read(addr, val) +# define AO_HAVE_and_acquire_read +#endif +#if !defined(AO_HAVE_and_acquire_read) \ + && defined(AO_HAVE_and_acquire) +# define AO_and_acquire_read(addr, val) AO_and_acquire(addr, val) +# define AO_HAVE_and_acquire_read +#endif + +/* or */ +#if defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_or_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_or_full(volatile AO_t *addr, AO_t value) + { + AO_t old; + + do + { + old = *(AO_t *)addr; + } + while (AO_EXPECT_FALSE(!AO_compare_and_swap_full(addr, old, + old | value))); + } +# define AO_HAVE_or_full +#endif + +#if defined(AO_HAVE_or_full) +# if !defined(AO_HAVE_or_release) +# define AO_or_release(addr, val) AO_or_full(addr, val) +# define AO_HAVE_or_release +# endif +# if !defined(AO_HAVE_or_acquire) +# define AO_or_acquire(addr, val) AO_or_full(addr, val) +# define AO_HAVE_or_acquire +# endif +# if !defined(AO_HAVE_or_write) +# define AO_or_write(addr, val) AO_or_full(addr, val) +# define AO_HAVE_or_write +# endif +# if !defined(AO_HAVE_or_read) +# define AO_or_read(addr, val) AO_or_full(addr, val) +# define AO_HAVE_or_read +# endif +#endif /* AO_HAVE_or_full */ + +#if !defined(AO_HAVE_or) && defined(AO_HAVE_or_release) +# define AO_or(addr, val) AO_or_release(addr, val) +# define AO_HAVE_or +#endif +#if !defined(AO_HAVE_or) && defined(AO_HAVE_or_acquire) +# define AO_or(addr, val) AO_or_acquire(addr, val) +# define AO_HAVE_or +#endif +#if !defined(AO_HAVE_or) && defined(AO_HAVE_or_write) +# define AO_or(addr, val) AO_or_write(addr, val) +# define AO_HAVE_or +#endif +#if !defined(AO_HAVE_or) && defined(AO_HAVE_or_read) +# define AO_or(addr, val) AO_or_read(addr, val) +# define AO_HAVE_or +#endif + +#if defined(AO_HAVE_or_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_or_full) +# define AO_or_full(addr, val) \ + (AO_nop_full(), AO_or_acquire(addr, val)) +# define AO_HAVE_or_full +#endif + +#if !defined(AO_HAVE_or_release_write) \ + && defined(AO_HAVE_or_write) +# define AO_or_release_write(addr, val) AO_or_write(addr, val) +# define AO_HAVE_or_release_write +#endif +#if !defined(AO_HAVE_or_release_write) \ + && defined(AO_HAVE_or_release) +# define AO_or_release_write(addr, val) AO_or_release(addr, val) +# define AO_HAVE_or_release_write +#endif +#if !defined(AO_HAVE_or_acquire_read) && defined(AO_HAVE_or_read) +# define AO_or_acquire_read(addr, val) AO_or_read(addr, val) +# define AO_HAVE_or_acquire_read +#endif +#if !defined(AO_HAVE_or_acquire_read) \ + && defined(AO_HAVE_or_acquire) +# define AO_or_acquire_read(addr, val) AO_or_acquire(addr, val) +# define AO_HAVE_or_acquire_read +#endif + +/* xor */ +#if defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_xor_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_xor_full(volatile AO_t *addr, AO_t value) + { + AO_t old; + + do + { + old = *(AO_t *)addr; + } + while (AO_EXPECT_FALSE(!AO_compare_and_swap_full(addr, old, + old ^ value))); + } +# define AO_HAVE_xor_full +#endif + +#if defined(AO_HAVE_xor_full) +# if !defined(AO_HAVE_xor_release) +# define AO_xor_release(addr, val) AO_xor_full(addr, val) +# define AO_HAVE_xor_release +# endif +# if !defined(AO_HAVE_xor_acquire) +# define AO_xor_acquire(addr, val) AO_xor_full(addr, val) +# define AO_HAVE_xor_acquire +# endif +# if !defined(AO_HAVE_xor_write) +# define AO_xor_write(addr, val) AO_xor_full(addr, val) +# define AO_HAVE_xor_write +# endif +# if !defined(AO_HAVE_xor_read) +# define AO_xor_read(addr, val) AO_xor_full(addr, val) +# define AO_HAVE_xor_read +# endif +#endif /* AO_HAVE_xor_full */ + +#if !defined(AO_HAVE_xor) && defined(AO_HAVE_xor_release) +# define AO_xor(addr, val) AO_xor_release(addr, val) +# define AO_HAVE_xor +#endif +#if !defined(AO_HAVE_xor) && defined(AO_HAVE_xor_acquire) +# define AO_xor(addr, val) AO_xor_acquire(addr, val) +# define AO_HAVE_xor +#endif +#if !defined(AO_HAVE_xor) && defined(AO_HAVE_xor_write) +# define AO_xor(addr, val) AO_xor_write(addr, val) +# define AO_HAVE_xor +#endif +#if !defined(AO_HAVE_xor) && defined(AO_HAVE_xor_read) +# define AO_xor(addr, val) AO_xor_read(addr, val) +# define AO_HAVE_xor +#endif + +#if defined(AO_HAVE_xor_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_xor_full) +# define AO_xor_full(addr, val) \ + (AO_nop_full(), AO_xor_acquire(addr, val)) +# define AO_HAVE_xor_full +#endif + +#if !defined(AO_HAVE_xor_release_write) \ + && defined(AO_HAVE_xor_write) +# define AO_xor_release_write(addr, val) AO_xor_write(addr, val) +# define AO_HAVE_xor_release_write +#endif +#if !defined(AO_HAVE_xor_release_write) \ + && defined(AO_HAVE_xor_release) +# define AO_xor_release_write(addr, val) AO_xor_release(addr, val) +# define AO_HAVE_xor_release_write +#endif +#if !defined(AO_HAVE_xor_acquire_read) \ + && defined(AO_HAVE_xor_read) +# define AO_xor_acquire_read(addr, val) AO_xor_read(addr, val) +# define AO_HAVE_xor_acquire_read +#endif +#if !defined(AO_HAVE_xor_acquire_read) \ + && defined(AO_HAVE_xor_acquire) +# define AO_xor_acquire_read(addr, val) AO_xor_acquire(addr, val) +# define AO_HAVE_xor_acquire_read +#endif + +/* and/or/xor_dd_acquire_read are meaningless. */ diff --git a/libatomic_ops/src/atomic_ops/generalize-arithm.template b/libatomic_ops/src/atomic_ops/generalize-arithm.template new file mode 100644 index 000000000..0a21ec234 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/generalize-arithm.template @@ -0,0 +1,852 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* XSIZE_compare_and_swap (based on fetch_compare_and_swap) */ +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_full) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_full) + AO_INLINE int + AO_XSIZE_compare_and_swap_full(volatile XCTYPE *addr, XCTYPE old_val, + XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap_full(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap_full +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_acquire) + AO_INLINE int + AO_XSIZE_compare_and_swap_acquire(volatile XCTYPE *addr, XCTYPE old_val, + XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap_acquire(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap_acquire +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_release) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_release) + AO_INLINE int + AO_XSIZE_compare_and_swap_release(volatile XCTYPE *addr, XCTYPE old_val, + XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap_release(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap_release +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_write) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_write) + AO_INLINE int + AO_XSIZE_compare_and_swap_write(volatile XCTYPE *addr, XCTYPE old_val, + XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap_write(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap_write +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_read) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_read) + AO_INLINE int + AO_XSIZE_compare_and_swap_read(volatile XCTYPE *addr, XCTYPE old_val, + XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap_read(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap_read +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap) + AO_INLINE int + AO_XSIZE_compare_and_swap(volatile XCTYPE *addr, XCTYPE old_val, + XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap(addr, old_val, new_val) == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_release_write) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_release_write) + AO_INLINE int + AO_XSIZE_compare_and_swap_release_write(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap_release_write(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap_release_write +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire_read) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_acquire_read) + AO_INLINE int + AO_XSIZE_compare_and_swap_acquire_read(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap_acquire_read +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_dd_acquire_read) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_dd_acquire_read) + AO_INLINE int + AO_XSIZE_compare_and_swap_dd_acquire_read(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + return AO_XSIZE_fetch_compare_and_swap_dd_acquire_read(addr, old_val, + new_val) == old_val; + } +# define AO_HAVE_XSIZE_compare_and_swap_dd_acquire_read +#endif + +/* XSIZE_fetch_and_add */ +/* We first try to implement fetch_and_add variants in terms of the */ +/* corresponding compare_and_swap variants to minimize adding barriers. */ +#if defined(AO_HAVE_XSIZE_compare_and_swap_full) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE XCTYPE + AO_XSIZE_fetch_and_add_full(volatile XCTYPE *addr, XCTYPE incr) + { + XCTYPE old; + + do + { + old = *(XCTYPE *)addr; + } + while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_full(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_XSIZE_fetch_and_add_full +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_acquire) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE XCTYPE + AO_XSIZE_fetch_and_add_acquire(volatile XCTYPE *addr, XCTYPE incr) + { + XCTYPE old; + + do + { + old = *(XCTYPE *)addr; + } + while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_acquire(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_XSIZE_fetch_and_add_acquire +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_release) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add_release) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE XCTYPE + AO_XSIZE_fetch_and_add_release(volatile XCTYPE *addr, XCTYPE incr) + { + XCTYPE old; + + do + { + old = *(XCTYPE *)addr; + } + while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_release(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_XSIZE_fetch_and_add_release +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE XCTYPE + AO_XSIZE_fetch_and_add(volatile XCTYPE *addr, XCTYPE incr) + { + XCTYPE old; + + do + { + old = *(XCTYPE *)addr; + } + while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap(addr, old, + old + incr))); + return old; + } +# define AO_HAVE_XSIZE_fetch_and_add +#endif + +#if defined(AO_HAVE_XSIZE_fetch_and_add_full) +# if !defined(AO_HAVE_XSIZE_fetch_and_add_release) +# define AO_XSIZE_fetch_and_add_release(addr, val) \ + AO_XSIZE_fetch_and_add_full(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_release +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_add_acquire) +# define AO_XSIZE_fetch_and_add_acquire(addr, val) \ + AO_XSIZE_fetch_and_add_full(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_acquire +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_add_write) +# define AO_XSIZE_fetch_and_add_write(addr, val) \ + AO_XSIZE_fetch_and_add_full(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_write +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_add_read) +# define AO_XSIZE_fetch_and_add_read(addr, val) \ + AO_XSIZE_fetch_and_add_full(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_read +# endif +#endif /* AO_HAVE_XSIZE_fetch_and_add_full */ + +#if defined(AO_HAVE_XSIZE_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add_acquire) + AO_INLINE XCTYPE + AO_XSIZE_fetch_and_add_acquire(volatile XCTYPE *addr, XCTYPE incr) + { + XCTYPE result = AO_XSIZE_fetch_and_add(addr, incr); + AO_nop_full(); + return result; + } +# define AO_HAVE_XSIZE_fetch_and_add_acquire +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add_release) +# define AO_XSIZE_fetch_and_add_release(addr, incr) \ + (AO_nop_full(), AO_XSIZE_fetch_and_add(addr, incr)) +# define AO_HAVE_XSIZE_fetch_and_add_release +#endif + +#if !defined(AO_HAVE_XSIZE_fetch_and_add) \ + && defined(AO_HAVE_XSIZE_fetch_and_add_release) +# define AO_XSIZE_fetch_and_add(addr, val) \ + AO_XSIZE_fetch_and_add_release(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add) \ + && defined(AO_HAVE_XSIZE_fetch_and_add_acquire) +# define AO_XSIZE_fetch_and_add(addr, val) \ + AO_XSIZE_fetch_and_add_acquire(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add) \ + && defined(AO_HAVE_XSIZE_fetch_and_add_write) +# define AO_XSIZE_fetch_and_add(addr, val) \ + AO_XSIZE_fetch_and_add_write(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add) \ + && defined(AO_HAVE_XSIZE_fetch_and_add_read) +# define AO_XSIZE_fetch_and_add(addr, val) \ + AO_XSIZE_fetch_and_add_read(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add +#endif + +#if defined(AO_HAVE_XSIZE_fetch_and_add_acquire) \ + && defined(AO_HAVE_nop_full) && !defined(AO_HAVE_XSIZE_fetch_and_add_full) +# define AO_XSIZE_fetch_and_add_full(addr, val) \ + (AO_nop_full(), AO_XSIZE_fetch_and_add_acquire(addr, val)) +# define AO_HAVE_XSIZE_fetch_and_add_full +#endif + +#if !defined(AO_HAVE_XSIZE_fetch_and_add_release_write) \ + && defined(AO_HAVE_XSIZE_fetch_and_add_write) +# define AO_XSIZE_fetch_and_add_release_write(addr, val) \ + AO_XSIZE_fetch_and_add_write(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_release_write +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add_release_write) \ + && defined(AO_HAVE_XSIZE_fetch_and_add_release) +# define AO_XSIZE_fetch_and_add_release_write(addr, val) \ + AO_XSIZE_fetch_and_add_release(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_release_write +#endif + +#if !defined(AO_HAVE_XSIZE_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_XSIZE_fetch_and_add_read) +# define AO_XSIZE_fetch_and_add_acquire_read(addr, val) \ + AO_XSIZE_fetch_and_add_read(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_acquire_read +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add_acquire_read) \ + && defined(AO_HAVE_XSIZE_fetch_and_add_acquire) +# define AO_XSIZE_fetch_and_add_acquire_read(addr, val) \ + AO_XSIZE_fetch_and_add_acquire(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_XSIZE_fetch_and_add_acquire_read) +# define AO_XSIZE_fetch_and_add_dd_acquire_read(addr, val) \ + AO_XSIZE_fetch_and_add_acquire_read(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_XSIZE_fetch_and_add) +# define AO_XSIZE_fetch_and_add_dd_acquire_read(addr, val) \ + AO_XSIZE_fetch_and_add(addr, val) +# define AO_HAVE_XSIZE_fetch_and_add_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* XSIZE_fetch_and_add1 */ +#if defined(AO_HAVE_XSIZE_fetch_and_add_full) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1_full) +# define AO_XSIZE_fetch_and_add1_full(addr) \ + AO_XSIZE_fetch_and_add_full(addr, 1) +# define AO_HAVE_XSIZE_fetch_and_add1_full +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_release) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1_release) +# define AO_XSIZE_fetch_and_add1_release(addr) \ + AO_XSIZE_fetch_and_add_release(addr, 1) +# define AO_HAVE_XSIZE_fetch_and_add1_release +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_acquire) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1_acquire) +# define AO_XSIZE_fetch_and_add1_acquire(addr) \ + AO_XSIZE_fetch_and_add_acquire(addr, 1) +# define AO_HAVE_XSIZE_fetch_and_add1_acquire +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_write) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1_write) +# define AO_XSIZE_fetch_and_add1_write(addr) \ + AO_XSIZE_fetch_and_add_write(addr, 1) +# define AO_HAVE_XSIZE_fetch_and_add1_write +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_read) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1_read) +# define AO_XSIZE_fetch_and_add1_read(addr) \ + AO_XSIZE_fetch_and_add_read(addr, 1) +# define AO_HAVE_XSIZE_fetch_and_add1_read +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_release_write) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1_release_write) +# define AO_XSIZE_fetch_and_add1_release_write(addr) \ + AO_XSIZE_fetch_and_add_release_write(addr, 1) +# define AO_HAVE_XSIZE_fetch_and_add1_release_write +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1_acquire_read) +# define AO_XSIZE_fetch_and_add1_acquire_read(addr) \ + AO_XSIZE_fetch_and_add_acquire_read(addr, 1) +# define AO_HAVE_XSIZE_fetch_and_add1_acquire_read +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1) +# define AO_XSIZE_fetch_and_add1(addr) AO_XSIZE_fetch_and_add(addr, 1) +# define AO_HAVE_XSIZE_fetch_and_add1 +#endif + +#if defined(AO_HAVE_XSIZE_fetch_and_add1_full) +# if !defined(AO_HAVE_XSIZE_fetch_and_add1_release) +# define AO_XSIZE_fetch_and_add1_release(addr) \ + AO_XSIZE_fetch_and_add1_full(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_release +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_add1_acquire) +# define AO_XSIZE_fetch_and_add1_acquire(addr) \ + AO_XSIZE_fetch_and_add1_full(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_acquire +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_add1_write) +# define AO_XSIZE_fetch_and_add1_write(addr) \ + AO_XSIZE_fetch_and_add1_full(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_write +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_add1_read) +# define AO_XSIZE_fetch_and_add1_read(addr) \ + AO_XSIZE_fetch_and_add1_full(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_read +# endif +#endif /* AO_HAVE_XSIZE_fetch_and_add1_full */ + +#if !defined(AO_HAVE_XSIZE_fetch_and_add1) \ + && defined(AO_HAVE_XSIZE_fetch_and_add1_release) +# define AO_XSIZE_fetch_and_add1(addr) AO_XSIZE_fetch_and_add1_release(addr) +# define AO_HAVE_XSIZE_fetch_and_add1 +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add1) \ + && defined(AO_HAVE_XSIZE_fetch_and_add1_acquire) +# define AO_XSIZE_fetch_and_add1(addr) AO_XSIZE_fetch_and_add1_acquire(addr) +# define AO_HAVE_XSIZE_fetch_and_add1 +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add1) \ + && defined(AO_HAVE_XSIZE_fetch_and_add1_write) +# define AO_XSIZE_fetch_and_add1(addr) AO_XSIZE_fetch_and_add1_write(addr) +# define AO_HAVE_XSIZE_fetch_and_add1 +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add1) \ + && defined(AO_HAVE_XSIZE_fetch_and_add1_read) +# define AO_XSIZE_fetch_and_add1(addr) AO_XSIZE_fetch_and_add1_read(addr) +# define AO_HAVE_XSIZE_fetch_and_add1 +#endif + +#if defined(AO_HAVE_XSIZE_fetch_and_add1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_fetch_and_add1_full) +# define AO_XSIZE_fetch_and_add1_full(addr) \ + (AO_nop_full(), AO_XSIZE_fetch_and_add1_acquire(addr)) +# define AO_HAVE_XSIZE_fetch_and_add1_full +#endif + +#if !defined(AO_HAVE_XSIZE_fetch_and_add1_release_write) \ + && defined(AO_HAVE_XSIZE_fetch_and_add1_write) +# define AO_XSIZE_fetch_and_add1_release_write(addr) \ + AO_XSIZE_fetch_and_add1_write(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add1_release_write) \ + && defined(AO_HAVE_XSIZE_fetch_and_add1_release) +# define AO_XSIZE_fetch_and_add1_release_write(addr) \ + AO_XSIZE_fetch_and_add1_release(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_release_write +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_XSIZE_fetch_and_add1_read) +# define AO_XSIZE_fetch_and_add1_acquire_read(addr) \ + AO_XSIZE_fetch_and_add1_read(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_acquire_read +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_add1_acquire_read) \ + && defined(AO_HAVE_XSIZE_fetch_and_add1_acquire) +# define AO_XSIZE_fetch_and_add1_acquire_read(addr) \ + AO_XSIZE_fetch_and_add1_acquire(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_XSIZE_fetch_and_add1_acquire_read) +# define AO_XSIZE_fetch_and_add1_dd_acquire_read(addr) \ + AO_XSIZE_fetch_and_add1_acquire_read(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_XSIZE_fetch_and_add1) +# define AO_XSIZE_fetch_and_add1_dd_acquire_read(addr) \ + AO_XSIZE_fetch_and_add1(addr) +# define AO_HAVE_XSIZE_fetch_and_add1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* XSIZE_fetch_and_sub1 */ +#if defined(AO_HAVE_XSIZE_fetch_and_add_full) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1_full) +# define AO_XSIZE_fetch_and_sub1_full(addr) \ + AO_XSIZE_fetch_and_add_full(addr, (XCTYPE)(-1)) +# define AO_HAVE_XSIZE_fetch_and_sub1_full +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_release) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1_release) +# define AO_XSIZE_fetch_and_sub1_release(addr) \ + AO_XSIZE_fetch_and_add_release(addr, (XCTYPE)(-1)) +# define AO_HAVE_XSIZE_fetch_and_sub1_release +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_acquire) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire) +# define AO_XSIZE_fetch_and_sub1_acquire(addr) \ + AO_XSIZE_fetch_and_add_acquire(addr, (XCTYPE)(-1)) +# define AO_HAVE_XSIZE_fetch_and_sub1_acquire +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_write) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1_write) +# define AO_XSIZE_fetch_and_sub1_write(addr) \ + AO_XSIZE_fetch_and_add_write(addr, (XCTYPE)(-1)) +# define AO_HAVE_XSIZE_fetch_and_sub1_write +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_read) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1_read) +# define AO_XSIZE_fetch_and_sub1_read(addr) \ + AO_XSIZE_fetch_and_add_read(addr, (XCTYPE)(-1)) +# define AO_HAVE_XSIZE_fetch_and_sub1_read +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_release_write) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1_release_write) +# define AO_XSIZE_fetch_and_sub1_release_write(addr) \ + AO_XSIZE_fetch_and_add_release_write(addr, (XCTYPE)(-1)) +# define AO_HAVE_XSIZE_fetch_and_sub1_release_write +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add_acquire_read) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire_read) +# define AO_XSIZE_fetch_and_sub1_acquire_read(addr) \ + AO_XSIZE_fetch_and_add_acquire_read(addr, (XCTYPE)(-1)) +# define AO_HAVE_XSIZE_fetch_and_sub1_acquire_read +#endif +#if defined(AO_HAVE_XSIZE_fetch_and_add) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1) +# define AO_XSIZE_fetch_and_sub1(addr) \ + AO_XSIZE_fetch_and_add(addr, (XCTYPE)(-1)) +# define AO_HAVE_XSIZE_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_XSIZE_fetch_and_sub1_full) +# if !defined(AO_HAVE_XSIZE_fetch_and_sub1_release) +# define AO_XSIZE_fetch_and_sub1_release(addr) \ + AO_XSIZE_fetch_and_sub1_full(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_release +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire) +# define AO_XSIZE_fetch_and_sub1_acquire(addr) \ + AO_XSIZE_fetch_and_sub1_full(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_acquire +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_sub1_write) +# define AO_XSIZE_fetch_and_sub1_write(addr) \ + AO_XSIZE_fetch_and_sub1_full(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_write +# endif +# if !defined(AO_HAVE_XSIZE_fetch_and_sub1_read) +# define AO_XSIZE_fetch_and_sub1_read(addr) \ + AO_XSIZE_fetch_and_sub1_full(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_read +# endif +#endif /* AO_HAVE_XSIZE_fetch_and_sub1_full */ + +#if !defined(AO_HAVE_XSIZE_fetch_and_sub1) \ + && defined(AO_HAVE_XSIZE_fetch_and_sub1_release) +# define AO_XSIZE_fetch_and_sub1(addr) AO_XSIZE_fetch_and_sub1_release(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_sub1) \ + && defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire) +# define AO_XSIZE_fetch_and_sub1(addr) AO_XSIZE_fetch_and_sub1_acquire(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_sub1) \ + && defined(AO_HAVE_XSIZE_fetch_and_sub1_write) +# define AO_XSIZE_fetch_and_sub1(addr) AO_XSIZE_fetch_and_sub1_write(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1 +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_sub1) \ + && defined(AO_HAVE_XSIZE_fetch_and_sub1_read) +# define AO_XSIZE_fetch_and_sub1(addr) AO_XSIZE_fetch_and_sub1_read(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_fetch_and_sub1_full) +# define AO_XSIZE_fetch_and_sub1_full(addr) \ + (AO_nop_full(), AO_XSIZE_fetch_and_sub1_acquire(addr)) +# define AO_HAVE_XSIZE_fetch_and_sub1_full +#endif + +#if !defined(AO_HAVE_XSIZE_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_XSIZE_fetch_and_sub1_write) +# define AO_XSIZE_fetch_and_sub1_release_write(addr) \ + AO_XSIZE_fetch_and_sub1_write(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_sub1_release_write) \ + && defined(AO_HAVE_XSIZE_fetch_and_sub1_release) +# define AO_XSIZE_fetch_and_sub1_release_write(addr) \ + AO_XSIZE_fetch_and_sub1_release(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_release_write +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_XSIZE_fetch_and_sub1_read) +# define AO_XSIZE_fetch_and_sub1_acquire_read(addr) \ + AO_XSIZE_fetch_and_sub1_read(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_acquire_read +#endif +#if !defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire_read) \ + && defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire) +# define AO_XSIZE_fetch_and_sub1_acquire_read(addr) \ + AO_XSIZE_fetch_and_sub1_acquire(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_XSIZE_fetch_and_sub1_acquire_read) +# define AO_XSIZE_fetch_and_sub1_dd_acquire_read(addr) \ + AO_XSIZE_fetch_and_sub1_acquire_read(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_XSIZE_fetch_and_sub1) +# define AO_XSIZE_fetch_and_sub1_dd_acquire_read(addr) \ + AO_XSIZE_fetch_and_sub1(addr) +# define AO_HAVE_XSIZE_fetch_and_sub1_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* XSIZE_and */ +#if defined(AO_HAVE_XSIZE_compare_and_swap_full) \ + && !defined(AO_HAVE_XSIZE_and_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_XSIZE_and_full(volatile XCTYPE *addr, XCTYPE value) + { + XCTYPE old; + + do + { + old = *(XCTYPE *)addr; + } + while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_full(addr, old, + old & value))); + } +# define AO_HAVE_XSIZE_and_full +#endif + +#if defined(AO_HAVE_XSIZE_and_full) +# if !defined(AO_HAVE_XSIZE_and_release) +# define AO_XSIZE_and_release(addr, val) AO_XSIZE_and_full(addr, val) +# define AO_HAVE_XSIZE_and_release +# endif +# if !defined(AO_HAVE_XSIZE_and_acquire) +# define AO_XSIZE_and_acquire(addr, val) AO_XSIZE_and_full(addr, val) +# define AO_HAVE_XSIZE_and_acquire +# endif +# if !defined(AO_HAVE_XSIZE_and_write) +# define AO_XSIZE_and_write(addr, val) AO_XSIZE_and_full(addr, val) +# define AO_HAVE_XSIZE_and_write +# endif +# if !defined(AO_HAVE_XSIZE_and_read) +# define AO_XSIZE_and_read(addr, val) AO_XSIZE_and_full(addr, val) +# define AO_HAVE_XSIZE_and_read +# endif +#endif /* AO_HAVE_XSIZE_and_full */ + +#if !defined(AO_HAVE_XSIZE_and) && defined(AO_HAVE_XSIZE_and_release) +# define AO_XSIZE_and(addr, val) AO_XSIZE_and_release(addr, val) +# define AO_HAVE_XSIZE_and +#endif +#if !defined(AO_HAVE_XSIZE_and) && defined(AO_HAVE_XSIZE_and_acquire) +# define AO_XSIZE_and(addr, val) AO_XSIZE_and_acquire(addr, val) +# define AO_HAVE_XSIZE_and +#endif +#if !defined(AO_HAVE_XSIZE_and) && defined(AO_HAVE_XSIZE_and_write) +# define AO_XSIZE_and(addr, val) AO_XSIZE_and_write(addr, val) +# define AO_HAVE_XSIZE_and +#endif +#if !defined(AO_HAVE_XSIZE_and) && defined(AO_HAVE_XSIZE_and_read) +# define AO_XSIZE_and(addr, val) AO_XSIZE_and_read(addr, val) +# define AO_HAVE_XSIZE_and +#endif + +#if defined(AO_HAVE_XSIZE_and_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_and_full) +# define AO_XSIZE_and_full(addr, val) \ + (AO_nop_full(), AO_XSIZE_and_acquire(addr, val)) +# define AO_HAVE_XSIZE_and_full +#endif + +#if !defined(AO_HAVE_XSIZE_and_release_write) \ + && defined(AO_HAVE_XSIZE_and_write) +# define AO_XSIZE_and_release_write(addr, val) AO_XSIZE_and_write(addr, val) +# define AO_HAVE_XSIZE_and_release_write +#endif +#if !defined(AO_HAVE_XSIZE_and_release_write) \ + && defined(AO_HAVE_XSIZE_and_release) +# define AO_XSIZE_and_release_write(addr, val) AO_XSIZE_and_release(addr, val) +# define AO_HAVE_XSIZE_and_release_write +#endif +#if !defined(AO_HAVE_XSIZE_and_acquire_read) \ + && defined(AO_HAVE_XSIZE_and_read) +# define AO_XSIZE_and_acquire_read(addr, val) AO_XSIZE_and_read(addr, val) +# define AO_HAVE_XSIZE_and_acquire_read +#endif +#if !defined(AO_HAVE_XSIZE_and_acquire_read) \ + && defined(AO_HAVE_XSIZE_and_acquire) +# define AO_XSIZE_and_acquire_read(addr, val) AO_XSIZE_and_acquire(addr, val) +# define AO_HAVE_XSIZE_and_acquire_read +#endif + +/* XSIZE_or */ +#if defined(AO_HAVE_XSIZE_compare_and_swap_full) \ + && !defined(AO_HAVE_XSIZE_or_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_XSIZE_or_full(volatile XCTYPE *addr, XCTYPE value) + { + XCTYPE old; + + do + { + old = *(XCTYPE *)addr; + } + while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_full(addr, old, + old | value))); + } +# define AO_HAVE_XSIZE_or_full +#endif + +#if defined(AO_HAVE_XSIZE_or_full) +# if !defined(AO_HAVE_XSIZE_or_release) +# define AO_XSIZE_or_release(addr, val) AO_XSIZE_or_full(addr, val) +# define AO_HAVE_XSIZE_or_release +# endif +# if !defined(AO_HAVE_XSIZE_or_acquire) +# define AO_XSIZE_or_acquire(addr, val) AO_XSIZE_or_full(addr, val) +# define AO_HAVE_XSIZE_or_acquire +# endif +# if !defined(AO_HAVE_XSIZE_or_write) +# define AO_XSIZE_or_write(addr, val) AO_XSIZE_or_full(addr, val) +# define AO_HAVE_XSIZE_or_write +# endif +# if !defined(AO_HAVE_XSIZE_or_read) +# define AO_XSIZE_or_read(addr, val) AO_XSIZE_or_full(addr, val) +# define AO_HAVE_XSIZE_or_read +# endif +#endif /* AO_HAVE_XSIZE_or_full */ + +#if !defined(AO_HAVE_XSIZE_or) && defined(AO_HAVE_XSIZE_or_release) +# define AO_XSIZE_or(addr, val) AO_XSIZE_or_release(addr, val) +# define AO_HAVE_XSIZE_or +#endif +#if !defined(AO_HAVE_XSIZE_or) && defined(AO_HAVE_XSIZE_or_acquire) +# define AO_XSIZE_or(addr, val) AO_XSIZE_or_acquire(addr, val) +# define AO_HAVE_XSIZE_or +#endif +#if !defined(AO_HAVE_XSIZE_or) && defined(AO_HAVE_XSIZE_or_write) +# define AO_XSIZE_or(addr, val) AO_XSIZE_or_write(addr, val) +# define AO_HAVE_XSIZE_or +#endif +#if !defined(AO_HAVE_XSIZE_or) && defined(AO_HAVE_XSIZE_or_read) +# define AO_XSIZE_or(addr, val) AO_XSIZE_or_read(addr, val) +# define AO_HAVE_XSIZE_or +#endif + +#if defined(AO_HAVE_XSIZE_or_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_or_full) +# define AO_XSIZE_or_full(addr, val) \ + (AO_nop_full(), AO_XSIZE_or_acquire(addr, val)) +# define AO_HAVE_XSIZE_or_full +#endif + +#if !defined(AO_HAVE_XSIZE_or_release_write) \ + && defined(AO_HAVE_XSIZE_or_write) +# define AO_XSIZE_or_release_write(addr, val) AO_XSIZE_or_write(addr, val) +# define AO_HAVE_XSIZE_or_release_write +#endif +#if !defined(AO_HAVE_XSIZE_or_release_write) \ + && defined(AO_HAVE_XSIZE_or_release) +# define AO_XSIZE_or_release_write(addr, val) AO_XSIZE_or_release(addr, val) +# define AO_HAVE_XSIZE_or_release_write +#endif +#if !defined(AO_HAVE_XSIZE_or_acquire_read) && defined(AO_HAVE_XSIZE_or_read) +# define AO_XSIZE_or_acquire_read(addr, val) AO_XSIZE_or_read(addr, val) +# define AO_HAVE_XSIZE_or_acquire_read +#endif +#if !defined(AO_HAVE_XSIZE_or_acquire_read) \ + && defined(AO_HAVE_XSIZE_or_acquire) +# define AO_XSIZE_or_acquire_read(addr, val) AO_XSIZE_or_acquire(addr, val) +# define AO_HAVE_XSIZE_or_acquire_read +#endif + +/* XSIZE_xor */ +#if defined(AO_HAVE_XSIZE_compare_and_swap_full) \ + && !defined(AO_HAVE_XSIZE_xor_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_XSIZE_xor_full(volatile XCTYPE *addr, XCTYPE value) + { + XCTYPE old; + + do + { + old = *(XCTYPE *)addr; + } + while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_full(addr, old, + old ^ value))); + } +# define AO_HAVE_XSIZE_xor_full +#endif + +#if defined(AO_HAVE_XSIZE_xor_full) +# if !defined(AO_HAVE_XSIZE_xor_release) +# define AO_XSIZE_xor_release(addr, val) AO_XSIZE_xor_full(addr, val) +# define AO_HAVE_XSIZE_xor_release +# endif +# if !defined(AO_HAVE_XSIZE_xor_acquire) +# define AO_XSIZE_xor_acquire(addr, val) AO_XSIZE_xor_full(addr, val) +# define AO_HAVE_XSIZE_xor_acquire +# endif +# if !defined(AO_HAVE_XSIZE_xor_write) +# define AO_XSIZE_xor_write(addr, val) AO_XSIZE_xor_full(addr, val) +# define AO_HAVE_XSIZE_xor_write +# endif +# if !defined(AO_HAVE_XSIZE_xor_read) +# define AO_XSIZE_xor_read(addr, val) AO_XSIZE_xor_full(addr, val) +# define AO_HAVE_XSIZE_xor_read +# endif +#endif /* AO_HAVE_XSIZE_xor_full */ + +#if !defined(AO_HAVE_XSIZE_xor) && defined(AO_HAVE_XSIZE_xor_release) +# define AO_XSIZE_xor(addr, val) AO_XSIZE_xor_release(addr, val) +# define AO_HAVE_XSIZE_xor +#endif +#if !defined(AO_HAVE_XSIZE_xor) && defined(AO_HAVE_XSIZE_xor_acquire) +# define AO_XSIZE_xor(addr, val) AO_XSIZE_xor_acquire(addr, val) +# define AO_HAVE_XSIZE_xor +#endif +#if !defined(AO_HAVE_XSIZE_xor) && defined(AO_HAVE_XSIZE_xor_write) +# define AO_XSIZE_xor(addr, val) AO_XSIZE_xor_write(addr, val) +# define AO_HAVE_XSIZE_xor +#endif +#if !defined(AO_HAVE_XSIZE_xor) && defined(AO_HAVE_XSIZE_xor_read) +# define AO_XSIZE_xor(addr, val) AO_XSIZE_xor_read(addr, val) +# define AO_HAVE_XSIZE_xor +#endif + +#if defined(AO_HAVE_XSIZE_xor_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_xor_full) +# define AO_XSIZE_xor_full(addr, val) \ + (AO_nop_full(), AO_XSIZE_xor_acquire(addr, val)) +# define AO_HAVE_XSIZE_xor_full +#endif + +#if !defined(AO_HAVE_XSIZE_xor_release_write) \ + && defined(AO_HAVE_XSIZE_xor_write) +# define AO_XSIZE_xor_release_write(addr, val) AO_XSIZE_xor_write(addr, val) +# define AO_HAVE_XSIZE_xor_release_write +#endif +#if !defined(AO_HAVE_XSIZE_xor_release_write) \ + && defined(AO_HAVE_XSIZE_xor_release) +# define AO_XSIZE_xor_release_write(addr, val) AO_XSIZE_xor_release(addr, val) +# define AO_HAVE_XSIZE_xor_release_write +#endif +#if !defined(AO_HAVE_XSIZE_xor_acquire_read) \ + && defined(AO_HAVE_XSIZE_xor_read) +# define AO_XSIZE_xor_acquire_read(addr, val) AO_XSIZE_xor_read(addr, val) +# define AO_HAVE_XSIZE_xor_acquire_read +#endif +#if !defined(AO_HAVE_XSIZE_xor_acquire_read) \ + && defined(AO_HAVE_XSIZE_xor_acquire) +# define AO_XSIZE_xor_acquire_read(addr, val) AO_XSIZE_xor_acquire(addr, val) +# define AO_HAVE_XSIZE_xor_acquire_read +#endif + +/* XSIZE_and/or/xor_dd_acquire_read are meaningless. */ diff --git a/libatomic_ops/src/atomic_ops/generalize-small.h b/libatomic_ops/src/atomic_ops/generalize-small.h new file mode 100644 index 000000000..d93d0e827 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/generalize-small.h @@ -0,0 +1,2640 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* char_fetch_compare_and_swap */ +#if defined(AO_HAVE_char_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_fetch_compare_and_swap_acquire) + AO_INLINE unsigned/**/char + AO_char_fetch_compare_and_swap_acquire(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + unsigned/**/char result = AO_char_fetch_compare_and_swap(addr, old_val, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_char_fetch_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_char_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_fetch_compare_and_swap_release) +# define AO_char_fetch_compare_and_swap_release(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_char_fetch_compare_and_swap(addr, old_val, new_val)) +# define AO_HAVE_char_fetch_compare_and_swap_release +#endif +#if defined(AO_HAVE_char_fetch_compare_and_swap_full) +# if !defined(AO_HAVE_char_fetch_compare_and_swap_release) +# define AO_char_fetch_compare_and_swap_release(addr, old_val, new_val) \ + AO_char_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_release +# endif +# if !defined(AO_HAVE_char_fetch_compare_and_swap_acquire) +# define AO_char_fetch_compare_and_swap_acquire(addr, old_val, new_val) \ + AO_char_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_char_fetch_compare_and_swap_write) +# define AO_char_fetch_compare_and_swap_write(addr, old_val, new_val) \ + AO_char_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_write +# endif +# if !defined(AO_HAVE_char_fetch_compare_and_swap_read) +# define AO_char_fetch_compare_and_swap_read(addr, old_val, new_val) \ + AO_char_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_read +# endif +#endif /* AO_HAVE_char_fetch_compare_and_swap_full */ + +#if !defined(AO_HAVE_char_fetch_compare_and_swap) \ + && defined(AO_HAVE_char_fetch_compare_and_swap_release) +# define AO_char_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_char_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_char_fetch_compare_and_swap) \ + && defined(AO_HAVE_char_fetch_compare_and_swap_acquire) +# define AO_char_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_char_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_char_fetch_compare_and_swap) \ + && defined(AO_HAVE_char_fetch_compare_and_swap_write) +# define AO_char_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_char_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_char_fetch_compare_and_swap) \ + && defined(AO_HAVE_char_fetch_compare_and_swap_read) +# define AO_char_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_char_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap +#endif + +#if defined(AO_HAVE_char_fetch_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_fetch_compare_and_swap_full) +# define AO_char_fetch_compare_and_swap_full(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_char_fetch_compare_and_swap_acquire(addr, old_val, new_val)) +# define AO_HAVE_char_fetch_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_char_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_char_fetch_compare_and_swap_write) +# define AO_char_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_char_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_char_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_char_fetch_compare_and_swap_release) +# define AO_char_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_char_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_char_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_char_fetch_compare_and_swap_read) +# define AO_char_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_char_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_char_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_char_fetch_compare_and_swap_acquire) +# define AO_char_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_char_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_char_fetch_compare_and_swap_acquire_read) +# define AO_char_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_char_fetch_compare_and_swap_acquire_read(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_char_fetch_compare_and_swap) +# define AO_char_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_char_fetch_compare_and_swap(addr, old_val, new_val) +# define AO_HAVE_char_fetch_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* char_compare_and_swap */ +#if defined(AO_HAVE_char_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_compare_and_swap_acquire) + AO_INLINE int + AO_char_compare_and_swap_acquire(volatile unsigned/**/char *addr, unsigned/**/char old, + unsigned/**/char new_val) + { + int result = AO_char_compare_and_swap(addr, old, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_char_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_char_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_compare_and_swap_release) +# define AO_char_compare_and_swap_release(addr, old, new_val) \ + (AO_nop_full(), AO_char_compare_and_swap(addr, old, new_val)) +# define AO_HAVE_char_compare_and_swap_release +#endif +#if defined(AO_HAVE_char_compare_and_swap_full) +# if !defined(AO_HAVE_char_compare_and_swap_release) +# define AO_char_compare_and_swap_release(addr, old, new_val) \ + AO_char_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_release +# endif +# if !defined(AO_HAVE_char_compare_and_swap_acquire) +# define AO_char_compare_and_swap_acquire(addr, old, new_val) \ + AO_char_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_char_compare_and_swap_write) +# define AO_char_compare_and_swap_write(addr, old, new_val) \ + AO_char_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_write +# endif +# if !defined(AO_HAVE_char_compare_and_swap_read) +# define AO_char_compare_and_swap_read(addr, old, new_val) \ + AO_char_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_read +# endif +#endif /* AO_HAVE_char_compare_and_swap_full */ + +#if !defined(AO_HAVE_char_compare_and_swap) \ + && defined(AO_HAVE_char_compare_and_swap_release) +# define AO_char_compare_and_swap(addr, old, new_val) \ + AO_char_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap +#endif +#if !defined(AO_HAVE_char_compare_and_swap) \ + && defined(AO_HAVE_char_compare_and_swap_acquire) +# define AO_char_compare_and_swap(addr, old, new_val) \ + AO_char_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap +#endif +#if !defined(AO_HAVE_char_compare_and_swap) \ + && defined(AO_HAVE_char_compare_and_swap_write) +# define AO_char_compare_and_swap(addr, old, new_val) \ + AO_char_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap +#endif +#if !defined(AO_HAVE_char_compare_and_swap) \ + && defined(AO_HAVE_char_compare_and_swap_read) +# define AO_char_compare_and_swap(addr, old, new_val) \ + AO_char_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap +#endif + +#if defined(AO_HAVE_char_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_compare_and_swap_full) +# define AO_char_compare_and_swap_full(addr, old, new_val) \ + (AO_nop_full(), \ + AO_char_compare_and_swap_acquire(addr, old, new_val)) +# define AO_HAVE_char_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_char_compare_and_swap_release_write) \ + && defined(AO_HAVE_char_compare_and_swap_write) +# define AO_char_compare_and_swap_release_write(addr, old, new_val) \ + AO_char_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_char_compare_and_swap_release_write) \ + && defined(AO_HAVE_char_compare_and_swap_release) +# define AO_char_compare_and_swap_release_write(addr, old, new_val) \ + AO_char_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_char_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_char_compare_and_swap_read) +# define AO_char_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_char_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_char_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_char_compare_and_swap_acquire) +# define AO_char_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_char_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_char_compare_and_swap_acquire_read) +# define AO_char_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_char_compare_and_swap_acquire_read(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_char_compare_and_swap) +# define AO_char_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_char_compare_and_swap(addr, old, new_val) +# define AO_HAVE_char_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* char_load */ +#if defined(AO_HAVE_char_load_full) && !defined(AO_HAVE_char_load_acquire) +# define AO_char_load_acquire(addr) AO_char_load_full(addr) +# define AO_HAVE_char_load_acquire +#endif + +#if defined(AO_HAVE_char_load_acquire) && !defined(AO_HAVE_char_load) +# define AO_char_load(addr) AO_char_load_acquire(addr) +# define AO_HAVE_char_load +#endif + +#if defined(AO_HAVE_char_load_full) && !defined(AO_HAVE_char_load_read) +# define AO_char_load_read(addr) AO_char_load_full(addr) +# define AO_HAVE_char_load_read +#endif + +#if !defined(AO_HAVE_char_load_acquire_read) \ + && defined(AO_HAVE_char_load_acquire) +# define AO_char_load_acquire_read(addr) AO_char_load_acquire(addr) +# define AO_HAVE_char_load_acquire_read +#endif + +#if defined(AO_HAVE_char_load) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_load_acquire) + AO_INLINE unsigned/**/char + AO_char_load_acquire(const volatile unsigned/**/char *addr) + { + unsigned/**/char result = AO_char_load(addr); + + /* Acquire barrier would be useless, since the load could be delayed */ + /* beyond it. */ + AO_nop_full(); + return result; + } +# define AO_HAVE_char_load_acquire +#endif + +#if defined(AO_HAVE_char_load) && defined(AO_HAVE_nop_read) \ + && !defined(AO_HAVE_char_load_read) + AO_INLINE unsigned/**/char + AO_char_load_read(const volatile unsigned/**/char *addr) + { + unsigned/**/char result = AO_char_load(addr); + + AO_nop_read(); + return result; + } +# define AO_HAVE_char_load_read +#endif + +#if defined(AO_HAVE_char_load_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_load_full) +# define AO_char_load_full(addr) (AO_nop_full(), AO_char_load_acquire(addr)) +# define AO_HAVE_char_load_full +#endif + +#if defined(AO_HAVE_char_compare_and_swap_read) \ + && !defined(AO_HAVE_char_load_read) +# define AO_char_CAS_BASED_LOAD_READ + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/char + AO_char_load_read(const volatile unsigned/**/char *addr) + { + unsigned/**/char result; + + do { + result = *(const unsigned/**/char *)addr; + } while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_read( + (volatile unsigned/**/char *)addr, + result, result))); + return result; + } +# define AO_HAVE_char_load_read +#endif + +#if !defined(AO_HAVE_char_load_acquire_read) \ + && defined(AO_HAVE_char_load_read) +# define AO_char_load_acquire_read(addr) AO_char_load_read(addr) +# define AO_HAVE_char_load_acquire_read +#endif + +#if defined(AO_HAVE_char_load_acquire_read) && !defined(AO_HAVE_char_load) \ + && (!defined(AO_char_CAS_BASED_LOAD_READ) \ + || !defined(AO_HAVE_char_compare_and_swap)) +# define AO_char_load(addr) AO_char_load_acquire_read(addr) +# define AO_HAVE_char_load +#endif + +#if defined(AO_HAVE_char_compare_and_swap_full) \ + && !defined(AO_HAVE_char_load_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/char + AO_char_load_full(const volatile unsigned/**/char *addr) + { + unsigned/**/char result; + + do { + result = *(const unsigned/**/char *)addr; + } while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_full( + (volatile unsigned/**/char *)addr, + result, result))); + return result; + } +# define AO_HAVE_char_load_full +#endif + +#if defined(AO_HAVE_char_compare_and_swap_acquire) \ + && !defined(AO_HAVE_char_load_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/char + AO_char_load_acquire(const volatile unsigned/**/char *addr) + { + unsigned/**/char result; + + do { + result = *(const unsigned/**/char *)addr; + } while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_acquire( + (volatile unsigned/**/char *)addr, + result, result))); + return result; + } +# define AO_HAVE_char_load_acquire +#endif + +#if defined(AO_HAVE_char_compare_and_swap) && !defined(AO_HAVE_char_load) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/char + AO_char_load(const volatile unsigned/**/char *addr) + { + unsigned/**/char result; + + do { + result = *(const unsigned/**/char *)addr; + } while (AO_EXPECT_FALSE(!AO_char_compare_and_swap( + (volatile unsigned/**/char *)addr, + result, result))); + return result; + } +# define AO_HAVE_char_load +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_char_load_acquire_read) +# define AO_char_load_dd_acquire_read(addr) \ + AO_char_load_acquire_read(addr) +# define AO_HAVE_char_load_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_char_load) +# define AO_char_load_dd_acquire_read(addr) AO_char_load(addr) +# define AO_HAVE_char_load_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* char_store */ +#if defined(AO_HAVE_char_store_full) && !defined(AO_HAVE_char_store_release) +# define AO_char_store_release(addr, val) AO_char_store_full(addr, val) +# define AO_HAVE_char_store_release +#endif + +#if defined(AO_HAVE_char_store_release) && !defined(AO_HAVE_char_store) +# define AO_char_store(addr, val) AO_char_store_release(addr, val) +# define AO_HAVE_char_store +#endif + +#if defined(AO_HAVE_char_store_full) && !defined(AO_HAVE_char_store_write) +# define AO_char_store_write(addr, val) AO_char_store_full(addr, val) +# define AO_HAVE_char_store_write +#endif + +#if defined(AO_HAVE_char_store_release) \ + && !defined(AO_HAVE_char_store_release_write) +# define AO_char_store_release_write(addr, val) \ + AO_char_store_release(addr, val) +# define AO_HAVE_char_store_release_write +#endif + +#if defined(AO_HAVE_char_store_write) && !defined(AO_HAVE_char_store) +# define AO_char_store(addr, val) AO_char_store_write(addr, val) +# define AO_HAVE_char_store +#endif + +#if defined(AO_HAVE_char_store) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_store_release) +# define AO_char_store_release(addr, val) \ + (AO_nop_full(), AO_char_store(addr, val)) +# define AO_HAVE_char_store_release +#endif + +#if defined(AO_HAVE_char_store) && defined(AO_HAVE_nop_write) \ + && !defined(AO_HAVE_char_store_write) +# define AO_char_store_write(addr, val) \ + (AO_nop_write(), AO_char_store(addr, val)) +# define AO_HAVE_char_store_write +#endif + +#if defined(AO_HAVE_char_compare_and_swap_write) \ + && !defined(AO_HAVE_char_store_write) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_char_store_write(volatile unsigned/**/char *addr, unsigned/**/char new_val) + { + unsigned/**/char old_val; + + do { + old_val = *(unsigned/**/char *)addr; + } while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_write(addr, old_val, + new_val))); + } +# define AO_HAVE_char_store_write +#endif + +#if defined(AO_HAVE_char_store_write) \ + && !defined(AO_HAVE_char_store_release_write) +# define AO_char_store_release_write(addr, val) \ + AO_char_store_write(addr, val) +# define AO_HAVE_char_store_release_write +#endif + +#if defined(AO_HAVE_char_store_release) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_char_store_full) +# define AO_char_store_full(addr, val) \ + (AO_char_store_release(addr, val), \ + AO_nop_full()) +# define AO_HAVE_char_store_full +#endif + +#if defined(AO_HAVE_char_compare_and_swap) && !defined(AO_HAVE_char_store) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_char_store(volatile unsigned/**/char *addr, unsigned/**/char new_val) + { + unsigned/**/char old_val; + + do { + old_val = *(unsigned/**/char *)addr; + } while (AO_EXPECT_FALSE(!AO_char_compare_and_swap(addr, + old_val, new_val))); + } +# define AO_HAVE_char_store +#endif + +#if defined(AO_HAVE_char_compare_and_swap_release) \ + && !defined(AO_HAVE_char_store_release) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_char_store_release(volatile unsigned/**/char *addr, unsigned/**/char new_val) + { + unsigned/**/char old_val; + + do { + old_val = *(unsigned/**/char *)addr; + } while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_release(addr, old_val, + new_val))); + } +# define AO_HAVE_char_store_release +#endif + +#if defined(AO_HAVE_char_compare_and_swap_full) \ + && !defined(AO_HAVE_char_store_full) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_char_store_full(volatile unsigned/**/char *addr, unsigned/**/char new_val) + { + unsigned/**/char old_val; + + do { + old_val = *(unsigned/**/char *)addr; + } while (AO_EXPECT_FALSE(!AO_char_compare_and_swap_full(addr, old_val, + new_val))); + } +# define AO_HAVE_char_store_full +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* short_fetch_compare_and_swap */ +#if defined(AO_HAVE_short_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_fetch_compare_and_swap_acquire) + AO_INLINE unsigned/**/short + AO_short_fetch_compare_and_swap_acquire(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + unsigned/**/short result = AO_short_fetch_compare_and_swap(addr, old_val, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_short_fetch_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_short_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_fetch_compare_and_swap_release) +# define AO_short_fetch_compare_and_swap_release(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_short_fetch_compare_and_swap(addr, old_val, new_val)) +# define AO_HAVE_short_fetch_compare_and_swap_release +#endif +#if defined(AO_HAVE_short_fetch_compare_and_swap_full) +# if !defined(AO_HAVE_short_fetch_compare_and_swap_release) +# define AO_short_fetch_compare_and_swap_release(addr, old_val, new_val) \ + AO_short_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_release +# endif +# if !defined(AO_HAVE_short_fetch_compare_and_swap_acquire) +# define AO_short_fetch_compare_and_swap_acquire(addr, old_val, new_val) \ + AO_short_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_short_fetch_compare_and_swap_write) +# define AO_short_fetch_compare_and_swap_write(addr, old_val, new_val) \ + AO_short_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_write +# endif +# if !defined(AO_HAVE_short_fetch_compare_and_swap_read) +# define AO_short_fetch_compare_and_swap_read(addr, old_val, new_val) \ + AO_short_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_read +# endif +#endif /* AO_HAVE_short_fetch_compare_and_swap_full */ + +#if !defined(AO_HAVE_short_fetch_compare_and_swap) \ + && defined(AO_HAVE_short_fetch_compare_and_swap_release) +# define AO_short_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_short_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_short_fetch_compare_and_swap) \ + && defined(AO_HAVE_short_fetch_compare_and_swap_acquire) +# define AO_short_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_short_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_short_fetch_compare_and_swap) \ + && defined(AO_HAVE_short_fetch_compare_and_swap_write) +# define AO_short_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_short_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_short_fetch_compare_and_swap) \ + && defined(AO_HAVE_short_fetch_compare_and_swap_read) +# define AO_short_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_short_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap +#endif + +#if defined(AO_HAVE_short_fetch_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_fetch_compare_and_swap_full) +# define AO_short_fetch_compare_and_swap_full(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_short_fetch_compare_and_swap_acquire(addr, old_val, new_val)) +# define AO_HAVE_short_fetch_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_short_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_short_fetch_compare_and_swap_write) +# define AO_short_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_short_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_short_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_short_fetch_compare_and_swap_release) +# define AO_short_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_short_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_short_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_short_fetch_compare_and_swap_read) +# define AO_short_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_short_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_short_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_short_fetch_compare_and_swap_acquire) +# define AO_short_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_short_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_short_fetch_compare_and_swap_acquire_read) +# define AO_short_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_short_fetch_compare_and_swap_acquire_read(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_short_fetch_compare_and_swap) +# define AO_short_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_short_fetch_compare_and_swap(addr, old_val, new_val) +# define AO_HAVE_short_fetch_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* short_compare_and_swap */ +#if defined(AO_HAVE_short_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_compare_and_swap_acquire) + AO_INLINE int + AO_short_compare_and_swap_acquire(volatile unsigned/**/short *addr, unsigned/**/short old, + unsigned/**/short new_val) + { + int result = AO_short_compare_and_swap(addr, old, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_short_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_short_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_compare_and_swap_release) +# define AO_short_compare_and_swap_release(addr, old, new_val) \ + (AO_nop_full(), AO_short_compare_and_swap(addr, old, new_val)) +# define AO_HAVE_short_compare_and_swap_release +#endif +#if defined(AO_HAVE_short_compare_and_swap_full) +# if !defined(AO_HAVE_short_compare_and_swap_release) +# define AO_short_compare_and_swap_release(addr, old, new_val) \ + AO_short_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_release +# endif +# if !defined(AO_HAVE_short_compare_and_swap_acquire) +# define AO_short_compare_and_swap_acquire(addr, old, new_val) \ + AO_short_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_short_compare_and_swap_write) +# define AO_short_compare_and_swap_write(addr, old, new_val) \ + AO_short_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_write +# endif +# if !defined(AO_HAVE_short_compare_and_swap_read) +# define AO_short_compare_and_swap_read(addr, old, new_val) \ + AO_short_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_read +# endif +#endif /* AO_HAVE_short_compare_and_swap_full */ + +#if !defined(AO_HAVE_short_compare_and_swap) \ + && defined(AO_HAVE_short_compare_and_swap_release) +# define AO_short_compare_and_swap(addr, old, new_val) \ + AO_short_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap +#endif +#if !defined(AO_HAVE_short_compare_and_swap) \ + && defined(AO_HAVE_short_compare_and_swap_acquire) +# define AO_short_compare_and_swap(addr, old, new_val) \ + AO_short_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap +#endif +#if !defined(AO_HAVE_short_compare_and_swap) \ + && defined(AO_HAVE_short_compare_and_swap_write) +# define AO_short_compare_and_swap(addr, old, new_val) \ + AO_short_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap +#endif +#if !defined(AO_HAVE_short_compare_and_swap) \ + && defined(AO_HAVE_short_compare_and_swap_read) +# define AO_short_compare_and_swap(addr, old, new_val) \ + AO_short_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap +#endif + +#if defined(AO_HAVE_short_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_compare_and_swap_full) +# define AO_short_compare_and_swap_full(addr, old, new_val) \ + (AO_nop_full(), \ + AO_short_compare_and_swap_acquire(addr, old, new_val)) +# define AO_HAVE_short_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_short_compare_and_swap_release_write) \ + && defined(AO_HAVE_short_compare_and_swap_write) +# define AO_short_compare_and_swap_release_write(addr, old, new_val) \ + AO_short_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_short_compare_and_swap_release_write) \ + && defined(AO_HAVE_short_compare_and_swap_release) +# define AO_short_compare_and_swap_release_write(addr, old, new_val) \ + AO_short_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_short_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_short_compare_and_swap_read) +# define AO_short_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_short_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_short_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_short_compare_and_swap_acquire) +# define AO_short_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_short_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_short_compare_and_swap_acquire_read) +# define AO_short_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_short_compare_and_swap_acquire_read(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_short_compare_and_swap) +# define AO_short_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_short_compare_and_swap(addr, old, new_val) +# define AO_HAVE_short_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* short_load */ +#if defined(AO_HAVE_short_load_full) && !defined(AO_HAVE_short_load_acquire) +# define AO_short_load_acquire(addr) AO_short_load_full(addr) +# define AO_HAVE_short_load_acquire +#endif + +#if defined(AO_HAVE_short_load_acquire) && !defined(AO_HAVE_short_load) +# define AO_short_load(addr) AO_short_load_acquire(addr) +# define AO_HAVE_short_load +#endif + +#if defined(AO_HAVE_short_load_full) && !defined(AO_HAVE_short_load_read) +# define AO_short_load_read(addr) AO_short_load_full(addr) +# define AO_HAVE_short_load_read +#endif + +#if !defined(AO_HAVE_short_load_acquire_read) \ + && defined(AO_HAVE_short_load_acquire) +# define AO_short_load_acquire_read(addr) AO_short_load_acquire(addr) +# define AO_HAVE_short_load_acquire_read +#endif + +#if defined(AO_HAVE_short_load) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_load_acquire) + AO_INLINE unsigned/**/short + AO_short_load_acquire(const volatile unsigned/**/short *addr) + { + unsigned/**/short result = AO_short_load(addr); + + /* Acquire barrier would be useless, since the load could be delayed */ + /* beyond it. */ + AO_nop_full(); + return result; + } +# define AO_HAVE_short_load_acquire +#endif + +#if defined(AO_HAVE_short_load) && defined(AO_HAVE_nop_read) \ + && !defined(AO_HAVE_short_load_read) + AO_INLINE unsigned/**/short + AO_short_load_read(const volatile unsigned/**/short *addr) + { + unsigned/**/short result = AO_short_load(addr); + + AO_nop_read(); + return result; + } +# define AO_HAVE_short_load_read +#endif + +#if defined(AO_HAVE_short_load_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_load_full) +# define AO_short_load_full(addr) (AO_nop_full(), AO_short_load_acquire(addr)) +# define AO_HAVE_short_load_full +#endif + +#if defined(AO_HAVE_short_compare_and_swap_read) \ + && !defined(AO_HAVE_short_load_read) +# define AO_short_CAS_BASED_LOAD_READ + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/short + AO_short_load_read(const volatile unsigned/**/short *addr) + { + unsigned/**/short result; + + do { + result = *(const unsigned/**/short *)addr; + } while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_read( + (volatile unsigned/**/short *)addr, + result, result))); + return result; + } +# define AO_HAVE_short_load_read +#endif + +#if !defined(AO_HAVE_short_load_acquire_read) \ + && defined(AO_HAVE_short_load_read) +# define AO_short_load_acquire_read(addr) AO_short_load_read(addr) +# define AO_HAVE_short_load_acquire_read +#endif + +#if defined(AO_HAVE_short_load_acquire_read) && !defined(AO_HAVE_short_load) \ + && (!defined(AO_short_CAS_BASED_LOAD_READ) \ + || !defined(AO_HAVE_short_compare_and_swap)) +# define AO_short_load(addr) AO_short_load_acquire_read(addr) +# define AO_HAVE_short_load +#endif + +#if defined(AO_HAVE_short_compare_and_swap_full) \ + && !defined(AO_HAVE_short_load_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/short + AO_short_load_full(const volatile unsigned/**/short *addr) + { + unsigned/**/short result; + + do { + result = *(const unsigned/**/short *)addr; + } while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_full( + (volatile unsigned/**/short *)addr, + result, result))); + return result; + } +# define AO_HAVE_short_load_full +#endif + +#if defined(AO_HAVE_short_compare_and_swap_acquire) \ + && !defined(AO_HAVE_short_load_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/short + AO_short_load_acquire(const volatile unsigned/**/short *addr) + { + unsigned/**/short result; + + do { + result = *(const unsigned/**/short *)addr; + } while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_acquire( + (volatile unsigned/**/short *)addr, + result, result))); + return result; + } +# define AO_HAVE_short_load_acquire +#endif + +#if defined(AO_HAVE_short_compare_and_swap) && !defined(AO_HAVE_short_load) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned/**/short + AO_short_load(const volatile unsigned/**/short *addr) + { + unsigned/**/short result; + + do { + result = *(const unsigned/**/short *)addr; + } while (AO_EXPECT_FALSE(!AO_short_compare_and_swap( + (volatile unsigned/**/short *)addr, + result, result))); + return result; + } +# define AO_HAVE_short_load +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_short_load_acquire_read) +# define AO_short_load_dd_acquire_read(addr) \ + AO_short_load_acquire_read(addr) +# define AO_HAVE_short_load_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_short_load) +# define AO_short_load_dd_acquire_read(addr) AO_short_load(addr) +# define AO_HAVE_short_load_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* short_store */ +#if defined(AO_HAVE_short_store_full) && !defined(AO_HAVE_short_store_release) +# define AO_short_store_release(addr, val) AO_short_store_full(addr, val) +# define AO_HAVE_short_store_release +#endif + +#if defined(AO_HAVE_short_store_release) && !defined(AO_HAVE_short_store) +# define AO_short_store(addr, val) AO_short_store_release(addr, val) +# define AO_HAVE_short_store +#endif + +#if defined(AO_HAVE_short_store_full) && !defined(AO_HAVE_short_store_write) +# define AO_short_store_write(addr, val) AO_short_store_full(addr, val) +# define AO_HAVE_short_store_write +#endif + +#if defined(AO_HAVE_short_store_release) \ + && !defined(AO_HAVE_short_store_release_write) +# define AO_short_store_release_write(addr, val) \ + AO_short_store_release(addr, val) +# define AO_HAVE_short_store_release_write +#endif + +#if defined(AO_HAVE_short_store_write) && !defined(AO_HAVE_short_store) +# define AO_short_store(addr, val) AO_short_store_write(addr, val) +# define AO_HAVE_short_store +#endif + +#if defined(AO_HAVE_short_store) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_store_release) +# define AO_short_store_release(addr, val) \ + (AO_nop_full(), AO_short_store(addr, val)) +# define AO_HAVE_short_store_release +#endif + +#if defined(AO_HAVE_short_store) && defined(AO_HAVE_nop_write) \ + && !defined(AO_HAVE_short_store_write) +# define AO_short_store_write(addr, val) \ + (AO_nop_write(), AO_short_store(addr, val)) +# define AO_HAVE_short_store_write +#endif + +#if defined(AO_HAVE_short_compare_and_swap_write) \ + && !defined(AO_HAVE_short_store_write) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_short_store_write(volatile unsigned/**/short *addr, unsigned/**/short new_val) + { + unsigned/**/short old_val; + + do { + old_val = *(unsigned/**/short *)addr; + } while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_write(addr, old_val, + new_val))); + } +# define AO_HAVE_short_store_write +#endif + +#if defined(AO_HAVE_short_store_write) \ + && !defined(AO_HAVE_short_store_release_write) +# define AO_short_store_release_write(addr, val) \ + AO_short_store_write(addr, val) +# define AO_HAVE_short_store_release_write +#endif + +#if defined(AO_HAVE_short_store_release) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_short_store_full) +# define AO_short_store_full(addr, val) \ + (AO_short_store_release(addr, val), \ + AO_nop_full()) +# define AO_HAVE_short_store_full +#endif + +#if defined(AO_HAVE_short_compare_and_swap) && !defined(AO_HAVE_short_store) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_short_store(volatile unsigned/**/short *addr, unsigned/**/short new_val) + { + unsigned/**/short old_val; + + do { + old_val = *(unsigned/**/short *)addr; + } while (AO_EXPECT_FALSE(!AO_short_compare_and_swap(addr, + old_val, new_val))); + } +# define AO_HAVE_short_store +#endif + +#if defined(AO_HAVE_short_compare_and_swap_release) \ + && !defined(AO_HAVE_short_store_release) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_short_store_release(volatile unsigned/**/short *addr, unsigned/**/short new_val) + { + unsigned/**/short old_val; + + do { + old_val = *(unsigned/**/short *)addr; + } while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_release(addr, old_val, + new_val))); + } +# define AO_HAVE_short_store_release +#endif + +#if defined(AO_HAVE_short_compare_and_swap_full) \ + && !defined(AO_HAVE_short_store_full) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_short_store_full(volatile unsigned/**/short *addr, unsigned/**/short new_val) + { + unsigned/**/short old_val; + + do { + old_val = *(unsigned/**/short *)addr; + } while (AO_EXPECT_FALSE(!AO_short_compare_and_swap_full(addr, old_val, + new_val))); + } +# define AO_HAVE_short_store_full +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* int_fetch_compare_and_swap */ +#if defined(AO_HAVE_int_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_acquire) + AO_INLINE unsigned + AO_int_fetch_compare_and_swap_acquire(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + unsigned result = AO_int_fetch_compare_and_swap(addr, old_val, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_int_fetch_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_int_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_release) +# define AO_int_fetch_compare_and_swap_release(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_int_fetch_compare_and_swap(addr, old_val, new_val)) +# define AO_HAVE_int_fetch_compare_and_swap_release +#endif +#if defined(AO_HAVE_int_fetch_compare_and_swap_full) +# if !defined(AO_HAVE_int_fetch_compare_and_swap_release) +# define AO_int_fetch_compare_and_swap_release(addr, old_val, new_val) \ + AO_int_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_release +# endif +# if !defined(AO_HAVE_int_fetch_compare_and_swap_acquire) +# define AO_int_fetch_compare_and_swap_acquire(addr, old_val, new_val) \ + AO_int_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_int_fetch_compare_and_swap_write) +# define AO_int_fetch_compare_and_swap_write(addr, old_val, new_val) \ + AO_int_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_write +# endif +# if !defined(AO_HAVE_int_fetch_compare_and_swap_read) +# define AO_int_fetch_compare_and_swap_read(addr, old_val, new_val) \ + AO_int_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_read +# endif +#endif /* AO_HAVE_int_fetch_compare_and_swap_full */ + +#if !defined(AO_HAVE_int_fetch_compare_and_swap) \ + && defined(AO_HAVE_int_fetch_compare_and_swap_release) +# define AO_int_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_int_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_int_fetch_compare_and_swap) \ + && defined(AO_HAVE_int_fetch_compare_and_swap_acquire) +# define AO_int_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_int_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_int_fetch_compare_and_swap) \ + && defined(AO_HAVE_int_fetch_compare_and_swap_write) +# define AO_int_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_int_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_int_fetch_compare_and_swap) \ + && defined(AO_HAVE_int_fetch_compare_and_swap_read) +# define AO_int_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_int_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap +#endif + +#if defined(AO_HAVE_int_fetch_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_full) +# define AO_int_fetch_compare_and_swap_full(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_int_fetch_compare_and_swap_acquire(addr, old_val, new_val)) +# define AO_HAVE_int_fetch_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_int_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_int_fetch_compare_and_swap_write) +# define AO_int_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_int_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_int_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_int_fetch_compare_and_swap_release) +# define AO_int_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_int_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_int_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_int_fetch_compare_and_swap_read) +# define AO_int_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_int_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_int_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_int_fetch_compare_and_swap_acquire) +# define AO_int_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_int_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_int_fetch_compare_and_swap_acquire_read) +# define AO_int_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_int_fetch_compare_and_swap_acquire_read(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_int_fetch_compare_and_swap) +# define AO_int_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_int_fetch_compare_and_swap(addr, old_val, new_val) +# define AO_HAVE_int_fetch_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* int_compare_and_swap */ +#if defined(AO_HAVE_int_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_compare_and_swap_acquire) + AO_INLINE int + AO_int_compare_and_swap_acquire(volatile unsigned *addr, unsigned old, + unsigned new_val) + { + int result = AO_int_compare_and_swap(addr, old, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_int_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_int_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_compare_and_swap_release) +# define AO_int_compare_and_swap_release(addr, old, new_val) \ + (AO_nop_full(), AO_int_compare_and_swap(addr, old, new_val)) +# define AO_HAVE_int_compare_and_swap_release +#endif +#if defined(AO_HAVE_int_compare_and_swap_full) +# if !defined(AO_HAVE_int_compare_and_swap_release) +# define AO_int_compare_and_swap_release(addr, old, new_val) \ + AO_int_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_release +# endif +# if !defined(AO_HAVE_int_compare_and_swap_acquire) +# define AO_int_compare_and_swap_acquire(addr, old, new_val) \ + AO_int_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_int_compare_and_swap_write) +# define AO_int_compare_and_swap_write(addr, old, new_val) \ + AO_int_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_write +# endif +# if !defined(AO_HAVE_int_compare_and_swap_read) +# define AO_int_compare_and_swap_read(addr, old, new_val) \ + AO_int_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_read +# endif +#endif /* AO_HAVE_int_compare_and_swap_full */ + +#if !defined(AO_HAVE_int_compare_and_swap) \ + && defined(AO_HAVE_int_compare_and_swap_release) +# define AO_int_compare_and_swap(addr, old, new_val) \ + AO_int_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap +#endif +#if !defined(AO_HAVE_int_compare_and_swap) \ + && defined(AO_HAVE_int_compare_and_swap_acquire) +# define AO_int_compare_and_swap(addr, old, new_val) \ + AO_int_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap +#endif +#if !defined(AO_HAVE_int_compare_and_swap) \ + && defined(AO_HAVE_int_compare_and_swap_write) +# define AO_int_compare_and_swap(addr, old, new_val) \ + AO_int_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap +#endif +#if !defined(AO_HAVE_int_compare_and_swap) \ + && defined(AO_HAVE_int_compare_and_swap_read) +# define AO_int_compare_and_swap(addr, old, new_val) \ + AO_int_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap +#endif + +#if defined(AO_HAVE_int_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_compare_and_swap_full) +# define AO_int_compare_and_swap_full(addr, old, new_val) \ + (AO_nop_full(), \ + AO_int_compare_and_swap_acquire(addr, old, new_val)) +# define AO_HAVE_int_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_int_compare_and_swap_release_write) \ + && defined(AO_HAVE_int_compare_and_swap_write) +# define AO_int_compare_and_swap_release_write(addr, old, new_val) \ + AO_int_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_int_compare_and_swap_release_write) \ + && defined(AO_HAVE_int_compare_and_swap_release) +# define AO_int_compare_and_swap_release_write(addr, old, new_val) \ + AO_int_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_int_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_int_compare_and_swap_read) +# define AO_int_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_int_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_int_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_int_compare_and_swap_acquire) +# define AO_int_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_int_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_int_compare_and_swap_acquire_read) +# define AO_int_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_int_compare_and_swap_acquire_read(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_int_compare_and_swap) +# define AO_int_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_int_compare_and_swap(addr, old, new_val) +# define AO_HAVE_int_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* int_load */ +#if defined(AO_HAVE_int_load_full) && !defined(AO_HAVE_int_load_acquire) +# define AO_int_load_acquire(addr) AO_int_load_full(addr) +# define AO_HAVE_int_load_acquire +#endif + +#if defined(AO_HAVE_int_load_acquire) && !defined(AO_HAVE_int_load) +# define AO_int_load(addr) AO_int_load_acquire(addr) +# define AO_HAVE_int_load +#endif + +#if defined(AO_HAVE_int_load_full) && !defined(AO_HAVE_int_load_read) +# define AO_int_load_read(addr) AO_int_load_full(addr) +# define AO_HAVE_int_load_read +#endif + +#if !defined(AO_HAVE_int_load_acquire_read) \ + && defined(AO_HAVE_int_load_acquire) +# define AO_int_load_acquire_read(addr) AO_int_load_acquire(addr) +# define AO_HAVE_int_load_acquire_read +#endif + +#if defined(AO_HAVE_int_load) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_load_acquire) + AO_INLINE unsigned + AO_int_load_acquire(const volatile unsigned *addr) + { + unsigned result = AO_int_load(addr); + + /* Acquire barrier would be useless, since the load could be delayed */ + /* beyond it. */ + AO_nop_full(); + return result; + } +# define AO_HAVE_int_load_acquire +#endif + +#if defined(AO_HAVE_int_load) && defined(AO_HAVE_nop_read) \ + && !defined(AO_HAVE_int_load_read) + AO_INLINE unsigned + AO_int_load_read(const volatile unsigned *addr) + { + unsigned result = AO_int_load(addr); + + AO_nop_read(); + return result; + } +# define AO_HAVE_int_load_read +#endif + +#if defined(AO_HAVE_int_load_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_load_full) +# define AO_int_load_full(addr) (AO_nop_full(), AO_int_load_acquire(addr)) +# define AO_HAVE_int_load_full +#endif + +#if defined(AO_HAVE_int_compare_and_swap_read) \ + && !defined(AO_HAVE_int_load_read) +# define AO_int_CAS_BASED_LOAD_READ + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned + AO_int_load_read(const volatile unsigned *addr) + { + unsigned result; + + do { + result = *(const unsigned *)addr; + } while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_read( + (volatile unsigned *)addr, + result, result))); + return result; + } +# define AO_HAVE_int_load_read +#endif + +#if !defined(AO_HAVE_int_load_acquire_read) \ + && defined(AO_HAVE_int_load_read) +# define AO_int_load_acquire_read(addr) AO_int_load_read(addr) +# define AO_HAVE_int_load_acquire_read +#endif + +#if defined(AO_HAVE_int_load_acquire_read) && !defined(AO_HAVE_int_load) \ + && (!defined(AO_int_CAS_BASED_LOAD_READ) \ + || !defined(AO_HAVE_int_compare_and_swap)) +# define AO_int_load(addr) AO_int_load_acquire_read(addr) +# define AO_HAVE_int_load +#endif + +#if defined(AO_HAVE_int_compare_and_swap_full) \ + && !defined(AO_HAVE_int_load_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned + AO_int_load_full(const volatile unsigned *addr) + { + unsigned result; + + do { + result = *(const unsigned *)addr; + } while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_full( + (volatile unsigned *)addr, + result, result))); + return result; + } +# define AO_HAVE_int_load_full +#endif + +#if defined(AO_HAVE_int_compare_and_swap_acquire) \ + && !defined(AO_HAVE_int_load_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned + AO_int_load_acquire(const volatile unsigned *addr) + { + unsigned result; + + do { + result = *(const unsigned *)addr; + } while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_acquire( + (volatile unsigned *)addr, + result, result))); + return result; + } +# define AO_HAVE_int_load_acquire +#endif + +#if defined(AO_HAVE_int_compare_and_swap) && !defined(AO_HAVE_int_load) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE unsigned + AO_int_load(const volatile unsigned *addr) + { + unsigned result; + + do { + result = *(const unsigned *)addr; + } while (AO_EXPECT_FALSE(!AO_int_compare_and_swap( + (volatile unsigned *)addr, + result, result))); + return result; + } +# define AO_HAVE_int_load +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_int_load_acquire_read) +# define AO_int_load_dd_acquire_read(addr) \ + AO_int_load_acquire_read(addr) +# define AO_HAVE_int_load_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_int_load) +# define AO_int_load_dd_acquire_read(addr) AO_int_load(addr) +# define AO_HAVE_int_load_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* int_store */ +#if defined(AO_HAVE_int_store_full) && !defined(AO_HAVE_int_store_release) +# define AO_int_store_release(addr, val) AO_int_store_full(addr, val) +# define AO_HAVE_int_store_release +#endif + +#if defined(AO_HAVE_int_store_release) && !defined(AO_HAVE_int_store) +# define AO_int_store(addr, val) AO_int_store_release(addr, val) +# define AO_HAVE_int_store +#endif + +#if defined(AO_HAVE_int_store_full) && !defined(AO_HAVE_int_store_write) +# define AO_int_store_write(addr, val) AO_int_store_full(addr, val) +# define AO_HAVE_int_store_write +#endif + +#if defined(AO_HAVE_int_store_release) \ + && !defined(AO_HAVE_int_store_release_write) +# define AO_int_store_release_write(addr, val) \ + AO_int_store_release(addr, val) +# define AO_HAVE_int_store_release_write +#endif + +#if defined(AO_HAVE_int_store_write) && !defined(AO_HAVE_int_store) +# define AO_int_store(addr, val) AO_int_store_write(addr, val) +# define AO_HAVE_int_store +#endif + +#if defined(AO_HAVE_int_store) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_store_release) +# define AO_int_store_release(addr, val) \ + (AO_nop_full(), AO_int_store(addr, val)) +# define AO_HAVE_int_store_release +#endif + +#if defined(AO_HAVE_int_store) && defined(AO_HAVE_nop_write) \ + && !defined(AO_HAVE_int_store_write) +# define AO_int_store_write(addr, val) \ + (AO_nop_write(), AO_int_store(addr, val)) +# define AO_HAVE_int_store_write +#endif + +#if defined(AO_HAVE_int_compare_and_swap_write) \ + && !defined(AO_HAVE_int_store_write) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_int_store_write(volatile unsigned *addr, unsigned new_val) + { + unsigned old_val; + + do { + old_val = *(unsigned *)addr; + } while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_write(addr, old_val, + new_val))); + } +# define AO_HAVE_int_store_write +#endif + +#if defined(AO_HAVE_int_store_write) \ + && !defined(AO_HAVE_int_store_release_write) +# define AO_int_store_release_write(addr, val) \ + AO_int_store_write(addr, val) +# define AO_HAVE_int_store_release_write +#endif + +#if defined(AO_HAVE_int_store_release) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_int_store_full) +# define AO_int_store_full(addr, val) \ + (AO_int_store_release(addr, val), \ + AO_nop_full()) +# define AO_HAVE_int_store_full +#endif + +#if defined(AO_HAVE_int_compare_and_swap) && !defined(AO_HAVE_int_store) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_int_store(volatile unsigned *addr, unsigned new_val) + { + unsigned old_val; + + do { + old_val = *(unsigned *)addr; + } while (AO_EXPECT_FALSE(!AO_int_compare_and_swap(addr, + old_val, new_val))); + } +# define AO_HAVE_int_store +#endif + +#if defined(AO_HAVE_int_compare_and_swap_release) \ + && !defined(AO_HAVE_int_store_release) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_int_store_release(volatile unsigned *addr, unsigned new_val) + { + unsigned old_val; + + do { + old_val = *(unsigned *)addr; + } while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_release(addr, old_val, + new_val))); + } +# define AO_HAVE_int_store_release +#endif + +#if defined(AO_HAVE_int_compare_and_swap_full) \ + && !defined(AO_HAVE_int_store_full) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_int_store_full(volatile unsigned *addr, unsigned new_val) + { + unsigned old_val; + + do { + old_val = *(unsigned *)addr; + } while (AO_EXPECT_FALSE(!AO_int_compare_and_swap_full(addr, old_val, + new_val))); + } +# define AO_HAVE_int_store_full +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* fetch_compare_and_swap */ +#if defined(AO_HAVE_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_fetch_compare_and_swap_acquire) + AO_INLINE AO_t + AO_fetch_compare_and_swap_acquire(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + AO_t result = AO_fetch_compare_and_swap(addr, old_val, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_fetch_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_fetch_compare_and_swap_release) +# define AO_fetch_compare_and_swap_release(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_fetch_compare_and_swap(addr, old_val, new_val)) +# define AO_HAVE_fetch_compare_and_swap_release +#endif +#if defined(AO_HAVE_fetch_compare_and_swap_full) +# if !defined(AO_HAVE_fetch_compare_and_swap_release) +# define AO_fetch_compare_and_swap_release(addr, old_val, new_val) \ + AO_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_release +# endif +# if !defined(AO_HAVE_fetch_compare_and_swap_acquire) +# define AO_fetch_compare_and_swap_acquire(addr, old_val, new_val) \ + AO_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_fetch_compare_and_swap_write) +# define AO_fetch_compare_and_swap_write(addr, old_val, new_val) \ + AO_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_write +# endif +# if !defined(AO_HAVE_fetch_compare_and_swap_read) +# define AO_fetch_compare_and_swap_read(addr, old_val, new_val) \ + AO_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_read +# endif +#endif /* AO_HAVE_fetch_compare_and_swap_full */ + +#if !defined(AO_HAVE_fetch_compare_and_swap) \ + && defined(AO_HAVE_fetch_compare_and_swap_release) +# define AO_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_fetch_compare_and_swap) \ + && defined(AO_HAVE_fetch_compare_and_swap_acquire) +# define AO_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_fetch_compare_and_swap) \ + && defined(AO_HAVE_fetch_compare_and_swap_write) +# define AO_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_fetch_compare_and_swap) \ + && defined(AO_HAVE_fetch_compare_and_swap_read) +# define AO_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_fetch_compare_and_swap_full) +# define AO_fetch_compare_and_swap_full(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_fetch_compare_and_swap_acquire(addr, old_val, new_val)) +# define AO_HAVE_fetch_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_fetch_compare_and_swap_write) +# define AO_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_fetch_compare_and_swap_release) +# define AO_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_fetch_compare_and_swap_read) +# define AO_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_fetch_compare_and_swap_acquire) +# define AO_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_fetch_compare_and_swap_acquire_read) +# define AO_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_fetch_compare_and_swap_acquire_read(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_fetch_compare_and_swap) +# define AO_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_fetch_compare_and_swap(addr, old_val, new_val) +# define AO_HAVE_fetch_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* compare_and_swap */ +#if defined(AO_HAVE_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_and_swap_acquire) + AO_INLINE int + AO_compare_and_swap_acquire(volatile AO_t *addr, AO_t old, + AO_t new_val) + { + int result = AO_compare_and_swap(addr, old, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_and_swap_release) +# define AO_compare_and_swap_release(addr, old, new_val) \ + (AO_nop_full(), AO_compare_and_swap(addr, old, new_val)) +# define AO_HAVE_compare_and_swap_release +#endif +#if defined(AO_HAVE_compare_and_swap_full) +# if !defined(AO_HAVE_compare_and_swap_release) +# define AO_compare_and_swap_release(addr, old, new_val) \ + AO_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_compare_and_swap_release +# endif +# if !defined(AO_HAVE_compare_and_swap_acquire) +# define AO_compare_and_swap_acquire(addr, old, new_val) \ + AO_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_compare_and_swap_write) +# define AO_compare_and_swap_write(addr, old, new_val) \ + AO_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_compare_and_swap_write +# endif +# if !defined(AO_HAVE_compare_and_swap_read) +# define AO_compare_and_swap_read(addr, old, new_val) \ + AO_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_compare_and_swap_read +# endif +#endif /* AO_HAVE_compare_and_swap_full */ + +#if !defined(AO_HAVE_compare_and_swap) \ + && defined(AO_HAVE_compare_and_swap_release) +# define AO_compare_and_swap(addr, old, new_val) \ + AO_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_compare_and_swap +#endif +#if !defined(AO_HAVE_compare_and_swap) \ + && defined(AO_HAVE_compare_and_swap_acquire) +# define AO_compare_and_swap(addr, old, new_val) \ + AO_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_compare_and_swap +#endif +#if !defined(AO_HAVE_compare_and_swap) \ + && defined(AO_HAVE_compare_and_swap_write) +# define AO_compare_and_swap(addr, old, new_val) \ + AO_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_compare_and_swap +#endif +#if !defined(AO_HAVE_compare_and_swap) \ + && defined(AO_HAVE_compare_and_swap_read) +# define AO_compare_and_swap(addr, old, new_val) \ + AO_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_compare_and_swap +#endif + +#if defined(AO_HAVE_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_and_swap_full) +# define AO_compare_and_swap_full(addr, old, new_val) \ + (AO_nop_full(), \ + AO_compare_and_swap_acquire(addr, old, new_val)) +# define AO_HAVE_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_compare_and_swap_release_write) \ + && defined(AO_HAVE_compare_and_swap_write) +# define AO_compare_and_swap_release_write(addr, old, new_val) \ + AO_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_compare_and_swap_release_write) \ + && defined(AO_HAVE_compare_and_swap_release) +# define AO_compare_and_swap_release_write(addr, old, new_val) \ + AO_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_compare_and_swap_read) +# define AO_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_compare_and_swap_acquire) +# define AO_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_compare_and_swap_acquire_read) +# define AO_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_compare_and_swap_acquire_read(addr, old, new_val) +# define AO_HAVE_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_compare_and_swap) +# define AO_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_compare_and_swap(addr, old, new_val) +# define AO_HAVE_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* load */ +#if defined(AO_HAVE_load_full) && !defined(AO_HAVE_load_acquire) +# define AO_load_acquire(addr) AO_load_full(addr) +# define AO_HAVE_load_acquire +#endif + +#if defined(AO_HAVE_load_acquire) && !defined(AO_HAVE_load) +# define AO_load(addr) AO_load_acquire(addr) +# define AO_HAVE_load +#endif + +#if defined(AO_HAVE_load_full) && !defined(AO_HAVE_load_read) +# define AO_load_read(addr) AO_load_full(addr) +# define AO_HAVE_load_read +#endif + +#if !defined(AO_HAVE_load_acquire_read) \ + && defined(AO_HAVE_load_acquire) +# define AO_load_acquire_read(addr) AO_load_acquire(addr) +# define AO_HAVE_load_acquire_read +#endif + +#if defined(AO_HAVE_load) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_load_acquire) + AO_INLINE AO_t + AO_load_acquire(const volatile AO_t *addr) + { + AO_t result = AO_load(addr); + + /* Acquire barrier would be useless, since the load could be delayed */ + /* beyond it. */ + AO_nop_full(); + return result; + } +# define AO_HAVE_load_acquire +#endif + +#if defined(AO_HAVE_load) && defined(AO_HAVE_nop_read) \ + && !defined(AO_HAVE_load_read) + AO_INLINE AO_t + AO_load_read(const volatile AO_t *addr) + { + AO_t result = AO_load(addr); + + AO_nop_read(); + return result; + } +# define AO_HAVE_load_read +#endif + +#if defined(AO_HAVE_load_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_load_full) +# define AO_load_full(addr) (AO_nop_full(), AO_load_acquire(addr)) +# define AO_HAVE_load_full +#endif + +#if defined(AO_HAVE_compare_and_swap_read) \ + && !defined(AO_HAVE_load_read) +# define AO_CAS_BASED_LOAD_READ + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_t + AO_load_read(const volatile AO_t *addr) + { + AO_t result; + + do { + result = *(const AO_t *)addr; + } while (AO_EXPECT_FALSE(!AO_compare_and_swap_read( + (volatile AO_t *)addr, + result, result))); + return result; + } +# define AO_HAVE_load_read +#endif + +#if !defined(AO_HAVE_load_acquire_read) \ + && defined(AO_HAVE_load_read) +# define AO_load_acquire_read(addr) AO_load_read(addr) +# define AO_HAVE_load_acquire_read +#endif + +#if defined(AO_HAVE_load_acquire_read) && !defined(AO_HAVE_load) \ + && (!defined(AO_CAS_BASED_LOAD_READ) \ + || !defined(AO_HAVE_compare_and_swap)) +# define AO_load(addr) AO_load_acquire_read(addr) +# define AO_HAVE_load +#endif + +#if defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_load_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_t + AO_load_full(const volatile AO_t *addr) + { + AO_t result; + + do { + result = *(const AO_t *)addr; + } while (AO_EXPECT_FALSE(!AO_compare_and_swap_full( + (volatile AO_t *)addr, + result, result))); + return result; + } +# define AO_HAVE_load_full +#endif + +#if defined(AO_HAVE_compare_and_swap_acquire) \ + && !defined(AO_HAVE_load_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_t + AO_load_acquire(const volatile AO_t *addr) + { + AO_t result; + + do { + result = *(const AO_t *)addr; + } while (AO_EXPECT_FALSE(!AO_compare_and_swap_acquire( + (volatile AO_t *)addr, + result, result))); + return result; + } +# define AO_HAVE_load_acquire +#endif + +#if defined(AO_HAVE_compare_and_swap) && !defined(AO_HAVE_load) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_t + AO_load(const volatile AO_t *addr) + { + AO_t result; + + do { + result = *(const AO_t *)addr; + } while (AO_EXPECT_FALSE(!AO_compare_and_swap( + (volatile AO_t *)addr, + result, result))); + return result; + } +# define AO_HAVE_load +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_load_acquire_read) +# define AO_load_dd_acquire_read(addr) \ + AO_load_acquire_read(addr) +# define AO_HAVE_load_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_load) +# define AO_load_dd_acquire_read(addr) AO_load(addr) +# define AO_HAVE_load_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* store */ +#if defined(AO_HAVE_store_full) && !defined(AO_HAVE_store_release) +# define AO_store_release(addr, val) AO_store_full(addr, val) +# define AO_HAVE_store_release +#endif + +#if defined(AO_HAVE_store_release) && !defined(AO_HAVE_store) +# define AO_store(addr, val) AO_store_release(addr, val) +# define AO_HAVE_store +#endif + +#if defined(AO_HAVE_store_full) && !defined(AO_HAVE_store_write) +# define AO_store_write(addr, val) AO_store_full(addr, val) +# define AO_HAVE_store_write +#endif + +#if defined(AO_HAVE_store_release) \ + && !defined(AO_HAVE_store_release_write) +# define AO_store_release_write(addr, val) \ + AO_store_release(addr, val) +# define AO_HAVE_store_release_write +#endif + +#if defined(AO_HAVE_store_write) && !defined(AO_HAVE_store) +# define AO_store(addr, val) AO_store_write(addr, val) +# define AO_HAVE_store +#endif + +#if defined(AO_HAVE_store) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_store_release) +# define AO_store_release(addr, val) \ + (AO_nop_full(), AO_store(addr, val)) +# define AO_HAVE_store_release +#endif + +#if defined(AO_HAVE_store) && defined(AO_HAVE_nop_write) \ + && !defined(AO_HAVE_store_write) +# define AO_store_write(addr, val) \ + (AO_nop_write(), AO_store(addr, val)) +# define AO_HAVE_store_write +#endif + +#if defined(AO_HAVE_compare_and_swap_write) \ + && !defined(AO_HAVE_store_write) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_store_write(volatile AO_t *addr, AO_t new_val) + { + AO_t old_val; + + do { + old_val = *(AO_t *)addr; + } while (AO_EXPECT_FALSE(!AO_compare_and_swap_write(addr, old_val, + new_val))); + } +# define AO_HAVE_store_write +#endif + +#if defined(AO_HAVE_store_write) \ + && !defined(AO_HAVE_store_release_write) +# define AO_store_release_write(addr, val) \ + AO_store_write(addr, val) +# define AO_HAVE_store_release_write +#endif + +#if defined(AO_HAVE_store_release) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_store_full) +# define AO_store_full(addr, val) \ + (AO_store_release(addr, val), \ + AO_nop_full()) +# define AO_HAVE_store_full +#endif + +#if defined(AO_HAVE_compare_and_swap) && !defined(AO_HAVE_store) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_store(volatile AO_t *addr, AO_t new_val) + { + AO_t old_val; + + do { + old_val = *(AO_t *)addr; + } while (AO_EXPECT_FALSE(!AO_compare_and_swap(addr, + old_val, new_val))); + } +# define AO_HAVE_store +#endif + +#if defined(AO_HAVE_compare_and_swap_release) \ + && !defined(AO_HAVE_store_release) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_store_release(volatile AO_t *addr, AO_t new_val) + { + AO_t old_val; + + do { + old_val = *(AO_t *)addr; + } while (AO_EXPECT_FALSE(!AO_compare_and_swap_release(addr, old_val, + new_val))); + } +# define AO_HAVE_store_release +#endif + +#if defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_store_full) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_store_full(volatile AO_t *addr, AO_t new_val) + { + AO_t old_val; + + do { + old_val = *(AO_t *)addr; + } while (AO_EXPECT_FALSE(!AO_compare_and_swap_full(addr, old_val, + new_val))); + } +# define AO_HAVE_store_full +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* double_fetch_compare_and_swap */ +#if defined(AO_HAVE_double_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_fetch_compare_and_swap_acquire) + AO_INLINE AO_double_t + AO_double_fetch_compare_and_swap_acquire(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_double_t result = AO_double_fetch_compare_and_swap(addr, old_val, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_double_fetch_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_double_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_fetch_compare_and_swap_release) +# define AO_double_fetch_compare_and_swap_release(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_double_fetch_compare_and_swap(addr, old_val, new_val)) +# define AO_HAVE_double_fetch_compare_and_swap_release +#endif +#if defined(AO_HAVE_double_fetch_compare_and_swap_full) +# if !defined(AO_HAVE_double_fetch_compare_and_swap_release) +# define AO_double_fetch_compare_and_swap_release(addr, old_val, new_val) \ + AO_double_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_release +# endif +# if !defined(AO_HAVE_double_fetch_compare_and_swap_acquire) +# define AO_double_fetch_compare_and_swap_acquire(addr, old_val, new_val) \ + AO_double_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_double_fetch_compare_and_swap_write) +# define AO_double_fetch_compare_and_swap_write(addr, old_val, new_val) \ + AO_double_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_write +# endif +# if !defined(AO_HAVE_double_fetch_compare_and_swap_read) +# define AO_double_fetch_compare_and_swap_read(addr, old_val, new_val) \ + AO_double_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_read +# endif +#endif /* AO_HAVE_double_fetch_compare_and_swap_full */ + +#if !defined(AO_HAVE_double_fetch_compare_and_swap) \ + && defined(AO_HAVE_double_fetch_compare_and_swap_release) +# define AO_double_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_double_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_double_fetch_compare_and_swap) \ + && defined(AO_HAVE_double_fetch_compare_and_swap_acquire) +# define AO_double_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_double_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_double_fetch_compare_and_swap) \ + && defined(AO_HAVE_double_fetch_compare_and_swap_write) +# define AO_double_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_double_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_double_fetch_compare_and_swap) \ + && defined(AO_HAVE_double_fetch_compare_and_swap_read) +# define AO_double_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_double_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap +#endif + +#if defined(AO_HAVE_double_fetch_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_fetch_compare_and_swap_full) +# define AO_double_fetch_compare_and_swap_full(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_double_fetch_compare_and_swap_acquire(addr, old_val, new_val)) +# define AO_HAVE_double_fetch_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_double_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_double_fetch_compare_and_swap_write) +# define AO_double_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_double_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_double_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_double_fetch_compare_and_swap_release) +# define AO_double_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_double_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_double_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_double_fetch_compare_and_swap_read) +# define AO_double_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_double_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_double_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_double_fetch_compare_and_swap_acquire) +# define AO_double_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_double_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_double_fetch_compare_and_swap_acquire_read) +# define AO_double_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_double_fetch_compare_and_swap_acquire_read(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_double_fetch_compare_and_swap) +# define AO_double_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_double_fetch_compare_and_swap(addr, old_val, new_val) +# define AO_HAVE_double_fetch_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* double_compare_and_swap */ +#if defined(AO_HAVE_double_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_compare_and_swap_acquire) + AO_INLINE int + AO_double_compare_and_swap_acquire(volatile AO_double_t *addr, AO_double_t old, + AO_double_t new_val) + { + int result = AO_double_compare_and_swap(addr, old, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_double_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_double_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_compare_and_swap_release) +# define AO_double_compare_and_swap_release(addr, old, new_val) \ + (AO_nop_full(), AO_double_compare_and_swap(addr, old, new_val)) +# define AO_HAVE_double_compare_and_swap_release +#endif +#if defined(AO_HAVE_double_compare_and_swap_full) +# if !defined(AO_HAVE_double_compare_and_swap_release) +# define AO_double_compare_and_swap_release(addr, old, new_val) \ + AO_double_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_release +# endif +# if !defined(AO_HAVE_double_compare_and_swap_acquire) +# define AO_double_compare_and_swap_acquire(addr, old, new_val) \ + AO_double_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_double_compare_and_swap_write) +# define AO_double_compare_and_swap_write(addr, old, new_val) \ + AO_double_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_write +# endif +# if !defined(AO_HAVE_double_compare_and_swap_read) +# define AO_double_compare_and_swap_read(addr, old, new_val) \ + AO_double_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_read +# endif +#endif /* AO_HAVE_double_compare_and_swap_full */ + +#if !defined(AO_HAVE_double_compare_and_swap) \ + && defined(AO_HAVE_double_compare_and_swap_release) +# define AO_double_compare_and_swap(addr, old, new_val) \ + AO_double_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap +#endif +#if !defined(AO_HAVE_double_compare_and_swap) \ + && defined(AO_HAVE_double_compare_and_swap_acquire) +# define AO_double_compare_and_swap(addr, old, new_val) \ + AO_double_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap +#endif +#if !defined(AO_HAVE_double_compare_and_swap) \ + && defined(AO_HAVE_double_compare_and_swap_write) +# define AO_double_compare_and_swap(addr, old, new_val) \ + AO_double_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap +#endif +#if !defined(AO_HAVE_double_compare_and_swap) \ + && defined(AO_HAVE_double_compare_and_swap_read) +# define AO_double_compare_and_swap(addr, old, new_val) \ + AO_double_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap +#endif + +#if defined(AO_HAVE_double_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_compare_and_swap_full) +# define AO_double_compare_and_swap_full(addr, old, new_val) \ + (AO_nop_full(), \ + AO_double_compare_and_swap_acquire(addr, old, new_val)) +# define AO_HAVE_double_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_double_compare_and_swap_release_write) \ + && defined(AO_HAVE_double_compare_and_swap_write) +# define AO_double_compare_and_swap_release_write(addr, old, new_val) \ + AO_double_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_double_compare_and_swap_release_write) \ + && defined(AO_HAVE_double_compare_and_swap_release) +# define AO_double_compare_and_swap_release_write(addr, old, new_val) \ + AO_double_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_double_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_double_compare_and_swap_read) +# define AO_double_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_double_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_double_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_double_compare_and_swap_acquire) +# define AO_double_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_double_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_double_compare_and_swap_acquire_read) +# define AO_double_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_double_compare_and_swap_acquire_read(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_double_compare_and_swap) +# define AO_double_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_double_compare_and_swap(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* double_load */ +#if defined(AO_HAVE_double_load_full) && !defined(AO_HAVE_double_load_acquire) +# define AO_double_load_acquire(addr) AO_double_load_full(addr) +# define AO_HAVE_double_load_acquire +#endif + +#if defined(AO_HAVE_double_load_acquire) && !defined(AO_HAVE_double_load) +# define AO_double_load(addr) AO_double_load_acquire(addr) +# define AO_HAVE_double_load +#endif + +#if defined(AO_HAVE_double_load_full) && !defined(AO_HAVE_double_load_read) +# define AO_double_load_read(addr) AO_double_load_full(addr) +# define AO_HAVE_double_load_read +#endif + +#if !defined(AO_HAVE_double_load_acquire_read) \ + && defined(AO_HAVE_double_load_acquire) +# define AO_double_load_acquire_read(addr) AO_double_load_acquire(addr) +# define AO_HAVE_double_load_acquire_read +#endif + +#if defined(AO_HAVE_double_load) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_load_acquire) + AO_INLINE AO_double_t + AO_double_load_acquire(const volatile AO_double_t *addr) + { + AO_double_t result = AO_double_load(addr); + + /* Acquire barrier would be useless, since the load could be delayed */ + /* beyond it. */ + AO_nop_full(); + return result; + } +# define AO_HAVE_double_load_acquire +#endif + +#if defined(AO_HAVE_double_load) && defined(AO_HAVE_nop_read) \ + && !defined(AO_HAVE_double_load_read) + AO_INLINE AO_double_t + AO_double_load_read(const volatile AO_double_t *addr) + { + AO_double_t result = AO_double_load(addr); + + AO_nop_read(); + return result; + } +# define AO_HAVE_double_load_read +#endif + +#if defined(AO_HAVE_double_load_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_load_full) +# define AO_double_load_full(addr) (AO_nop_full(), AO_double_load_acquire(addr)) +# define AO_HAVE_double_load_full +#endif + +#if defined(AO_HAVE_double_compare_and_swap_read) \ + && !defined(AO_HAVE_double_load_read) +# define AO_double_CAS_BASED_LOAD_READ + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_double_t + AO_double_load_read(const volatile AO_double_t *addr) + { + AO_double_t result; + + do { + result = *(const AO_double_t *)addr; + } while (AO_EXPECT_FALSE(!AO_double_compare_and_swap_read( + (volatile AO_double_t *)addr, + result, result))); + return result; + } +# define AO_HAVE_double_load_read +#endif + +#if !defined(AO_HAVE_double_load_acquire_read) \ + && defined(AO_HAVE_double_load_read) +# define AO_double_load_acquire_read(addr) AO_double_load_read(addr) +# define AO_HAVE_double_load_acquire_read +#endif + +#if defined(AO_HAVE_double_load_acquire_read) && !defined(AO_HAVE_double_load) \ + && (!defined(AO_double_CAS_BASED_LOAD_READ) \ + || !defined(AO_HAVE_double_compare_and_swap)) +# define AO_double_load(addr) AO_double_load_acquire_read(addr) +# define AO_HAVE_double_load +#endif + +#if defined(AO_HAVE_double_compare_and_swap_full) \ + && !defined(AO_HAVE_double_load_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_double_t + AO_double_load_full(const volatile AO_double_t *addr) + { + AO_double_t result; + + do { + result = *(const AO_double_t *)addr; + } while (AO_EXPECT_FALSE(!AO_double_compare_and_swap_full( + (volatile AO_double_t *)addr, + result, result))); + return result; + } +# define AO_HAVE_double_load_full +#endif + +#if defined(AO_HAVE_double_compare_and_swap_acquire) \ + && !defined(AO_HAVE_double_load_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_double_t + AO_double_load_acquire(const volatile AO_double_t *addr) + { + AO_double_t result; + + do { + result = *(const AO_double_t *)addr; + } while (AO_EXPECT_FALSE(!AO_double_compare_and_swap_acquire( + (volatile AO_double_t *)addr, + result, result))); + return result; + } +# define AO_HAVE_double_load_acquire +#endif + +#if defined(AO_HAVE_double_compare_and_swap) && !defined(AO_HAVE_double_load) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE AO_double_t + AO_double_load(const volatile AO_double_t *addr) + { + AO_double_t result; + + do { + result = *(const AO_double_t *)addr; + } while (AO_EXPECT_FALSE(!AO_double_compare_and_swap( + (volatile AO_double_t *)addr, + result, result))); + return result; + } +# define AO_HAVE_double_load +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_double_load_acquire_read) +# define AO_double_load_dd_acquire_read(addr) \ + AO_double_load_acquire_read(addr) +# define AO_HAVE_double_load_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_double_load) +# define AO_double_load_dd_acquire_read(addr) AO_double_load(addr) +# define AO_HAVE_double_load_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* double_store */ +#if defined(AO_HAVE_double_store_full) && !defined(AO_HAVE_double_store_release) +# define AO_double_store_release(addr, val) AO_double_store_full(addr, val) +# define AO_HAVE_double_store_release +#endif + +#if defined(AO_HAVE_double_store_release) && !defined(AO_HAVE_double_store) +# define AO_double_store(addr, val) AO_double_store_release(addr, val) +# define AO_HAVE_double_store +#endif + +#if defined(AO_HAVE_double_store_full) && !defined(AO_HAVE_double_store_write) +# define AO_double_store_write(addr, val) AO_double_store_full(addr, val) +# define AO_HAVE_double_store_write +#endif + +#if defined(AO_HAVE_double_store_release) \ + && !defined(AO_HAVE_double_store_release_write) +# define AO_double_store_release_write(addr, val) \ + AO_double_store_release(addr, val) +# define AO_HAVE_double_store_release_write +#endif + +#if defined(AO_HAVE_double_store_write) && !defined(AO_HAVE_double_store) +# define AO_double_store(addr, val) AO_double_store_write(addr, val) +# define AO_HAVE_double_store +#endif + +#if defined(AO_HAVE_double_store) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_store_release) +# define AO_double_store_release(addr, val) \ + (AO_nop_full(), AO_double_store(addr, val)) +# define AO_HAVE_double_store_release +#endif + +#if defined(AO_HAVE_double_store) && defined(AO_HAVE_nop_write) \ + && !defined(AO_HAVE_double_store_write) +# define AO_double_store_write(addr, val) \ + (AO_nop_write(), AO_double_store(addr, val)) +# define AO_HAVE_double_store_write +#endif + +#if defined(AO_HAVE_double_compare_and_swap_write) \ + && !defined(AO_HAVE_double_store_write) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_double_store_write(volatile AO_double_t *addr, AO_double_t new_val) + { + AO_double_t old_val; + + do { + old_val = *(AO_double_t *)addr; + } while (AO_EXPECT_FALSE(!AO_double_compare_and_swap_write(addr, old_val, + new_val))); + } +# define AO_HAVE_double_store_write +#endif + +#if defined(AO_HAVE_double_store_write) \ + && !defined(AO_HAVE_double_store_release_write) +# define AO_double_store_release_write(addr, val) \ + AO_double_store_write(addr, val) +# define AO_HAVE_double_store_release_write +#endif + +#if defined(AO_HAVE_double_store_release) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_double_store_full) +# define AO_double_store_full(addr, val) \ + (AO_double_store_release(addr, val), \ + AO_nop_full()) +# define AO_HAVE_double_store_full +#endif + +#if defined(AO_HAVE_double_compare_and_swap) && !defined(AO_HAVE_double_store) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_double_store(volatile AO_double_t *addr, AO_double_t new_val) + { + AO_double_t old_val; + + do { + old_val = *(AO_double_t *)addr; + } while (AO_EXPECT_FALSE(!AO_double_compare_and_swap(addr, + old_val, new_val))); + } +# define AO_HAVE_double_store +#endif + +#if defined(AO_HAVE_double_compare_and_swap_release) \ + && !defined(AO_HAVE_double_store_release) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_double_store_release(volatile AO_double_t *addr, AO_double_t new_val) + { + AO_double_t old_val; + + do { + old_val = *(AO_double_t *)addr; + } while (AO_EXPECT_FALSE(!AO_double_compare_and_swap_release(addr, old_val, + new_val))); + } +# define AO_HAVE_double_store_release +#endif + +#if defined(AO_HAVE_double_compare_and_swap_full) \ + && !defined(AO_HAVE_double_store_full) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_double_store_full(volatile AO_double_t *addr, AO_double_t new_val) + { + AO_double_t old_val; + + do { + old_val = *(AO_double_t *)addr; + } while (AO_EXPECT_FALSE(!AO_double_compare_and_swap_full(addr, old_val, + new_val))); + } +# define AO_HAVE_double_store_full +#endif diff --git a/libatomic_ops/src/atomic_ops/generalize-small.template b/libatomic_ops/src/atomic_ops/generalize-small.template new file mode 100644 index 000000000..d3490f87b --- /dev/null +++ b/libatomic_ops/src/atomic_ops/generalize-small.template @@ -0,0 +1,528 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* XSIZE_fetch_compare_and_swap */ +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire) + AO_INLINE XCTYPE + AO_XSIZE_fetch_compare_and_swap_acquire(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + XCTYPE result = AO_XSIZE_fetch_compare_and_swap(addr, old_val, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_XSIZE_fetch_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_release) +# define AO_XSIZE_fetch_compare_and_swap_release(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_XSIZE_fetch_compare_and_swap(addr, old_val, new_val)) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_release +#endif +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_full) +# if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_release) +# define AO_XSIZE_fetch_compare_and_swap_release(addr, old_val, new_val) \ + AO_XSIZE_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_release +# endif +# if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire) +# define AO_XSIZE_fetch_compare_and_swap_acquire(addr, old_val, new_val) \ + AO_XSIZE_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_write) +# define AO_XSIZE_fetch_compare_and_swap_write(addr, old_val, new_val) \ + AO_XSIZE_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_write +# endif +# if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_read) +# define AO_XSIZE_fetch_compare_and_swap_read(addr, old_val, new_val) \ + AO_XSIZE_fetch_compare_and_swap_full(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_read +# endif +#endif /* AO_HAVE_XSIZE_fetch_compare_and_swap_full */ + +#if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap) \ + && defined(AO_HAVE_XSIZE_fetch_compare_and_swap_release) +# define AO_XSIZE_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_XSIZE_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap) \ + && defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire) +# define AO_XSIZE_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_XSIZE_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap) \ + && defined(AO_HAVE_XSIZE_fetch_compare_and_swap_write) +# define AO_XSIZE_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_XSIZE_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap +#endif +#if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap) \ + && defined(AO_HAVE_XSIZE_fetch_compare_and_swap_read) +# define AO_XSIZE_fetch_compare_and_swap(addr, old_val, new_val) \ + AO_XSIZE_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap +#endif + +#if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_full) +# define AO_XSIZE_fetch_compare_and_swap_full(addr, old_val, new_val) \ + (AO_nop_full(), \ + AO_XSIZE_fetch_compare_and_swap_acquire(addr, old_val, new_val)) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_XSIZE_fetch_compare_and_swap_write) +# define AO_XSIZE_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_XSIZE_fetch_compare_and_swap_write(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_release_write) \ + && defined(AO_HAVE_XSIZE_fetch_compare_and_swap_release) +# define AO_XSIZE_fetch_compare_and_swap_release_write(addr,old_val,new_val) \ + AO_XSIZE_fetch_compare_and_swap_release(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_XSIZE_fetch_compare_and_swap_read) +# define AO_XSIZE_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_XSIZE_fetch_compare_and_swap_read(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire) +# define AO_XSIZE_fetch_compare_and_swap_acquire_read(addr,old_val,new_val) \ + AO_XSIZE_fetch_compare_and_swap_acquire(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_XSIZE_fetch_compare_and_swap_acquire_read) +# define AO_XSIZE_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_XSIZE_fetch_compare_and_swap_acquire_read(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_XSIZE_fetch_compare_and_swap) +# define AO_XSIZE_fetch_compare_and_swap_dd_acquire_read(addr,old_val,new_val) \ + AO_XSIZE_fetch_compare_and_swap(addr, old_val, new_val) +# define AO_HAVE_XSIZE_fetch_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* XSIZE_compare_and_swap */ +#if defined(AO_HAVE_XSIZE_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_acquire) + AO_INLINE int + AO_XSIZE_compare_and_swap_acquire(volatile XCTYPE *addr, XCTYPE old, + XCTYPE new_val) + { + int result = AO_XSIZE_compare_and_swap(addr, old, new_val); + AO_nop_full(); + return result; + } +# define AO_HAVE_XSIZE_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_XSIZE_compare_and_swap) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_release) +# define AO_XSIZE_compare_and_swap_release(addr, old, new_val) \ + (AO_nop_full(), AO_XSIZE_compare_and_swap(addr, old, new_val)) +# define AO_HAVE_XSIZE_compare_and_swap_release +#endif +#if defined(AO_HAVE_XSIZE_compare_and_swap_full) +# if !defined(AO_HAVE_XSIZE_compare_and_swap_release) +# define AO_XSIZE_compare_and_swap_release(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_release +# endif +# if !defined(AO_HAVE_XSIZE_compare_and_swap_acquire) +# define AO_XSIZE_compare_and_swap_acquire(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_acquire +# endif +# if !defined(AO_HAVE_XSIZE_compare_and_swap_write) +# define AO_XSIZE_compare_and_swap_write(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_write +# endif +# if !defined(AO_HAVE_XSIZE_compare_and_swap_read) +# define AO_XSIZE_compare_and_swap_read(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_full(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_read +# endif +#endif /* AO_HAVE_XSIZE_compare_and_swap_full */ + +#if !defined(AO_HAVE_XSIZE_compare_and_swap) \ + && defined(AO_HAVE_XSIZE_compare_and_swap_release) +# define AO_XSIZE_compare_and_swap(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap +#endif +#if !defined(AO_HAVE_XSIZE_compare_and_swap) \ + && defined(AO_HAVE_XSIZE_compare_and_swap_acquire) +# define AO_XSIZE_compare_and_swap(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap +#endif +#if !defined(AO_HAVE_XSIZE_compare_and_swap) \ + && defined(AO_HAVE_XSIZE_compare_and_swap_write) +# define AO_XSIZE_compare_and_swap(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap +#endif +#if !defined(AO_HAVE_XSIZE_compare_and_swap) \ + && defined(AO_HAVE_XSIZE_compare_and_swap_read) +# define AO_XSIZE_compare_and_swap(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_compare_and_swap_full) +# define AO_XSIZE_compare_and_swap_full(addr, old, new_val) \ + (AO_nop_full(), \ + AO_XSIZE_compare_and_swap_acquire(addr, old, new_val)) +# define AO_HAVE_XSIZE_compare_and_swap_full +#endif + +#if !defined(AO_HAVE_XSIZE_compare_and_swap_release_write) \ + && defined(AO_HAVE_XSIZE_compare_and_swap_write) +# define AO_XSIZE_compare_and_swap_release_write(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_write(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_XSIZE_compare_and_swap_release_write) \ + && defined(AO_HAVE_XSIZE_compare_and_swap_release) +# define AO_XSIZE_compare_and_swap_release_write(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_release(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_release_write +#endif +#if !defined(AO_HAVE_XSIZE_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_XSIZE_compare_and_swap_read) +# define AO_XSIZE_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_read(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_acquire_read +#endif +#if !defined(AO_HAVE_XSIZE_compare_and_swap_acquire_read) \ + && defined(AO_HAVE_XSIZE_compare_and_swap_acquire) +# define AO_XSIZE_compare_and_swap_acquire_read(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_acquire(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_XSIZE_compare_and_swap_acquire_read) +# define AO_XSIZE_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_XSIZE_compare_and_swap_acquire_read(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_XSIZE_compare_and_swap) +# define AO_XSIZE_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_XSIZE_compare_and_swap(addr, old, new_val) +# define AO_HAVE_XSIZE_compare_and_swap_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* XSIZE_load */ +#if defined(AO_HAVE_XSIZE_load_full) && !defined(AO_HAVE_XSIZE_load_acquire) +# define AO_XSIZE_load_acquire(addr) AO_XSIZE_load_full(addr) +# define AO_HAVE_XSIZE_load_acquire +#endif + +#if defined(AO_HAVE_XSIZE_load_acquire) && !defined(AO_HAVE_XSIZE_load) +# define AO_XSIZE_load(addr) AO_XSIZE_load_acquire(addr) +# define AO_HAVE_XSIZE_load +#endif + +#if defined(AO_HAVE_XSIZE_load_full) && !defined(AO_HAVE_XSIZE_load_read) +# define AO_XSIZE_load_read(addr) AO_XSIZE_load_full(addr) +# define AO_HAVE_XSIZE_load_read +#endif + +#if !defined(AO_HAVE_XSIZE_load_acquire_read) \ + && defined(AO_HAVE_XSIZE_load_acquire) +# define AO_XSIZE_load_acquire_read(addr) AO_XSIZE_load_acquire(addr) +# define AO_HAVE_XSIZE_load_acquire_read +#endif + +#if defined(AO_HAVE_XSIZE_load) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_load_acquire) + AO_INLINE XCTYPE + AO_XSIZE_load_acquire(const volatile XCTYPE *addr) + { + XCTYPE result = AO_XSIZE_load(addr); + + /* Acquire barrier would be useless, since the load could be delayed */ + /* beyond it. */ + AO_nop_full(); + return result; + } +# define AO_HAVE_XSIZE_load_acquire +#endif + +#if defined(AO_HAVE_XSIZE_load) && defined(AO_HAVE_nop_read) \ + && !defined(AO_HAVE_XSIZE_load_read) + AO_INLINE XCTYPE + AO_XSIZE_load_read(const volatile XCTYPE *addr) + { + XCTYPE result = AO_XSIZE_load(addr); + + AO_nop_read(); + return result; + } +# define AO_HAVE_XSIZE_load_read +#endif + +#if defined(AO_HAVE_XSIZE_load_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_load_full) +# define AO_XSIZE_load_full(addr) (AO_nop_full(), AO_XSIZE_load_acquire(addr)) +# define AO_HAVE_XSIZE_load_full +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_read) \ + && !defined(AO_HAVE_XSIZE_load_read) +# define AO_XSIZE_CAS_BASED_LOAD_READ + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE XCTYPE + AO_XSIZE_load_read(const volatile XCTYPE *addr) + { + XCTYPE result; + + do { + result = *(const XCTYPE *)addr; + } while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_read( + (volatile XCTYPE *)addr, + result, result))); + return result; + } +# define AO_HAVE_XSIZE_load_read +#endif + +#if !defined(AO_HAVE_XSIZE_load_acquire_read) \ + && defined(AO_HAVE_XSIZE_load_read) +# define AO_XSIZE_load_acquire_read(addr) AO_XSIZE_load_read(addr) +# define AO_HAVE_XSIZE_load_acquire_read +#endif + +#if defined(AO_HAVE_XSIZE_load_acquire_read) && !defined(AO_HAVE_XSIZE_load) \ + && (!defined(AO_XSIZE_CAS_BASED_LOAD_READ) \ + || !defined(AO_HAVE_XSIZE_compare_and_swap)) +# define AO_XSIZE_load(addr) AO_XSIZE_load_acquire_read(addr) +# define AO_HAVE_XSIZE_load +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_full) \ + && !defined(AO_HAVE_XSIZE_load_full) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE XCTYPE + AO_XSIZE_load_full(const volatile XCTYPE *addr) + { + XCTYPE result; + + do { + result = *(const XCTYPE *)addr; + } while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_full( + (volatile XCTYPE *)addr, + result, result))); + return result; + } +# define AO_HAVE_XSIZE_load_full +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_acquire) \ + && !defined(AO_HAVE_XSIZE_load_acquire) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE XCTYPE + AO_XSIZE_load_acquire(const volatile XCTYPE *addr) + { + XCTYPE result; + + do { + result = *(const XCTYPE *)addr; + } while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_acquire( + (volatile XCTYPE *)addr, + result, result))); + return result; + } +# define AO_HAVE_XSIZE_load_acquire +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap) && !defined(AO_HAVE_XSIZE_load) + AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE XCTYPE + AO_XSIZE_load(const volatile XCTYPE *addr) + { + XCTYPE result; + + do { + result = *(const XCTYPE *)addr; + } while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap( + (volatile XCTYPE *)addr, + result, result))); + return result; + } +# define AO_HAVE_XSIZE_load +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_XSIZE_load_acquire_read) +# define AO_XSIZE_load_dd_acquire_read(addr) \ + AO_XSIZE_load_acquire_read(addr) +# define AO_HAVE_XSIZE_load_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_XSIZE_load) +# define AO_XSIZE_load_dd_acquire_read(addr) AO_XSIZE_load(addr) +# define AO_HAVE_XSIZE_load_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* XSIZE_store */ +#if defined(AO_HAVE_XSIZE_store_full) && !defined(AO_HAVE_XSIZE_store_release) +# define AO_XSIZE_store_release(addr, val) AO_XSIZE_store_full(addr, val) +# define AO_HAVE_XSIZE_store_release +#endif + +#if defined(AO_HAVE_XSIZE_store_release) && !defined(AO_HAVE_XSIZE_store) +# define AO_XSIZE_store(addr, val) AO_XSIZE_store_release(addr, val) +# define AO_HAVE_XSIZE_store +#endif + +#if defined(AO_HAVE_XSIZE_store_full) && !defined(AO_HAVE_XSIZE_store_write) +# define AO_XSIZE_store_write(addr, val) AO_XSIZE_store_full(addr, val) +# define AO_HAVE_XSIZE_store_write +#endif + +#if defined(AO_HAVE_XSIZE_store_release) \ + && !defined(AO_HAVE_XSIZE_store_release_write) +# define AO_XSIZE_store_release_write(addr, val) \ + AO_XSIZE_store_release(addr, val) +# define AO_HAVE_XSIZE_store_release_write +#endif + +#if defined(AO_HAVE_XSIZE_store_write) && !defined(AO_HAVE_XSIZE_store) +# define AO_XSIZE_store(addr, val) AO_XSIZE_store_write(addr, val) +# define AO_HAVE_XSIZE_store +#endif + +#if defined(AO_HAVE_XSIZE_store) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_store_release) +# define AO_XSIZE_store_release(addr, val) \ + (AO_nop_full(), AO_XSIZE_store(addr, val)) +# define AO_HAVE_XSIZE_store_release +#endif + +#if defined(AO_HAVE_XSIZE_store) && defined(AO_HAVE_nop_write) \ + && !defined(AO_HAVE_XSIZE_store_write) +# define AO_XSIZE_store_write(addr, val) \ + (AO_nop_write(), AO_XSIZE_store(addr, val)) +# define AO_HAVE_XSIZE_store_write +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_write) \ + && !defined(AO_HAVE_XSIZE_store_write) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_XSIZE_store_write(volatile XCTYPE *addr, XCTYPE new_val) + { + XCTYPE old_val; + + do { + old_val = *(XCTYPE *)addr; + } while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_write(addr, old_val, + new_val))); + } +# define AO_HAVE_XSIZE_store_write +#endif + +#if defined(AO_HAVE_XSIZE_store_write) \ + && !defined(AO_HAVE_XSIZE_store_release_write) +# define AO_XSIZE_store_release_write(addr, val) \ + AO_XSIZE_store_write(addr, val) +# define AO_HAVE_XSIZE_store_release_write +#endif + +#if defined(AO_HAVE_XSIZE_store_release) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_XSIZE_store_full) +# define AO_XSIZE_store_full(addr, val) \ + (AO_XSIZE_store_release(addr, val), \ + AO_nop_full()) +# define AO_HAVE_XSIZE_store_full +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap) && !defined(AO_HAVE_XSIZE_store) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_XSIZE_store(volatile XCTYPE *addr, XCTYPE new_val) + { + XCTYPE old_val; + + do { + old_val = *(XCTYPE *)addr; + } while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap(addr, + old_val, new_val))); + } +# define AO_HAVE_XSIZE_store +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_release) \ + && !defined(AO_HAVE_XSIZE_store_release) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_XSIZE_store_release(volatile XCTYPE *addr, XCTYPE new_val) + { + XCTYPE old_val; + + do { + old_val = *(XCTYPE *)addr; + } while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_release(addr, old_val, + new_val))); + } +# define AO_HAVE_XSIZE_store_release +#endif + +#if defined(AO_HAVE_XSIZE_compare_and_swap_full) \ + && !defined(AO_HAVE_XSIZE_store_full) + AO_ATTR_NO_SANITIZE_MEMORY AO_ATTR_NO_SANITIZE_THREAD + AO_INLINE void + AO_XSIZE_store_full(volatile XCTYPE *addr, XCTYPE new_val) + { + XCTYPE old_val; + + do { + old_val = *(XCTYPE *)addr; + } while (AO_EXPECT_FALSE(!AO_XSIZE_compare_and_swap_full(addr, old_val, + new_val))); + } +# define AO_HAVE_XSIZE_store_full +#endif diff --git a/libatomic_ops/src/atomic_ops/generalize.h b/libatomic_ops/src/atomic_ops/generalize.h new file mode 100644 index 000000000..73b06ed10 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/generalize.h @@ -0,0 +1,729 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Generalize atomic operations for atomic_ops.h. + * Should not be included directly. + * + * We make no attempt to define useless operations, such as + * AO_nop_acquire + * AO_nop_release + * + * We have also so far neglected to define some others, which + * do not appear likely to be useful, e.g. stores with acquire + * or read barriers. + * + * This file is sometimes included twice by atomic_ops.h. + * All definitions include explicit checks that we are not replacing + * an earlier definition. In general, more desirable expansions + * appear earlier so that we are more likely to use them. + * + * We only make safe generalizations, except that by default we define + * the ...dd_acquire_read operations to be equivalent to those without + * a barrier. On platforms for which this is unsafe, the platform-specific + * file must define AO_NO_DD_ORDERING. + */ + +#ifndef AO_ATOMIC_OPS_H +# error This file should not be included directly. +#endif + +/* Generate test_and_set_full, if necessary and possible. */ +#if !defined(AO_HAVE_test_and_set) && !defined(AO_HAVE_test_and_set_release) \ + && !defined(AO_HAVE_test_and_set_acquire) \ + && !defined(AO_HAVE_test_and_set_read) \ + && !defined(AO_HAVE_test_and_set_full) + + /* Emulate AO_compare_and_swap() via AO_fetch_compare_and_swap(). */ +# if defined(AO_HAVE_fetch_compare_and_swap) \ + && !defined(AO_HAVE_compare_and_swap) + AO_INLINE int + AO_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) + { + return AO_fetch_compare_and_swap(addr, old_val, new_val) == old_val; + } +# define AO_HAVE_compare_and_swap +# endif + +# if defined(AO_HAVE_fetch_compare_and_swap_full) \ + && !defined(AO_HAVE_compare_and_swap_full) + AO_INLINE int + AO_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, AO_t new_val) + { + return AO_fetch_compare_and_swap_full(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_compare_and_swap_full +# endif + +# if defined(AO_HAVE_fetch_compare_and_swap_acquire) \ + && !defined(AO_HAVE_compare_and_swap_acquire) + AO_INLINE int + AO_compare_and_swap_acquire(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return AO_fetch_compare_and_swap_acquire(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_compare_and_swap_acquire +# endif + +# if defined(AO_HAVE_fetch_compare_and_swap_release) \ + && !defined(AO_HAVE_compare_and_swap_release) + AO_INLINE int + AO_compare_and_swap_release(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return AO_fetch_compare_and_swap_release(addr, old_val, new_val) + == old_val; + } +# define AO_HAVE_compare_and_swap_release +# endif + +# if defined(AO_CHAR_TS_T) +# define AO_TS_COMPARE_AND_SWAP_FULL(a,o,n) \ + AO_char_compare_and_swap_full(a,o,n) +# define AO_TS_COMPARE_AND_SWAP_ACQUIRE(a,o,n) \ + AO_char_compare_and_swap_acquire(a,o,n) +# define AO_TS_COMPARE_AND_SWAP_RELEASE(a,o,n) \ + AO_char_compare_and_swap_release(a,o,n) +# define AO_TS_COMPARE_AND_SWAP(a,o,n) AO_char_compare_and_swap(a,o,n) +# endif + +# if defined(AO_AO_TS_T) +# define AO_TS_COMPARE_AND_SWAP_FULL(a,o,n) AO_compare_and_swap_full(a,o,n) +# define AO_TS_COMPARE_AND_SWAP_ACQUIRE(a,o,n) \ + AO_compare_and_swap_acquire(a,o,n) +# define AO_TS_COMPARE_AND_SWAP_RELEASE(a,o,n) \ + AO_compare_and_swap_release(a,o,n) +# define AO_TS_COMPARE_AND_SWAP(a,o,n) AO_compare_and_swap(a,o,n) +# endif + +# if (defined(AO_AO_TS_T) && defined(AO_HAVE_compare_and_swap_full)) \ + || (defined(AO_CHAR_TS_T) && defined(AO_HAVE_char_compare_and_swap_full)) + AO_INLINE AO_TS_VAL_t + AO_test_and_set_full(volatile AO_TS_t *addr) + { + if (AO_TS_COMPARE_AND_SWAP_FULL(addr, AO_TS_CLEAR, AO_TS_SET)) + return AO_TS_CLEAR; + else + return AO_TS_SET; + } +# define AO_HAVE_test_and_set_full +# endif /* AO_HAVE_compare_and_swap_full */ + +# if (defined(AO_AO_TS_T) && defined(AO_HAVE_compare_and_swap_acquire)) \ + || (defined(AO_CHAR_TS_T) \ + && defined(AO_HAVE_char_compare_and_swap_acquire)) + AO_INLINE AO_TS_VAL_t + AO_test_and_set_acquire(volatile AO_TS_t *addr) + { + if (AO_TS_COMPARE_AND_SWAP_ACQUIRE(addr, AO_TS_CLEAR, AO_TS_SET)) + return AO_TS_CLEAR; + else + return AO_TS_SET; + } +# define AO_HAVE_test_and_set_acquire +# endif /* AO_HAVE_compare_and_swap_acquire */ + +# if (defined(AO_AO_TS_T) && defined(AO_HAVE_compare_and_swap_release)) \ + || (defined(AO_CHAR_TS_T) \ + && defined(AO_HAVE_char_compare_and_swap_release)) + AO_INLINE AO_TS_VAL_t + AO_test_and_set_release(volatile AO_TS_t *addr) + { + if (AO_TS_COMPARE_AND_SWAP_RELEASE(addr, AO_TS_CLEAR, AO_TS_SET)) + return AO_TS_CLEAR; + else + return AO_TS_SET; + } +# define AO_HAVE_test_and_set_release +# endif /* AO_HAVE_compare_and_swap_release */ + +# if (defined(AO_AO_TS_T) && defined(AO_HAVE_compare_and_swap)) \ + || (defined(AO_CHAR_TS_T) && defined(AO_HAVE_char_compare_and_swap)) + AO_INLINE AO_TS_VAL_t + AO_test_and_set(volatile AO_TS_t *addr) + { + if (AO_TS_COMPARE_AND_SWAP(addr, AO_TS_CLEAR, AO_TS_SET)) + return AO_TS_CLEAR; + else + return AO_TS_SET; + } +# define AO_HAVE_test_and_set +# endif /* AO_HAVE_compare_and_swap */ +#endif /* No prior test and set */ + +/* Nop */ +#if !defined(AO_HAVE_nop) + AO_INLINE void AO_nop(void) {} +# define AO_HAVE_nop +#endif + +#if defined(AO_HAVE_test_and_set_full) && !defined(AO_HAVE_nop_full) + AO_INLINE void + AO_nop_full(void) + { + AO_TS_t dummy = AO_TS_INITIALIZER; + AO_test_and_set_full(&dummy); + } +# define AO_HAVE_nop_full +#endif + +#if defined(AO_HAVE_nop_acquire) && !defined(CPPCHECK) +# error AO_nop_acquire is useless: do not define. +#endif + +#if defined(AO_HAVE_nop_release) && !defined(CPPCHECK) +# error AO_nop_release is useless: do not define. +#endif + +#if defined(AO_HAVE_nop_full) && !defined(AO_HAVE_nop_read) +# define AO_nop_read() AO_nop_full() +# define AO_HAVE_nop_read +#endif + +#if defined(AO_HAVE_nop_full) && !defined(AO_HAVE_nop_write) +# define AO_nop_write() AO_nop_full() +# define AO_HAVE_nop_write +#endif + +/* Test_and_set */ +#if defined(AO_HAVE_test_and_set) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_test_and_set_release) +# define AO_test_and_set_release(addr) (AO_nop_full(), AO_test_and_set(addr)) +# define AO_HAVE_test_and_set_release +#endif + +#if defined(AO_HAVE_test_and_set) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_test_and_set_acquire) + AO_INLINE AO_TS_VAL_t + AO_test_and_set_acquire(volatile AO_TS_t *addr) + { + AO_TS_VAL_t result = AO_test_and_set(addr); + AO_nop_full(); + return result; + } +# define AO_HAVE_test_and_set_acquire +#endif + +#if defined(AO_HAVE_test_and_set_full) +# if !defined(AO_HAVE_test_and_set_release) +# define AO_test_and_set_release(addr) AO_test_and_set_full(addr) +# define AO_HAVE_test_and_set_release +# endif +# if !defined(AO_HAVE_test_and_set_acquire) +# define AO_test_and_set_acquire(addr) AO_test_and_set_full(addr) +# define AO_HAVE_test_and_set_acquire +# endif +# if !defined(AO_HAVE_test_and_set_write) +# define AO_test_and_set_write(addr) AO_test_and_set_full(addr) +# define AO_HAVE_test_and_set_write +# endif +# if !defined(AO_HAVE_test_and_set_read) +# define AO_test_and_set_read(addr) AO_test_and_set_full(addr) +# define AO_HAVE_test_and_set_read +# endif +#endif /* AO_HAVE_test_and_set_full */ + +#if !defined(AO_HAVE_test_and_set) && defined(AO_HAVE_test_and_set_release) +# define AO_test_and_set(addr) AO_test_and_set_release(addr) +# define AO_HAVE_test_and_set +#endif +#if !defined(AO_HAVE_test_and_set) && defined(AO_HAVE_test_and_set_acquire) +# define AO_test_and_set(addr) AO_test_and_set_acquire(addr) +# define AO_HAVE_test_and_set +#endif +#if !defined(AO_HAVE_test_and_set) && defined(AO_HAVE_test_and_set_write) +# define AO_test_and_set(addr) AO_test_and_set_write(addr) +# define AO_HAVE_test_and_set +#endif +#if !defined(AO_HAVE_test_and_set) && defined(AO_HAVE_test_and_set_read) +# define AO_test_and_set(addr) AO_test_and_set_read(addr) +# define AO_HAVE_test_and_set +#endif + +#if defined(AO_HAVE_test_and_set_acquire) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_test_and_set_full) +# define AO_test_and_set_full(addr) \ + (AO_nop_full(), AO_test_and_set_acquire(addr)) +# define AO_HAVE_test_and_set_full +#endif + +#if !defined(AO_HAVE_test_and_set_release_write) \ + && defined(AO_HAVE_test_and_set_write) +# define AO_test_and_set_release_write(addr) AO_test_and_set_write(addr) +# define AO_HAVE_test_and_set_release_write +#endif +#if !defined(AO_HAVE_test_and_set_release_write) \ + && defined(AO_HAVE_test_and_set_release) +# define AO_test_and_set_release_write(addr) AO_test_and_set_release(addr) +# define AO_HAVE_test_and_set_release_write +#endif +#if !defined(AO_HAVE_test_and_set_acquire_read) \ + && defined(AO_HAVE_test_and_set_read) +# define AO_test_and_set_acquire_read(addr) AO_test_and_set_read(addr) +# define AO_HAVE_test_and_set_acquire_read +#endif +#if !defined(AO_HAVE_test_and_set_acquire_read) \ + && defined(AO_HAVE_test_and_set_acquire) +# define AO_test_and_set_acquire_read(addr) AO_test_and_set_acquire(addr) +# define AO_HAVE_test_and_set_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_test_and_set_acquire_read) +# define AO_test_and_set_dd_acquire_read(addr) \ + AO_test_and_set_acquire_read(addr) +# define AO_HAVE_test_and_set_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_test_and_set) +# define AO_test_and_set_dd_acquire_read(addr) AO_test_and_set(addr) +# define AO_HAVE_test_and_set_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +#include "generalize-small.h" + +#include "generalize-arithm.h" + +/* Compare_double_and_swap_double based on double_compare_and_swap. */ +#ifdef AO_HAVE_DOUBLE_PTR_STORAGE +# if defined(AO_HAVE_double_compare_and_swap) \ + && !defined(AO_HAVE_compare_double_and_swap_double) + AO_INLINE int + AO_compare_double_and_swap_double(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + AO_double_t old_w; + AO_double_t new_w; + old_w.AO_val1 = old_val1; + old_w.AO_val2 = old_val2; + new_w.AO_val1 = new_val1; + new_w.AO_val2 = new_val2; + return AO_double_compare_and_swap(addr, old_w, new_w); + } +# define AO_HAVE_compare_double_and_swap_double +# endif +# if defined(AO_HAVE_double_compare_and_swap_acquire) \ + && !defined(AO_HAVE_compare_double_and_swap_double_acquire) + AO_INLINE int + AO_compare_double_and_swap_double_acquire(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + AO_double_t old_w; + AO_double_t new_w; + old_w.AO_val1 = old_val1; + old_w.AO_val2 = old_val2; + new_w.AO_val1 = new_val1; + new_w.AO_val2 = new_val2; + return AO_double_compare_and_swap_acquire(addr, old_w, new_w); + } +# define AO_HAVE_compare_double_and_swap_double_acquire +# endif +# if defined(AO_HAVE_double_compare_and_swap_release) \ + && !defined(AO_HAVE_compare_double_and_swap_double_release) + AO_INLINE int + AO_compare_double_and_swap_double_release(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + AO_double_t old_w; + AO_double_t new_w; + old_w.AO_val1 = old_val1; + old_w.AO_val2 = old_val2; + new_w.AO_val1 = new_val1; + new_w.AO_val2 = new_val2; + return AO_double_compare_and_swap_release(addr, old_w, new_w); + } +# define AO_HAVE_compare_double_and_swap_double_release +# endif +# if defined(AO_HAVE_double_compare_and_swap_full) \ + && !defined(AO_HAVE_compare_double_and_swap_double_full) + AO_INLINE int + AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + AO_double_t old_w; + AO_double_t new_w; + old_w.AO_val1 = old_val1; + old_w.AO_val2 = old_val2; + new_w.AO_val1 = new_val1; + new_w.AO_val2 = new_val2; + return AO_double_compare_and_swap_full(addr, old_w, new_w); + } +# define AO_HAVE_compare_double_and_swap_double_full +# endif +#endif /* AO_HAVE_DOUBLE_PTR_STORAGE */ + +/* Compare_double_and_swap_double */ +#if defined(AO_HAVE_compare_double_and_swap_double) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_double_and_swap_double_acquire) + AO_INLINE int + AO_compare_double_and_swap_double_acquire(volatile AO_double_t *addr, + AO_t o1, AO_t o2, + AO_t n1, AO_t n2) + { + int result = AO_compare_double_and_swap_double(addr, o1, o2, n1, n2); + AO_nop_full(); + return result; + } +# define AO_HAVE_compare_double_and_swap_double_acquire +#endif +#if defined(AO_HAVE_compare_double_and_swap_double) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_double_and_swap_double_release) +# define AO_compare_double_and_swap_double_release(addr,o1,o2,n1,n2) \ + (AO_nop_full(), AO_compare_double_and_swap_double(addr,o1,o2,n1,n2)) +# define AO_HAVE_compare_double_and_swap_double_release +#endif +#if defined(AO_HAVE_compare_double_and_swap_double_full) +# if !defined(AO_HAVE_compare_double_and_swap_double_release) +# define AO_compare_double_and_swap_double_release(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_full(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_release +# endif +# if !defined(AO_HAVE_compare_double_and_swap_double_acquire) +# define AO_compare_double_and_swap_double_acquire(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_full(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_acquire +# endif +# if !defined(AO_HAVE_compare_double_and_swap_double_write) +# define AO_compare_double_and_swap_double_write(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_full(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_write +# endif +# if !defined(AO_HAVE_compare_double_and_swap_double_read) +# define AO_compare_double_and_swap_double_read(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_full(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_read +# endif +#endif /* AO_HAVE_compare_double_and_swap_double_full */ + +#if !defined(AO_HAVE_compare_double_and_swap_double) \ + && defined(AO_HAVE_compare_double_and_swap_double_release) +# define AO_compare_double_and_swap_double(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_release(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double +#endif +#if !defined(AO_HAVE_compare_double_and_swap_double) \ + && defined(AO_HAVE_compare_double_and_swap_double_acquire) +# define AO_compare_double_and_swap_double(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_acquire(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double +#endif +#if !defined(AO_HAVE_compare_double_and_swap_double) \ + && defined(AO_HAVE_compare_double_and_swap_double_write) +# define AO_compare_double_and_swap_double(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_write(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double +#endif +#if !defined(AO_HAVE_compare_double_and_swap_double) \ + && defined(AO_HAVE_compare_double_and_swap_double_read) +# define AO_compare_double_and_swap_double(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_read(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double +#endif + +#if defined(AO_HAVE_compare_double_and_swap_double_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_double_and_swap_double_full) +# define AO_compare_double_and_swap_double_full(addr,o1,o2,n1,n2) \ + (AO_nop_full(), \ + AO_compare_double_and_swap_double_acquire(addr,o1,o2,n1,n2)) +# define AO_HAVE_compare_double_and_swap_double_full +#endif + +#if !defined(AO_HAVE_compare_double_and_swap_double_release_write) \ + && defined(AO_HAVE_compare_double_and_swap_double_write) +# define AO_compare_double_and_swap_double_release_write(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_write(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_release_write +#endif +#if !defined(AO_HAVE_compare_double_and_swap_double_release_write) \ + && defined(AO_HAVE_compare_double_and_swap_double_release) +# define AO_compare_double_and_swap_double_release_write(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_release(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_release_write +#endif +#if !defined(AO_HAVE_compare_double_and_swap_double_acquire_read) \ + && defined(AO_HAVE_compare_double_and_swap_double_read) +# define AO_compare_double_and_swap_double_acquire_read(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_read(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_acquire_read +#endif +#if !defined(AO_HAVE_compare_double_and_swap_double_acquire_read) \ + && defined(AO_HAVE_compare_double_and_swap_double_acquire) +# define AO_compare_double_and_swap_double_acquire_read(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_acquire(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_compare_double_and_swap_double_acquire_read) +# define AO_compare_double_and_swap_double_dd_acquire_read(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double_acquire_read(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_compare_double_and_swap_double) +# define AO_compare_double_and_swap_double_dd_acquire_read(addr,o1,o2,n1,n2) \ + AO_compare_double_and_swap_double(addr,o1,o2,n1,n2) +# define AO_HAVE_compare_double_and_swap_double_dd_acquire_read +# endif +#endif /* !AO_NO_DD_ORDERING */ + +/* Compare_and_swap_double */ +#if defined(AO_HAVE_compare_and_swap_double) && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_and_swap_double_acquire) + AO_INLINE int + AO_compare_and_swap_double_acquire(volatile AO_double_t *addr, + AO_t o1, + AO_t n1, AO_t n2) + { + int result = AO_compare_and_swap_double(addr, o1, n1, n2); + AO_nop_full(); + return result; + } +# define AO_HAVE_compare_and_swap_double_acquire +#endif +#if defined(AO_HAVE_compare_and_swap_double) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_and_swap_double_release) +# define AO_compare_and_swap_double_release(addr,o1,n1,n2) \ + (AO_nop_full(), AO_compare_and_swap_double(addr,o1,n1,n2)) +# define AO_HAVE_compare_and_swap_double_release +#endif +#if defined(AO_HAVE_compare_and_swap_double_full) +# if !defined(AO_HAVE_compare_and_swap_double_release) +# define AO_compare_and_swap_double_release(addr,o1,n1,n2) \ + AO_compare_and_swap_double_full(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_release +# endif +# if !defined(AO_HAVE_compare_and_swap_double_acquire) +# define AO_compare_and_swap_double_acquire(addr,o1,n1,n2) \ + AO_compare_and_swap_double_full(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_acquire +# endif +# if !defined(AO_HAVE_compare_and_swap_double_write) +# define AO_compare_and_swap_double_write(addr,o1,n1,n2) \ + AO_compare_and_swap_double_full(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_write +# endif +# if !defined(AO_HAVE_compare_and_swap_double_read) +# define AO_compare_and_swap_double_read(addr,o1,n1,n2) \ + AO_compare_and_swap_double_full(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_read +# endif +#endif /* AO_HAVE_compare_and_swap_double_full */ + +#if !defined(AO_HAVE_compare_and_swap_double) \ + && defined(AO_HAVE_compare_and_swap_double_release) +# define AO_compare_and_swap_double(addr,o1,n1,n2) \ + AO_compare_and_swap_double_release(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double +#endif +#if !defined(AO_HAVE_compare_and_swap_double) \ + && defined(AO_HAVE_compare_and_swap_double_acquire) +# define AO_compare_and_swap_double(addr,o1,n1,n2) \ + AO_compare_and_swap_double_acquire(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double +#endif +#if !defined(AO_HAVE_compare_and_swap_double) \ + && defined(AO_HAVE_compare_and_swap_double_write) +# define AO_compare_and_swap_double(addr,o1,n1,n2) \ + AO_compare_and_swap_double_write(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double +#endif +#if !defined(AO_HAVE_compare_and_swap_double) \ + && defined(AO_HAVE_compare_and_swap_double_read) +# define AO_compare_and_swap_double(addr,o1,n1,n2) \ + AO_compare_and_swap_double_read(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double +#endif + +#if defined(AO_HAVE_compare_and_swap_double_acquire) \ + && defined(AO_HAVE_nop_full) \ + && !defined(AO_HAVE_compare_and_swap_double_full) +# define AO_compare_and_swap_double_full(addr,o1,n1,n2) \ + (AO_nop_full(), AO_compare_and_swap_double_acquire(addr,o1,n1,n2)) +# define AO_HAVE_compare_and_swap_double_full +#endif + +#if !defined(AO_HAVE_compare_and_swap_double_release_write) \ + && defined(AO_HAVE_compare_and_swap_double_write) +# define AO_compare_and_swap_double_release_write(addr,o1,n1,n2) \ + AO_compare_and_swap_double_write(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_release_write +#endif +#if !defined(AO_HAVE_compare_and_swap_double_release_write) \ + && defined(AO_HAVE_compare_and_swap_double_release) +# define AO_compare_and_swap_double_release_write(addr,o1,n1,n2) \ + AO_compare_and_swap_double_release(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_release_write +#endif +#if !defined(AO_HAVE_compare_and_swap_double_acquire_read) \ + && defined(AO_HAVE_compare_and_swap_double_read) +# define AO_compare_and_swap_double_acquire_read(addr,o1,n1,n2) \ + AO_compare_and_swap_double_read(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_acquire_read +#endif +#if !defined(AO_HAVE_compare_and_swap_double_acquire_read) \ + && defined(AO_HAVE_compare_and_swap_double_acquire) +# define AO_compare_and_swap_double_acquire_read(addr,o1,n1,n2) \ + AO_compare_and_swap_double_acquire(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_acquire_read +#endif + +#ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_compare_and_swap_double_acquire_read) +# define AO_compare_and_swap_double_dd_acquire_read(addr,o1,n1,n2) \ + AO_compare_and_swap_double_acquire_read(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_dd_acquire_read +# endif +#else +# if defined(AO_HAVE_compare_and_swap_double) +# define AO_compare_and_swap_double_dd_acquire_read(addr,o1,n1,n2) \ + AO_compare_and_swap_double(addr,o1,n1,n2) +# define AO_HAVE_compare_and_swap_double_dd_acquire_read +# endif +#endif + +/* Convenience functions for AO_double compare-and-swap which types and */ +/* reads easier in code. */ +#if defined(AO_HAVE_compare_double_and_swap_double) \ + && !defined(AO_HAVE_double_compare_and_swap) + AO_INLINE int + AO_double_compare_and_swap(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return AO_compare_double_and_swap_double(addr, + old_val.AO_val1, old_val.AO_val2, + new_val.AO_val1, new_val.AO_val2); + } +# define AO_HAVE_double_compare_and_swap +#endif +#if defined(AO_HAVE_compare_double_and_swap_double_release) \ + && !defined(AO_HAVE_double_compare_and_swap_release) + AO_INLINE int + AO_double_compare_and_swap_release(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return AO_compare_double_and_swap_double_release(addr, + old_val.AO_val1, old_val.AO_val2, + new_val.AO_val1, new_val.AO_val2); + } +# define AO_HAVE_double_compare_and_swap_release +#endif +#if defined(AO_HAVE_compare_double_and_swap_double_acquire) \ + && !defined(AO_HAVE_double_compare_and_swap_acquire) + AO_INLINE int + AO_double_compare_and_swap_acquire(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return AO_compare_double_and_swap_double_acquire(addr, + old_val.AO_val1, old_val.AO_val2, + new_val.AO_val1, new_val.AO_val2); + } +# define AO_HAVE_double_compare_and_swap_acquire +#endif +#if defined(AO_HAVE_compare_double_and_swap_double_read) \ + && !defined(AO_HAVE_double_compare_and_swap_read) + AO_INLINE int + AO_double_compare_and_swap_read(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return AO_compare_double_and_swap_double_read(addr, + old_val.AO_val1, old_val.AO_val2, + new_val.AO_val1, new_val.AO_val2); + } +# define AO_HAVE_double_compare_and_swap_read +#endif +#if defined(AO_HAVE_compare_double_and_swap_double_write) \ + && !defined(AO_HAVE_double_compare_and_swap_write) + AO_INLINE int + AO_double_compare_and_swap_write(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return AO_compare_double_and_swap_double_write(addr, + old_val.AO_val1, old_val.AO_val2, + new_val.AO_val1, new_val.AO_val2); + } +# define AO_HAVE_double_compare_and_swap_write +#endif +#if defined(AO_HAVE_compare_double_and_swap_double_release_write) \ + && !defined(AO_HAVE_double_compare_and_swap_release_write) + AO_INLINE int + AO_double_compare_and_swap_release_write(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return AO_compare_double_and_swap_double_release_write(addr, + old_val.AO_val1, old_val.AO_val2, + new_val.AO_val1, new_val.AO_val2); + } +# define AO_HAVE_double_compare_and_swap_release_write +#endif +#if defined(AO_HAVE_compare_double_and_swap_double_acquire_read) \ + && !defined(AO_HAVE_double_compare_and_swap_acquire_read) + AO_INLINE int + AO_double_compare_and_swap_acquire_read(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return AO_compare_double_and_swap_double_acquire_read(addr, + old_val.AO_val1, old_val.AO_val2, + new_val.AO_val1, new_val.AO_val2); + } +# define AO_HAVE_double_compare_and_swap_acquire_read +#endif +#if defined(AO_HAVE_compare_double_and_swap_double_full) \ + && !defined(AO_HAVE_double_compare_and_swap_full) + AO_INLINE int + AO_double_compare_and_swap_full(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return AO_compare_double_and_swap_double_full(addr, + old_val.AO_val1, old_val.AO_val2, + new_val.AO_val1, new_val.AO_val2); + } +# define AO_HAVE_double_compare_and_swap_full +#endif + +#ifndef AO_HAVE_double_compare_and_swap_dd_acquire_read + /* Duplicated from generalize-small because double CAS might be */ + /* defined after the include. */ +# ifdef AO_NO_DD_ORDERING +# if defined(AO_HAVE_double_compare_and_swap_acquire_read) +# define AO_double_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_double_compare_and_swap_acquire_read(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_dd_acquire_read +# endif +# elif defined(AO_HAVE_double_compare_and_swap) +# define AO_double_compare_and_swap_dd_acquire_read(addr, old, new_val) \ + AO_double_compare_and_swap(addr, old, new_val) +# define AO_HAVE_double_compare_and_swap_dd_acquire_read +# endif /* !AO_NO_DD_ORDERING */ +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/README b/libatomic_ops/src/atomic_ops/sysdeps/README new file mode 100644 index 000000000..605699fe4 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/README @@ -0,0 +1,7 @@ +There are two kinds of entities in this directory: + +- Subdirectories corresponding to specific compilers (or compiler/OS combinations). + Each of these includes one or more architecture-specific headers. + +- More generic header files corresponding to a particular ordering and/or + atomicity property that might be shared by multiple hardware platforms. diff --git a/libatomic_ops/src/atomic_ops/sysdeps/all_acquire_release_volatile.h b/libatomic_ops/src/atomic_ops/sysdeps/all_acquire_release_volatile.h new file mode 100644 index 000000000..f0240e4e6 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/all_acquire_release_volatile.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Describes architectures on which volatile AO_t, unsigned char, */ +/* unsigned short, and unsigned int loads and stores have */ +/* acquire/release semantics for all normally legal alignments. */ + +#include "loadstore/acquire_release_volatile.h" +#include "loadstore/char_acquire_release_volatile.h" +#include "loadstore/short_acquire_release_volatile.h" +#include "loadstore/int_acquire_release_volatile.h" diff --git a/libatomic_ops/src/atomic_ops/sysdeps/all_aligned_atomic_load_store.h b/libatomic_ops/src/atomic_ops/sysdeps/all_aligned_atomic_load_store.h new file mode 100644 index 000000000..7bcaa99bd --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/all_aligned_atomic_load_store.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Describes architectures on which AO_t, unsigned char, unsigned */ +/* short, and unsigned int loads and stores are atomic but only if data */ +/* is suitably aligned. */ + +#if defined(__m68k__) && !defined(AO_ALIGNOF_SUPPORTED) + /* Even though AO_t is redefined in m68k.h, some clients use AO */ + /* pointer size primitives to access variables not declared as AO_t. */ + /* Such variables may have 2-byte alignment, while their sizeof is 4. */ +#else +# define AO_ACCESS_CHECK_ALIGNED +#endif + +/* Check for char type is a misnomer. */ +#define AO_ACCESS_short_CHECK_ALIGNED +#define AO_ACCESS_int_CHECK_ALIGNED +#include "all_atomic_load_store.h" diff --git a/libatomic_ops/src/atomic_ops/sysdeps/all_atomic_load_store.h b/libatomic_ops/src/atomic_ops/sysdeps/all_atomic_load_store.h new file mode 100644 index 000000000..b9e414f78 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/all_atomic_load_store.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Describes architectures on which AO_t, unsigned char, unsigned */ +/* short, and unsigned int loads and stores are atomic for all normally */ +/* legal alignments. */ + +#include "all_atomic_only_load.h" + +#include "loadstore/atomic_store.h" +#include "loadstore/char_atomic_store.h" +#include "loadstore/short_atomic_store.h" +#include "loadstore/int_atomic_store.h" diff --git a/libatomic_ops/src/atomic_ops/sysdeps/all_atomic_only_load.h b/libatomic_ops/src/atomic_ops/sysdeps/all_atomic_only_load.h new file mode 100644 index 000000000..695f6b802 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/all_atomic_only_load.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Describes architectures on which AO_t, unsigned char, unsigned */ +/* short, and unsigned int loads are atomic for all normally legal */ +/* alignments. */ + +#include "loadstore/atomic_load.h" +#include "loadstore/char_atomic_load.h" +#include "loadstore/short_atomic_load.h" +#include "loadstore/int_atomic_load.h" diff --git a/libatomic_ops/src/atomic_ops/sysdeps/ao_t_is_int.h b/libatomic_ops/src/atomic_ops/sysdeps/ao_t_is_int.h new file mode 100644 index 000000000..295998ef2 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/ao_t_is_int.h @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Inclusion of this file signifies that AO_t is in fact int. */ +/* Hence any AO_... operation can also serve as AO_int_... operation. */ + +#if defined(AO_HAVE_load) && !defined(AO_HAVE_int_load) +# define AO_int_load(addr) \ + (unsigned)AO_load((const volatile AO_t *)(addr)) +# define AO_HAVE_int_load +#endif + +#if defined(AO_HAVE_store) && !defined(AO_HAVE_int_store) +# define AO_int_store(addr, val) \ + AO_store((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_store +#endif + +#if defined(AO_HAVE_fetch_and_add) \ + && !defined(AO_HAVE_int_fetch_and_add) +# define AO_int_fetch_and_add(addr, incr) \ + (unsigned)AO_fetch_and_add((volatile AO_t *)(addr), \ + (AO_t)(incr)) +# define AO_HAVE_int_fetch_and_add +#endif + +#if defined(AO_HAVE_fetch_and_add1) \ + && !defined(AO_HAVE_int_fetch_and_add1) +# define AO_int_fetch_and_add1(addr) \ + (unsigned)AO_fetch_and_add1((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_add1 +#endif + +#if defined(AO_HAVE_fetch_and_sub1) \ + && !defined(AO_HAVE_int_fetch_and_sub1) +# define AO_int_fetch_and_sub1(addr) \ + (unsigned)AO_fetch_and_sub1((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_sub1 +#endif + +#if defined(AO_HAVE_and) && !defined(AO_HAVE_int_and) +# define AO_int_and(addr, val) \ + AO_and((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_and +#endif + +#if defined(AO_HAVE_or) && !defined(AO_HAVE_int_or) +# define AO_int_or(addr, val) \ + AO_or((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_or +#endif + +#if defined(AO_HAVE_xor) && !defined(AO_HAVE_int_xor) +# define AO_int_xor(addr, val) \ + AO_xor((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_xor +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap) +# define AO_int_fetch_compare_and_swap(addr, old, new_val) \ + (unsigned)AO_fetch_compare_and_swap((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_fetch_compare_and_swap +#endif + +#if defined(AO_HAVE_compare_and_swap) \ + && !defined(AO_HAVE_int_compare_and_swap) +# define AO_int_compare_and_swap(addr, old, new_val) \ + AO_compare_and_swap((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_compare_and_swap +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Inclusion of this file signifies that AO_t is in fact int. */ +/* Hence any AO_... operation can also serve as AO_int_... operation. */ + +#if defined(AO_HAVE_load_full) && !defined(AO_HAVE_int_load_full) +# define AO_int_load_full(addr) \ + (unsigned)AO_load_full((const volatile AO_t *)(addr)) +# define AO_HAVE_int_load_full +#endif + +#if defined(AO_HAVE_store_full) && !defined(AO_HAVE_int_store_full) +# define AO_int_store_full(addr, val) \ + AO_store_full((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_store_full +#endif + +#if defined(AO_HAVE_fetch_and_add_full) \ + && !defined(AO_HAVE_int_fetch_and_add_full) +# define AO_int_fetch_and_add_full(addr, incr) \ + (unsigned)AO_fetch_and_add_full((volatile AO_t *)(addr), \ + (AO_t)(incr)) +# define AO_HAVE_int_fetch_and_add_full +#endif + +#if defined(AO_HAVE_fetch_and_add1_full) \ + && !defined(AO_HAVE_int_fetch_and_add1_full) +# define AO_int_fetch_and_add1_full(addr) \ + (unsigned)AO_fetch_and_add1_full((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_add1_full +#endif + +#if defined(AO_HAVE_fetch_and_sub1_full) \ + && !defined(AO_HAVE_int_fetch_and_sub1_full) +# define AO_int_fetch_and_sub1_full(addr) \ + (unsigned)AO_fetch_and_sub1_full((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_sub1_full +#endif + +#if defined(AO_HAVE_and_full) && !defined(AO_HAVE_int_and_full) +# define AO_int_and_full(addr, val) \ + AO_and_full((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_and_full +#endif + +#if defined(AO_HAVE_or_full) && !defined(AO_HAVE_int_or_full) +# define AO_int_or_full(addr, val) \ + AO_or_full((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_or_full +#endif + +#if defined(AO_HAVE_xor_full) && !defined(AO_HAVE_int_xor_full) +# define AO_int_xor_full(addr, val) \ + AO_xor_full((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_xor_full +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_full) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_full) +# define AO_int_fetch_compare_and_swap_full(addr, old, new_val) \ + (unsigned)AO_fetch_compare_and_swap_full((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_fetch_compare_and_swap_full +#endif + +#if defined(AO_HAVE_compare_and_swap_full) \ + && !defined(AO_HAVE_int_compare_and_swap_full) +# define AO_int_compare_and_swap_full(addr, old, new_val) \ + AO_compare_and_swap_full((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_compare_and_swap_full +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Inclusion of this file signifies that AO_t is in fact int. */ +/* Hence any AO_... operation can also serve as AO_int_... operation. */ + +#if defined(AO_HAVE_load_acquire) && !defined(AO_HAVE_int_load_acquire) +# define AO_int_load_acquire(addr) \ + (unsigned)AO_load_acquire((const volatile AO_t *)(addr)) +# define AO_HAVE_int_load_acquire +#endif + +#if defined(AO_HAVE_store_acquire) && !defined(AO_HAVE_int_store_acquire) +# define AO_int_store_acquire(addr, val) \ + AO_store_acquire((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_store_acquire +#endif + +#if defined(AO_HAVE_fetch_and_add_acquire) \ + && !defined(AO_HAVE_int_fetch_and_add_acquire) +# define AO_int_fetch_and_add_acquire(addr, incr) \ + (unsigned)AO_fetch_and_add_acquire((volatile AO_t *)(addr), \ + (AO_t)(incr)) +# define AO_HAVE_int_fetch_and_add_acquire +#endif + +#if defined(AO_HAVE_fetch_and_add1_acquire) \ + && !defined(AO_HAVE_int_fetch_and_add1_acquire) +# define AO_int_fetch_and_add1_acquire(addr) \ + (unsigned)AO_fetch_and_add1_acquire((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_add1_acquire +#endif + +#if defined(AO_HAVE_fetch_and_sub1_acquire) \ + && !defined(AO_HAVE_int_fetch_and_sub1_acquire) +# define AO_int_fetch_and_sub1_acquire(addr) \ + (unsigned)AO_fetch_and_sub1_acquire((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_sub1_acquire +#endif + +#if defined(AO_HAVE_and_acquire) && !defined(AO_HAVE_int_and_acquire) +# define AO_int_and_acquire(addr, val) \ + AO_and_acquire((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_and_acquire +#endif + +#if defined(AO_HAVE_or_acquire) && !defined(AO_HAVE_int_or_acquire) +# define AO_int_or_acquire(addr, val) \ + AO_or_acquire((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_or_acquire +#endif + +#if defined(AO_HAVE_xor_acquire) && !defined(AO_HAVE_int_xor_acquire) +# define AO_int_xor_acquire(addr, val) \ + AO_xor_acquire((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_xor_acquire +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_acquire) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_acquire) +# define AO_int_fetch_compare_and_swap_acquire(addr, old, new_val) \ + (unsigned)AO_fetch_compare_and_swap_acquire((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_fetch_compare_and_swap_acquire +#endif + +#if defined(AO_HAVE_compare_and_swap_acquire) \ + && !defined(AO_HAVE_int_compare_and_swap_acquire) +# define AO_int_compare_and_swap_acquire(addr, old, new_val) \ + AO_compare_and_swap_acquire((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_compare_and_swap_acquire +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Inclusion of this file signifies that AO_t is in fact int. */ +/* Hence any AO_... operation can also serve as AO_int_... operation. */ + +#if defined(AO_HAVE_load_release) && !defined(AO_HAVE_int_load_release) +# define AO_int_load_release(addr) \ + (unsigned)AO_load_release((const volatile AO_t *)(addr)) +# define AO_HAVE_int_load_release +#endif + +#if defined(AO_HAVE_store_release) && !defined(AO_HAVE_int_store_release) +# define AO_int_store_release(addr, val) \ + AO_store_release((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_store_release +#endif + +#if defined(AO_HAVE_fetch_and_add_release) \ + && !defined(AO_HAVE_int_fetch_and_add_release) +# define AO_int_fetch_and_add_release(addr, incr) \ + (unsigned)AO_fetch_and_add_release((volatile AO_t *)(addr), \ + (AO_t)(incr)) +# define AO_HAVE_int_fetch_and_add_release +#endif + +#if defined(AO_HAVE_fetch_and_add1_release) \ + && !defined(AO_HAVE_int_fetch_and_add1_release) +# define AO_int_fetch_and_add1_release(addr) \ + (unsigned)AO_fetch_and_add1_release((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_add1_release +#endif + +#if defined(AO_HAVE_fetch_and_sub1_release) \ + && !defined(AO_HAVE_int_fetch_and_sub1_release) +# define AO_int_fetch_and_sub1_release(addr) \ + (unsigned)AO_fetch_and_sub1_release((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_sub1_release +#endif + +#if defined(AO_HAVE_and_release) && !defined(AO_HAVE_int_and_release) +# define AO_int_and_release(addr, val) \ + AO_and_release((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_and_release +#endif + +#if defined(AO_HAVE_or_release) && !defined(AO_HAVE_int_or_release) +# define AO_int_or_release(addr, val) \ + AO_or_release((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_or_release +#endif + +#if defined(AO_HAVE_xor_release) && !defined(AO_HAVE_int_xor_release) +# define AO_int_xor_release(addr, val) \ + AO_xor_release((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_xor_release +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_release) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_release) +# define AO_int_fetch_compare_and_swap_release(addr, old, new_val) \ + (unsigned)AO_fetch_compare_and_swap_release((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_fetch_compare_and_swap_release +#endif + +#if defined(AO_HAVE_compare_and_swap_release) \ + && !defined(AO_HAVE_int_compare_and_swap_release) +# define AO_int_compare_and_swap_release(addr, old, new_val) \ + AO_compare_and_swap_release((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_compare_and_swap_release +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Inclusion of this file signifies that AO_t is in fact int. */ +/* Hence any AO_... operation can also serve as AO_int_... operation. */ + +#if defined(AO_HAVE_load_write) && !defined(AO_HAVE_int_load_write) +# define AO_int_load_write(addr) \ + (unsigned)AO_load_write((const volatile AO_t *)(addr)) +# define AO_HAVE_int_load_write +#endif + +#if defined(AO_HAVE_store_write) && !defined(AO_HAVE_int_store_write) +# define AO_int_store_write(addr, val) \ + AO_store_write((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_store_write +#endif + +#if defined(AO_HAVE_fetch_and_add_write) \ + && !defined(AO_HAVE_int_fetch_and_add_write) +# define AO_int_fetch_and_add_write(addr, incr) \ + (unsigned)AO_fetch_and_add_write((volatile AO_t *)(addr), \ + (AO_t)(incr)) +# define AO_HAVE_int_fetch_and_add_write +#endif + +#if defined(AO_HAVE_fetch_and_add1_write) \ + && !defined(AO_HAVE_int_fetch_and_add1_write) +# define AO_int_fetch_and_add1_write(addr) \ + (unsigned)AO_fetch_and_add1_write((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_add1_write +#endif + +#if defined(AO_HAVE_fetch_and_sub1_write) \ + && !defined(AO_HAVE_int_fetch_and_sub1_write) +# define AO_int_fetch_and_sub1_write(addr) \ + (unsigned)AO_fetch_and_sub1_write((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_sub1_write +#endif + +#if defined(AO_HAVE_and_write) && !defined(AO_HAVE_int_and_write) +# define AO_int_and_write(addr, val) \ + AO_and_write((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_and_write +#endif + +#if defined(AO_HAVE_or_write) && !defined(AO_HAVE_int_or_write) +# define AO_int_or_write(addr, val) \ + AO_or_write((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_or_write +#endif + +#if defined(AO_HAVE_xor_write) && !defined(AO_HAVE_int_xor_write) +# define AO_int_xor_write(addr, val) \ + AO_xor_write((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_xor_write +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_write) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_write) +# define AO_int_fetch_compare_and_swap_write(addr, old, new_val) \ + (unsigned)AO_fetch_compare_and_swap_write((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_fetch_compare_and_swap_write +#endif + +#if defined(AO_HAVE_compare_and_swap_write) \ + && !defined(AO_HAVE_int_compare_and_swap_write) +# define AO_int_compare_and_swap_write(addr, old, new_val) \ + AO_compare_and_swap_write((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_compare_and_swap_write +#endif +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Inclusion of this file signifies that AO_t is in fact int. */ +/* Hence any AO_... operation can also serve as AO_int_... operation. */ + +#if defined(AO_HAVE_load_read) && !defined(AO_HAVE_int_load_read) +# define AO_int_load_read(addr) \ + (unsigned)AO_load_read((const volatile AO_t *)(addr)) +# define AO_HAVE_int_load_read +#endif + +#if defined(AO_HAVE_store_read) && !defined(AO_HAVE_int_store_read) +# define AO_int_store_read(addr, val) \ + AO_store_read((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_store_read +#endif + +#if defined(AO_HAVE_fetch_and_add_read) \ + && !defined(AO_HAVE_int_fetch_and_add_read) +# define AO_int_fetch_and_add_read(addr, incr) \ + (unsigned)AO_fetch_and_add_read((volatile AO_t *)(addr), \ + (AO_t)(incr)) +# define AO_HAVE_int_fetch_and_add_read +#endif + +#if defined(AO_HAVE_fetch_and_add1_read) \ + && !defined(AO_HAVE_int_fetch_and_add1_read) +# define AO_int_fetch_and_add1_read(addr) \ + (unsigned)AO_fetch_and_add1_read((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_add1_read +#endif + +#if defined(AO_HAVE_fetch_and_sub1_read) \ + && !defined(AO_HAVE_int_fetch_and_sub1_read) +# define AO_int_fetch_and_sub1_read(addr) \ + (unsigned)AO_fetch_and_sub1_read((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_sub1_read +#endif + +#if defined(AO_HAVE_and_read) && !defined(AO_HAVE_int_and_read) +# define AO_int_and_read(addr, val) \ + AO_and_read((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_and_read +#endif + +#if defined(AO_HAVE_or_read) && !defined(AO_HAVE_int_or_read) +# define AO_int_or_read(addr, val) \ + AO_or_read((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_or_read +#endif + +#if defined(AO_HAVE_xor_read) && !defined(AO_HAVE_int_xor_read) +# define AO_int_xor_read(addr, val) \ + AO_xor_read((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_xor_read +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_read) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_read) +# define AO_int_fetch_compare_and_swap_read(addr, old, new_val) \ + (unsigned)AO_fetch_compare_and_swap_read((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_fetch_compare_and_swap_read +#endif + +#if defined(AO_HAVE_compare_and_swap_read) \ + && !defined(AO_HAVE_int_compare_and_swap_read) +# define AO_int_compare_and_swap_read(addr, old, new_val) \ + AO_compare_and_swap_read((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_compare_and_swap_read +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/ao_t_is_int.template b/libatomic_ops/src/atomic_ops/sysdeps/ao_t_is_int.template new file mode 100644 index 000000000..620faeabc --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/ao_t_is_int.template @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Inclusion of this file signifies that AO_t is in fact int. */ +/* Hence any AO_... operation can also serve as AO_int_... operation. */ + +#if defined(AO_HAVE_load_XBAR) && !defined(AO_HAVE_int_load_XBAR) +# define AO_int_load_XBAR(addr) \ + (unsigned)AO_load_XBAR((const volatile AO_t *)(addr)) +# define AO_HAVE_int_load_XBAR +#endif + +#if defined(AO_HAVE_store_XBAR) && !defined(AO_HAVE_int_store_XBAR) +# define AO_int_store_XBAR(addr, val) \ + AO_store_XBAR((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_store_XBAR +#endif + +#if defined(AO_HAVE_fetch_and_add_XBAR) \ + && !defined(AO_HAVE_int_fetch_and_add_XBAR) +# define AO_int_fetch_and_add_XBAR(addr, incr) \ + (unsigned)AO_fetch_and_add_XBAR((volatile AO_t *)(addr), \ + (AO_t)(incr)) +# define AO_HAVE_int_fetch_and_add_XBAR +#endif + +#if defined(AO_HAVE_fetch_and_add1_XBAR) \ + && !defined(AO_HAVE_int_fetch_and_add1_XBAR) +# define AO_int_fetch_and_add1_XBAR(addr) \ + (unsigned)AO_fetch_and_add1_XBAR((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_add1_XBAR +#endif + +#if defined(AO_HAVE_fetch_and_sub1_XBAR) \ + && !defined(AO_HAVE_int_fetch_and_sub1_XBAR) +# define AO_int_fetch_and_sub1_XBAR(addr) \ + (unsigned)AO_fetch_and_sub1_XBAR((volatile AO_t *)(addr)) +# define AO_HAVE_int_fetch_and_sub1_XBAR +#endif + +#if defined(AO_HAVE_and_XBAR) && !defined(AO_HAVE_int_and_XBAR) +# define AO_int_and_XBAR(addr, val) \ + AO_and_XBAR((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_and_XBAR +#endif + +#if defined(AO_HAVE_or_XBAR) && !defined(AO_HAVE_int_or_XBAR) +# define AO_int_or_XBAR(addr, val) \ + AO_or_XBAR((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_or_XBAR +#endif + +#if defined(AO_HAVE_xor_XBAR) && !defined(AO_HAVE_int_xor_XBAR) +# define AO_int_xor_XBAR(addr, val) \ + AO_xor_XBAR((volatile AO_t *)(addr), (AO_t)(val)) +# define AO_HAVE_int_xor_XBAR +#endif + +#if defined(AO_HAVE_fetch_compare_and_swap_XBAR) \ + && !defined(AO_HAVE_int_fetch_compare_and_swap_XBAR) +# define AO_int_fetch_compare_and_swap_XBAR(addr, old, new_val) \ + (unsigned)AO_fetch_compare_and_swap_XBAR((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_fetch_compare_and_swap_XBAR +#endif + +#if defined(AO_HAVE_compare_and_swap_XBAR) \ + && !defined(AO_HAVE_int_compare_and_swap_XBAR) +# define AO_int_compare_and_swap_XBAR(addr, old, new_val) \ + AO_compare_and_swap_XBAR((volatile AO_t *)(addr), \ + (AO_t)(old), (AO_t)(new_val)) +# define AO_HAVE_int_compare_and_swap_XBAR +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/armcc/arm_v6.h b/libatomic_ops/src/atomic_ops/sysdeps/armcc/arm_v6.h new file mode 100644 index 000000000..3c3beb463 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/armcc/arm_v6.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2007 by NEC LE-IT: All rights reserved. + * A transcription of ARMv6 atomic operations for the ARM Realview Toolchain. + * This code works with armcc from RVDS 3.1 + * This is based on work in gcc/arm.h by + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * + * + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * 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. + * + */ + +#include "../test_and_set_t_is_ao_t.h" /* Probably suboptimal */ + +#if __TARGET_ARCH_ARM < 6 +# if !defined(CPPCHECK) +# error Do not use with ARM instruction sets lower than v6 +# endif +#else + +#define AO_ACCESS_CHECK_ALIGNED +#define AO_ACCESS_short_CHECK_ALIGNED +#define AO_ACCESS_int_CHECK_ALIGNED +#include "../all_atomic_only_load.h" + +#include "../standard_ao_double_t.h" + +/* NEC LE-IT: ARMv6 is the first architecture providing support for simple LL/SC + * A data memory barrier must be raised via CP15 command (see documentation). + * + * ARMv7 is compatible to ARMv6 but has a simpler command for issuing a + * memory barrier (DMB). Raising it via CP15 should still work as told me by the + * support engineers. If it turns out to be much quicker than we should implement + * custom code for ARMv7 using the asm { dmb } command. + * + * If only a single processor is used, we can define AO_UNIPROCESSOR + * and do not need to access CP15 for ensuring a DMB at all. +*/ + +AO_INLINE void +AO_nop_full(void) +{ +# ifndef AO_UNIPROCESSOR + unsigned int dest=0; + /* Issue a data memory barrier (keeps ordering of memory transactions */ + /* before and after this operation). */ + __asm { + mcr p15,0,dest,c7,c10,5 + }; +# else + AO_compiler_barrier(); +# endif +} +#define AO_HAVE_nop_full + +/* NEC LE-IT: atomic "store" - according to ARM documentation this is + * the only safe way to set variables also used in LL/SC environment. + * A direct write won't be recognized by the LL/SC construct in other CPUs. + * + * HB: Based on subsequent discussion, I think it would be OK to use an + * ordinary store here if we knew that interrupt handlers always cleared + * the reservation. They should, but there is some doubt that this is + * currently always the case for e.g. Linux. +*/ +AO_INLINE void AO_store(volatile AO_t *addr, AO_t value) +{ + unsigned long tmp; + +retry: +__asm { + ldrex tmp, [addr] + strex tmp, value, [addr] + teq tmp, #0 + bne retry + }; +} +#define AO_HAVE_store + +/* NEC LE-IT: replace the SWAP as recommended by ARM: + + "Applies to: ARM11 Cores + Though the SWP instruction will still work with ARM V6 cores, it is recommended + to use the new V6 synchronization instructions. The SWP instruction produces + locked read and write accesses which are atomic, i.e. another operation cannot + be done between these locked accesses which ties up external bus (AHB,AXI) + bandwidth and can increase worst case interrupt latencies. LDREX,STREX are + more flexible, other instructions can be done between the LDREX and STREX accesses. + " +*/ +#ifndef AO_PREFER_GENERALIZED +AO_INLINE AO_TS_VAL_t +AO_test_and_set(volatile AO_TS_t *addr) { + AO_TS_VAL_t oldval; + unsigned long tmp; + unsigned long one = 1; +retry: +__asm { + ldrex oldval, [addr] + strex tmp, one, [addr] + teq tmp, #0 + bne retry + } + + return oldval; +} +#define AO_HAVE_test_and_set + +AO_INLINE AO_t +AO_fetch_and_add(volatile AO_t *p, AO_t incr) +{ + unsigned long tmp,tmp2; + AO_t result; + +retry: +__asm { + ldrex result, [p] + add tmp, incr, result + strex tmp2, tmp, [p] + teq tmp2, #0 + bne retry + } + + return result; +} +#define AO_HAVE_fetch_and_add + +AO_INLINE AO_t +AO_fetch_and_add1(volatile AO_t *p) +{ + unsigned long tmp,tmp2; + AO_t result; + +retry: +__asm { + ldrex result, [p] + add tmp, result, #1 + strex tmp2, tmp, [p] + teq tmp2, #0 + bne retry + } + + return result; +} +#define AO_HAVE_fetch_and_add1 + +AO_INLINE AO_t +AO_fetch_and_sub1(volatile AO_t *p) +{ + unsigned long tmp,tmp2; + AO_t result; + +retry: +__asm { + ldrex result, [p] + sub tmp, result, #1 + strex tmp2, tmp, [p] + teq tmp2, #0 + bne retry + } + + return result; +} +#define AO_HAVE_fetch_and_sub1 +#endif /* !AO_PREFER_GENERALIZED */ + +#ifndef AO_GENERALIZE_ASM_BOOL_CAS + /* Returns nonzero if the comparison succeeded. */ + AO_INLINE int + AO_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) + { + AO_t result, tmp; + + retry: + __asm__ { + mov result, #2 + ldrex tmp, [addr] + teq tmp, old_val +# ifdef __thumb__ + it eq +# endif + strexeq result, new_val, [addr] + teq result, #1 + beq retry + } + return !(result&2); + } +# define AO_HAVE_compare_and_swap +#endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) +{ + AO_t fetched_val, tmp; + +retry: +__asm__ { + mov tmp, #2 + ldrex fetched_val, [addr] + teq fetched_val, old_val +# ifdef __thumb__ + it eq +# endif + strexeq tmp, new_val, [addr] + teq tmp, #1 + beq retry + } + return fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap + +/* helper functions for the Realview compiler: LDREXD is not usable + * with inline assembler, so use the "embedded" assembler as + * suggested by ARM Dev. support (June 2008). */ +__asm inline double_ptr_storage AO_load_ex(const volatile AO_double_t *addr) { + LDREXD r0,r1,[r0] +} + +__asm inline int AO_store_ex(AO_t val1, AO_t val2, volatile AO_double_t *addr) { + STREXD r3,r0,r1,[r2] + MOV r0,r3 +} + +AO_INLINE AO_double_t +AO_double_load(const volatile AO_double_t *addr) +{ + AO_double_t result; + + result.AO_whole = AO_load_ex(addr); + return result; +} +#define AO_HAVE_double_load + +AO_INLINE int +AO_compare_double_and_swap_double(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) +{ + double_ptr_storage old_val = + ((double_ptr_storage)old_val2 << 32) | old_val1; + double_ptr_storage tmp; + int result; + + while(1) { + tmp = AO_load_ex(addr); + if(tmp != old_val) return 0; + result = AO_store_ex(new_val1, new_val2, addr); + if(!result) return 1; + } +} +#define AO_HAVE_compare_double_and_swap_double + +#endif /* __TARGET_ARCH_ARM >= 6 */ + +#define AO_T_IS_INT diff --git a/libatomic_ops/src/atomic_ops/sysdeps/emul_cas.h b/libatomic_ops/src/atomic_ops/sysdeps/emul_cas.h new file mode 100644 index 000000000..c322a5b2e --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/emul_cas.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Ensure, if at all possible, that AO_compare_and_swap_full() is + * available. The emulation should be brute-force signal-safe, even + * though it actually blocks. + * Including this file will generate an error if AO_compare_and_swap_full() + * cannot be made available. + * This will be included from platform-specific atomic_ops files + * if appropriate, and if AO_REQUIRE_CAS is defined. It should not be + * included directly, especially since it affects the implementation + * of other atomic update primitives. + * The implementation assumes that only AO_store_XXX and AO_test_and_set_XXX + * variants are defined, and that AO_test_and_set_XXX is not used to + * operate on compare_and_swap locations. + */ + +#ifndef AO_ATOMIC_OPS_H +# error This file should not be included directly. +#endif + +#ifndef AO_HAVE_double_t +# include "standard_ao_double_t.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +AO_API AO_t AO_fetch_compare_and_swap_emulation(volatile AO_t *addr, + AO_t old_val, AO_t new_val); + +AO_API int +AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2); + +AO_API void AO_store_full_emulation(volatile AO_t *addr, AO_t val); + +#ifndef AO_HAVE_fetch_compare_and_swap_full +# define AO_fetch_compare_and_swap_full(addr, old, newval) \ + AO_fetch_compare_and_swap_emulation(addr, old, newval) +# define AO_HAVE_fetch_compare_and_swap_full +#endif + +#ifndef AO_HAVE_compare_double_and_swap_double_full +# define AO_compare_double_and_swap_double_full(addr, old1, old2, \ + newval1, newval2) \ + AO_compare_double_and_swap_double_emulation(addr, old1, old2, \ + newval1, newval2) +# define AO_HAVE_compare_double_and_swap_double_full +#endif + +#undef AO_store +#undef AO_HAVE_store +#undef AO_store_write +#undef AO_HAVE_store_write +#undef AO_store_release +#undef AO_HAVE_store_release +#undef AO_store_full +#undef AO_HAVE_store_full +#define AO_store_full(addr, val) AO_store_full_emulation(addr, val) +#define AO_HAVE_store_full + +#ifdef __cplusplus + } /* extern "C" */ +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/aarch64.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/aarch64.h new file mode 100644 index 000000000..7ba6bb7bc --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/aarch64.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2013-2017 Ivan Maidanski + * + * 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. + * + */ + +/* As of clang-5.0 (and gcc-5.4), __atomic_thread_fence is always */ +/* translated to DMB (which is inefficient for AO_nop_write). */ +/* TODO: Update it for newer Clang and GCC releases. */ +#if !defined(AO_PREFER_BUILTIN_ATOMICS) && !defined(AO_THREAD_SANITIZER) \ + && !defined(AO_UNIPROCESSOR) + AO_INLINE void + AO_nop_write(void) + { + __asm__ __volatile__("dmb ishst" : : : "memory"); + } +# define AO_HAVE_nop_write +#endif + +/* There were some bugs in the older clang releases (related to */ +/* optimization of functions dealing with __int128 values, supposedly), */ +/* so even asm-based implementation did not work correctly. */ +#if !defined(__clang__) || AO_CLANG_PREREQ(3, 9) + +# include "../standard_ao_double_t.h" + +/* As of gcc-5.4, all built-in load/store and CAS atomics for double */ +/* word require -latomic, are not lock-free and cause test_stack */ +/* failure, so the asm-based implementation is used for now. */ +/* TODO: Update it for newer GCC releases. */ +#if (!defined(__ILP32__) && !defined(__clang__)) \ + || defined(AO_AARCH64_ASM_LOAD_STORE_CAS) + +# ifndef AO_PREFER_GENERALIZED + AO_INLINE AO_double_t + AO_double_load(const volatile AO_double_t *addr) + { + AO_double_t result; + int status; + + /* Note that STXP cannot be discarded because LD[A]XP is not */ + /* single-copy atomic (unlike LDREXD for 32-bit ARM). */ + do { + __asm__ __volatile__("//AO_double_load\n" +# ifdef __ILP32__ + " ldxp %w0, %w1, %3\n" + " stxp %w2, %w0, %w1, %3" +# else + " ldxp %0, %1, %3\n" + " stxp %w2, %0, %1, %3" +# endif + : "=&r" (result.AO_val1), "=&r" (result.AO_val2), "=&r" (status) + : "Q" (*addr)); + } while (AO_EXPECT_FALSE(status)); + return result; + } +# define AO_HAVE_double_load + + AO_INLINE AO_double_t + AO_double_load_acquire(const volatile AO_double_t *addr) + { + AO_double_t result; + int status; + + do { + __asm__ __volatile__("//AO_double_load_acquire\n" +# ifdef __ILP32__ + " ldaxp %w0, %w1, %3\n" + " stxp %w2, %w0, %w1, %3" +# else + " ldaxp %0, %1, %3\n" + " stxp %w2, %0, %1, %3" +# endif + : "=&r" (result.AO_val1), "=&r" (result.AO_val2), "=&r" (status) + : "Q" (*addr)); + } while (AO_EXPECT_FALSE(status)); + return result; + } +# define AO_HAVE_double_load_acquire + + AO_INLINE void + AO_double_store(volatile AO_double_t *addr, AO_double_t value) + { + AO_double_t old_val; + int status; + + do { + __asm__ __volatile__("//AO_double_store\n" +# ifdef __ILP32__ + " ldxp %w0, %w1, %3\n" + " stxp %w2, %w4, %w5, %3" +# else + " ldxp %0, %1, %3\n" + " stxp %w2, %4, %5, %3" +# endif + : "=&r" (old_val.AO_val1), "=&r" (old_val.AO_val2), "=&r" (status), + "=Q" (*addr) + : "r" (value.AO_val1), "r" (value.AO_val2)); + /* Compared to the arm.h implementation, the 'cc' (flags) are */ + /* not clobbered because A64 has no concept of conditional */ + /* execution. */ + } while (AO_EXPECT_FALSE(status)); + } +# define AO_HAVE_double_store + + AO_INLINE void + AO_double_store_release(volatile AO_double_t *addr, AO_double_t value) + { + AO_double_t old_val; + int status; + + do { + __asm__ __volatile__("//AO_double_store_release\n" +# ifdef __ILP32__ + " ldxp %w0, %w1, %3\n" + " stlxp %w2, %w4, %w5, %3" +# else + " ldxp %0, %1, %3\n" + " stlxp %w2, %4, %5, %3" +# endif + : "=&r" (old_val.AO_val1), "=&r" (old_val.AO_val2), "=&r" (status), + "=Q" (*addr) + : "r" (value.AO_val1), "r" (value.AO_val2)); + } while (AO_EXPECT_FALSE(status)); + } +# define AO_HAVE_double_store_release +# endif /* !AO_PREFER_GENERALIZED */ + + AO_INLINE int + AO_double_compare_and_swap(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_double_t tmp; + int result = 1; + + do { + __asm__ __volatile__("//AO_double_compare_and_swap\n" +# ifdef __ILP32__ + " ldxp %w0, %w1, %2\n" +# else + " ldxp %0, %1, %2\n" +# endif + : "=&r" (tmp.AO_val1), "=&r" (tmp.AO_val2) + : "Q" (*addr)); + if (tmp.AO_val1 != old_val.AO_val1 || tmp.AO_val2 != old_val.AO_val2) + break; + __asm__ __volatile__( +# ifdef __ILP32__ + " stxp %w0, %w2, %w3, %1\n" +# else + " stxp %w0, %2, %3, %1\n" +# endif + : "=&r" (result), "=Q" (*addr) + : "r" (new_val.AO_val1), "r" (new_val.AO_val2)); + } while (AO_EXPECT_FALSE(result)); + return !result; + } +# define AO_HAVE_double_compare_and_swap + + AO_INLINE int + AO_double_compare_and_swap_acquire(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_double_t tmp; + int result = 1; + + do { + __asm__ __volatile__("//AO_double_compare_and_swap_acquire\n" +# ifdef __ILP32__ + " ldaxp %w0, %w1, %2\n" +# else + " ldaxp %0, %1, %2\n" +# endif + : "=&r" (tmp.AO_val1), "=&r" (tmp.AO_val2) + : "Q" (*addr)); + if (tmp.AO_val1 != old_val.AO_val1 || tmp.AO_val2 != old_val.AO_val2) + break; + __asm__ __volatile__( +# ifdef __ILP32__ + " stxp %w0, %w2, %w3, %1\n" +# else + " stxp %w0, %2, %3, %1\n" +# endif + : "=&r" (result), "=Q" (*addr) + : "r" (new_val.AO_val1), "r" (new_val.AO_val2)); + } while (AO_EXPECT_FALSE(result)); + return !result; + } +# define AO_HAVE_double_compare_and_swap_acquire + + AO_INLINE int + AO_double_compare_and_swap_release(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_double_t tmp; + int result = 1; + + do { + __asm__ __volatile__("//AO_double_compare_and_swap_release\n" +# ifdef __ILP32__ + " ldxp %w0, %w1, %2\n" +# else + " ldxp %0, %1, %2\n" +# endif + : "=&r" (tmp.AO_val1), "=&r" (tmp.AO_val2) + : "Q" (*addr)); + if (tmp.AO_val1 != old_val.AO_val1 || tmp.AO_val2 != old_val.AO_val2) + break; + __asm__ __volatile__( +# ifdef __ILP32__ + " stlxp %w0, %w2, %w3, %1\n" +# else + " stlxp %w0, %2, %3, %1\n" +# endif + : "=&r" (result), "=Q" (*addr) + : "r" (new_val.AO_val1), "r" (new_val.AO_val2)); + } while (AO_EXPECT_FALSE(result)); + return !result; + } +# define AO_HAVE_double_compare_and_swap_release + + AO_INLINE int + AO_double_compare_and_swap_full(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_double_t tmp; + int result = 1; + + do { + __asm__ __volatile__("//AO_double_compare_and_swap_full\n" +# ifdef __ILP32__ + " ldaxp %w0, %w1, %2\n" +# else + " ldaxp %0, %1, %2\n" +# endif + : "=&r" (tmp.AO_val1), "=&r" (tmp.AO_val2) + : "Q" (*addr)); + if (tmp.AO_val1 != old_val.AO_val1 || tmp.AO_val2 != old_val.AO_val2) + break; + __asm__ __volatile__( +# ifdef __ILP32__ + " stlxp %w0, %w2, %w3, %1\n" +# else + " stlxp %w0, %2, %3, %1\n" +# endif + : "=&r" (result), "=Q" (*addr) + : "r" (new_val.AO_val1), "r" (new_val.AO_val2)); + } while (AO_EXPECT_FALSE(result)); + return !result; + } +# define AO_HAVE_double_compare_and_swap_full + +#endif /* !__ILP32__ && !__clang__ || AO_AARCH64_ASM_LOAD_STORE_CAS */ + +/* As of clang-5.0 and gcc-8.1, __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16 */ +/* macro is still missing (while the double-word CAS is available). */ +# ifndef __ILP32__ +# define AO_GCC_HAVE_double_SYNC_CAS +# endif + +#endif /* !__clang__ || AO_CLANG_PREREQ(3, 9) */ + +#if (defined(__clang__) && !AO_CLANG_PREREQ(3, 8)) || defined(__APPLE_CC__) + /* __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n macros are missing. */ +# define AO_GCC_FORCE_HAVE_CAS +#endif + +#include "generic.h" + +#undef AO_GCC_FORCE_HAVE_CAS +#undef AO_GCC_HAVE_double_SYNC_CAS diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/alpha.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/alpha.h new file mode 100644 index 000000000..59c669e0b --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/alpha.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * 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. + * + */ + +#include "../loadstore/atomic_load.h" +#include "../loadstore/atomic_store.h" + +#include "../test_and_set_t_is_ao_t.h" + +#define AO_NO_DD_ORDERING + /* Data dependence does not imply read ordering. */ + +AO_INLINE void +AO_nop_full(void) +{ + __asm__ __volatile__("mb" : : : "memory"); +} +#define AO_HAVE_nop_full + +AO_INLINE void +AO_nop_write(void) +{ + __asm__ __volatile__("wmb" : : : "memory"); +} +#define AO_HAVE_nop_write + +/* mb should be used for AO_nop_read(). That's the default. */ + +/* TODO: implement AO_fetch_and_add explicitly. */ + +/* We believe that ldq_l ... stq_c does not imply any memory barrier. */ +AO_INLINE int +AO_compare_and_swap(volatile AO_t *addr, + AO_t old, AO_t new_val) +{ + unsigned long was_equal; + unsigned long temp; + + __asm__ __volatile__( + "1: ldq_l %0,%1\n" + " cmpeq %0,%4,%2\n" + " mov %3,%0\n" + " beq %2,2f\n" + " stq_c %0,%1\n" + " beq %0,1b\n" + "2:\n" + : "=&r" (temp), "+m" (*addr), "=&r" (was_equal) + : "r" (new_val), "Ir" (old) + :"memory"); + return (int)was_equal; +} +#define AO_HAVE_compare_and_swap + +/* TODO: implement AO_fetch_compare_and_swap */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/arm.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/arm.h new file mode 100644 index 000000000..e02aad17c --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/arm.h @@ -0,0 +1,742 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2017 Ivan Maidanski + * + * 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. + * + */ + +#if (AO_GNUC_PREREQ(4, 8) || AO_CLANG_PREREQ(3, 5)) \ + && !defined(AO_DISABLE_GCC_ATOMICS) + /* Probably, it could be enabled even for earlier gcc/clang versions. */ +# define AO_GCC_ATOMIC_TEST_AND_SET +#endif + +#ifdef __native_client__ + /* Mask instruction should immediately precede access instruction. */ +# define AO_MASK_PTR(reg) " bical " reg ", " reg ", #0xc0000000\n" +# define AO_BR_ALIGN " .align 4\n" +#else +# define AO_MASK_PTR(reg) /* empty */ +# define AO_BR_ALIGN /* empty */ +#endif + +#if defined(__thumb__) && !defined(__thumb2__) + /* Thumb One mode does not have ARM "mcr", "swp" and some load/store */ + /* instructions, so we temporarily switch to ARM mode and go back */ + /* afterwards (clobbering "r3" register). */ +# define AO_THUMB_GO_ARM \ + " adr r3, 4f\n" \ + " bx r3\n" \ + " .align\n" \ + " .arm\n" \ + AO_BR_ALIGN \ + "4:\n" +# define AO_THUMB_RESTORE_MODE \ + " adr r3, 5f + 1\n" \ + " bx r3\n" \ + " .thumb\n" \ + AO_BR_ALIGN \ + "5:\n" +# define AO_THUMB_SWITCH_CLOBBERS "r3", +#else +# define AO_THUMB_GO_ARM /* empty */ +# define AO_THUMB_RESTORE_MODE /* empty */ +# define AO_THUMB_SWITCH_CLOBBERS /* empty */ +#endif /* !__thumb__ */ + +/* NEC LE-IT: gcc has no way to easily check the arm architecture */ +/* but it defines only one (or several) of __ARM_ARCH_x__ to be true. */ +#if !defined(__ARM_ARCH_2__) && !defined(__ARM_ARCH_3__) \ + && !defined(__ARM_ARCH_3M__) && !defined(__ARM_ARCH_4__) \ + && !defined(__ARM_ARCH_4T__) \ + && ((!defined(__ARM_ARCH_5__) && !defined(__ARM_ARCH_5E__) \ + && !defined(__ARM_ARCH_5T__) && !defined(__ARM_ARCH_5TE__) \ + && !defined(__ARM_ARCH_5TEJ__) && !defined(__ARM_ARCH_6M__)) \ + || defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_8A__)) +# define AO_ARM_HAVE_LDREX +# if !defined(__ARM_ARCH_6__) && !defined(__ARM_ARCH_6J__) \ + && !defined(__ARM_ARCH_6T2__) + /* LDREXB/STREXB and LDREXH/STREXH are present in ARMv6K/Z+. */ +# define AO_ARM_HAVE_LDREXBH +# endif +# if !defined(__ARM_ARCH_6__) && !defined(__ARM_ARCH_6J__) \ + && !defined(__ARM_ARCH_6T2__) && !defined(__ARM_ARCH_6Z__) \ + && !defined(__ARM_ARCH_6ZT2__) +# if !defined(__ARM_ARCH_6K__) && !defined(__ARM_ARCH_6KZ__) \ + && !defined(__ARM_ARCH_6ZK__) + /* DMB is present in ARMv6M and ARMv7+. */ +# define AO_ARM_HAVE_DMB +# endif +# if (!defined(__thumb__) \ + || (defined(__thumb2__) && !defined(__ARM_ARCH_7__) \ + && !defined(__ARM_ARCH_7M__) && !defined(__ARM_ARCH_7EM__))) \ + && (!defined(__clang__) || AO_CLANG_PREREQ(3, 3)) + /* LDREXD/STREXD present in ARMv6K/M+ (see gas/config/tc-arm.c). */ + /* In the Thumb mode, this works only starting from ARMv7 (except */ + /* for the base and 'M' models). Clang3.2 (and earlier) does not */ + /* allocate register pairs for LDREXD/STREXD properly (besides, */ + /* Clang3.1 does not support "%H" operand specification). */ +# define AO_ARM_HAVE_LDREXD +# endif /* !thumb || ARMv7A || ARMv7R+ */ +# endif /* ARMv7+ */ +#endif /* ARMv6+ */ + +#if !defined(__ARM_ARCH_2__) && !defined(__ARM_ARCH_6M__) \ + && !defined(__ARM_ARCH_8A__) && !defined(__thumb2__) +# define AO_ARM_HAVE_SWP + /* Note: ARMv6M is excluded due to no ARM mode support. */ + /* Also, SWP is obsoleted for ARMv8+. */ +#endif /* !__thumb2__ */ + +#if !defined(AO_UNIPROCESSOR) && defined(AO_ARM_HAVE_DMB) \ + && !defined(AO_PREFER_BUILTIN_ATOMICS) + AO_INLINE void + AO_nop_write(void) + { + /* AO_THUMB_GO_ARM is empty. */ + /* This will target the system domain and thus be overly */ + /* conservative as the CPUs (even in case of big.LITTLE SoC) will */ + /* occupy the inner shareable domain. */ + /* The plain variant (dmb st) is theoretically slower, and should */ + /* not be needed. That said, with limited experimentation, a CPU */ + /* implementation for which it actually matters has not been found */ + /* yet, though they should already exist. */ + /* Anyway, note that the "st" and "ishst" barriers are actually */ + /* quite weak and, as the libatomic_ops documentation states, */ + /* usually not what you really want. */ + __asm__ __volatile__("dmb ishst" : : : "memory"); + } +# define AO_HAVE_nop_write +#endif /* AO_ARM_HAVE_DMB */ + +#ifndef AO_GCC_ATOMIC_TEST_AND_SET + +#ifdef AO_UNIPROCESSOR + /* If only a single processor (core) is used, AO_UNIPROCESSOR could */ + /* be defined by the client to avoid unnecessary memory barrier. */ + AO_INLINE void + AO_nop_full(void) + { + AO_compiler_barrier(); + } +# define AO_HAVE_nop_full + +#elif defined(AO_ARM_HAVE_DMB) + /* ARMv7 is compatible to ARMv6 but has a simpler command for issuing */ + /* a memory barrier (DMB). Raising it via CP15 should still work */ + /* (but slightly less efficient because it requires the use of */ + /* a general-purpose register). */ + AO_INLINE void + AO_nop_full(void) + { + /* AO_THUMB_GO_ARM is empty. */ + __asm__ __volatile__("dmb" : : : "memory"); + } +# define AO_HAVE_nop_full + +#elif defined(AO_ARM_HAVE_LDREX) + /* ARMv6 is the first architecture providing support for a simple */ + /* LL/SC. A data memory barrier must be raised via CP15 command. */ + AO_INLINE void + AO_nop_full(void) + { + unsigned dest = 0; + + /* Issue a data memory barrier (keeps ordering of memory */ + /* transactions before and after this operation). */ + __asm__ __volatile__("@AO_nop_full\n" + AO_THUMB_GO_ARM + " mcr p15,0,%0,c7,c10,5\n" + AO_THUMB_RESTORE_MODE + : "=&r"(dest) + : /* empty */ + : AO_THUMB_SWITCH_CLOBBERS "memory"); + } +# define AO_HAVE_nop_full + +#else + /* AO_nop_full() is emulated using AO_test_and_set_full(). */ +#endif /* !AO_UNIPROCESSOR && !AO_ARM_HAVE_LDREX */ + +#endif /* !AO_GCC_ATOMIC_TEST_AND_SET */ + +#ifdef AO_ARM_HAVE_LDREX + + /* "ARM Architecture Reference Manual" (chapter A3.5.3) says that the */ + /* single-copy atomic processor accesses are all byte accesses, all */ + /* halfword accesses to halfword-aligned locations, all word accesses */ + /* to word-aligned locations. */ + /* There is only a single concern related to AO store operations: */ + /* a direct write (by STR[B/H] instruction) will not be recognized */ + /* by the LL/SC construct on the same CPU (i.e., according to ARM */ + /* documentation, e.g., see CortexA8 TRM reference, point 8.5, */ + /* atomic "store" (using LDREX/STREX[B/H]) is the only safe way to */ + /* set variables also used in LL/SC environment). */ + /* This is only a problem if interrupt handlers do not clear the */ + /* reservation (by CLREX instruction or a dummy STREX one), as they */ + /* almost certainly should (e.g., see restore_user_regs defined in */ + /* arch/arm/kernel/entry-header.S of Linux. Nonetheless, there is */ + /* a doubt this was properly implemented in some ancient OS releases. */ +# ifdef AO_BROKEN_TASKSWITCH_CLREX + +# define AO_SKIPATOMIC_store +# define AO_SKIPATOMIC_store_release +# define AO_SKIPATOMIC_char_store +# define AO_SKIPATOMIC_char_store_release +# define AO_SKIPATOMIC_short_store +# define AO_SKIPATOMIC_short_store_release +# define AO_SKIPATOMIC_int_store +# define AO_SKIPATOMIC_int_store_release + +# ifndef AO_PREFER_BUILTIN_ATOMICS + + AO_INLINE void AO_store(volatile AO_t *addr, AO_t value) + { + int flag; + + __asm__ __volatile__("@AO_store\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%2") + " ldrex %0, [%2]\n" + AO_MASK_PTR("%2") + " strex %0, %3, [%2]\n" + " teq %0, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r" (flag), "+m" (*addr) + : "r" (addr), "r" (value) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + } +# define AO_HAVE_store + +# ifdef AO_ARM_HAVE_LDREXBH + AO_INLINE void AO_char_store(volatile unsigned char *addr, + unsigned char value) + { + int flag; + + __asm__ __volatile__("@AO_char_store\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%2") + " ldrexb %0, [%2]\n" + AO_MASK_PTR("%2") + " strexb %0, %3, [%2]\n" + " teq %0, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r" (flag), "+m" (*addr) + : "r" (addr), "r" (value) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + } +# define AO_HAVE_char_store + + AO_INLINE void AO_short_store(volatile unsigned short *addr, + unsigned short value) + { + int flag; + + __asm__ __volatile__("@AO_short_store\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%2") + " ldrexh %0, [%2]\n" + AO_MASK_PTR("%2") + " strexh %0, %3, [%2]\n" + " teq %0, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r" (flag), "+m" (*addr) + : "r" (addr), "r" (value) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + } +# define AO_HAVE_short_store +# endif /* AO_ARM_HAVE_LDREXBH */ + +# endif /* !AO_PREFER_BUILTIN_ATOMICS */ + +# elif !defined(AO_GCC_ATOMIC_TEST_AND_SET) +# include "../loadstore/atomic_store.h" + /* AO_int_store is defined in ao_t_is_int.h. */ +# endif /* !AO_BROKEN_TASKSWITCH_CLREX */ + +#endif /* AO_ARM_HAVE_LDREX */ + +#ifndef AO_GCC_ATOMIC_TEST_AND_SET + +# include "../test_and_set_t_is_ao_t.h" /* Probably suboptimal */ + +#ifdef AO_ARM_HAVE_LDREX + + /* AO_t/char/short/int load is simple reading. */ + /* Unaligned accesses are not guaranteed to be atomic. */ +# define AO_ACCESS_CHECK_ALIGNED +# define AO_ACCESS_short_CHECK_ALIGNED +# define AO_ACCESS_int_CHECK_ALIGNED +# include "../all_atomic_only_load.h" + +# ifndef AO_HAVE_char_store +# include "../loadstore/char_atomic_store.h" +# include "../loadstore/short_atomic_store.h" +# endif + +/* NEC LE-IT: replace the SWAP as recommended by ARM: + "Applies to: ARM11 Cores + Though the SWP instruction will still work with ARM V6 cores, it is + recommended to use the new V6 synchronization instructions. The SWP + instruction produces 'locked' read and write accesses which are atomic, + i.e. another operation cannot be done between these locked accesses which + ties up external bus (AHB, AXI) bandwidth and can increase worst case + interrupt latencies. LDREX, STREX are more flexible, other instructions + can be done between the LDREX and STREX accesses." +*/ +#ifndef AO_PREFER_GENERALIZED +#if !defined(AO_FORCE_USE_SWP) || !defined(AO_ARM_HAVE_SWP) + /* But, on the other hand, there could be a considerable performance */ + /* degradation in case of a race. Eg., test_atomic.c executing */ + /* test_and_set test on a dual-core ARMv7 processor using LDREX/STREX */ + /* showed around 35 times lower performance than that using SWP. */ + /* To force use of SWP instruction, use -D AO_FORCE_USE_SWP option */ + /* (the latter is ignored if SWP instruction is unsupported). */ + AO_INLINE AO_TS_VAL_t + AO_test_and_set(volatile AO_TS_t *addr) + { + AO_TS_VAL_t oldval; + int flag; + + __asm__ __volatile__("@AO_test_and_set\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%3") + " ldrex %0, [%3]\n" + AO_MASK_PTR("%3") + " strex %1, %4, [%3]\n" + " teq %1, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r"(oldval), "=&r"(flag), "+m"(*addr) + : "r"(addr), "r"(1) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + return oldval; + } +# define AO_HAVE_test_and_set +#endif /* !AO_FORCE_USE_SWP */ + +AO_INLINE AO_t +AO_fetch_and_add(volatile AO_t *p, AO_t incr) +{ + AO_t result, tmp; + int flag; + + __asm__ __volatile__("@AO_fetch_and_add\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%5") + " ldrex %0, [%5]\n" /* get original */ + " add %2, %0, %4\n" /* sum up in incr */ + AO_MASK_PTR("%5") + " strex %1, %2, [%5]\n" /* store them */ + " teq %1, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r"(result), "=&r"(flag), "=&r"(tmp), "+m"(*p) /* 0..3 */ + : "r"(incr), "r"(p) /* 4..5 */ + : AO_THUMB_SWITCH_CLOBBERS "cc"); + return result; +} +#define AO_HAVE_fetch_and_add + +AO_INLINE AO_t +AO_fetch_and_add1(volatile AO_t *p) +{ + AO_t result, tmp; + int flag; + + __asm__ __volatile__("@AO_fetch_and_add1\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%4") + " ldrex %0, [%4]\n" /* get original */ + " add %1, %0, #1\n" /* increment */ + AO_MASK_PTR("%4") + " strex %2, %1, [%4]\n" /* store them */ + " teq %2, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r"(result), "=&r"(tmp), "=&r"(flag), "+m"(*p) + : "r"(p) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + return result; +} +#define AO_HAVE_fetch_and_add1 + +AO_INLINE AO_t +AO_fetch_and_sub1(volatile AO_t *p) +{ + AO_t result, tmp; + int flag; + + __asm__ __volatile__("@AO_fetch_and_sub1\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%4") + " ldrex %0, [%4]\n" /* get original */ + " sub %1, %0, #1\n" /* decrement */ + AO_MASK_PTR("%4") + " strex %2, %1, [%4]\n" /* store them */ + " teq %2, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r"(result), "=&r"(tmp), "=&r"(flag), "+m"(*p) + : "r"(p) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + return result; +} +#define AO_HAVE_fetch_and_sub1 + +AO_INLINE void +AO_and(volatile AO_t *p, AO_t value) +{ + AO_t tmp, result; + + __asm__ __volatile__("@AO_and\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%4") + " ldrex %0, [%4]\n" + " and %1, %0, %3\n" + AO_MASK_PTR("%4") + " strex %0, %1, [%4]\n" + " teq %0, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r" (tmp), "=&r" (result), "+m" (*p) + : "r" (value), "r" (p) + : AO_THUMB_SWITCH_CLOBBERS "cc"); +} +#define AO_HAVE_and + +AO_INLINE void +AO_or(volatile AO_t *p, AO_t value) +{ + AO_t tmp, result; + + __asm__ __volatile__("@AO_or\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%4") + " ldrex %0, [%4]\n" + " orr %1, %0, %3\n" + AO_MASK_PTR("%4") + " strex %0, %1, [%4]\n" + " teq %0, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r" (tmp), "=&r" (result), "+m" (*p) + : "r" (value), "r" (p) + : AO_THUMB_SWITCH_CLOBBERS "cc"); +} +#define AO_HAVE_or + +AO_INLINE void +AO_xor(volatile AO_t *p, AO_t value) +{ + AO_t tmp, result; + + __asm__ __volatile__("@AO_xor\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%4") + " ldrex %0, [%4]\n" + " eor %1, %0, %3\n" + AO_MASK_PTR("%4") + " strex %0, %1, [%4]\n" + " teq %0, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r" (tmp), "=&r" (result), "+m" (*p) + : "r" (value), "r" (p) + : AO_THUMB_SWITCH_CLOBBERS "cc"); +} +#define AO_HAVE_xor +#endif /* !AO_PREFER_GENERALIZED */ + +#ifdef AO_ARM_HAVE_LDREXBH + AO_INLINE unsigned char + AO_char_fetch_and_add(volatile unsigned char *p, unsigned char incr) + { + unsigned result, tmp; + int flag; + + __asm__ __volatile__("@AO_char_fetch_and_add\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%5") + " ldrexb %0, [%5]\n" + " add %2, %0, %4\n" + AO_MASK_PTR("%5") + " strexb %1, %2, [%5]\n" + " teq %1, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r" (result), "=&r" (flag), "=&r" (tmp), "+m" (*p) + : "r" ((unsigned)incr), "r" (p) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + return (unsigned char)result; + } +# define AO_HAVE_char_fetch_and_add + + AO_INLINE unsigned short + AO_short_fetch_and_add(volatile unsigned short *p, unsigned short incr) + { + unsigned result, tmp; + int flag; + + __asm__ __volatile__("@AO_short_fetch_and_add\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: " AO_MASK_PTR("%5") + " ldrexh %0, [%5]\n" + " add %2, %0, %4\n" + AO_MASK_PTR("%5") + " strexh %1, %2, [%5]\n" + " teq %1, #0\n" + " bne 1b\n" + AO_THUMB_RESTORE_MODE + : "=&r" (result), "=&r" (flag), "=&r" (tmp), "+m" (*p) + : "r" ((unsigned)incr), "r" (p) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + return (unsigned short)result; + } +# define AO_HAVE_short_fetch_and_add +#endif /* AO_ARM_HAVE_LDREXBH */ + +#ifndef AO_GENERALIZE_ASM_BOOL_CAS + /* Returns nonzero if the comparison succeeded. */ + AO_INLINE int + AO_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) + { + AO_t result, tmp; + + __asm__ __volatile__("@AO_compare_and_swap\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: mov %0, #2\n" /* store a flag */ + AO_MASK_PTR("%3") + " ldrex %1, [%3]\n" /* get original */ + " teq %1, %4\n" /* see if match */ + AO_MASK_PTR("%3") +# ifdef __thumb2__ + /* TODO: Eliminate warning: it blocks containing wide Thumb */ + /* instructions are deprecated in ARMv8. */ + " it eq\n" +# endif + " strexeq %0, %5, [%3]\n" /* store new one if matched */ + " teq %0, #1\n" + " beq 1b\n" /* if update failed, repeat */ + AO_THUMB_RESTORE_MODE + : "=&r"(result), "=&r"(tmp), "+m"(*addr) + : "r"(addr), "r"(old_val), "r"(new_val) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + return !(result&2); /* if succeeded then return 1 else 0 */ + } +# define AO_HAVE_compare_and_swap +#endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) +{ + AO_t fetched_val; + int flag; + + __asm__ __volatile__("@AO_fetch_compare_and_swap\n" + AO_THUMB_GO_ARM + AO_BR_ALIGN + "1: mov %0, #2\n" /* store a flag */ + AO_MASK_PTR("%3") + " ldrex %1, [%3]\n" /* get original */ + " teq %1, %4\n" /* see if match */ + AO_MASK_PTR("%3") +# ifdef __thumb2__ + " it eq\n" +# endif + " strexeq %0, %5, [%3]\n" /* store new one if matched */ + " teq %0, #1\n" + " beq 1b\n" /* if update failed, repeat */ + AO_THUMB_RESTORE_MODE + : "=&r"(flag), "=&r"(fetched_val), "+m"(*addr) + : "r"(addr), "r"(old_val), "r"(new_val) + : AO_THUMB_SWITCH_CLOBBERS "cc"); + return fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap + +#ifdef AO_ARM_HAVE_LDREXD +# include "../standard_ao_double_t.h" + + /* "ARM Architecture Reference Manual ARMv7-A/R edition" (chapter */ + /* A3.5.3) says that memory accesses caused by LDREXD and STREXD */ + /* instructions to doubleword-aligned locations are single-copy */ + /* atomic; accesses to 64-bit elements by other instructions might */ + /* not be single-copy atomic as they are executed as a sequence of */ + /* 32-bit accesses. */ + AO_INLINE AO_double_t + AO_double_load(const volatile AO_double_t *addr) + { + AO_double_t result; + + /* AO_THUMB_GO_ARM is empty. */ + __asm__ __volatile__("@AO_double_load\n" + AO_MASK_PTR("%1") + " ldrexd %0, %H0, [%1]" + : "=&r" (result.AO_whole) + : "r" (addr) + /* : no clobber */); + return result; + } +# define AO_HAVE_double_load + + AO_INLINE void + AO_double_store(volatile AO_double_t *addr, AO_double_t new_val) + { + AO_double_t old_val; + int status; + + do { + /* AO_THUMB_GO_ARM is empty. */ + __asm__ __volatile__("@AO_double_store\n" + AO_MASK_PTR("%3") + " ldrexd %0, %H0, [%3]\n" + AO_MASK_PTR("%3") + " strexd %1, %4, %H4, [%3]" + : "=&r" (old_val.AO_whole), "=&r" (status), "+m" (*addr) + : "r" (addr), "r" (new_val.AO_whole) + : "cc"); + } while (AO_EXPECT_FALSE(status)); + } +# define AO_HAVE_double_store + + AO_INLINE int + AO_double_compare_and_swap(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + double_ptr_storage tmp; + int result = 1; + + do { + /* AO_THUMB_GO_ARM is empty. */ + __asm__ __volatile__("@AO_double_compare_and_swap\n" + AO_MASK_PTR("%1") + " ldrexd %0, %H0, [%1]\n" /* get original to r1 & r2 */ + : "=&r"(tmp) + : "r"(addr) + /* : no clobber */); + if (tmp != old_val.AO_whole) + break; + __asm__ __volatile__( + AO_MASK_PTR("%2") + " strexd %0, %3, %H3, [%2]\n" /* store new one if matched */ + : "=&r"(result), "+m"(*addr) + : "r" (addr), "r" (new_val.AO_whole) + : "cc"); + } while (AO_EXPECT_FALSE(result)); + return !result; /* if succeeded then return 1 else 0 */ + } +# define AO_HAVE_double_compare_and_swap +#endif /* AO_ARM_HAVE_LDREXD */ + +#else +/* pre ARMv6 architectures ... */ + +/* I found a slide set that, if I read it correctly, claims that */ +/* Loads followed by either a Load or Store are ordered, but nothing */ +/* else is. */ +/* It appears that SWP is the only simple memory barrier. */ +#include "../all_aligned_atomic_load_store.h" + +/* The code should run correctly on a multi-core ARMv6+ as well. */ + +#endif /* !AO_ARM_HAVE_LDREX */ + +#if !defined(AO_HAVE_test_and_set_full) && !defined(AO_HAVE_test_and_set) \ + && defined (AO_ARM_HAVE_SWP) && (!defined(AO_PREFER_GENERALIZED) \ + || !defined(AO_HAVE_fetch_compare_and_swap)) + AO_INLINE AO_TS_VAL_t + AO_test_and_set_full(volatile AO_TS_t *addr) + { + AO_TS_VAL_t oldval; + /* SWP on ARM is very similar to XCHG on x86. */ + /* The first operand is the result, the second the value */ + /* to be stored. Both registers must be different from addr. */ + /* Make the address operand an early clobber output so it */ + /* doesn't overlap with the other operands. The early clobber */ + /* on oldval is necessary to prevent the compiler allocating */ + /* them to the same register if they are both unused. */ + + __asm__ __volatile__("@AO_test_and_set_full\n" + AO_THUMB_GO_ARM + AO_MASK_PTR("%3") + " swp %0, %2, [%3]\n" + /* Ignore GCC "SWP is deprecated for this architecture" */ + /* warning here (for ARMv6+). */ + AO_THUMB_RESTORE_MODE + : "=&r"(oldval), "=&r"(addr) + : "r"(1), "1"(addr) + : AO_THUMB_SWITCH_CLOBBERS "memory"); + return oldval; + } +# define AO_HAVE_test_and_set_full +#endif /* !AO_HAVE_test_and_set[_full] && AO_ARM_HAVE_SWP */ + +#define AO_T_IS_INT + +#else /* AO_GCC_ATOMIC_TEST_AND_SET */ + +# if defined(__clang__) && !defined(AO_ARM_HAVE_LDREX) + /* As of clang-3.8, it cannot compile __atomic_and/or/xor_fetch */ + /* library calls yet for pre ARMv6. */ +# define AO_SKIPATOMIC_ANY_and_ANY +# define AO_SKIPATOMIC_ANY_or_ANY +# define AO_SKIPATOMIC_ANY_xor_ANY +# endif + +# ifdef AO_ARM_HAVE_LDREXD +# include "../standard_ao_double_t.h" +# endif +# include "generic.h" + +#endif /* AO_GCC_ATOMIC_TEST_AND_SET */ + +#undef AO_ARM_HAVE_DMB +#undef AO_ARM_HAVE_LDREX +#undef AO_ARM_HAVE_LDREXBH +#undef AO_ARM_HAVE_LDREXD +#undef AO_ARM_HAVE_SWP +#undef AO_BR_ALIGN +#undef AO_MASK_PTR +#undef AO_SKIPATOMIC_ANY_and_ANY +#undef AO_SKIPATOMIC_ANY_or_ANY +#undef AO_SKIPATOMIC_ANY_xor_ANY +#undef AO_SKIPATOMIC_char_store +#undef AO_SKIPATOMIC_char_store_release +#undef AO_SKIPATOMIC_int_store +#undef AO_SKIPATOMIC_int_store_release +#undef AO_SKIPATOMIC_short_store +#undef AO_SKIPATOMIC_short_store_release +#undef AO_SKIPATOMIC_store +#undef AO_SKIPATOMIC_store_release +#undef AO_THUMB_GO_ARM +#undef AO_THUMB_RESTORE_MODE +#undef AO_THUMB_SWITCH_CLOBBERS diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/avr32.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/avr32.h new file mode 100644 index 000000000..b0c52c70e --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/avr32.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 Bradley Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "../all_atomic_load_store.h" + +#include "../ordered.h" /* There are no multiprocessor implementations. */ + +#include "../test_and_set_t_is_ao_t.h" + +#ifndef AO_PREFER_GENERALIZED + AO_INLINE AO_TS_VAL_t + AO_test_and_set_full(volatile AO_TS_t *addr) + { + register long ret; + + __asm__ __volatile__( + "xchg %[oldval], %[mem], %[newval]" + : [oldval] "=&r"(ret) + : [mem] "r"(addr), [newval] "r"(1) + : "memory"); + + return (AO_TS_VAL_t)ret; + } +# define AO_HAVE_test_and_set_full +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE int +AO_compare_and_swap_full(volatile AO_t *addr, AO_t old, AO_t new_val) +{ + register long ret; + + __asm__ __volatile__( + "1: ssrf 5\n" + " ld.w %[res], %[mem]\n" + " eor %[res], %[oldval]\n" + " brne 2f\n" + " stcond %[mem], %[newval]\n" + " brne 1b\n" + "2:\n" + : [res] "=&r"(ret), [mem] "=m"(*addr) + : "m"(*addr), [newval] "r"(new_val), [oldval] "r"(old) + : "cc", "memory"); + + return (int)ret; +} +#define AO_HAVE_compare_and_swap_full + +/* TODO: implement AO_fetch_compare_and_swap. */ + +#define AO_T_IS_INT diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/cris.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/cris.h new file mode 100644 index 000000000..f31c21eb2 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/cris.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* FIXME: seems to be untested. */ + +#include "../all_atomic_load_store.h" + +#include "../ordered.h" /* There are no multiprocessor implementations. */ + +#include "../test_and_set_t_is_ao_t.h" + +/* + * The architecture apparently supports an "f" flag which is + * set on preemption. This essentially gives us load-locked, + * store-conditional primitives, though I'm not quite sure how + * this would work on a hypothetical multiprocessor. -HB + * + * For details, see + * http://developer.axis.com/doc/hardware/etrax100lx/prog_man/ + * 1_architectural_description.pdf + * + * TODO: Presumably many other primitives (notably CAS, including the double- + * width versions) could be implemented in this manner, if someone got + * around to it. + */ + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr) { + /* Ripped from linuxthreads/sysdeps/cris/pt-machine.h */ + register unsigned long int ret; + + /* Note the use of a dummy output of *addr to expose the write. The + memory barrier is to stop *other* writes being moved past this code. */ + __asm__ __volatile__("clearf\n" + "0:\n\t" + "movu.b [%2],%0\n\t" + "ax\n\t" + "move.b %3,[%2]\n\t" + "bwf 0b\n\t" + "clearf" + : "=&r" (ret), "=m" (*addr) + : "r" (addr), "r" ((int) 1), "m" (*addr) + : "memory"); + return ret; +} +#define AO_HAVE_test_and_set_full diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/e2k.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/e2k.h new file mode 100644 index 000000000..087584b30 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/e2k.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* As of clang-9, all __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n are missing. */ +#define AO_GCC_FORCE_HAVE_CAS + +#include "generic.h" + +#undef AO_GCC_FORCE_HAVE_CAS diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-arithm.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-arithm.h new file mode 100644 index 000000000..268832836 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-arithm.h @@ -0,0 +1,864 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_char_ARITHM + +AO_INLINE unsigned/**/char +AO_char_fetch_and_add(volatile unsigned/**/char *addr, unsigned/**/char incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_RELAXED); +} +#define AO_HAVE_char_fetch_and_add + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_char_and(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_char_and +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_char_or(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_char_or +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_char_xor(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_char_xor +#endif + +#endif /* !AO_NO_char_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_short_ARITHM + +AO_INLINE unsigned/**/short +AO_short_fetch_and_add(volatile unsigned/**/short *addr, unsigned/**/short incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_RELAXED); +} +#define AO_HAVE_short_fetch_and_add + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_short_and(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_short_and +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_short_or(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_short_or +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_short_xor(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_short_xor +#endif + +#endif /* !AO_NO_short_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_int_ARITHM + +AO_INLINE unsigned +AO_int_fetch_and_add(volatile unsigned *addr, unsigned incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_RELAXED); +} +#define AO_HAVE_int_fetch_and_add + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_int_and(volatile unsigned *addr, unsigned value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_int_and +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_int_or(volatile unsigned *addr, unsigned value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_int_or +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_int_xor(volatile unsigned *addr, unsigned value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_int_xor +#endif + +#endif /* !AO_NO_int_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_ARITHM + +AO_INLINE AO_t +AO_fetch_and_add(volatile AO_t *addr, AO_t incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_RELAXED); +} +#define AO_HAVE_fetch_and_add + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_and(volatile AO_t *addr, AO_t value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_and +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_or(volatile AO_t *addr, AO_t value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_or +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_xor(volatile AO_t *addr, AO_t value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_xor +#endif + +#endif /* !AO_NO_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_char_ARITHM + +AO_INLINE unsigned/**/char +AO_char_fetch_and_add_acquire(volatile unsigned/**/char *addr, unsigned/**/char incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_char_fetch_and_add_acquire + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_char_and_acquire(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_char_and_acquire +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_char_or_acquire(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_char_or_acquire +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_char_xor_acquire(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_char_xor_acquire +#endif + +#endif /* !AO_NO_char_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_short_ARITHM + +AO_INLINE unsigned/**/short +AO_short_fetch_and_add_acquire(volatile unsigned/**/short *addr, unsigned/**/short incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_short_fetch_and_add_acquire + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_short_and_acquire(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_short_and_acquire +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_short_or_acquire(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_short_or_acquire +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_short_xor_acquire(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_short_xor_acquire +#endif + +#endif /* !AO_NO_short_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_int_ARITHM + +AO_INLINE unsigned +AO_int_fetch_and_add_acquire(volatile unsigned *addr, unsigned incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_int_fetch_and_add_acquire + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_int_and_acquire(volatile unsigned *addr, unsigned value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_int_and_acquire +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_int_or_acquire(volatile unsigned *addr, unsigned value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_int_or_acquire +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_int_xor_acquire(volatile unsigned *addr, unsigned value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_int_xor_acquire +#endif + +#endif /* !AO_NO_int_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_ARITHM + +AO_INLINE AO_t +AO_fetch_and_add_acquire(volatile AO_t *addr, AO_t incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_fetch_and_add_acquire + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_and_acquire(volatile AO_t *addr, AO_t value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_and_acquire +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_or_acquire(volatile AO_t *addr, AO_t value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_or_acquire +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_xor_acquire(volatile AO_t *addr, AO_t value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_xor_acquire +#endif + +#endif /* !AO_NO_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_char_ARITHM + +AO_INLINE unsigned/**/char +AO_char_fetch_and_add_release(volatile unsigned/**/char *addr, unsigned/**/char incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_RELEASE); +} +#define AO_HAVE_char_fetch_and_add_release + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_char_and_release(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_char_and_release +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_char_or_release(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_char_or_release +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_char_xor_release(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_char_xor_release +#endif + +#endif /* !AO_NO_char_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_short_ARITHM + +AO_INLINE unsigned/**/short +AO_short_fetch_and_add_release(volatile unsigned/**/short *addr, unsigned/**/short incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_RELEASE); +} +#define AO_HAVE_short_fetch_and_add_release + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_short_and_release(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_short_and_release +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_short_or_release(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_short_or_release +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_short_xor_release(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_short_xor_release +#endif + +#endif /* !AO_NO_short_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_int_ARITHM + +AO_INLINE unsigned +AO_int_fetch_and_add_release(volatile unsigned *addr, unsigned incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_RELEASE); +} +#define AO_HAVE_int_fetch_and_add_release + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_int_and_release(volatile unsigned *addr, unsigned value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_int_and_release +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_int_or_release(volatile unsigned *addr, unsigned value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_int_or_release +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_int_xor_release(volatile unsigned *addr, unsigned value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_int_xor_release +#endif + +#endif /* !AO_NO_int_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_ARITHM + +AO_INLINE AO_t +AO_fetch_and_add_release(volatile AO_t *addr, AO_t incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_RELEASE); +} +#define AO_HAVE_fetch_and_add_release + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_and_release(volatile AO_t *addr, AO_t value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_and_release +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_or_release(volatile AO_t *addr, AO_t value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_or_release +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_xor_release(volatile AO_t *addr, AO_t value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_xor_release +#endif + +#endif /* !AO_NO_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_char_ARITHM + +AO_INLINE unsigned/**/char +AO_char_fetch_and_add_full(volatile unsigned/**/char *addr, unsigned/**/char incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_SEQ_CST); +} +#define AO_HAVE_char_fetch_and_add_full + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_char_and_full(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_char_and_full +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_char_or_full(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_char_or_full +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_char_xor_full(volatile unsigned/**/char *addr, unsigned/**/char value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_char_xor_full +#endif + +#endif /* !AO_NO_char_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_short_ARITHM + +AO_INLINE unsigned/**/short +AO_short_fetch_and_add_full(volatile unsigned/**/short *addr, unsigned/**/short incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_SEQ_CST); +} +#define AO_HAVE_short_fetch_and_add_full + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_short_and_full(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_short_and_full +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_short_or_full(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_short_or_full +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_short_xor_full(volatile unsigned/**/short *addr, unsigned/**/short value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_short_xor_full +#endif + +#endif /* !AO_NO_short_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_int_ARITHM + +AO_INLINE unsigned +AO_int_fetch_and_add_full(volatile unsigned *addr, unsigned incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_SEQ_CST); +} +#define AO_HAVE_int_fetch_and_add_full + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_int_and_full(volatile unsigned *addr, unsigned value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_int_and_full +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_int_or_full(volatile unsigned *addr, unsigned value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_int_or_full +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_int_xor_full(volatile unsigned *addr, unsigned value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_int_xor_full +#endif + +#endif /* !AO_NO_int_ARITHM */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_ARITHM + +AO_INLINE AO_t +AO_fetch_and_add_full(volatile AO_t *addr, AO_t incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_SEQ_CST); +} +#define AO_HAVE_fetch_and_add_full + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_and_full(volatile AO_t *addr, AO_t value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_and_full +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_or_full(volatile AO_t *addr, AO_t value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_or_full +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_xor_full(volatile AO_t *addr, AO_t value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_SEQ_CST); + } +# define AO_HAVE_xor_full +#endif + +#endif /* !AO_NO_ARITHM */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-arithm.template b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-arithm.template new file mode 100644 index 000000000..916d13381 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-arithm.template @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#ifndef AO_NO_XSIZE_ARITHM + +AO_INLINE XCTYPE +AO_XSIZE_fetch_and_add_XBAR(volatile XCTYPE *addr, XCTYPE incr) +{ + return __atomic_fetch_add(addr, incr, __ATOMIC_XGCCBAR); +} +#define AO_HAVE_XSIZE_fetch_and_add_XBAR + +#ifndef AO_SKIPATOMIC_ANY_and_ANY + AO_INLINE void + AO_XSIZE_and_XBAR(volatile XCTYPE *addr, XCTYPE value) + { + (void)__atomic_and_fetch(addr, value, __ATOMIC_XGCCBAR); + } +# define AO_HAVE_XSIZE_and_XBAR +#endif + +#ifndef AO_SKIPATOMIC_ANY_or_ANY + AO_INLINE void + AO_XSIZE_or_XBAR(volatile XCTYPE *addr, XCTYPE value) + { + (void)__atomic_or_fetch(addr, value, __ATOMIC_XGCCBAR); + } +# define AO_HAVE_XSIZE_or_XBAR +#endif + +#ifndef AO_SKIPATOMIC_ANY_xor_ANY + AO_INLINE void + AO_XSIZE_xor_XBAR(volatile XCTYPE *addr, XCTYPE value) + { + (void)__atomic_xor_fetch(addr, value, __ATOMIC_XGCCBAR); + } +# define AO_HAVE_XSIZE_xor_XBAR +#endif + +#endif /* !AO_NO_XSIZE_ARITHM */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-small.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-small.h new file mode 100644 index 000000000..fb78a75b8 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-small.h @@ -0,0 +1,632 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#if !defined(AO_GCC_HAVE_char_SYNC_CAS) || !defined(AO_PREFER_GENERALIZED) + +AO_INLINE unsigned/**/char +AO_char_load(const volatile unsigned/**/char *addr) +{ + return __atomic_load_n(addr, __ATOMIC_RELAXED); +} +#define AO_HAVE_char_load + +AO_INLINE unsigned/**/char +AO_char_load_acquire(const volatile unsigned/**/char *addr) +{ + return __atomic_load_n(addr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_char_load_acquire + +/* char_load_read is defined using load and nop_read. */ +/* TODO: Map it to ACQUIRE. We should be strengthening the read and */ +/* write stuff to the more general acquire/release versions. It almost */ +/* never makes a difference and is much less error-prone. */ + +/* char_load_full is generalized using load and nop_full. */ +/* TODO: Map it to SEQ_CST and clarify the documentation. */ + +/* TODO: Map load_dd_acquire_read to ACQUIRE. Ideally it should be */ +/* mapped to CONSUME, but the latter is currently broken. */ + +/* char_store_full definition is omitted similar to load_full reason. */ + +/* TODO: Map store_write to RELEASE. */ + +#ifndef AO_SKIPATOMIC_char_store + AO_INLINE void + AO_char_store(volatile unsigned/**/char *addr, unsigned/**/char value) + { + __atomic_store_n(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_char_store +#endif + +#ifndef AO_SKIPATOMIC_char_store_release + AO_INLINE void + AO_char_store_release(volatile unsigned/**/char *addr, unsigned/**/char value) + { + __atomic_store_n(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_char_store_release +#endif + +#endif /* !AO_GCC_HAVE_char_SYNC_CAS || !AO_PREFER_GENERALIZED */ + +#ifdef AO_GCC_HAVE_char_SYNC_CAS + + AO_INLINE unsigned/**/char + AO_char_fetch_compare_and_swap(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + (void)__atomic_compare_exchange_n(addr, + &old_val /* p_expected */, + new_val /* desired */, + 0 /* is_weak: false */, + __ATOMIC_RELAXED /* success */, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_char_fetch_compare_and_swap + + AO_INLINE unsigned/**/char + AO_char_fetch_compare_and_swap_acquire(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + return old_val; + } +# define AO_HAVE_char_fetch_compare_and_swap_acquire + + AO_INLINE unsigned/**/char + AO_char_fetch_compare_and_swap_release(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_char_fetch_compare_and_swap_release + + AO_INLINE unsigned/**/char + AO_char_fetch_compare_and_swap_full(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + return old_val; + } +# define AO_HAVE_char_fetch_compare_and_swap_full + +# ifndef AO_GENERALIZE_ASM_BOOL_CAS + AO_INLINE int + AO_char_compare_and_swap(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + } +# define AO_HAVE_char_compare_and_swap + + AO_INLINE int + AO_char_compare_and_swap_acquire(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_char_compare_and_swap_acquire + + AO_INLINE int + AO_char_compare_and_swap_release(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + } +# define AO_HAVE_char_compare_and_swap_release + + AO_INLINE int + AO_char_compare_and_swap_full(volatile unsigned/**/char *addr, + unsigned/**/char old_val, unsigned/**/char new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + } +# define AO_HAVE_char_compare_and_swap_full + +# endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +#endif /* AO_GCC_HAVE_char_SYNC_CAS */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#if !defined(AO_GCC_HAVE_short_SYNC_CAS) || !defined(AO_PREFER_GENERALIZED) + +AO_INLINE unsigned/**/short +AO_short_load(const volatile unsigned/**/short *addr) +{ + return __atomic_load_n(addr, __ATOMIC_RELAXED); +} +#define AO_HAVE_short_load + +AO_INLINE unsigned/**/short +AO_short_load_acquire(const volatile unsigned/**/short *addr) +{ + return __atomic_load_n(addr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_short_load_acquire + +/* short_load_read is defined using load and nop_read. */ +/* TODO: Map it to ACQUIRE. We should be strengthening the read and */ +/* write stuff to the more general acquire/release versions. It almost */ +/* never makes a difference and is much less error-prone. */ + +/* short_load_full is generalized using load and nop_full. */ +/* TODO: Map it to SEQ_CST and clarify the documentation. */ + +/* TODO: Map load_dd_acquire_read to ACQUIRE. Ideally it should be */ +/* mapped to CONSUME, but the latter is currently broken. */ + +/* short_store_full definition is omitted similar to load_full reason. */ + +/* TODO: Map store_write to RELEASE. */ + +#ifndef AO_SKIPATOMIC_short_store + AO_INLINE void + AO_short_store(volatile unsigned/**/short *addr, unsigned/**/short value) + { + __atomic_store_n(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_short_store +#endif + +#ifndef AO_SKIPATOMIC_short_store_release + AO_INLINE void + AO_short_store_release(volatile unsigned/**/short *addr, unsigned/**/short value) + { + __atomic_store_n(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_short_store_release +#endif + +#endif /* !AO_GCC_HAVE_short_SYNC_CAS || !AO_PREFER_GENERALIZED */ + +#ifdef AO_GCC_HAVE_short_SYNC_CAS + + AO_INLINE unsigned/**/short + AO_short_fetch_compare_and_swap(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + (void)__atomic_compare_exchange_n(addr, + &old_val /* p_expected */, + new_val /* desired */, + 0 /* is_weak: false */, + __ATOMIC_RELAXED /* success */, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_short_fetch_compare_and_swap + + AO_INLINE unsigned/**/short + AO_short_fetch_compare_and_swap_acquire(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + return old_val; + } +# define AO_HAVE_short_fetch_compare_and_swap_acquire + + AO_INLINE unsigned/**/short + AO_short_fetch_compare_and_swap_release(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_short_fetch_compare_and_swap_release + + AO_INLINE unsigned/**/short + AO_short_fetch_compare_and_swap_full(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + return old_val; + } +# define AO_HAVE_short_fetch_compare_and_swap_full + +# ifndef AO_GENERALIZE_ASM_BOOL_CAS + AO_INLINE int + AO_short_compare_and_swap(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + } +# define AO_HAVE_short_compare_and_swap + + AO_INLINE int + AO_short_compare_and_swap_acquire(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_short_compare_and_swap_acquire + + AO_INLINE int + AO_short_compare_and_swap_release(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + } +# define AO_HAVE_short_compare_and_swap_release + + AO_INLINE int + AO_short_compare_and_swap_full(volatile unsigned/**/short *addr, + unsigned/**/short old_val, unsigned/**/short new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + } +# define AO_HAVE_short_compare_and_swap_full + +# endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +#endif /* AO_GCC_HAVE_short_SYNC_CAS */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#if !defined(AO_GCC_HAVE_int_SYNC_CAS) || !defined(AO_PREFER_GENERALIZED) + +AO_INLINE unsigned +AO_int_load(const volatile unsigned *addr) +{ + return __atomic_load_n(addr, __ATOMIC_RELAXED); +} +#define AO_HAVE_int_load + +AO_INLINE unsigned +AO_int_load_acquire(const volatile unsigned *addr) +{ + return __atomic_load_n(addr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_int_load_acquire + +/* int_load_read is defined using load and nop_read. */ +/* TODO: Map it to ACQUIRE. We should be strengthening the read and */ +/* write stuff to the more general acquire/release versions. It almost */ +/* never makes a difference and is much less error-prone. */ + +/* int_load_full is generalized using load and nop_full. */ +/* TODO: Map it to SEQ_CST and clarify the documentation. */ + +/* TODO: Map load_dd_acquire_read to ACQUIRE. Ideally it should be */ +/* mapped to CONSUME, but the latter is currently broken. */ + +/* int_store_full definition is omitted similar to load_full reason. */ + +/* TODO: Map store_write to RELEASE. */ + +#ifndef AO_SKIPATOMIC_int_store + AO_INLINE void + AO_int_store(volatile unsigned *addr, unsigned value) + { + __atomic_store_n(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_int_store +#endif + +#ifndef AO_SKIPATOMIC_int_store_release + AO_INLINE void + AO_int_store_release(volatile unsigned *addr, unsigned value) + { + __atomic_store_n(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_int_store_release +#endif + +#endif /* !AO_GCC_HAVE_int_SYNC_CAS || !AO_PREFER_GENERALIZED */ + +#ifdef AO_GCC_HAVE_int_SYNC_CAS + + AO_INLINE unsigned + AO_int_fetch_compare_and_swap(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + (void)__atomic_compare_exchange_n(addr, + &old_val /* p_expected */, + new_val /* desired */, + 0 /* is_weak: false */, + __ATOMIC_RELAXED /* success */, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_int_fetch_compare_and_swap + + AO_INLINE unsigned + AO_int_fetch_compare_and_swap_acquire(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + return old_val; + } +# define AO_HAVE_int_fetch_compare_and_swap_acquire + + AO_INLINE unsigned + AO_int_fetch_compare_and_swap_release(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_int_fetch_compare_and_swap_release + + AO_INLINE unsigned + AO_int_fetch_compare_and_swap_full(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + return old_val; + } +# define AO_HAVE_int_fetch_compare_and_swap_full + +# ifndef AO_GENERALIZE_ASM_BOOL_CAS + AO_INLINE int + AO_int_compare_and_swap(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + } +# define AO_HAVE_int_compare_and_swap + + AO_INLINE int + AO_int_compare_and_swap_acquire(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_int_compare_and_swap_acquire + + AO_INLINE int + AO_int_compare_and_swap_release(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + } +# define AO_HAVE_int_compare_and_swap_release + + AO_INLINE int + AO_int_compare_and_swap_full(volatile unsigned *addr, + unsigned old_val, unsigned new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + } +# define AO_HAVE_int_compare_and_swap_full + +# endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +#endif /* AO_GCC_HAVE_int_SYNC_CAS */ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#if !defined(AO_GCC_HAVE_SYNC_CAS) || !defined(AO_PREFER_GENERALIZED) + +AO_INLINE AO_t +AO_load(const volatile AO_t *addr) +{ + return __atomic_load_n(addr, __ATOMIC_RELAXED); +} +#define AO_HAVE_load + +AO_INLINE AO_t +AO_load_acquire(const volatile AO_t *addr) +{ + return __atomic_load_n(addr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_load_acquire + +/* load_read is defined using load and nop_read. */ +/* TODO: Map it to ACQUIRE. We should be strengthening the read and */ +/* write stuff to the more general acquire/release versions. It almost */ +/* never makes a difference and is much less error-prone. */ + +/* load_full is generalized using load and nop_full. */ +/* TODO: Map it to SEQ_CST and clarify the documentation. */ + +/* TODO: Map load_dd_acquire_read to ACQUIRE. Ideally it should be */ +/* mapped to CONSUME, but the latter is currently broken. */ + +/* store_full definition is omitted similar to load_full reason. */ + +/* TODO: Map store_write to RELEASE. */ + +#ifndef AO_SKIPATOMIC_store + AO_INLINE void + AO_store(volatile AO_t *addr, AO_t value) + { + __atomic_store_n(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_store +#endif + +#ifndef AO_SKIPATOMIC_store_release + AO_INLINE void + AO_store_release(volatile AO_t *addr, AO_t value) + { + __atomic_store_n(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_store_release +#endif + +#endif /* !AO_GCC_HAVE_SYNC_CAS || !AO_PREFER_GENERALIZED */ + +#ifdef AO_GCC_HAVE_SYNC_CAS + + AO_INLINE AO_t + AO_fetch_compare_and_swap(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + (void)__atomic_compare_exchange_n(addr, + &old_val /* p_expected */, + new_val /* desired */, + 0 /* is_weak: false */, + __ATOMIC_RELAXED /* success */, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_fetch_compare_and_swap + + AO_INLINE AO_t + AO_fetch_compare_and_swap_acquire(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + return old_val; + } +# define AO_HAVE_fetch_compare_and_swap_acquire + + AO_INLINE AO_t + AO_fetch_compare_and_swap_release(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_fetch_compare_and_swap_release + + AO_INLINE AO_t + AO_fetch_compare_and_swap_full(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + return old_val; + } +# define AO_HAVE_fetch_compare_and_swap_full + +# ifndef AO_GENERALIZE_ASM_BOOL_CAS + AO_INLINE int + AO_compare_and_swap(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + } +# define AO_HAVE_compare_and_swap + + AO_INLINE int + AO_compare_and_swap_acquire(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_compare_and_swap_acquire + + AO_INLINE int + AO_compare_and_swap_release(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + } +# define AO_HAVE_compare_and_swap_release + + AO_INLINE int + AO_compare_and_swap_full(volatile AO_t *addr, + AO_t old_val, AO_t new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + } +# define AO_HAVE_compare_and_swap_full + +# endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +#endif /* AO_GCC_HAVE_SYNC_CAS */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-small.template b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-small.template new file mode 100644 index 000000000..7c2f738f3 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic-small.template @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +#if !defined(AO_GCC_HAVE_XSIZE_SYNC_CAS) || !defined(AO_PREFER_GENERALIZED) + +AO_INLINE XCTYPE +AO_XSIZE_load(const volatile XCTYPE *addr) +{ + return __atomic_load_n(addr, __ATOMIC_RELAXED); +} +#define AO_HAVE_XSIZE_load + +AO_INLINE XCTYPE +AO_XSIZE_load_acquire(const volatile XCTYPE *addr) +{ + return __atomic_load_n(addr, __ATOMIC_ACQUIRE); +} +#define AO_HAVE_XSIZE_load_acquire + +/* XSIZE_load_read is defined using load and nop_read. */ +/* TODO: Map it to ACQUIRE. We should be strengthening the read and */ +/* write stuff to the more general acquire/release versions. It almost */ +/* never makes a difference and is much less error-prone. */ + +/* XSIZE_load_full is generalized using load and nop_full. */ +/* TODO: Map it to SEQ_CST and clarify the documentation. */ + +/* TODO: Map load_dd_acquire_read to ACQUIRE. Ideally it should be */ +/* mapped to CONSUME, but the latter is currently broken. */ + +/* XSIZE_store_full definition is omitted similar to load_full reason. */ + +/* TODO: Map store_write to RELEASE. */ + +#ifndef AO_SKIPATOMIC_XSIZE_store + AO_INLINE void + AO_XSIZE_store(volatile XCTYPE *addr, XCTYPE value) + { + __atomic_store_n(addr, value, __ATOMIC_RELAXED); + } +# define AO_HAVE_XSIZE_store +#endif + +#ifndef AO_SKIPATOMIC_XSIZE_store_release + AO_INLINE void + AO_XSIZE_store_release(volatile XCTYPE *addr, XCTYPE value) + { + __atomic_store_n(addr, value, __ATOMIC_RELEASE); + } +# define AO_HAVE_XSIZE_store_release +#endif + +#endif /* !AO_GCC_HAVE_XSIZE_SYNC_CAS || !AO_PREFER_GENERALIZED */ + +#ifdef AO_GCC_HAVE_XSIZE_SYNC_CAS + + AO_INLINE XCTYPE + AO_XSIZE_fetch_compare_and_swap(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + (void)__atomic_compare_exchange_n(addr, + &old_val /* p_expected */, + new_val /* desired */, + 0 /* is_weak: false */, + __ATOMIC_RELAXED /* success */, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_XSIZE_fetch_compare_and_swap + + AO_INLINE XCTYPE + AO_XSIZE_fetch_compare_and_swap_acquire(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + return old_val; + } +# define AO_HAVE_XSIZE_fetch_compare_and_swap_acquire + + AO_INLINE XCTYPE + AO_XSIZE_fetch_compare_and_swap_release(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + return old_val; + } +# define AO_HAVE_XSIZE_fetch_compare_and_swap_release + + AO_INLINE XCTYPE + AO_XSIZE_fetch_compare_and_swap_full(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + (void)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + return old_val; + } +# define AO_HAVE_XSIZE_fetch_compare_and_swap_full + +# ifndef AO_GENERALIZE_ASM_BOOL_CAS + AO_INLINE int + AO_XSIZE_compare_and_swap(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + } +# define AO_HAVE_XSIZE_compare_and_swap + + AO_INLINE int + AO_XSIZE_compare_and_swap_acquire(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_XSIZE_compare_and_swap_acquire + + AO_INLINE int + AO_XSIZE_compare_and_swap_release(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + } +# define AO_HAVE_XSIZE_compare_and_swap_release + + AO_INLINE int + AO_XSIZE_compare_and_swap_full(volatile XCTYPE *addr, + XCTYPE old_val, XCTYPE new_val) + { + return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + } +# define AO_HAVE_XSIZE_compare_and_swap_full + +# endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +#endif /* AO_GCC_HAVE_XSIZE_SYNC_CAS */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic.h new file mode 100644 index 000000000..032990ca5 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/generic.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2013-2017 Ivan Maidanski + * + * 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. + * + */ + +/* The following implementation assumes GCC 4.7 or later. */ +/* For the details, see GNU Manual, chapter 6.52 (Built-in functions */ +/* for memory model aware atomic operations). */ + +#define AO_GCC_ATOMIC_TEST_AND_SET +#include "../test_and_set_t_is_char.h" + +#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) \ + || defined(AO_GCC_FORCE_HAVE_CAS) +# define AO_GCC_HAVE_char_SYNC_CAS +#endif + +#if (__SIZEOF_SHORT__ == 2 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2)) \ + || defined(AO_GCC_FORCE_HAVE_CAS) +# define AO_GCC_HAVE_short_SYNC_CAS +#endif + +#if (__SIZEOF_INT__ == 4 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) \ + || (__SIZEOF_INT__ == 8 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) \ + || defined(AO_GCC_FORCE_HAVE_CAS) +# define AO_GCC_HAVE_int_SYNC_CAS +#endif + +#if (__SIZEOF_SIZE_T__ == 4 && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) \ + || (__SIZEOF_SIZE_T__ == 8 \ + && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) \ + || defined(AO_GCC_FORCE_HAVE_CAS) +# define AO_GCC_HAVE_SYNC_CAS +#endif + +#undef AO_compiler_barrier +#define AO_compiler_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) + +#ifdef AO_UNIPROCESSOR + /* If only a single processor (core) is used, AO_UNIPROCESSOR could */ + /* be defined by the client to avoid unnecessary memory barrier. */ + AO_INLINE void + AO_nop_full(void) + { + AO_compiler_barrier(); + } +# define AO_HAVE_nop_full + +#elif defined(AO_THREAD_SANITIZER) && !defined(AO_USE_ATOMIC_THREAD_FENCE) + /* Workaround a compiler warning (reported by gcc-11, at least) */ + /* that atomic_thread_fence is unsupported with thread sanitizer. */ + +#else + AO_INLINE void + AO_nop_read(void) + { + __atomic_thread_fence(__ATOMIC_ACQUIRE); + } +# define AO_HAVE_nop_read + +# ifndef AO_HAVE_nop_write + AO_INLINE void + AO_nop_write(void) + { + __atomic_thread_fence(__ATOMIC_RELEASE); + } +# define AO_HAVE_nop_write +# endif + + AO_INLINE void + AO_nop_full(void) + { + /* __sync_synchronize() could be used instead. */ + __atomic_thread_fence(__ATOMIC_SEQ_CST); + } +# define AO_HAVE_nop_full +#endif /* !AO_UNIPROCESSOR && !AO_THREAD_SANITIZER */ + +#include "generic-small.h" + +#ifndef AO_PREFER_GENERALIZED +# include "generic-arithm.h" + +# define AO_CLEAR(addr) __atomic_clear(addr, __ATOMIC_RELEASE) +# define AO_HAVE_CLEAR + + AO_INLINE AO_TS_VAL_t + AO_test_and_set(volatile AO_TS_t *addr) + { + return (AO_TS_VAL_t)(__atomic_test_and_set(addr, __ATOMIC_RELAXED) + ? AO_TS_SET : AO_TS_CLEAR); + } +# define AO_HAVE_test_and_set + + AO_INLINE AO_TS_VAL_t + AO_test_and_set_acquire(volatile AO_TS_t *addr) + { + return (AO_TS_VAL_t)(__atomic_test_and_set(addr, __ATOMIC_ACQUIRE) + ? AO_TS_SET : AO_TS_CLEAR); + } +# define AO_HAVE_test_and_set_acquire + + AO_INLINE AO_TS_VAL_t + AO_test_and_set_release(volatile AO_TS_t *addr) + { + return (AO_TS_VAL_t)(__atomic_test_and_set(addr, __ATOMIC_RELEASE) + ? AO_TS_SET : AO_TS_CLEAR); + } +# define AO_HAVE_test_and_set_release + + AO_INLINE AO_TS_VAL_t + AO_test_and_set_full(volatile AO_TS_t *addr) + { + return (AO_TS_VAL_t)(__atomic_test_and_set(addr, __ATOMIC_SEQ_CST) + ? AO_TS_SET : AO_TS_CLEAR); + } +# define AO_HAVE_test_and_set_full +#endif /* !AO_PREFER_GENERALIZED */ + +#ifdef AO_HAVE_DOUBLE_PTR_STORAGE + +# if ((__SIZEOF_SIZE_T__ == 4 \ + && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) \ + || (__SIZEOF_SIZE_T__ == 8 /* half of AO_double_t */ \ + && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16))) \ + && !defined(AO_SKIPATOMIC_double_compare_and_swap_ANY) +# define AO_GCC_HAVE_double_SYNC_CAS +# endif + +# if !defined(AO_GCC_HAVE_double_SYNC_CAS) || !defined(AO_PREFER_GENERALIZED) + +# if !defined(AO_HAVE_double_load) && !defined(AO_SKIPATOMIC_double_load) + AO_INLINE AO_double_t + AO_double_load(const volatile AO_double_t *addr) + { + AO_double_t result; + + result.AO_whole = __atomic_load_n(&addr->AO_whole, __ATOMIC_RELAXED); + return result; + } +# define AO_HAVE_double_load +# endif + +# if !defined(AO_HAVE_double_load_acquire) \ + && !defined(AO_SKIPATOMIC_double_load_acquire) + AO_INLINE AO_double_t + AO_double_load_acquire(const volatile AO_double_t *addr) + { + AO_double_t result; + + result.AO_whole = __atomic_load_n(&addr->AO_whole, __ATOMIC_ACQUIRE); + return result; + } +# define AO_HAVE_double_load_acquire +# endif + +# if !defined(AO_HAVE_double_store) && !defined(AO_SKIPATOMIC_double_store) + AO_INLINE void + AO_double_store(volatile AO_double_t *addr, AO_double_t value) + { + __atomic_store_n(&addr->AO_whole, value.AO_whole, __ATOMIC_RELAXED); + } +# define AO_HAVE_double_store +# endif + +# if !defined(AO_HAVE_double_store_release) \ + && !defined(AO_SKIPATOMIC_double_store_release) + AO_INLINE void + AO_double_store_release(volatile AO_double_t *addr, AO_double_t value) + { + __atomic_store_n(&addr->AO_whole, value.AO_whole, __ATOMIC_RELEASE); + } +# define AO_HAVE_double_store_release +# endif + +#endif /* !AO_GCC_HAVE_double_SYNC_CAS || !AO_PREFER_GENERALIZED */ + +#endif /* AO_HAVE_DOUBLE_PTR_STORAGE */ + +#ifdef AO_GCC_HAVE_double_SYNC_CAS +# ifndef AO_HAVE_double_compare_and_swap + AO_INLINE int + AO_double_compare_and_swap(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return (int)__atomic_compare_exchange_n(&addr->AO_whole, + &old_val.AO_whole /* p_expected */, + new_val.AO_whole /* desired */, + 0 /* is_weak: false */, + __ATOMIC_RELAXED /* success */, + __ATOMIC_RELAXED /* failure */); + } +# define AO_HAVE_double_compare_and_swap +# endif + +# ifndef AO_HAVE_double_compare_and_swap_acquire + AO_INLINE int + AO_double_compare_and_swap_acquire(volatile AO_double_t *addr, + AO_double_t old_val, + AO_double_t new_val) + { + return (int)__atomic_compare_exchange_n(&addr->AO_whole, + &old_val.AO_whole, new_val.AO_whole, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + } +# define AO_HAVE_double_compare_and_swap_acquire +# endif + +# ifndef AO_HAVE_double_compare_and_swap_release + AO_INLINE int + AO_double_compare_and_swap_release(volatile AO_double_t *addr, + AO_double_t old_val, + AO_double_t new_val) + { + return (int)__atomic_compare_exchange_n(&addr->AO_whole, + &old_val.AO_whole, new_val.AO_whole, 0, + __ATOMIC_RELEASE, + __ATOMIC_RELAXED /* failure */); + } +# define AO_HAVE_double_compare_and_swap_release +# endif + +# ifndef AO_HAVE_double_compare_and_swap_full + AO_INLINE int + AO_double_compare_and_swap_full(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + return (int)__atomic_compare_exchange_n(&addr->AO_whole, + &old_val.AO_whole, new_val.AO_whole, 0, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE /* failure */); + } +# define AO_HAVE_double_compare_and_swap_full +# endif +#endif /* AO_GCC_HAVE_double_SYNC_CAS */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/hexagon.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/hexagon.h new file mode 100644 index 000000000..cffffe81b --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/hexagon.h @@ -0,0 +1,140 @@ +/* + * 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. + */ + +#if AO_CLANG_PREREQ(3, 9) && !defined(AO_DISABLE_GCC_ATOMICS) + /* Probably, it could be enabled for earlier clang versions as well. */ + + /* As of clang-3.9, __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n are missing. */ +# define AO_GCC_FORCE_HAVE_CAS + +# define AO_GCC_HAVE_double_SYNC_CAS +# include "../standard_ao_double_t.h" + +# include "generic.h" + +#else /* AO_DISABLE_GCC_ATOMICS */ + +#include "../all_aligned_atomic_load_store.h" + +#include "../test_and_set_t_is_ao_t.h" + +/* There's also "isync" and "barrier"; however, for all current CPU */ +/* versions, "syncht" should suffice. Likewise, it seems that the */ +/* auto-defined versions of *_acquire, *_release or *_full suffice for */ +/* all current ISA implementations. */ +AO_INLINE void +AO_nop_full(void) +{ + __asm__ __volatile__("syncht" : : : "memory"); +} +#define AO_HAVE_nop_full + +/* The Hexagon has load-locked, store-conditional primitives, and so */ +/* resulting code is very nearly identical to that of PowerPC. */ + +#ifndef AO_PREFER_GENERALIZED +AO_INLINE AO_t +AO_fetch_and_add(volatile AO_t *addr, AO_t incr) +{ + AO_t oldval; + AO_t newval; + __asm__ __volatile__( + "1:\n" + " %0 = memw_locked(%3);\n" /* load and reserve */ + " %1 = add (%0,%4);\n" /* increment */ + " memw_locked(%3,p1) = %1;\n" /* store conditional */ + " if (!p1) jump 1b;\n" /* retry if lost reservation */ + : "=&r"(oldval), "=&r"(newval), "+m"(*addr) + : "r"(addr), "r"(incr) + : "memory", "p1"); + return oldval; +} +#define AO_HAVE_fetch_and_add + +AO_INLINE AO_TS_VAL_t +AO_test_and_set(volatile AO_TS_t *addr) +{ + int oldval; + int locked_value = 1; + + __asm__ __volatile__( + "1:\n" + " %0 = memw_locked(%2);\n" /* load and reserve */ + " {\n" + " p2 = cmp.eq(%0,#0);\n" /* if load is not zero, */ + " if (!p2.new) jump:nt 2f;\n" /* we are done */ + " }\n" + " memw_locked(%2,p1) = %3;\n" /* else store conditional */ + " if (!p1) jump 1b;\n" /* retry if lost reservation */ + "2:\n" /* oldval is zero if we set */ + : "=&r"(oldval), "+m"(*addr) + : "r"(addr), "r"(locked_value) + : "memory", "p1", "p2"); + return (AO_TS_VAL_t)oldval; +} +#define AO_HAVE_test_and_set +#endif /* !AO_PREFER_GENERALIZED */ + +#ifndef AO_GENERALIZE_ASM_BOOL_CAS + AO_INLINE int + AO_compare_and_swap(volatile AO_t *addr, AO_t old, AO_t new_val) + { + AO_t __oldval; + int result = 0; + __asm__ __volatile__( + "1:\n" + " %0 = memw_locked(%3);\n" /* load and reserve */ + " {\n" + " p2 = cmp.eq(%0,%4);\n" /* if load is not equal to */ + " if (!p2.new) jump:nt 2f;\n" /* old, fail */ + " }\n" + " memw_locked(%3,p1) = %5;\n" /* else store conditional */ + " if (!p1) jump 1b;\n" /* retry if lost reservation */ + " %1 = #1\n" /* success, result = 1 */ + "2:\n" + : "=&r" (__oldval), "+r" (result), "+m"(*addr) + : "r" (addr), "r" (old), "r" (new_val) + : "p1", "p2", "memory" + ); + return result; + } +# define AO_HAVE_compare_and_swap +#endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) +{ + AO_t __oldval; + + __asm__ __volatile__( + "1:\n" + " %0 = memw_locked(%2);\n" /* load and reserve */ + " {\n" + " p2 = cmp.eq(%0,%3);\n" /* if load is not equal to */ + " if (!p2.new) jump:nt 2f;\n" /* old_val, fail */ + " }\n" + " memw_locked(%2,p1) = %4;\n" /* else store conditional */ + " if (!p1) jump 1b;\n" /* retry if lost reservation */ + "2:\n" + : "=&r" (__oldval), "+m"(*addr) + : "r" (addr), "r" (old_val), "r" (new_val) + : "p1", "p2", "memory" + ); + return __oldval; +} +#define AO_HAVE_fetch_compare_and_swap + +#define AO_T_IS_INT + +#endif /* AO_DISABLE_GCC_ATOMICS */ + +#undef AO_GCC_FORCE_HAVE_CAS +#undef AO_GCC_HAVE_double_SYNC_CAS diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/hppa.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/hppa.h new file mode 100644 index 000000000..2d0c491c3 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/hppa.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "../all_atomic_load_store.h" + +/* Some architecture set descriptions include special "ordered" memory */ +/* operations. As far as we can tell, no existing processors actually */ +/* require those. Nor does it appear likely that future processors */ +/* will. */ +#include "../ordered.h" + +/* GCC will not guarantee the alignment we need, use four lock words */ +/* and select the correctly aligned datum. See the glibc 2.3.2 */ +/* linuxthread port for the original implementation. */ +struct AO_pa_clearable_loc { + int data[4]; +}; + +#undef AO_TS_INITIALIZER +#define AO_TS_t struct AO_pa_clearable_loc +#define AO_TS_INITIALIZER { { 1, 1, 1, 1 } } +/* Switch meaning of set and clear, since we only have an atomic clear */ +/* instruction. */ +typedef enum {AO_PA_TS_set = 0, AO_PA_TS_clear = 1} AO_PA_TS_val; +#define AO_TS_VAL_t AO_PA_TS_val +#define AO_TS_CLEAR AO_PA_TS_clear +#define AO_TS_SET AO_PA_TS_set + +/* The hppa only has one atomic read and modify memory operation, */ +/* load and clear, so hppa spinlocks must use zero to signify that */ +/* someone is holding the lock. The address used for the ldcw */ +/* semaphore must be 16-byte aligned. */ +#define AO_ldcw(a, ret) \ + __asm__ __volatile__("ldcw 0(%2), %0" \ + : "=r" (ret), "=m" (*(a)) : "r" (a)) + +/* Because malloc only guarantees 8-byte alignment for malloc'd data, */ +/* and GCC only guarantees 8-byte alignment for stack locals, we can't */ +/* be assured of 16-byte alignment for atomic lock data even if we */ +/* specify "__attribute ((aligned(16)))" in the type declaration. So, */ +/* we use a struct containing an array of four ints for the atomic lock */ +/* type and dynamically select the 16-byte aligned int from the array */ +/* for the semaphore. */ +#define AO_PA_LDCW_ALIGNMENT 16 +#define AO_ldcw_align(addr) \ + ((volatile unsigned *)(((unsigned long)(addr) \ + + (AO_PA_LDCW_ALIGNMENT - 1)) \ + & ~(AO_PA_LDCW_ALIGNMENT - 1))) + +/* Works on PA 1.1 and PA 2.0 systems */ +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t * addr) +{ + volatile unsigned int ret; + volatile unsigned *a = AO_ldcw_align(addr); + + AO_ldcw(a, ret); + return (AO_TS_VAL_t)ret; +} +#define AO_HAVE_test_and_set_full + +AO_INLINE void +AO_pa_clear(volatile AO_TS_t * addr) +{ + volatile unsigned *a = AO_ldcw_align(addr); + + AO_compiler_barrier(); + *a = 1; +} +#define AO_CLEAR(addr) AO_pa_clear(addr) +#define AO_HAVE_CLEAR + +#undef AO_PA_LDCW_ALIGNMENT +#undef AO_ldcw +#undef AO_ldcw_align diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/ia64.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/ia64.h new file mode 100644 index 000000000..98627d53b --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/ia64.h @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "../all_atomic_load_store.h" + +#include "../all_acquire_release_volatile.h" + +#include "../test_and_set_t_is_char.h" + +#ifdef _ILP32 + /* 32-bit HP/UX code. */ + /* This requires pointer "swizzling". Pointers need to be expanded */ + /* to 64 bits using the addp4 instruction before use. This makes it */ + /* hard to share code, but we try anyway. */ +# define AO_LEN "4" + /* We assume that addr always appears in argument position 1 in asm */ + /* code. If it is clobbered due to swizzling, we also need it in */ + /* second position. Any later arguments are referenced symbolically, */ + /* so that we don't have to worry about their position. This requires*/ + /* gcc 3.1, but you shouldn't be using anything older than that on */ + /* IA64 anyway. */ + /* The AO_MASK macro is a workaround for the fact that HP/UX gcc */ + /* appears to otherwise store 64-bit pointers in ar.ccv, i.e. it */ + /* doesn't appear to clear high bits in a pointer value we pass into */ + /* assembly code, even if it is supposedly of type AO_t. */ +# define AO_IN_ADDR "1"(addr) +# define AO_OUT_ADDR , "=r"(addr) +# define AO_SWIZZLE "addp4 %1=0,%1;;\n" +# define AO_MASK(ptr) __asm__ __volatile__("zxt4 %1=%1": "=r"(ptr) : "0"(ptr)) +#else +# define AO_LEN "8" +# define AO_IN_ADDR "r"(addr) +# define AO_OUT_ADDR +# define AO_SWIZZLE +# define AO_MASK(ptr) /* empty */ +#endif /* !_ILP32 */ + +AO_INLINE void +AO_nop_full(void) +{ + __asm__ __volatile__("mf" : : : "memory"); +} +#define AO_HAVE_nop_full + +#ifndef AO_PREFER_GENERALIZED +AO_INLINE AO_t +AO_fetch_and_add1_acquire (volatile AO_t *addr) +{ + AO_t result; + + __asm__ __volatile__ (AO_SWIZZLE + "fetchadd" AO_LEN ".acq %0=[%1],1": + "=r" (result) AO_OUT_ADDR: AO_IN_ADDR :"memory"); + return result; +} +#define AO_HAVE_fetch_and_add1_acquire + +AO_INLINE AO_t +AO_fetch_and_add1_release (volatile AO_t *addr) +{ + AO_t result; + + __asm__ __volatile__ (AO_SWIZZLE + "fetchadd" AO_LEN ".rel %0=[%1],1": + "=r" (result) AO_OUT_ADDR: AO_IN_ADDR :"memory"); + return result; +} +#define AO_HAVE_fetch_and_add1_release + +AO_INLINE AO_t +AO_fetch_and_sub1_acquire (volatile AO_t *addr) +{ + AO_t result; + + __asm__ __volatile__ (AO_SWIZZLE + "fetchadd" AO_LEN ".acq %0=[%1],-1": + "=r" (result) AO_OUT_ADDR: AO_IN_ADDR :"memory"); + return result; +} +#define AO_HAVE_fetch_and_sub1_acquire + +AO_INLINE AO_t +AO_fetch_and_sub1_release (volatile AO_t *addr) +{ + AO_t result; + + __asm__ __volatile__ (AO_SWIZZLE + "fetchadd" AO_LEN ".rel %0=[%1],-1": + "=r" (result) AO_OUT_ADDR: AO_IN_ADDR :"memory"); + return result; +} +#define AO_HAVE_fetch_and_sub1_release +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap_acquire(volatile AO_t *addr, AO_t old, AO_t new_val) +{ + AO_t fetched_val; + AO_MASK(old); + __asm__ __volatile__(AO_SWIZZLE + "mov ar.ccv=%[old] ;; cmpxchg" AO_LEN + ".acq %0=[%1],%[new_val],ar.ccv" + : "=r"(fetched_val) AO_OUT_ADDR + : AO_IN_ADDR, [new_val]"r"(new_val), [old]"r"(old) + : "memory"); + return fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap_acquire + +AO_INLINE AO_t +AO_fetch_compare_and_swap_release(volatile AO_t *addr, AO_t old, AO_t new_val) +{ + AO_t fetched_val; + AO_MASK(old); + __asm__ __volatile__(AO_SWIZZLE + "mov ar.ccv=%[old] ;; cmpxchg" AO_LEN + ".rel %0=[%1],%[new_val],ar.ccv" + : "=r"(fetched_val) AO_OUT_ADDR + : AO_IN_ADDR, [new_val]"r"(new_val), [old]"r"(old) + : "memory"); + return fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap_release + +AO_INLINE unsigned char +AO_char_fetch_compare_and_swap_acquire(volatile unsigned char *addr, + unsigned char old, unsigned char new_val) +{ + unsigned char fetched_val; + __asm__ __volatile__(AO_SWIZZLE + "mov ar.ccv=%[old] ;; cmpxchg1.acq %0=[%1],%[new_val],ar.ccv" + : "=r"(fetched_val) AO_OUT_ADDR + : AO_IN_ADDR, [new_val]"r"(new_val), [old]"r"((AO_t)old) + : "memory"); + return fetched_val; +} +#define AO_HAVE_char_fetch_compare_and_swap_acquire + +AO_INLINE unsigned char +AO_char_fetch_compare_and_swap_release(volatile unsigned char *addr, + unsigned char old, unsigned char new_val) +{ + unsigned char fetched_val; + __asm__ __volatile__(AO_SWIZZLE + "mov ar.ccv=%[old] ;; cmpxchg1.rel %0=[%1],%[new_val],ar.ccv" + : "=r"(fetched_val) AO_OUT_ADDR + : AO_IN_ADDR, [new_val]"r"(new_val), [old]"r"((AO_t)old) + : "memory"); + return fetched_val; +} +#define AO_HAVE_char_fetch_compare_and_swap_release + +AO_INLINE unsigned short +AO_short_fetch_compare_and_swap_acquire(volatile unsigned short *addr, + unsigned short old, unsigned short new_val) +{ + unsigned short fetched_val; + __asm__ __volatile__(AO_SWIZZLE + "mov ar.ccv=%[old] ;; cmpxchg2.acq %0=[%1],%[new_val],ar.ccv" + : "=r"(fetched_val) AO_OUT_ADDR + : AO_IN_ADDR, [new_val]"r"(new_val), [old]"r"((AO_t)old) + : "memory"); + return fetched_val; +} +#define AO_HAVE_short_fetch_compare_and_swap_acquire + +AO_INLINE unsigned short +AO_short_fetch_compare_and_swap_release(volatile unsigned short *addr, + unsigned short old, unsigned short new_val) +{ + unsigned short fetched_val; + __asm__ __volatile__(AO_SWIZZLE + "mov ar.ccv=%[old] ;; cmpxchg2.rel %0=[%1],%[new_val],ar.ccv" + : "=r"(fetched_val) AO_OUT_ADDR + : AO_IN_ADDR, [new_val]"r"(new_val), [old]"r"((AO_t)old) + : "memory"); + return fetched_val; +} +#define AO_HAVE_short_fetch_compare_and_swap_release + +#ifdef _ILP32 + +# define AO_T_IS_INT + + /* TODO: Add compare_double_and_swap_double for the _ILP32 case. */ +#else + +# ifndef AO_PREFER_GENERALIZED + AO_INLINE unsigned int + AO_int_fetch_and_add1_acquire(volatile unsigned int *addr) + { + unsigned int result; + __asm__ __volatile__("fetchadd4.acq %0=[%1],1" + : "=r" (result) : AO_IN_ADDR + : "memory"); + return result; + } +# define AO_HAVE_int_fetch_and_add1_acquire + + AO_INLINE unsigned int + AO_int_fetch_and_add1_release(volatile unsigned int *addr) + { + unsigned int result; + __asm__ __volatile__("fetchadd4.rel %0=[%1],1" + : "=r" (result) : AO_IN_ADDR + : "memory"); + return result; + } +# define AO_HAVE_int_fetch_and_add1_release + + AO_INLINE unsigned int + AO_int_fetch_and_sub1_acquire(volatile unsigned int *addr) + { + unsigned int result; + __asm__ __volatile__("fetchadd4.acq %0=[%1],-1" + : "=r" (result) : AO_IN_ADDR + : "memory"); + return result; + } +# define AO_HAVE_int_fetch_and_sub1_acquire + + AO_INLINE unsigned int + AO_int_fetch_and_sub1_release(volatile unsigned int *addr) + { + unsigned int result; + __asm__ __volatile__("fetchadd4.rel %0=[%1],-1" + : "=r" (result) : AO_IN_ADDR + : "memory"); + return result; + } +# define AO_HAVE_int_fetch_and_sub1_release +# endif /* !AO_PREFER_GENERALIZED */ + + AO_INLINE unsigned int + AO_int_fetch_compare_and_swap_acquire(volatile unsigned int *addr, + unsigned int old, unsigned int new_val) + { + unsigned int fetched_val; + __asm__ __volatile__("mov ar.ccv=%3 ;; cmpxchg4.acq %0=[%1],%2,ar.ccv" + : "=r"(fetched_val) + : AO_IN_ADDR, "r"(new_val), "r"((AO_t)old) + : "memory"); + return fetched_val; + } +# define AO_HAVE_int_fetch_compare_and_swap_acquire + + AO_INLINE unsigned int + AO_int_fetch_compare_and_swap_release(volatile unsigned int *addr, + unsigned int old, unsigned int new_val) + { + unsigned int fetched_val; + __asm__ __volatile__("mov ar.ccv=%3 ;; cmpxchg4.rel %0=[%1],%2,ar.ccv" + : "=r"(fetched_val) + : AO_IN_ADDR, "r"(new_val), "r"((AO_t)old) + : "memory"); + return fetched_val; + } +# define AO_HAVE_int_fetch_compare_and_swap_release +#endif /* !_ILP32 */ + +/* TODO: Add compare_and_swap_double as soon as there is widely */ +/* available hardware that implements it. */ + +#undef AO_IN_ADDR +#undef AO_LEN +#undef AO_MASK +#undef AO_OUT_ADDR +#undef AO_SWIZZLE diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/m68k.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/m68k.h new file mode 100644 index 000000000..27e62f6db --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/m68k.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * 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. + * + */ + +/* The cas instruction causes an emulation trap for the */ +/* 060 with a misaligned pointer, so let's avoid this. */ +#undef AO_t +typedef unsigned long AO_t __attribute__((__aligned__(4))); + +/* FIXME. Very incomplete. */ +#include "../all_aligned_atomic_load_store.h" + +/* Are there any m68k multiprocessors still around? */ +/* AFAIK, Alliants were sequentially consistent. */ +#include "../ordered.h" + +#include "../test_and_set_t_is_char.h" + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr) { + AO_TS_t oldval; + + /* The value at addr is semi-phony. */ + /* 'tas' sets bit 7 while the return */ + /* value pretends all bits were set, */ + /* which at least matches AO_TS_SET. */ + __asm__ __volatile__( + "tas %1; sne %0" + : "=d" (oldval), "=m" (*addr) + : "m" (*addr) + : "memory"); + /* This cast works due to the above. */ + return (AO_TS_VAL_t)oldval; +} +#define AO_HAVE_test_and_set_full + +/* Returns nonzero if the comparison succeeded. */ +AO_INLINE int +AO_compare_and_swap_full(volatile AO_t *addr, + AO_t old, AO_t new_val) +{ + char result; + + __asm__ __volatile__( + "cas.l %3,%4,%1; seq %0" + : "=d" (result), "=m" (*addr) + : "m" (*addr), "d" (old), "d" (new_val) + : "memory"); + return -result; +} +#define AO_HAVE_compare_and_swap_full + +/* TODO: implement AO_fetch_compare_and_swap. */ + +#define AO_T_IS_INT diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/mips.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/mips.h new file mode 100644 index 000000000..94c4e24d8 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/mips.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2005,2007 Thiemo Seufer + * + * 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. + */ + +/* + * FIXME: This should probably make finer distinctions. SGI MIPS is + * much more strongly ordered, and in fact closer to sequentially + * consistent. This is really aimed at modern embedded implementations. + */ + +/* Data dependence does not imply read ordering. */ +#define AO_NO_DD_ORDERING + +/* #include "../standard_ao_double_t.h" */ +/* TODO: Implement double-wide operations if available. */ + +#if (AO_GNUC_PREREQ(4, 9) || AO_CLANG_PREREQ(3, 5)) \ + && !defined(AO_DISABLE_GCC_ATOMICS) + /* Probably, it could be enabled even for earlier gcc/clang versions. */ + + /* As of clang-3.6/mips[64], __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n missing. */ +# if defined(__clang__) +# define AO_GCC_FORCE_HAVE_CAS +# endif + +# include "generic.h" + +#else /* AO_DISABLE_GCC_ATOMICS */ + +# include "../test_and_set_t_is_ao_t.h" +# include "../all_aligned_atomic_load_store.h" + +# if !defined(_ABI64) || _MIPS_SIM != _ABI64 +# define AO_T_IS_INT +# if __mips_isa_rev >= 6 + /* Encoding of ll/sc in mips rel6 differs from that of mips2/3. */ +# define AO_MIPS_SET_ISA "" +# else +# define AO_MIPS_SET_ISA " .set mips2\n" +# endif +# define AO_MIPS_LL_1(args) " ll " args "\n" +# define AO_MIPS_SC(args) " sc " args "\n" +# else +# if __mips_isa_rev >= 6 +# define AO_MIPS_SET_ISA "" +# else +# define AO_MIPS_SET_ISA " .set mips3\n" +# endif +# define AO_MIPS_LL_1(args) " lld " args "\n" +# define AO_MIPS_SC(args) " scd " args "\n" +# endif /* _MIPS_SIM == _ABI64 */ + +#ifdef AO_ICE9A1_LLSC_WAR + /* ICE9 rev A1 chip (used in very few systems) is reported to */ + /* have a low-frequency bug that causes LL to fail. */ + /* To workaround, just issue the second 'LL'. */ +# define AO_MIPS_LL(args) AO_MIPS_LL_1(args) AO_MIPS_LL_1(args) +#else +# define AO_MIPS_LL(args) AO_MIPS_LL_1(args) +#endif + +AO_INLINE void +AO_nop_full(void) +{ + __asm__ __volatile__( + " .set push\n" + AO_MIPS_SET_ISA + " .set noreorder\n" + " .set nomacro\n" + " sync\n" + " .set pop" + : : : "memory"); +} +#define AO_HAVE_nop_full + +#ifndef AO_PREFER_GENERALIZED +AO_INLINE AO_t +AO_fetch_and_add(volatile AO_t *addr, AO_t incr) +{ + register int result; + register int temp; + + __asm__ __volatile__( + " .set push\n" + AO_MIPS_SET_ISA + " .set noreorder\n" + " .set nomacro\n" + "1: " + AO_MIPS_LL("%0, %2") + " addu %1, %0, %3\n" + AO_MIPS_SC("%1, %2") + " beqz %1, 1b\n" + " nop\n" + " .set pop" + : "=&r" (result), "=&r" (temp), "+m" (*addr) + : "Ir" (incr) + : "memory"); + return (AO_t)result; +} +#define AO_HAVE_fetch_and_add + +AO_INLINE AO_TS_VAL_t +AO_test_and_set(volatile AO_TS_t *addr) +{ + register int oldval; + register int temp; + + __asm__ __volatile__( + " .set push\n" + AO_MIPS_SET_ISA + " .set noreorder\n" + " .set nomacro\n" + "1: " + AO_MIPS_LL("%0, %2") + " move %1, %3\n" + AO_MIPS_SC("%1, %2") + " beqz %1, 1b\n" + " nop\n" + " .set pop" + : "=&r" (oldval), "=&r" (temp), "+m" (*addr) + : "r" (1) + : "memory"); + return (AO_TS_VAL_t)oldval; +} +#define AO_HAVE_test_and_set + + /* TODO: Implement AO_and/or/xor primitives directly. */ +#endif /* !AO_PREFER_GENERALIZED */ + +#ifndef AO_GENERALIZE_ASM_BOOL_CAS + AO_INLINE int + AO_compare_and_swap(volatile AO_t *addr, AO_t old, AO_t new_val) + { + register int was_equal = 0; + register int temp; + + __asm__ __volatile__( + " .set push\n" + AO_MIPS_SET_ISA + " .set noreorder\n" + " .set nomacro\n" + "1: " + AO_MIPS_LL("%0, %1") + " bne %0, %4, 2f\n" + " move %0, %3\n" + AO_MIPS_SC("%0, %1") + " .set pop\n" + " beqz %0, 1b\n" + " li %2, 1\n" + "2:" + : "=&r" (temp), "+m" (*addr), "+r" (was_equal) + : "r" (new_val), "r" (old) + : "memory"); + return was_equal; + } +# define AO_HAVE_compare_and_swap +#endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap(volatile AO_t *addr, AO_t old, AO_t new_val) +{ + register int fetched_val; + register int temp; + + __asm__ __volatile__( + " .set push\n" + AO_MIPS_SET_ISA + " .set noreorder\n" + " .set nomacro\n" + "1: " + AO_MIPS_LL("%0, %2") + " bne %0, %4, 2f\n" + " move %1, %3\n" + AO_MIPS_SC("%1, %2") + " beqz %1, 1b\n" + " nop\n" + " .set pop\n" + "2:" + : "=&r" (fetched_val), "=&r" (temp), "+m" (*addr) + : "r" (new_val), "Jr" (old) + : "memory"); + return (AO_t)fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap + +#endif /* AO_DISABLE_GCC_ATOMICS */ + +/* CAS primitives with acquire, release and full semantics are */ +/* generated automatically (and AO_int_... primitives are */ +/* defined properly after the first generalization pass). */ + +#undef AO_GCC_FORCE_HAVE_CAS +#undef AO_MIPS_LL +#undef AO_MIPS_LL_1 +#undef AO_MIPS_SC +#undef AO_MIPS_SET_ISA diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/powerpc.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/powerpc.h new file mode 100644 index 000000000..509dd800c --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/powerpc.h @@ -0,0 +1,348 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * + * 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. + * + */ + +/* Memory model documented at http://www-106.ibm.com/developerworks/ */ +/* eserver/articles/archguide.html and (clearer) */ +/* http://www-106.ibm.com/developerworks/eserver/articles/powerpc.html. */ +/* There appears to be no implicit ordering between any kind of */ +/* independent memory references. */ + +/* TODO: Implement double-wide operations if available. */ + +#if (AO_GNUC_PREREQ(4, 8) || AO_CLANG_PREREQ(3, 8)) \ + && !defined(AO_DISABLE_GCC_ATOMICS) + /* Probably, it could be enabled even for earlier gcc/clang versions. */ + + /* TODO: As of clang-3.8.1, it emits lwsync in AO_load_acquire */ + /* (i.e., the code is less efficient than the one given below). */ + +# include "generic.h" + +#else /* AO_DISABLE_GCC_ATOMICS */ + +/* Architecture enforces some ordering based on control dependence. */ +/* I don't know if that could help. */ +/* Data-dependent loads are always ordered. */ +/* Based on the above references, eieio is intended for use on */ +/* uncached memory, which we don't support. It does not order loads */ +/* from cached memory. */ + +#include "../all_aligned_atomic_load_store.h" + +#include "../test_and_set_t_is_ao_t.h" + /* There seems to be no byte equivalent of lwarx, so this */ + /* may really be what we want, at least in the 32-bit case. */ + +AO_INLINE void +AO_nop_full(void) +{ + __asm__ __volatile__("sync" : : : "memory"); +} +#define AO_HAVE_nop_full + +/* lwsync apparently works for everything but a StoreLoad barrier. */ +AO_INLINE void +AO_lwsync(void) +{ +#ifdef __NO_LWSYNC__ + __asm__ __volatile__("sync" : : : "memory"); +#else + __asm__ __volatile__("lwsync" : : : "memory"); +#endif +} + +#define AO_nop_write() AO_lwsync() +#define AO_HAVE_nop_write + +#define AO_nop_read() AO_lwsync() +#define AO_HAVE_nop_read + +#if defined(__powerpc64__) || defined(__ppc64__) || defined(__64BIT__) + /* ppc64 uses ld not lwz */ +# define AO_PPC_LD "ld" +# define AO_PPC_LxARX "ldarx" +# define AO_PPC_CMPx "cmpd" +# define AO_PPC_STxCXd "stdcx." +# define AO_PPC_LOAD_CLOBBER "cr0" +#else +# define AO_PPC_LD "lwz" +# define AO_PPC_LxARX "lwarx" +# define AO_PPC_CMPx "cmpw" +# define AO_PPC_STxCXd "stwcx." +# define AO_PPC_LOAD_CLOBBER "cc" + /* FIXME: We should get gcc to allocate one of the condition */ + /* registers. I always got "impossible constraint" when I */ + /* tried the "y" constraint. */ +# define AO_T_IS_INT +#endif + +#ifdef _AIX + /* Labels are not supported on AIX. */ + /* ppc64 has same size of instructions as 32-bit one. */ +# define AO_PPC_L(label) /* empty */ +# define AO_PPC_BR_A(labelBF, addr) addr +#else +# define AO_PPC_L(label) label ": " +# define AO_PPC_BR_A(labelBF, addr) labelBF +#endif + +/* We explicitly specify load_acquire, since it is important, and can */ +/* be implemented relatively cheaply. It could be implemented */ +/* with an ordinary load followed by a lwsync. But the general wisdom */ +/* seems to be that a data dependent branch followed by an isync is */ +/* cheaper. And the documentation is fairly explicit that this also */ +/* has acquire semantics. */ +AO_INLINE AO_t +AO_load_acquire(const volatile AO_t *addr) +{ + AO_t result; + + __asm__ __volatile__ ( + AO_PPC_LD "%U1%X1 %0,%1\n" + "cmpw %0,%0\n" + "bne- " AO_PPC_BR_A("1f", "$+4") "\n" + AO_PPC_L("1") "isync\n" + : "=r" (result) + : "m"(*addr) : "memory", AO_PPC_LOAD_CLOBBER); + return result; +} +#define AO_HAVE_load_acquire + +/* We explicitly specify store_release, since it relies */ +/* on the fact that lwsync is also a LoadStore barrier. */ +AO_INLINE void +AO_store_release(volatile AO_t *addr, AO_t value) +{ + AO_lwsync(); + *addr = value; +} +#define AO_HAVE_store_release + +#ifndef AO_PREFER_GENERALIZED +/* This is similar to the code in the garbage collector. Deleting */ +/* this and having it synthesized from compare_and_swap would probably */ +/* only cost us a load immediate instruction. */ +AO_INLINE AO_TS_VAL_t +AO_test_and_set(volatile AO_TS_t *addr) { + /* TODO: And we should be using smaller objects anyway. */ + AO_t oldval; + AO_t temp = 1; /* locked value */ + + __asm__ __volatile__( + AO_PPC_L("1") AO_PPC_LxARX " %0,0,%1\n" + /* load and reserve */ + AO_PPC_CMPx "i %0, 0\n" /* if load is */ + "bne " AO_PPC_BR_A("2f", "$+12") "\n" + /* non-zero, return already set */ + AO_PPC_STxCXd " %2,0,%1\n" /* else store conditional */ + "bne- " AO_PPC_BR_A("1b", "$-16") "\n" + /* retry if lost reservation */ + AO_PPC_L("2") "\n" /* oldval is zero if we set */ + : "=&r"(oldval) + : "r"(addr), "r"(temp) + : "memory", "cr0"); + return (AO_TS_VAL_t)oldval; +} +#define AO_HAVE_test_and_set + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_acquire(volatile AO_TS_t *addr) { + AO_TS_VAL_t result = AO_test_and_set(addr); + AO_lwsync(); + return result; +} +#define AO_HAVE_test_and_set_acquire + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_release(volatile AO_TS_t *addr) { + AO_lwsync(); + return AO_test_and_set(addr); +} +#define AO_HAVE_test_and_set_release + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr) { + AO_TS_VAL_t result; + AO_lwsync(); + result = AO_test_and_set(addr); + AO_lwsync(); + return result; +} +#define AO_HAVE_test_and_set_full +#endif /* !AO_PREFER_GENERALIZED */ + +#ifndef AO_GENERALIZE_ASM_BOOL_CAS + + AO_INLINE int + AO_compare_and_swap(volatile AO_t *addr, AO_t old, AO_t new_val) + { + AO_t oldval; + int result = 0; + + __asm__ __volatile__( + AO_PPC_L("1") AO_PPC_LxARX " %0,0,%2\n" /* load and reserve */ + AO_PPC_CMPx " %0, %4\n" /* if load is not equal to */ + "bne " AO_PPC_BR_A("2f", "$+16") "\n" /* old, fail */ + AO_PPC_STxCXd " %3,0,%2\n" /* else store conditional */ + "bne- " AO_PPC_BR_A("1b", "$-16") "\n" + /* retry if lost reservation */ + "li %1,1\n" /* result = 1; */ + AO_PPC_L("2") "\n" + : "=&r"(oldval), "=&r"(result) + : "r"(addr), "r"(new_val), "r"(old), "1"(result) + : "memory", "cr0"); + return result; + } +# define AO_HAVE_compare_and_swap + + AO_INLINE int + AO_compare_and_swap_acquire(volatile AO_t *addr, AO_t old, AO_t new_val) + { + int result = AO_compare_and_swap(addr, old, new_val); + AO_lwsync(); + return result; + } +# define AO_HAVE_compare_and_swap_acquire + + AO_INLINE int + AO_compare_and_swap_release(volatile AO_t *addr, AO_t old, AO_t new_val) + { + AO_lwsync(); + return AO_compare_and_swap(addr, old, new_val); + } +# define AO_HAVE_compare_and_swap_release + + AO_INLINE int + AO_compare_and_swap_full(volatile AO_t *addr, AO_t old, AO_t new_val) + { + int result; + AO_lwsync(); + result = AO_compare_and_swap(addr, old, new_val); + if (result) + AO_lwsync(); + return result; + } +# define AO_HAVE_compare_and_swap_full + +#endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) +{ + AO_t fetched_val; + + __asm__ __volatile__( + AO_PPC_L("1") AO_PPC_LxARX " %0,0,%1\n" /* load and reserve */ + AO_PPC_CMPx " %0, %3\n" /* if load is not equal to */ + "bne " AO_PPC_BR_A("2f", "$+12") "\n" /* old_val, fail */ + AO_PPC_STxCXd " %2,0,%1\n" /* else store conditional */ + "bne- " AO_PPC_BR_A("1b", "$-16") "\n" + /* retry if lost reservation */ + AO_PPC_L("2") "\n" + : "=&r"(fetched_val) + : "r"(addr), "r"(new_val), "r"(old_val) + : "memory", "cr0"); + return fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap + +AO_INLINE AO_t +AO_fetch_compare_and_swap_acquire(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + AO_t result = AO_fetch_compare_and_swap(addr, old_val, new_val); + AO_lwsync(); + return result; +} +#define AO_HAVE_fetch_compare_and_swap_acquire + +AO_INLINE AO_t +AO_fetch_compare_and_swap_release(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + AO_lwsync(); + return AO_fetch_compare_and_swap(addr, old_val, new_val); +} +#define AO_HAVE_fetch_compare_and_swap_release + +AO_INLINE AO_t +AO_fetch_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + AO_t result; + AO_lwsync(); + result = AO_fetch_compare_and_swap(addr, old_val, new_val); + if (result == old_val) + AO_lwsync(); + return result; +} +#define AO_HAVE_fetch_compare_and_swap_full + +#ifndef AO_PREFER_GENERALIZED +AO_INLINE AO_t +AO_fetch_and_add(volatile AO_t *addr, AO_t incr) { + AO_t oldval; + AO_t newval; + + __asm__ __volatile__( + AO_PPC_L("1") AO_PPC_LxARX " %0,0,%2\n" /* load and reserve */ + "add %1,%0,%3\n" /* increment */ + AO_PPC_STxCXd " %1,0,%2\n" /* store conditional */ + "bne- " AO_PPC_BR_A("1b", "$-12") "\n" + /* retry if lost reservation */ + : "=&r"(oldval), "=&r"(newval) + : "r"(addr), "r"(incr) + : "memory", "cr0"); + return oldval; +} +#define AO_HAVE_fetch_and_add + +AO_INLINE AO_t +AO_fetch_and_add_acquire(volatile AO_t *addr, AO_t incr) { + AO_t result = AO_fetch_and_add(addr, incr); + AO_lwsync(); + return result; +} +#define AO_HAVE_fetch_and_add_acquire + +AO_INLINE AO_t +AO_fetch_and_add_release(volatile AO_t *addr, AO_t incr) { + AO_lwsync(); + return AO_fetch_and_add(addr, incr); +} +#define AO_HAVE_fetch_and_add_release + +AO_INLINE AO_t +AO_fetch_and_add_full(volatile AO_t *addr, AO_t incr) { + AO_t result; + AO_lwsync(); + result = AO_fetch_and_add(addr, incr); + AO_lwsync(); + return result; +} +#define AO_HAVE_fetch_and_add_full +#endif /* !AO_PREFER_GENERALIZED */ + +#undef AO_PPC_BR_A +#undef AO_PPC_CMPx +#undef AO_PPC_L +#undef AO_PPC_LD +#undef AO_PPC_LOAD_CLOBBER +#undef AO_PPC_LxARX +#undef AO_PPC_STxCXd + +#endif /* AO_DISABLE_GCC_ATOMICS */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/riscv.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/riscv.h new file mode 100644 index 000000000..ad6c67e86 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/riscv.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#if defined(__clang__) || defined(AO_PREFER_BUILTIN_ATOMICS) + /* All __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n macros are still missing. */ + /* The operations are lock-free even for the types smaller than word. */ +# define AO_GCC_FORCE_HAVE_CAS +#else + + /* As of gcc-7.5, CAS and arithmetic atomic operations for char and */ + /* short are supported by the compiler but require -latomic flag. */ +# if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) +# define AO_NO_char_ARITHM +# endif +# if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) +# define AO_NO_short_ARITHM +# endif +#endif /* !__clang__ */ + +#include "generic.h" + +#undef AO_GCC_FORCE_HAVE_CAS +#undef AO_NO_char_ARITHM +#undef AO_NO_short_ARITHM diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/s390.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/s390.h new file mode 100644 index 000000000..a302b7ab8 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/s390.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * 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. + * + */ + +#if (AO_GNUC_PREREQ(5, 4) || AO_CLANG_PREREQ(8, 0)) && defined(__s390x__) \ + && !defined(AO_DISABLE_GCC_ATOMICS) + /* Probably, it could be enabled for earlier clang/gcc versions. */ + /* But, e.g., clang-3.8.0 produces a backend error for AtomicFence. */ + +# include "generic.h" + +#else /* AO_DISABLE_GCC_ATOMICS */ + +/* The relevant documentation appears to be at */ +/* http://publibz.boulder.ibm.com/epubs/pdf/dz9zr003.pdf */ +/* around page 5-96. Apparently: */ +/* - Memory references in general are atomic only for a single */ +/* byte. But it appears that the most common load/store */ +/* instructions also guarantee atomicity for aligned */ +/* operands of standard types. WE FOOLISHLY ASSUME that */ +/* compilers only generate those. If that turns out to be */ +/* wrong, we need inline assembly code for AO_load and */ +/* AO_store. */ +/* - A store followed by a load is unordered since the store */ +/* may be delayed. Otherwise everything is ordered. */ +/* - There is a hardware compare-and-swap (CS) instruction. */ + +#include "../all_aligned_atomic_load_store.h" + +#include "../ordered_except_wr.h" + +#include "../test_and_set_t_is_ao_t.h" +/* TODO: Is there a way to do byte-sized test-and-set? */ + +/* TODO: AO_nop_full should probably be implemented directly. */ +/* It appears that certain BCR instructions have that effect. */ +/* Presumably they're cheaper than CS? */ + +#ifndef AO_GENERALIZE_ASM_BOOL_CAS +AO_INLINE int AO_compare_and_swap_full(volatile AO_t *addr, + AO_t old, AO_t new_val) +{ + int retval; + __asm__ __volatile__ ( +# ifndef __s390x__ + " cs %1,%2,0(%3)\n" +# else + " csg %1,%2,0(%3)\n" +# endif + " ipm %0\n" + " srl %0,28\n" + : "=&d" (retval), "+d" (old) + : "d" (new_val), "a" (addr) + : "cc", "memory"); + return retval == 0; +} +#define AO_HAVE_compare_and_swap_full +#endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap_full(volatile AO_t *addr, + AO_t old, AO_t new_val) +{ + __asm__ __volatile__ ( +# ifndef __s390x__ + " cs %0,%2,%1\n" +# else + " csg %0,%2,%1\n" +# endif + : "+d" (old), "=Q" (*addr) + : "d" (new_val), "m" (*addr) + : "cc", "memory"); + return old; +} +#define AO_HAVE_fetch_compare_and_swap_full + +#endif /* AO_DISABLE_GCC_ATOMICS */ + +/* TODO: Add double-wide operations for 32-bit executables. */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/sh.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/sh.h new file mode 100644 index 000000000..8534038cf --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/sh.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009 by Takashi YOSHII. All rights reserved. + * + * + * 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. + */ + +#include "../all_atomic_load_store.h" +#include "../ordered.h" + +/* sh has tas.b(byte) only */ +#include "../test_and_set_t_is_char.h" + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr) +{ + int oldval; + __asm__ __volatile__( + "tas.b @%1; movt %0" + : "=r" (oldval) + : "r" (addr) + : "t", "memory"); + return oldval? AO_TS_CLEAR : AO_TS_SET; +} +#define AO_HAVE_test_and_set_full + +/* TODO: Very incomplete. */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/sparc.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/sparc.h new file mode 100644 index 000000000..3f107d8f4 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/sparc.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * 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. + * + */ + +#if (AO_GNUC_PREREQ(12, 0) || AO_CLANG_PREREQ(13, 0)) \ + && !defined(AO_DISABLE_GCC_ATOMICS) + /* Probably, it could be enabled for earlier compiler versions as well. */ + +# include "generic.h" + +#else /* AO_DISABLE_GCC_ATOMICS */ + +#include "../all_atomic_load_store.h" + +/* Real SPARC code uses TSO: */ +#include "../ordered_except_wr.h" + +/* Test_and_set location is just a byte. */ +#include "../test_and_set_t_is_char.h" + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr) { + AO_TS_VAL_t oldval; + + __asm__ __volatile__("ldstub %1,%0" + : "=r"(oldval), "=m"(*addr) + : "m"(*addr) : "memory"); + return oldval; +} +#define AO_HAVE_test_and_set_full + +#ifndef AO_NO_SPARC_V9 + +# ifndef AO_GENERALIZE_ASM_BOOL_CAS +/* Returns nonzero if the comparison succeeded. */ +AO_INLINE int +AO_compare_and_swap_full(volatile AO_t *addr, AO_t old, AO_t new_val) { + __asm__ __volatile__ ("membar #StoreLoad | #LoadLoad\n\t" +# if defined(__arch64__) + "casx [%1],%2,%0\n\t" +# else + "cas [%1],%2,%0\n\t" /* 32-bit version */ +# endif + "membar #StoreLoad | #StoreStore\n\t" + : "+r" (new_val) + : "r" (addr), "r" (old) + : "memory"); + return new_val == old; +} +# define AO_HAVE_compare_and_swap_full +# endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap_full(volatile AO_t *addr, AO_t old, AO_t new_val) { + __asm__ __volatile__ ("membar #StoreLoad | #LoadLoad\n\t" +# if defined(__arch64__) + "casx [%1],%2,%0\n\t" +# else + "cas [%1],%2,%0\n\t" /* 32-bit version */ +# endif + "membar #StoreLoad | #StoreStore\n\t" + : "+r" (new_val) + : "r" (addr), "r" (old) + : "memory"); + return new_val; +} +#define AO_HAVE_fetch_compare_and_swap_full + +#endif /* !AO_NO_SPARC_V9 */ + +/* TODO: Extend this for SPARC v8 and v9 (V8 also has swap, V9 has CAS, */ +/* there are barriers like membar #LoadStore, CASA (32-bit) and */ +/* CASXA (64-bit) instructions added in V9). */ + +#endif /* AO_DISABLE_GCC_ATOMICS */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/tile.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/tile.h new file mode 100644 index 000000000..6d284b1ae --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/tile.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#if (AO_GNUC_PREREQ(4, 8) || AO_CLANG_PREREQ(3, 4)) \ + && !defined(AO_DISABLE_GCC_ATOMICS) + +# include "generic.h" + +#else /* AO_DISABLE_GCC_ATOMICS */ + + /* Minimal support for tile. */ + +# include "../all_atomic_load_store.h" + +# include "../test_and_set_t_is_ao_t.h" + + AO_INLINE void + AO_nop_full(void) + { + __sync_synchronize(); + } +# define AO_HAVE_nop_full + + AO_INLINE AO_t + AO_fetch_and_add_full(volatile AO_t *p, AO_t incr) + { + return __sync_fetch_and_add(p, incr); + } +# define AO_HAVE_fetch_and_add_full + + AO_INLINE AO_t + AO_fetch_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { + return __sync_val_compare_and_swap(addr, old_val, new_val + /* empty protection list */); + } +# define AO_HAVE_fetch_compare_and_swap_full + +#endif /* AO_DISABLE_GCC_ATOMICS */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/gcc/x86.h b/libatomic_ops/src/atomic_ops/sysdeps/gcc/x86.h new file mode 100644 index 000000000..13f4a7cc5 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/gcc/x86.h @@ -0,0 +1,657 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2018 Ivan Maidanski + * + * 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. + * + * Some of the machine specific code was borrowed from our GC distribution. + */ + +#if (AO_GNUC_PREREQ(4, 8) || AO_CLANG_PREREQ(3, 4)) \ + && !defined(__INTEL_COMPILER) /* TODO: test and enable icc */ \ + && !defined(AO_DISABLE_GCC_ATOMICS) +# define AO_GCC_ATOMIC_TEST_AND_SET + +# if defined(__APPLE_CC__) + /* OS X 10.7 clang-425 lacks __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n */ + /* predefined macro (unlike e.g. OS X 10.11 clang-703). */ +# define AO_GCC_FORCE_HAVE_CAS + +# ifdef __x86_64__ +# if !AO_CLANG_PREREQ(9, 0) /* < Apple clang-900 */ + /* Older Apple clang (e.g., clang-600 based on LLVM 3.5svn) had */ + /* some bug in the double word CAS implementation for x64. */ +# define AO_SKIPATOMIC_double_compare_and_swap_ANY +# endif + +# elif defined(__MACH__) + /* OS X 10.8 lacks __atomic_load/store symbols for arch i386 */ + /* (even with a non-Apple clang). */ +# ifndef MAC_OS_X_VERSION_MIN_REQUIRED + /* Include this header just to import the version macro. */ +# include +# endif +# if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 /* MAC_OS_X_VERSION_10_9 */ +# define AO_SKIPATOMIC_DOUBLE_LOAD_STORE_ANY +# endif +# endif /* __i386__ */ + +# elif defined(__clang__) +# if !defined(__x86_64__) +# if !defined(AO_PREFER_BUILTIN_ATOMICS) && !defined(__CYGWIN__) \ + && !AO_CLANG_PREREQ(5, 0) + /* At least clang-3.8/i686 (from NDK r11c) required to specify */ + /* -latomic in case of a double-word atomic operation use. */ +# define AO_SKIPATOMIC_double_compare_and_swap_ANY +# define AO_SKIPATOMIC_DOUBLE_LOAD_STORE_ANY +# endif /* !AO_PREFER_BUILTIN_ATOMICS */ + +# elif !defined(__ILP32__) +# if (!AO_CLANG_PREREQ(3, 5) && !defined(AO_PREFER_BUILTIN_ATOMICS)) \ + || (!AO_CLANG_PREREQ(4, 0) && defined(AO_ADDRESS_SANITIZER)) \ + || defined(AO_THREAD_SANITIZER) + /* clang-3.4/x64 required -latomic. clang-3.9/x64 seems to */ + /* pass double-wide arguments to atomic operations incorrectly */ + /* in case of ASan/TSan. */ + /* TODO: As of clang-4.0, lock-free test_stack fails if TSan. */ +# define AO_SKIPATOMIC_double_compare_and_swap_ANY +# define AO_SKIPATOMIC_DOUBLE_LOAD_STORE_ANY +# endif +# endif /* __x86_64__ */ + +# elif defined(__x86_64__) && !defined(AO_PREFER_BUILTIN_ATOMICS) \ + && !defined(AO_THREAD_SANITIZER) + /* gcc/x64 (as of gcc-12.2) requires -latomic flag in case */ + /* of double-word atomic operations use (but not in case of TSan). */ + /* TODO: Revise it for the future gcc releases. */ +# define AO_SKIPATOMIC_double_compare_and_swap_ANY +# define AO_SKIPATOMIC_DOUBLE_LOAD_STORE_ANY +# endif /* __x86_64__ && !__clang__ */ + +# ifdef AO_SKIPATOMIC_DOUBLE_LOAD_STORE_ANY +# define AO_SKIPATOMIC_double_load +# define AO_SKIPATOMIC_double_load_acquire +# define AO_SKIPATOMIC_double_store +# define AO_SKIPATOMIC_double_store_release +# undef AO_SKIPATOMIC_DOUBLE_LOAD_STORE_ANY +# endif + +#else /* AO_DISABLE_GCC_ATOMICS */ + +/* The following really assume we have a 486 or better. Unfortunately */ +/* gcc doesn't define a suitable feature test macro based on command */ +/* line options. */ +/* We should perhaps test dynamically. */ + +#include "../all_aligned_atomic_load_store.h" + +#include "../test_and_set_t_is_char.h" + +#if defined(__SSE2__) && !defined(AO_USE_PENTIUM4_INSTRS) + /* "mfence" is a part of SSE2 set (introduced on Intel Pentium 4). */ +# define AO_USE_PENTIUM4_INSTRS +#endif + +#if defined(AO_USE_PENTIUM4_INSTRS) + AO_INLINE void + AO_nop_full(void) + { + __asm__ __volatile__("mfence" : : : "memory"); + } +# define AO_HAVE_nop_full + +#else + /* We could use the cpuid instruction. But that seems to be slower */ + /* than the default implementation based on test_and_set_full. Thus */ + /* we omit that bit of misinformation here. */ +#endif /* !AO_USE_PENTIUM4_INSTRS */ + +/* As far as we can tell, the lfence and sfence instructions are not */ +/* currently needed or useful for cached memory accesses. */ + +/* Really only works for 486 and later */ +#ifndef AO_PREFER_GENERALIZED + AO_INLINE AO_t + AO_fetch_and_add_full (volatile AO_t *p, AO_t incr) + { + AO_t result; + + __asm__ __volatile__ ("lock; xadd %0, %1" + : "=r" (result), "+m" (*p) + : "0" (incr) + : "memory"); + return result; + } +# define AO_HAVE_fetch_and_add_full +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE unsigned char +AO_char_fetch_and_add_full (volatile unsigned char *p, unsigned char incr) +{ + unsigned char result; + + __asm__ __volatile__ ("lock; xaddb %0, %1" + : "=q" (result), "+m" (*p) + : "0" (incr) + : "memory"); + return result; +} +#define AO_HAVE_char_fetch_and_add_full + +AO_INLINE unsigned short +AO_short_fetch_and_add_full (volatile unsigned short *p, unsigned short incr) +{ + unsigned short result; + + __asm__ __volatile__ ("lock; xaddw %0, %1" + : "=r" (result), "+m" (*p) + : "0" (incr) + : "memory"); + return result; +} +#define AO_HAVE_short_fetch_and_add_full + +#ifndef AO_PREFER_GENERALIZED + AO_INLINE void + AO_and_full (volatile AO_t *p, AO_t value) + { + __asm__ __volatile__ ("lock; and %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_and_full + + AO_INLINE void + AO_or_full (volatile AO_t *p, AO_t value) + { + __asm__ __volatile__ ("lock; or %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_or_full + + AO_INLINE void + AO_xor_full (volatile AO_t *p, AO_t value) + { + __asm__ __volatile__ ("lock; xor %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_xor_full + + /* AO_store_full could be implemented directly using "xchg" but it */ + /* could be generalized efficiently as an ordinary store accomplished */ + /* with AO_nop_full ("mfence" instruction). */ + +AO_INLINE void +AO_char_and_full (volatile unsigned char *p, unsigned char value) +{ + __asm__ __volatile__ ("lock; andb %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); +} +#define AO_HAVE_char_and_full + +AO_INLINE void +AO_char_or_full (volatile unsigned char *p, unsigned char value) +{ + __asm__ __volatile__ ("lock; orb %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); +} +#define AO_HAVE_char_or_full + +AO_INLINE void +AO_char_xor_full (volatile unsigned char *p, unsigned char value) +{ + __asm__ __volatile__ ("lock; xorb %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); +} +#define AO_HAVE_char_xor_full + +AO_INLINE void +AO_short_and_full (volatile unsigned short *p, unsigned short value) +{ + __asm__ __volatile__ ("lock; andw %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); +} +#define AO_HAVE_short_and_full + +AO_INLINE void +AO_short_or_full (volatile unsigned short *p, unsigned short value) +{ + __asm__ __volatile__ ("lock; orw %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); +} +#define AO_HAVE_short_or_full + +AO_INLINE void +AO_short_xor_full (volatile unsigned short *p, unsigned short value) +{ + __asm__ __volatile__ ("lock; xorw %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); +} +#define AO_HAVE_short_xor_full +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr) +{ + unsigned char oldval; + /* Note: the "xchg" instruction does not need a "lock" prefix */ + __asm__ __volatile__ ("xchgb %0, %1" + : "=q" (oldval), "+m" (*addr) + : "0" ((unsigned char)0xff) + : "memory"); + return (AO_TS_VAL_t)oldval; +} +#define AO_HAVE_test_and_set_full + +#ifndef AO_GENERALIZE_ASM_BOOL_CAS + /* Returns nonzero if the comparison succeeded. */ + AO_INLINE int + AO_compare_and_swap_full(volatile AO_t *addr, AO_t old, AO_t new_val) + { +# ifdef AO_USE_SYNC_CAS_BUILTIN + return (int)__sync_bool_compare_and_swap(addr, old, new_val + /* empty protection list */); + /* Note: an empty list of variables protected by the */ + /* memory barrier should mean all globally accessible */ + /* variables are protected. */ +# else + char result; +# if defined(__GCC_ASM_FLAG_OUTPUTS__) + AO_t dummy; + + __asm__ __volatile__ ("lock; cmpxchg %3, %0" + : "+m" (*addr), "=@ccz" (result), "=a" (dummy) + : "r" (new_val), "a" (old) + : "memory"); +# else + __asm__ __volatile__ ("lock; cmpxchg %2, %0; setz %1" + : "+m" (*addr), "=a" (result) + : "r" (new_val), "a" (old) + : "memory"); +# endif + return (int)result; +# endif + } +# define AO_HAVE_compare_and_swap_full +#endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ +# ifdef AO_USE_SYNC_CAS_BUILTIN + return __sync_val_compare_and_swap(addr, old_val, new_val + /* empty protection list */); +# else + AO_t fetched_val; + __asm__ __volatile__ ("lock; cmpxchg %3, %1" + : "=a" (fetched_val), "+m" (*addr) + : "a" (old_val), "r" (new_val) + : "memory"); + return fetched_val; +# endif +} +#define AO_HAVE_fetch_compare_and_swap_full + + AO_INLINE unsigned char + AO_char_fetch_compare_and_swap_full(volatile unsigned char *addr, + unsigned char old_val, + unsigned char new_val) + { +# ifdef AO_USE_SYNC_CAS_BUILTIN + return __sync_val_compare_and_swap(addr, old_val, new_val + /* empty protection list */); +# else + unsigned char fetched_val; + + __asm__ __volatile__ ("lock; cmpxchgb %3, %1" + : "=a" (fetched_val), "+m" (*addr) + : "a" (old_val), "q" (new_val) + : "memory"); + return fetched_val; +# endif + } +# define AO_HAVE_char_fetch_compare_and_swap_full + + AO_INLINE unsigned short + AO_short_fetch_compare_and_swap_full(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) + { +# ifdef AO_USE_SYNC_CAS_BUILTIN + return __sync_val_compare_and_swap(addr, old_val, new_val + /* empty protection list */); +# else + unsigned short fetched_val; + + __asm__ __volatile__ ("lock; cmpxchgw %3, %1" + : "=a" (fetched_val), "+m" (*addr) + : "a" (old_val), "r" (new_val) + : "memory"); + return fetched_val; +# endif + } +# define AO_HAVE_short_fetch_compare_and_swap_full + +# if defined(__x86_64__) && !defined(__ILP32__) + AO_INLINE unsigned int + AO_int_fetch_compare_and_swap_full(volatile unsigned int *addr, + unsigned int old_val, + unsigned int new_val) + { +# ifdef AO_USE_SYNC_CAS_BUILTIN + return __sync_val_compare_and_swap(addr, old_val, new_val + /* empty protection list */); +# else + unsigned int fetched_val; + + __asm__ __volatile__ ("lock; cmpxchgl %3, %1" + : "=a" (fetched_val), "+m" (*addr) + : "a" (old_val), "r" (new_val) + : "memory"); + return fetched_val; +# endif + } +# define AO_HAVE_int_fetch_compare_and_swap_full + +# ifndef AO_PREFER_GENERALIZED + AO_INLINE unsigned int + AO_int_fetch_and_add_full (volatile unsigned int *p, unsigned int incr) + { + unsigned int result; + + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "=r" (result), "+m" (*p) + : "0" (incr) + : "memory"); + return result; + } +# define AO_HAVE_int_fetch_and_add_full + + AO_INLINE void + AO_int_and_full (volatile unsigned int *p, unsigned int value) + { + __asm__ __volatile__ ("lock; andl %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_int_and_full + + AO_INLINE void + AO_int_or_full (volatile unsigned int *p, unsigned int value) + { + __asm__ __volatile__ ("lock; orl %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_int_or_full + + AO_INLINE void + AO_int_xor_full (volatile unsigned int *p, unsigned int value) + { + __asm__ __volatile__ ("lock; xorl %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_int_xor_full +# endif /* !AO_PREFER_GENERALIZED */ + +# else +# define AO_T_IS_INT +# endif /* !x86_64 || ILP32 */ + + /* Real X86 implementations, except for some old 32-bit WinChips, */ + /* appear to enforce ordering between memory operations, EXCEPT that */ + /* a later read can pass earlier writes, presumably due to the */ + /* visible presence of store buffers. */ + /* We ignore both the WinChips and the fact that the official specs */ + /* seem to be much weaker (and arguably too weak to be usable). */ +# include "../ordered_except_wr.h" + +#endif /* AO_DISABLE_GCC_ATOMICS */ + +#if defined(AO_GCC_ATOMIC_TEST_AND_SET) \ + && !defined(AO_SKIPATOMIC_double_compare_and_swap_ANY) + +# if defined(__ILP32__) || !defined(__x86_64__) /* 32-bit AO_t */ \ + || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) /* 64-bit AO_t */ +# include "../standard_ao_double_t.h" +# endif + +#elif !defined(__x86_64__) && (!defined(AO_USE_SYNC_CAS_BUILTIN) \ + || defined(AO_GCC_ATOMIC_TEST_AND_SET)) +# include "../standard_ao_double_t.h" + + /* Reading or writing a quadword aligned on a 64-bit boundary is */ + /* always carried out atomically on at least a Pentium according to */ + /* Chapter 8.1.1 of Volume 3A Part 1 of Intel processor manuals. */ +# ifndef AO_PREFER_GENERALIZED +# define AO_ACCESS_double_CHECK_ALIGNED +# include "../loadstore/double_atomic_load_store.h" +# endif + + /* Returns nonzero if the comparison succeeded. */ + /* Really requires at least a Pentium. */ + AO_INLINE int + AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + char result; +# if defined(__PIC__) && !(AO_GNUC_PREREQ(5, 1) || AO_CLANG_PREREQ(4, 0)) + AO_t saved_ebx; + AO_t dummy; + + /* The following applies to an ancient GCC (and, probably, it was */ + /* never needed for Clang): */ + /* If PIC is turned on, we cannot use ebx as it is reserved for the */ + /* GOT pointer. We should save and restore ebx. The proposed */ + /* solution is not so efficient as the older alternatives using */ + /* push ebx or edi as new_val1 (w/o clobbering edi and temporary */ + /* local variable usage) but it is more portable (it works even if */ + /* ebx is not used as GOT pointer, and it works for the buggy GCC */ + /* releases that incorrectly evaluate memory operands offset in the */ + /* inline assembly after push). */ +# ifdef __OPTIMIZE__ + __asm__ __volatile__("mov %%ebx, %2\n\t" /* save ebx */ + "lea %0, %%edi\n\t" /* in case addr is in ebx */ + "mov %7, %%ebx\n\t" /* load new_val1 */ + "lock; cmpxchg8b (%%edi)\n\t" + "mov %2, %%ebx\n\t" /* restore ebx */ + "setz %1" + : "+m" (*addr), "=a" (result), + "=m" (saved_ebx), "=d" (dummy) + : "d" (old_val2), "a" (old_val1), + "c" (new_val2), "m" (new_val1) + : "%edi", "memory"); +# else + /* A less-efficient code manually preserving edi if GCC invoked */ + /* with -O0 option (otherwise it fails while finding a register */ + /* in class 'GENERAL_REGS'). */ + AO_t saved_edi; + __asm__ __volatile__("mov %%edi, %3\n\t" /* save edi */ + "mov %%ebx, %2\n\t" /* save ebx */ + "lea %0, %%edi\n\t" /* in case addr is in ebx */ + "mov %8, %%ebx\n\t" /* load new_val1 */ + "lock; cmpxchg8b (%%edi)\n\t" + "mov %2, %%ebx\n\t" /* restore ebx */ + "mov %3, %%edi\n\t" /* restore edi */ + "setz %1" + : "+m" (*addr), "=a" (result), + "=m" (saved_ebx), "=m" (saved_edi), "=d" (dummy) + : "d" (old_val2), "a" (old_val1), + "c" (new_val2), "m" (new_val1) + : "memory"); +# endif +# else + /* For non-PIC mode, this operation could be simplified (and be */ + /* faster) by using ebx as new_val1. Reuse of the PIC hard */ + /* register, instead of using a fixed register, is implemented */ + /* in Clang and GCC 5.1+, at least. (Older GCC refused to compile */ + /* such code for PIC mode). */ +# if defined(__GCC_ASM_FLAG_OUTPUTS__) + __asm__ __volatile__ ("lock; cmpxchg8b %0" + : "+m" (*addr), "=@ccz" (result), + "+d" (old_val2), "+a" (old_val1) + : "c" (new_val2), "b" (new_val1) + : "memory"); +# else + AO_t dummy; /* an output for clobbered edx */ + + __asm__ __volatile__ ("lock; cmpxchg8b %0; setz %1" + : "+m" (*addr), "=a" (result), "=d" (dummy) + : "d" (old_val2), "a" (old_val1), + "c" (new_val2), "b" (new_val1) + : "memory"); +# endif +# endif + return (int) result; + } +# define AO_HAVE_compare_double_and_swap_double_full + +#elif defined(__ILP32__) || !defined(__x86_64__) +# include "../standard_ao_double_t.h" + + /* Reading or writing a quadword aligned on a 64-bit boundary is */ + /* always carried out atomically (requires at least a Pentium). */ +# ifndef AO_PREFER_GENERALIZED +# define AO_ACCESS_double_CHECK_ALIGNED +# include "../loadstore/double_atomic_load_store.h" +# endif + + /* X32 has native support for 64-bit integer operations (AO_double_t */ + /* is a 64-bit integer and we could use 64-bit cmpxchg). */ + /* This primitive is used by compare_double_and_swap_double_full. */ + AO_INLINE int + AO_double_compare_and_swap_full(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + /* It is safe to use __sync CAS built-in here. */ + return __sync_bool_compare_and_swap(&addr->AO_whole, + old_val.AO_whole, new_val.AO_whole + /* empty protection list */); + } +# define AO_HAVE_double_compare_and_swap_full + +#elif defined(AO_CMPXCHG16B_AVAILABLE) \ + || (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) \ + && !defined(AO_THREAD_SANITIZER)) +# include "../standard_ao_double_t.h" + + /* The Intel and AMD Architecture Programmer Manuals state roughly */ + /* the following: */ + /* - CMPXCHG16B (with a LOCK prefix) can be used to perform 16-byte */ + /* atomic accesses in 64-bit mode (with certain alignment */ + /* restrictions); */ + /* - SSE instructions that access data larger than a quadword (like */ + /* MOVDQA) may be implemented using multiple memory accesses; */ + /* - LOCK prefix causes an invalid-opcode exception when used with */ + /* 128-bit media (SSE) instructions. */ + /* Thus, currently, the only way to implement lock-free double_load */ + /* and double_store on x86_64 is to use CMPXCHG16B (if available). */ + + /* NEC LE-IT: older AMD Opterons are missing this instruction. */ + /* On these machines SIGILL will be thrown. */ + /* Define AO_WEAK_DOUBLE_CAS_EMULATION to have an emulated (lock */ + /* based) version available. */ + /* HB: Changed this to not define either by default. There are */ + /* enough machines and tool chains around on which cmpxchg16b */ + /* doesn't work. And the emulation is unsafe by our usual rules. */ + /* However both are clearly useful in certain cases. */ + + AO_INLINE int + AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + char result; + +# if defined(__GCC_ASM_FLAG_OUTPUTS__) + __asm__ __volatile__("lock; cmpxchg16b %0" + : "+m" (*addr), "=@ccz" (result), + "+d" (old_val2), "+a" (old_val1) + : "c" (new_val2), "b" (new_val1) + : "memory"); +# else + AO_t dummy; /* an output for clobbered rdx */ + + __asm__ __volatile__("lock; cmpxchg16b %0; setz %1" + : "+m" (*addr), "=a" (result), "=d" (dummy) + : "d" (old_val2), "a" (old_val1), + "c" (new_val2), "b" (new_val1) + : "memory"); +# endif + return (int) result; + } +# define AO_HAVE_compare_double_and_swap_double_full + +#elif defined(AO_WEAK_DOUBLE_CAS_EMULATION) +# include "../standard_ao_double_t.h" + +# ifdef __cplusplus + extern "C" { +# endif + + /* This one provides spinlock based emulation of CAS implemented in */ + /* atomic_ops.c. We probably do not want to do this here, since it */ + /* is not atomic with respect to other kinds of updates of *addr. */ + /* On the other hand, this may be a useful facility on occasion. */ + int AO_compare_double_and_swap_double_emulation( + volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2); + +# ifdef __cplusplus + } /* extern "C" */ +# endif + + AO_INLINE int + AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + return AO_compare_double_and_swap_double_emulation(addr, + old_val1, old_val2, new_val1, new_val2); + } +# define AO_HAVE_compare_double_and_swap_double_full +#endif /* x86_64 && !ILP32 && CAS_EMULATION && !AO_CMPXCHG16B_AVAILABLE */ + +#ifdef AO_GCC_ATOMIC_TEST_AND_SET +# include "generic.h" +#endif + +#undef AO_GCC_FORCE_HAVE_CAS +#undef AO_SKIPATOMIC_double_compare_and_swap_ANY +#undef AO_SKIPATOMIC_double_load +#undef AO_SKIPATOMIC_double_load_acquire +#undef AO_SKIPATOMIC_double_store +#undef AO_SKIPATOMIC_double_store_release diff --git a/libatomic_ops/src/atomic_ops/sysdeps/generic_pthread.h b/libatomic_ops/src/atomic_ops/sysdeps/generic_pthread.h new file mode 100644 index 000000000..724b148b0 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/generic_pthread.h @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* The following is useful primarily for debugging and documentation. */ +/* We define various atomic operations by acquiring a global pthread */ +/* lock. The resulting implementation will perform poorly, but should */ +/* be correct unless it is used from signal handlers. */ +/* We assume that all pthread operations act like full memory barriers. */ +/* (We believe that is the intent of the specification.) */ + +#include + +#include "test_and_set_t_is_ao_t.h" + /* This is not necessarily compatible with the native */ + /* implementation. But those can't be safely mixed anyway. */ + +#ifdef __cplusplus + extern "C" { +#endif + +/* We define only the full barrier variants, and count on the */ +/* generalization section below to fill in the rest. */ +AO_API pthread_mutex_t AO_pt_lock; + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +AO_INLINE void +AO_nop_full(void) +{ + pthread_mutex_lock(&AO_pt_lock); + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_nop_full + +AO_INLINE AO_t +AO_load_full(const volatile AO_t *addr) +{ + AO_t result; + pthread_mutex_lock(&AO_pt_lock); + result = *addr; + pthread_mutex_unlock(&AO_pt_lock); + return result; +} +#define AO_HAVE_load_full + +AO_INLINE void +AO_store_full(volatile AO_t *addr, AO_t val) +{ + pthread_mutex_lock(&AO_pt_lock); + *addr = val; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_store_full + +AO_INLINE unsigned char +AO_char_load_full(const volatile unsigned char *addr) +{ + unsigned char result; + pthread_mutex_lock(&AO_pt_lock); + result = *addr; + pthread_mutex_unlock(&AO_pt_lock); + return result; +} +#define AO_HAVE_char_load_full + +AO_INLINE void +AO_char_store_full(volatile unsigned char *addr, unsigned char val) +{ + pthread_mutex_lock(&AO_pt_lock); + *addr = val; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_char_store_full + +AO_INLINE unsigned short +AO_short_load_full(const volatile unsigned short *addr) +{ + unsigned short result; + pthread_mutex_lock(&AO_pt_lock); + result = *addr; + pthread_mutex_unlock(&AO_pt_lock); + return result; +} +#define AO_HAVE_short_load_full + +AO_INLINE void +AO_short_store_full(volatile unsigned short *addr, unsigned short val) +{ + pthread_mutex_lock(&AO_pt_lock); + *addr = val; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_short_store_full + +AO_INLINE unsigned int +AO_int_load_full(const volatile unsigned int *addr) +{ + unsigned int result; + pthread_mutex_lock(&AO_pt_lock); + result = *addr; + pthread_mutex_unlock(&AO_pt_lock); + return result; +} +#define AO_HAVE_int_load_full + +AO_INLINE void +AO_int_store_full(volatile unsigned int *addr, unsigned int val) +{ + pthread_mutex_lock(&AO_pt_lock); + *addr = val; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_int_store_full + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr) +{ + AO_TS_VAL_t result; + pthread_mutex_lock(&AO_pt_lock); + result = (AO_TS_VAL_t)(*addr); + *addr = AO_TS_SET; + pthread_mutex_unlock(&AO_pt_lock); + assert(result == AO_TS_SET || result == AO_TS_CLEAR); + return result; +} +#define AO_HAVE_test_and_set_full + +AO_INLINE AO_t +AO_fetch_and_add_full(volatile AO_t *p, AO_t incr) +{ + AO_t old_val; + + pthread_mutex_lock(&AO_pt_lock); + old_val = *p; + *p = old_val + incr; + pthread_mutex_unlock(&AO_pt_lock); + return old_val; +} +#define AO_HAVE_fetch_and_add_full + +AO_INLINE unsigned char +AO_char_fetch_and_add_full(volatile unsigned char *p, unsigned char incr) +{ + unsigned char old_val; + + pthread_mutex_lock(&AO_pt_lock); + old_val = *p; + *p = old_val + incr; + pthread_mutex_unlock(&AO_pt_lock); + return old_val; +} +#define AO_HAVE_char_fetch_and_add_full + +AO_INLINE unsigned short +AO_short_fetch_and_add_full(volatile unsigned short *p, unsigned short incr) +{ + unsigned short old_val; + + pthread_mutex_lock(&AO_pt_lock); + old_val = *p; + *p = old_val + incr; + pthread_mutex_unlock(&AO_pt_lock); + return old_val; +} +#define AO_HAVE_short_fetch_and_add_full + +AO_INLINE unsigned int +AO_int_fetch_and_add_full(volatile unsigned int *p, unsigned int incr) +{ + unsigned int old_val; + + pthread_mutex_lock(&AO_pt_lock); + old_val = *p; + *p = old_val + incr; + pthread_mutex_unlock(&AO_pt_lock); + return old_val; +} +#define AO_HAVE_int_fetch_and_add_full + +AO_INLINE void +AO_and_full(volatile AO_t *p, AO_t value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p &= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_and_full + +AO_INLINE void +AO_or_full(volatile AO_t *p, AO_t value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p |= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_or_full + +AO_INLINE void +AO_xor_full(volatile AO_t *p, AO_t value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p ^= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_xor_full + +AO_INLINE void +AO_char_and_full(volatile unsigned char *p, unsigned char value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p &= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_char_and_full + +AO_INLINE void +AO_char_or_full(volatile unsigned char *p, unsigned char value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p |= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_char_or_full + +AO_INLINE void +AO_char_xor_full(volatile unsigned char *p, unsigned char value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p ^= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_char_xor_full + +AO_INLINE void +AO_short_and_full(volatile unsigned short *p, unsigned short value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p &= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_short_and_full + +AO_INLINE void +AO_short_or_full(volatile unsigned short *p, unsigned short value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p |= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_short_or_full + +AO_INLINE void +AO_short_xor_full(volatile unsigned short *p, unsigned short value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p ^= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_short_xor_full + +AO_INLINE void +AO_int_and_full(volatile unsigned *p, unsigned value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p &= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_int_and_full + +AO_INLINE void +AO_int_or_full(volatile unsigned *p, unsigned value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p |= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_int_or_full + +AO_INLINE void +AO_int_xor_full(volatile unsigned *p, unsigned value) +{ + pthread_mutex_lock(&AO_pt_lock); + *p ^= value; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_int_xor_full + +AO_INLINE AO_t +AO_fetch_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + AO_t fetched_val; + + pthread_mutex_lock(&AO_pt_lock); + fetched_val = *addr; + if (fetched_val == old_val) + *addr = new_val; + pthread_mutex_unlock(&AO_pt_lock); + return fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap_full + +AO_INLINE unsigned char +AO_char_fetch_compare_and_swap_full(volatile unsigned char *addr, + unsigned char old_val, + unsigned char new_val) +{ + unsigned char fetched_val; + + pthread_mutex_lock(&AO_pt_lock); + fetched_val = *addr; + if (fetched_val == old_val) + *addr = new_val; + pthread_mutex_unlock(&AO_pt_lock); + return fetched_val; +} +#define AO_HAVE_char_fetch_compare_and_swap_full + +AO_INLINE unsigned short +AO_short_fetch_compare_and_swap_full(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) +{ + unsigned short fetched_val; + + pthread_mutex_lock(&AO_pt_lock); + fetched_val = *addr; + if (fetched_val == old_val) + *addr = new_val; + pthread_mutex_unlock(&AO_pt_lock); + return fetched_val; +} +#define AO_HAVE_short_fetch_compare_and_swap_full + +AO_INLINE unsigned +AO_int_fetch_compare_and_swap_full(volatile unsigned *addr, unsigned old_val, + unsigned new_val) +{ + unsigned fetched_val; + + pthread_mutex_lock(&AO_pt_lock); + fetched_val = *addr; + if (fetched_val == old_val) + *addr = new_val; + pthread_mutex_unlock(&AO_pt_lock); + return fetched_val; +} +#define AO_HAVE_int_fetch_compare_and_swap_full + +/* Unlike real architectures, we define both double-width CAS variants. */ + +typedef struct { + AO_t AO_val1; + AO_t AO_val2; +} AO_double_t; +#define AO_HAVE_double_t + +#define AO_DOUBLE_T_INITIALIZER { (AO_t)0, (AO_t)0 } + +AO_INLINE AO_double_t +AO_double_load_full(const volatile AO_double_t *addr) +{ + AO_double_t result; + + pthread_mutex_lock(&AO_pt_lock); + result.AO_val1 = addr->AO_val1; + result.AO_val2 = addr->AO_val2; + pthread_mutex_unlock(&AO_pt_lock); + return result; +} +#define AO_HAVE_double_load_full + +AO_INLINE void +AO_double_store_full(volatile AO_double_t *addr, AO_double_t value) +{ + pthread_mutex_lock(&AO_pt_lock); + addr->AO_val1 = value.AO_val1; + addr->AO_val2 = value.AO_val2; + pthread_mutex_unlock(&AO_pt_lock); +} +#define AO_HAVE_double_store_full + +AO_INLINE int +AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old1, AO_t old2, + AO_t new1, AO_t new2) +{ + pthread_mutex_lock(&AO_pt_lock); + if (addr -> AO_val1 == old1 && addr -> AO_val2 == old2) + { + addr -> AO_val1 = new1; + addr -> AO_val2 = new2; + pthread_mutex_unlock(&AO_pt_lock); + return 1; + } + else + pthread_mutex_unlock(&AO_pt_lock); + return 0; +} +#define AO_HAVE_compare_double_and_swap_double_full + +AO_INLINE int +AO_compare_and_swap_double_full(volatile AO_double_t *addr, + AO_t old1, AO_t new1, AO_t new2) +{ + pthread_mutex_lock(&AO_pt_lock); + if (addr -> AO_val1 == old1) + { + addr -> AO_val1 = new1; + addr -> AO_val2 = new2; + pthread_mutex_unlock(&AO_pt_lock); + return 1; + } + else + pthread_mutex_unlock(&AO_pt_lock); + return 0; +} +#define AO_HAVE_compare_and_swap_double_full + +/* We can't use hardware loads and stores, since they don't */ +/* interact correctly with atomic updates. */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/hpc/hppa.h b/libatomic_ops/src/atomic_ops/sysdeps/hpc/hppa.h new file mode 100644 index 000000000..f0da9480d --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/hpc/hppa.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Derived from the corresponding header file for gcc. + */ + +#include "../loadstore/atomic_load.h" +#include "../loadstore/atomic_store.h" + +/* Some architecture set descriptions include special "ordered" memory */ +/* operations. As far as we can tell, no existing processors actually */ +/* require those. Nor does it appear likely that future processors */ +/* will. */ +/* FIXME: The PA emulator on Itanium may obey weaker restrictions. */ +/* There should be a mode in which we don't assume sequential */ +/* consistency here. */ +#include "../ordered.h" + +#include + +/* GCC will not guarantee the alignment we need, use four lock words */ +/* and select the correctly aligned datum. See the glibc 2.3.2 */ +/* linuxthread port for the original implementation. */ +struct AO_pa_clearable_loc { + int data[4]; +}; + +#undef AO_TS_INITIALIZER +#define AO_TS_t struct AO_pa_clearable_loc +#define AO_TS_INITIALIZER {1,1,1,1} +/* Switch meaning of set and clear, since we only have an atomic clear */ +/* instruction. */ +typedef enum {AO_PA_TS_set = 0, AO_PA_TS_clear = 1} AO_PA_TS_val; +#define AO_TS_VAL_t AO_PA_TS_val +#define AO_TS_CLEAR AO_PA_TS_clear +#define AO_TS_SET AO_PA_TS_set + +/* The hppa only has one atomic read and modify memory operation, */ +/* load and clear, so hppa spinlocks must use zero to signify that */ +/* someone is holding the lock. The address used for the ldcw */ +/* semaphore must be 16-byte aligned. */ +#define AO_ldcw(a, ret) \ + _LDCWX(0 /* index */, 0 /* s */, a /* base */, ret) + +/* Because malloc only guarantees 8-byte alignment for malloc'd data, */ +/* and GCC only guarantees 8-byte alignment for stack locals, we can't */ +/* be assured of 16-byte alignment for atomic lock data even if we */ +/* specify "__attribute ((aligned(16)))" in the type declaration. So, */ +/* we use a struct containing an array of four ints for the atomic lock */ +/* type and dynamically select the 16-byte aligned int from the array */ +/* for the semaphore. */ +#define AO_PA_LDCW_ALIGNMENT 16 +#define AO_ldcw_align(addr) \ + ((volatile unsigned *)(((unsigned long)(addr) \ + + (AO_PA_LDCW_ALIGNMENT - 1)) \ + & ~(AO_PA_LDCW_ALIGNMENT - 1))) + +/* Works on PA 1.1 and PA 2.0 systems */ +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t * addr) +{ + register unsigned int ret; + register unsigned long a = (unsigned long)AO_ldcw_align(addr); + +# if defined(CPPCHECK) + ret = 0; /* to void 'uninitialized variable' warning */ +# endif + AO_ldcw(a, ret); + return (AO_TS_VAL_t)ret; +} +#define AO_HAVE_test_and_set_full + +AO_INLINE void +AO_pa_clear(volatile AO_TS_t * addr) +{ + volatile unsigned *a = AO_ldcw_align(addr); + + AO_compiler_barrier(); + *a = 1; +} +#define AO_CLEAR(addr) AO_pa_clear(addr) +#define AO_HAVE_CLEAR + +#undef AO_PA_LDCW_ALIGNMENT +#undef AO_ldcw +#undef AO_ldcw_align diff --git a/libatomic_ops/src/atomic_ops/sysdeps/hpc/ia64.h b/libatomic_ops/src/atomic_ops/sysdeps/hpc/ia64.h new file mode 100644 index 000000000..061e55b3e --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/hpc/ia64.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This file specifies Itanimum primitives for use with the HP compiler + * under HP/UX. We use intrinsics instead of the inline assembly code in the + * gcc file. + */ + +#include "../all_atomic_load_store.h" + +#include "../all_acquire_release_volatile.h" + +#include "../test_and_set_t_is_char.h" + +#include + +#ifdef __LP64__ +# define AO_T_FASIZE _FASZ_D +# define AO_T_SIZE _SZ_D +#else +# define AO_T_FASIZE _FASZ_W +# define AO_T_SIZE _SZ_W +#endif + +AO_INLINE void +AO_nop_full(void) +{ + _Asm_mf(); +} +#define AO_HAVE_nop_full + +#ifndef AO_PREFER_GENERALIZED +AO_INLINE AO_t +AO_fetch_and_add1_acquire (volatile AO_t *p) +{ + return _Asm_fetchadd(AO_T_FASIZE, _SEM_ACQ, p, 1, + _LDHINT_NONE, _DOWN_MEM_FENCE); +} +#define AO_HAVE_fetch_and_add1_acquire + +AO_INLINE AO_t +AO_fetch_and_add1_release (volatile AO_t *p) +{ + return _Asm_fetchadd(AO_T_FASIZE, _SEM_REL, p, 1, + _LDHINT_NONE, _UP_MEM_FENCE); +} +#define AO_HAVE_fetch_and_add1_release + +AO_INLINE AO_t +AO_fetch_and_sub1_acquire (volatile AO_t *p) +{ + return _Asm_fetchadd(AO_T_FASIZE, _SEM_ACQ, p, -1, + _LDHINT_NONE, _DOWN_MEM_FENCE); +} +#define AO_HAVE_fetch_and_sub1_acquire + +AO_INLINE AO_t +AO_fetch_and_sub1_release (volatile AO_t *p) +{ + return _Asm_fetchadd(AO_T_FASIZE, _SEM_REL, p, -1, + _LDHINT_NONE, _UP_MEM_FENCE); +} +#define AO_HAVE_fetch_and_sub1_release +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap_acquire(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + _Asm_mov_to_ar(_AREG_CCV, old_val, _DOWN_MEM_FENCE); + return _Asm_cmpxchg(AO_T_SIZE, _SEM_ACQ, addr, + new_val, _LDHINT_NONE, _DOWN_MEM_FENCE); +} +#define AO_HAVE_fetch_compare_and_swap_acquire + +AO_INLINE AO_t +AO_fetch_compare_and_swap_release(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + _Asm_mov_to_ar(_AREG_CCV, old_val, _UP_MEM_FENCE); + return _Asm_cmpxchg(AO_T_SIZE, _SEM_REL, addr, + new_val, _LDHINT_NONE, _UP_MEM_FENCE); +} +#define AO_HAVE_fetch_compare_and_swap_release + +AO_INLINE unsigned char +AO_char_fetch_compare_and_swap_acquire(volatile unsigned char *addr, + unsigned char old_val, unsigned char new_val) +{ + _Asm_mov_to_ar(_AREG_CCV, old_val, _DOWN_MEM_FENCE); + return _Asm_cmpxchg(_SZ_B, _SEM_ACQ, addr, + new_val, _LDHINT_NONE, _DOWN_MEM_FENCE); +} +#define AO_HAVE_char_fetch_compare_and_swap_acquire + +AO_INLINE unsigned char +AO_char_fetch_compare_and_swap_release(volatile unsigned char *addr, + unsigned char old_val, unsigned char new_val) +{ + _Asm_mov_to_ar(_AREG_CCV, old_val, _UP_MEM_FENCE); + return _Asm_cmpxchg(_SZ_B, _SEM_REL, addr, + new_val, _LDHINT_NONE, _UP_MEM_FENCE); +} +#define AO_HAVE_char_fetch_compare_and_swap_release + +AO_INLINE unsigned short +AO_short_fetch_compare_and_swap_acquire(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) +{ + _Asm_mov_to_ar(_AREG_CCV, old_val, _DOWN_MEM_FENCE); + return _Asm_cmpxchg(_SZ_B, _SEM_ACQ, addr, + new_val, _LDHINT_NONE, _DOWN_MEM_FENCE); +} +#define AO_HAVE_short_fetch_compare_and_swap_acquire + +AO_INLINE unsigned short +AO_short_fetch_compare_and_swap_release(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) +{ + _Asm_mov_to_ar(_AREG_CCV, old_val, _UP_MEM_FENCE); + return _Asm_cmpxchg(_SZ_B, _SEM_REL, addr, + new_val, _LDHINT_NONE, _UP_MEM_FENCE); +} +#define AO_HAVE_short_fetch_compare_and_swap_release + +#ifndef __LP64__ +# define AO_T_IS_INT +#endif + +#undef AO_T_FASIZE +#undef AO_T_SIZE diff --git a/libatomic_ops/src/atomic_ops/sysdeps/ibmc/powerpc.h b/libatomic_ops/src/atomic_ops/sysdeps/ibmc/powerpc.h new file mode 100644 index 000000000..a930b1679 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/ibmc/powerpc.h @@ -0,0 +1,183 @@ + +/* Memory model documented at http://www-106.ibm.com/developerworks/ */ +/* eserver/articles/archguide.html and (clearer) */ +/* http://www-106.ibm.com/developerworks/eserver/articles/powerpc.html. */ +/* There appears to be no implicit ordering between any kind of */ +/* independent memory references. */ +/* Architecture enforces some ordering based on control dependence. */ +/* I don't know if that could help. */ +/* Data-dependent loads are always ordered. */ +/* Based on the above references, eieio is intended for use on */ +/* uncached memory, which we don't support. It does not order loads */ +/* from cached memory. */ +/* Thanks to Maged Michael, Doug Lea, and Roger Hoover for helping to */ +/* track some of this down and correcting my misunderstandings. -HB */ + +#include "../all_aligned_atomic_load_store.h" + +#include "../test_and_set_t_is_ao_t.h" + +void AO_sync(void); +#pragma mc_func AO_sync { "7c0004ac" } + +#ifdef __NO_LWSYNC__ +# define AO_lwsync AO_sync +#else + void AO_lwsync(void); +#pragma mc_func AO_lwsync { "7c2004ac" } +#endif + +#define AO_nop_write() AO_lwsync() +#define AO_HAVE_nop_write + +#define AO_nop_read() AO_lwsync() +#define AO_HAVE_nop_read + +/* We explicitly specify load_acquire and store_release, since these */ +/* rely on the fact that lwsync is also a LoadStore barrier. */ +AO_INLINE AO_t +AO_load_acquire(const volatile AO_t *addr) +{ + AO_t result = *addr; + AO_lwsync(); + return result; +} +#define AO_HAVE_load_acquire + +AO_INLINE void +AO_store_release(volatile AO_t *addr, AO_t value) +{ + AO_lwsync(); + *addr = value; +} +#define AO_HAVE_store_release + +#ifndef AO_PREFER_GENERALIZED +/* This is similar to the code in the garbage collector. Deleting */ +/* this and having it synthesized from compare_and_swap would probably */ +/* only cost us a load immediate instruction. */ +AO_INLINE AO_TS_VAL_t +AO_test_and_set(volatile AO_TS_t *addr) { +#if defined(__powerpc64__) || defined(__ppc64__) || defined(__64BIT__) +/* Completely untested. And we should be using smaller objects anyway. */ + unsigned long oldval; + unsigned long temp = 1; /* locked value */ + + __asm__ __volatile__( + "1:ldarx %0,0,%1\n" /* load and reserve */ + "cmpdi %0, 0\n" /* if load is */ + "bne 2f\n" /* non-zero, return already set */ + "stdcx. %2,0,%1\n" /* else store conditional */ + "bne- 1b\n" /* retry if lost reservation */ + "2:\n" /* oldval is zero if we set */ + : "=&r"(oldval) + : "r"(addr), "r"(temp) + : "memory", "cr0"); +#else + int oldval; + int temp = 1; /* locked value */ + + __asm__ __volatile__( + "1:lwarx %0,0,%1\n" /* load and reserve */ + "cmpwi %0, 0\n" /* if load is */ + "bne 2f\n" /* non-zero, return already set */ + "stwcx. %2,0,%1\n" /* else store conditional */ + "bne- 1b\n" /* retry if lost reservation */ + "2:\n" /* oldval is zero if we set */ + : "=&r"(oldval) + : "r"(addr), "r"(temp) + : "memory", "cr0"); +#endif + return (AO_TS_VAL_t)oldval; +} +#define AO_HAVE_test_and_set + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_acquire(volatile AO_TS_t *addr) { + AO_TS_VAL_t result = AO_test_and_set(addr); + AO_lwsync(); + return result; +} +#define AO_HAVE_test_and_set_acquire + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_release(volatile AO_TS_t *addr) { + AO_lwsync(); + return AO_test_and_set(addr); +} +#define AO_HAVE_test_and_set_release + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr) { + AO_TS_VAL_t result; + AO_lwsync(); + result = AO_test_and_set(addr); + AO_lwsync(); + return result; +} +#define AO_HAVE_test_and_set_full +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) +{ + AO_t fetched_val; +# if defined(__powerpc64__) || defined(__ppc64__) || defined(__64BIT__) + __asm__ __volatile__( + "1:ldarx %0,0,%1\n" /* load and reserve */ + "cmpd %0, %3\n" /* if load is not equal to */ + "bne 2f\n" /* old_val, fail */ + "stdcx. %2,0,%1\n" /* else store conditional */ + "bne- 1b\n" /* retry if lost reservation */ + "2:\n" + : "=&r"(fetched_val) + : "r"(addr), "r"(new_val), "r"(old_val) + : "memory", "cr0"); +# else + __asm__ __volatile__( + "1:lwarx %0,0,%1\n" /* load and reserve */ + "cmpw %0, %3\n" /* if load is not equal to */ + "bne 2f\n" /* old_val, fail */ + "stwcx. %2,0,%1\n" /* else store conditional */ + "bne- 1b\n" /* retry if lost reservation */ + "2:\n" + : "=&r"(fetched_val) + : "r"(addr), "r"(new_val), "r"(old_val) + : "memory", "cr0"); +# endif + return fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap + +AO_INLINE AO_t +AO_fetch_compare_and_swap_acquire(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + AO_t result = AO_fetch_compare_and_swap(addr, old_val, new_val); + AO_lwsync(); + return result; +} +#define AO_HAVE_fetch_compare_and_swap_acquire + +AO_INLINE AO_t +AO_fetch_compare_and_swap_release(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + AO_lwsync(); + return AO_fetch_compare_and_swap(addr, old_val, new_val); +} +#define AO_HAVE_fetch_compare_and_swap_release + +AO_INLINE AO_t +AO_fetch_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + AO_t result; + AO_lwsync(); + result = AO_fetch_compare_and_swap(addr, old_val, new_val); + AO_lwsync(); + return result; +} +#define AO_HAVE_fetch_compare_and_swap_full + +/* TODO: Implement AO_fetch_and_add, AO_and/or/xor directly. */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/icc/ia64.h b/libatomic_ops/src/atomic_ops/sysdeps/icc/ia64.h new file mode 100644 index 000000000..6654209dd --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/icc/ia64.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This file specifies Itanimum primitives for use with the Intel (ecc) + * compiler. We use intrinsics instead of the inline assembly code in the + * gcc file. + */ + +#include "../all_atomic_load_store.h" + +#include "../test_and_set_t_is_char.h" + +#include + +/* The acquire release semantics of volatile can be turned off. And volatile */ +/* operations in icc9 don't imply ordering with respect to other nonvolatile */ +/* operations. */ + +#define AO_INTEL_PTR_t void * + +AO_INLINE AO_t +AO_load_acquire(const volatile AO_t *p) +{ + return (AO_t)(__ld8_acq((AO_INTEL_PTR_t)p)); +} +#define AO_HAVE_load_acquire + +AO_INLINE void +AO_store_release(volatile AO_t *p, AO_t val) +{ + __st8_rel((AO_INTEL_PTR_t)p, (__int64)val); +} +#define AO_HAVE_store_release + +AO_INLINE unsigned char +AO_char_load_acquire(const volatile unsigned char *p) +{ + /* A normal volatile load generates an ld.acq */ + return (__ld1_acq((AO_INTEL_PTR_t)p)); +} +#define AO_HAVE_char_load_acquire + +AO_INLINE void +AO_char_store_release(volatile unsigned char *p, unsigned char val) +{ + __st1_rel((AO_INTEL_PTR_t)p, val); +} +#define AO_HAVE_char_store_release + +AO_INLINE unsigned short +AO_short_load_acquire(const volatile unsigned short *p) +{ + /* A normal volatile load generates an ld.acq */ + return (__ld2_acq((AO_INTEL_PTR_t)p)); +} +#define AO_HAVE_short_load_acquire + +AO_INLINE void +AO_short_store_release(volatile unsigned short *p, unsigned short val) +{ + __st2_rel((AO_INTEL_PTR_t)p, val); +} +#define AO_HAVE_short_store_release + +AO_INLINE unsigned int +AO_int_load_acquire(const volatile unsigned int *p) +{ + /* A normal volatile load generates an ld.acq */ + return (__ld4_acq((AO_INTEL_PTR_t)p)); +} +#define AO_HAVE_int_load_acquire + +AO_INLINE void +AO_int_store_release(volatile unsigned int *p, unsigned int val) +{ + __st4_rel((AO_INTEL_PTR_t)p, val); +} +#define AO_HAVE_int_store_release + +AO_INLINE void +AO_nop_full(void) +{ + __mf(); +} +#define AO_HAVE_nop_full + +#ifndef AO_PREFER_GENERALIZED +AO_INLINE AO_t +AO_fetch_and_add1_acquire(volatile AO_t *p) +{ + return __fetchadd8_acq((unsigned __int64 *)p, 1); +} +#define AO_HAVE_fetch_and_add1_acquire + +AO_INLINE AO_t +AO_fetch_and_add1_release(volatile AO_t *p) +{ + return __fetchadd8_rel((unsigned __int64 *)p, 1); +} +#define AO_HAVE_fetch_and_add1_release + +AO_INLINE AO_t +AO_fetch_and_sub1_acquire(volatile AO_t *p) +{ + return __fetchadd8_acq((unsigned __int64 *)p, -1); +} +#define AO_HAVE_fetch_and_sub1_acquire + +AO_INLINE AO_t +AO_fetch_and_sub1_release(volatile AO_t *p) +{ + return __fetchadd8_rel((unsigned __int64 *)p, -1); +} +#define AO_HAVE_fetch_and_sub1_release +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap_acquire(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + return _InterlockedCompareExchange64_acq(addr, new_val, old_val); +} +#define AO_HAVE_fetch_compare_and_swap_acquire + +AO_INLINE AO_t +AO_fetch_compare_and_swap_release(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + return _InterlockedCompareExchange64_rel(addr, new_val, old_val); +} +#define AO_HAVE_fetch_compare_and_swap_release + +AO_INLINE unsigned char +AO_char_fetch_compare_and_swap_acquire(volatile unsigned char *addr, + unsigned char old_val, + unsigned char new_val) +{ + return _InterlockedCompareExchange8_acq(addr, new_val, old_val); +} +#define AO_HAVE_char_fetch_compare_and_swap_acquire + +AO_INLINE unsigned char +AO_char_fetch_compare_and_swap_release(volatile unsigned char *addr, + unsigned char old_val, + unsigned char new_val) +{ + return _InterlockedCompareExchange8_rel(addr, new_val, old_val); +} +#define AO_HAVE_char_fetch_compare_and_swap_release + +AO_INLINE unsigned short +AO_short_fetch_compare_and_swap_acquire(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) +{ + return _InterlockedCompareExchange16_acq(addr, new_val, old_val); +} +#define AO_HAVE_short_fetch_compare_and_swap_acquire + +AO_INLINE unsigned short +AO_short_fetch_compare_and_swap_release(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) +{ + return _InterlockedCompareExchange16_rel(addr, new_val, old_val); +} +#define AO_HAVE_short_fetch_compare_and_swap_release + +AO_INLINE unsigned int +AO_int_fetch_compare_and_swap_acquire(volatile unsigned int *addr, + unsigned int old_val, + unsigned int new_val) +{ + return _InterlockedCompareExchange_acq(addr, new_val, old_val); +} +#define AO_HAVE_int_fetch_compare_and_swap_acquire + +AO_INLINE unsigned int +AO_int_fetch_compare_and_swap_release(volatile unsigned int *addr, + unsigned int old_val, + unsigned int new_val) +{ + return _InterlockedCompareExchange_rel(addr, new_val, old_val); +} +#define AO_HAVE_int_fetch_compare_and_swap_release + +#undef AO_INTEL_PTR_t diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.h new file mode 100644 index 000000000..2dd40ec9a --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003-2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This file adds definitions appropriate for environments in which */ +/* volatile load of a given type has acquire semantics, and volatile */ +/* store of a given type has release semantics. This is arguably */ +/* supposed to be true with the standard Itanium software conventions. */ +/* Empirically gcc/ia64 does some reordering of ordinary operations */ +/* around volatiles even when we think it should not. GCC v3.3 and */ +/* earlier could reorder a volatile store with another store. As of */ +/* March 2005, gcc pre-4 reuses some previously computed common */ +/* subexpressions across a volatile load; hence, we now add compiler */ +/* barriers for gcc. */ + +#ifndef AO_HAVE_GCC_BARRIER + /* TODO: Check GCC version (if workaround not needed for modern GCC). */ +# if defined(__GNUC__) +# define AO_GCC_BARRIER() AO_compiler_barrier() +# else +# define AO_GCC_BARRIER() (void)0 +# endif +# define AO_HAVE_GCC_BARRIER +#endif + +AO_INLINE AO_t +AO_load_acquire(const volatile AO_t *addr) +{ + AO_t result = *addr; + + /* A normal volatile load generates an ld.acq (on IA-64). */ + AO_GCC_BARRIER(); + return result; +} +#define AO_HAVE_load_acquire + +AO_INLINE void +AO_store_release(volatile AO_t *addr, AO_t new_val) +{ + AO_GCC_BARRIER(); + /* A normal volatile store generates an st.rel (on IA-64). */ + *addr = new_val; +} +#define AO_HAVE_store_release diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.template b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.template new file mode 100644 index 000000000..0d83b530a --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/acquire_release_volatile.template @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003-2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This file adds definitions appropriate for environments in which */ +/* volatile load of a given type has acquire semantics, and volatile */ +/* store of a given type has release semantics. This is arguably */ +/* supposed to be true with the standard Itanium software conventions. */ +/* Empirically gcc/ia64 does some reordering of ordinary operations */ +/* around volatiles even when we think it should not. GCC v3.3 and */ +/* earlier could reorder a volatile store with another store. As of */ +/* March 2005, gcc pre-4 reuses some previously computed common */ +/* subexpressions across a volatile load; hence, we now add compiler */ +/* barriers for gcc. */ + +#ifndef AO_HAVE_GCC_BARRIER + /* TODO: Check GCC version (if workaround not needed for modern GCC). */ +# if defined(__GNUC__) +# define AO_GCC_BARRIER() AO_compiler_barrier() +# else +# define AO_GCC_BARRIER() (void)0 +# endif +# define AO_HAVE_GCC_BARRIER +#endif + +AO_INLINE XCTYPE +AO_XSIZE_load_acquire(const volatile XCTYPE *addr) +{ + XCTYPE result = *addr; + + /* A normal volatile load generates an ld.acq (on IA-64). */ + AO_GCC_BARRIER(); + return result; +} +#define AO_HAVE_XSIZE_load_acquire + +AO_INLINE void +AO_XSIZE_store_release(volatile XCTYPE *addr, XCTYPE new_val) +{ + AO_GCC_BARRIER(); + /* A normal volatile store generates an st.rel (on IA-64). */ + *addr = new_val; +} +#define AO_HAVE_XSIZE_store_release diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_load.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_load.h new file mode 100644 index 000000000..38c23e409 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_load.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which loads of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE AO_t +AO_load(const volatile AO_t *addr) +{ +# ifdef AO_ACCESS_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + /* Cast away the volatile for architectures like IA64 where */ + /* volatile adds barrier (fence) semantics. */ + return *(const AO_t *)addr; +} +#define AO_HAVE_load diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_load.template b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_load.template new file mode 100644 index 000000000..26b7e4e31 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_load.template @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which loads of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE XCTYPE +AO_XSIZE_load(const volatile XCTYPE *addr) +{ +# ifdef AO_ACCESS_XSIZE_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + /* Cast away the volatile for architectures like IA64 where */ + /* volatile adds barrier (fence) semantics. */ + return *(const XCTYPE *)addr; +} +#define AO_HAVE_XSIZE_load diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_store.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_store.h new file mode 100644 index 000000000..9d5cf554f --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_store.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which stores of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE void +AO_store(volatile AO_t *addr, AO_t new_val) +{ +# ifdef AO_ACCESS_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + *(AO_t *)addr = new_val; +} +#define AO_HAVE_store diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_store.template b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_store.template new file mode 100644 index 000000000..56bba45df --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/atomic_store.template @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which stores of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE void +AO_XSIZE_store(volatile XCTYPE *addr, XCTYPE new_val) +{ +# ifdef AO_ACCESS_XSIZE_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + *(XCTYPE *)addr = new_val; +} +#define AO_HAVE_XSIZE_store diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h new file mode 100644 index 000000000..9c78b8561 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_acquire_release_volatile.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003-2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This file adds definitions appropriate for environments in which */ +/* volatile load of a given type has acquire semantics, and volatile */ +/* store of a given type has release semantics. This is arguably */ +/* supposed to be true with the standard Itanium software conventions. */ +/* Empirically gcc/ia64 does some reordering of ordinary operations */ +/* around volatiles even when we think it should not. GCC v3.3 and */ +/* earlier could reorder a volatile store with another store. As of */ +/* March 2005, gcc pre-4 reuses some previously computed common */ +/* subexpressions across a volatile load; hence, we now add compiler */ +/* barriers for gcc. */ + +#ifndef AO_HAVE_GCC_BARRIER + /* TODO: Check GCC version (if workaround not needed for modern GCC). */ +# if defined(__GNUC__) +# define AO_GCC_BARRIER() AO_compiler_barrier() +# else +# define AO_GCC_BARRIER() (void)0 +# endif +# define AO_HAVE_GCC_BARRIER +#endif + +AO_INLINE unsigned/**/char +AO_char_load_acquire(const volatile unsigned/**/char *addr) +{ + unsigned/**/char result = *addr; + + /* A normal volatile load generates an ld.acq (on IA-64). */ + AO_GCC_BARRIER(); + return result; +} +#define AO_HAVE_char_load_acquire + +AO_INLINE void +AO_char_store_release(volatile unsigned/**/char *addr, unsigned/**/char new_val) +{ + AO_GCC_BARRIER(); + /* A normal volatile store generates an st.rel (on IA-64). */ + *addr = new_val; +} +#define AO_HAVE_char_store_release diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_atomic_load.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_atomic_load.h new file mode 100644 index 000000000..8927b7d5e --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_atomic_load.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which loads of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE unsigned/**/char +AO_char_load(const volatile unsigned/**/char *addr) +{ +# ifdef AO_ACCESS_char_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + /* Cast away the volatile for architectures like IA64 where */ + /* volatile adds barrier (fence) semantics. */ + return *(const unsigned/**/char *)addr; +} +#define AO_HAVE_char_load diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_atomic_store.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_atomic_store.h new file mode 100644 index 000000000..f1fa8954d --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/char_atomic_store.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which stores of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE void +AO_char_store(volatile unsigned/**/char *addr, unsigned/**/char new_val) +{ +# ifdef AO_ACCESS_char_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + *(unsigned/**/char *)addr = new_val; +} +#define AO_HAVE_char_store diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/double_atomic_load_store.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/double_atomic_load_store.h new file mode 100644 index 000000000..85debd056 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/double_atomic_load_store.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2013 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which AO_double_t loads and stores */ +/* are atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE AO_double_t +AO_double_load(const volatile AO_double_t *addr) +{ + AO_double_t result; + +# ifdef AO_ACCESS_double_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + /* Cast away the volatile in case it adds fence semantics. */ + result.AO_whole = ((const AO_double_t *)addr)->AO_whole; + return result; +} +#define AO_HAVE_double_load + +AO_INLINE void +AO_double_store(volatile AO_double_t *addr, AO_double_t new_val) +{ +# ifdef AO_ACCESS_double_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + ((AO_double_t *)addr)->AO_whole = new_val.AO_whole; +} +#define AO_HAVE_double_store diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h new file mode 100644 index 000000000..13f2fe65d --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_acquire_release_volatile.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003-2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This file adds definitions appropriate for environments in which */ +/* volatile load of a given type has acquire semantics, and volatile */ +/* store of a given type has release semantics. This is arguably */ +/* supposed to be true with the standard Itanium software conventions. */ +/* Empirically gcc/ia64 does some reordering of ordinary operations */ +/* around volatiles even when we think it should not. GCC v3.3 and */ +/* earlier could reorder a volatile store with another store. As of */ +/* March 2005, gcc pre-4 reuses some previously computed common */ +/* subexpressions across a volatile load; hence, we now add compiler */ +/* barriers for gcc. */ + +#ifndef AO_HAVE_GCC_BARRIER + /* TODO: Check GCC version (if workaround not needed for modern GCC). */ +# if defined(__GNUC__) +# define AO_GCC_BARRIER() AO_compiler_barrier() +# else +# define AO_GCC_BARRIER() (void)0 +# endif +# define AO_HAVE_GCC_BARRIER +#endif + +AO_INLINE unsigned +AO_int_load_acquire(const volatile unsigned *addr) +{ + unsigned result = *addr; + + /* A normal volatile load generates an ld.acq (on IA-64). */ + AO_GCC_BARRIER(); + return result; +} +#define AO_HAVE_int_load_acquire + +AO_INLINE void +AO_int_store_release(volatile unsigned *addr, unsigned new_val) +{ + AO_GCC_BARRIER(); + /* A normal volatile store generates an st.rel (on IA-64). */ + *addr = new_val; +} +#define AO_HAVE_int_store_release diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_atomic_load.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_atomic_load.h new file mode 100644 index 000000000..ecc4b3a7c --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_atomic_load.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which loads of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE unsigned +AO_int_load(const volatile unsigned *addr) +{ +# ifdef AO_ACCESS_int_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + /* Cast away the volatile for architectures like IA64 where */ + /* volatile adds barrier (fence) semantics. */ + return *(const unsigned *)addr; +} +#define AO_HAVE_int_load diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_atomic_store.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_atomic_store.h new file mode 100644 index 000000000..3c32b306e --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/int_atomic_store.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which stores of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE void +AO_int_store(volatile unsigned *addr, unsigned new_val) +{ +# ifdef AO_ACCESS_int_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + *(unsigned *)addr = new_val; +} +#define AO_HAVE_int_store diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_loads_only.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_loads_only.h new file mode 100644 index 000000000..a64cfafc7 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_loads_only.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_char_load + /* char_load_read is defined in generalize-small. */ +# define AO_char_load_acquire(addr) AO_char_load_read(addr) +# define AO_HAVE_char_load_acquire +#endif +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_short_load + /* short_load_read is defined in generalize-small. */ +# define AO_short_load_acquire(addr) AO_short_load_read(addr) +# define AO_HAVE_short_load_acquire +#endif +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_int_load + /* int_load_read is defined in generalize-small. */ +# define AO_int_load_acquire(addr) AO_int_load_read(addr) +# define AO_HAVE_int_load_acquire +#endif +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_load + /* load_read is defined in generalize-small. */ +# define AO_load_acquire(addr) AO_load_read(addr) +# define AO_HAVE_load_acquire +#endif +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_double_load + /* double_load_read is defined in generalize-small. */ +# define AO_double_load_acquire(addr) AO_double_load_read(addr) +# define AO_HAVE_double_load_acquire +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_loads_only.template b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_loads_only.template new file mode 100644 index 000000000..45c5c1d58 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_loads_only.template @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_XSIZE_load + /* XSIZE_load_read is defined in generalize-small. */ +# define AO_XSIZE_load_acquire(addr) AO_XSIZE_load_read(addr) +# define AO_HAVE_XSIZE_load_acquire +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_stores_only.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_stores_only.h new file mode 100644 index 000000000..a427f3bd8 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_stores_only.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_char_store +# define AO_char_store_release(addr, val) \ + (AO_nop_write(), AO_char_store(addr, val)) +# define AO_HAVE_char_store_release +#endif +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_short_store +# define AO_short_store_release(addr, val) \ + (AO_nop_write(), AO_short_store(addr, val)) +# define AO_HAVE_short_store_release +#endif +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_int_store +# define AO_int_store_release(addr, val) \ + (AO_nop_write(), AO_int_store(addr, val)) +# define AO_HAVE_int_store_release +#endif +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_store +# define AO_store_release(addr, val) \ + (AO_nop_write(), AO_store(addr, val)) +# define AO_HAVE_store_release +#endif +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_double_store +# define AO_double_store_release(addr, val) \ + (AO_nop_write(), AO_double_store(addr, val)) +# define AO_HAVE_double_store_release +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_stores_only.template b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_stores_only.template new file mode 100644 index 000000000..c51abc5d2 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/ordered_stores_only.template @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AO_HAVE_XSIZE_store +# define AO_XSIZE_store_release(addr, val) \ + (AO_nop_write(), AO_XSIZE_store(addr, val)) +# define AO_HAVE_XSIZE_store_release +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h new file mode 100644 index 000000000..a7a611f0f --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_acquire_release_volatile.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003-2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This file adds definitions appropriate for environments in which */ +/* volatile load of a given type has acquire semantics, and volatile */ +/* store of a given type has release semantics. This is arguably */ +/* supposed to be true with the standard Itanium software conventions. */ +/* Empirically gcc/ia64 does some reordering of ordinary operations */ +/* around volatiles even when we think it should not. GCC v3.3 and */ +/* earlier could reorder a volatile store with another store. As of */ +/* March 2005, gcc pre-4 reuses some previously computed common */ +/* subexpressions across a volatile load; hence, we now add compiler */ +/* barriers for gcc. */ + +#ifndef AO_HAVE_GCC_BARRIER + /* TODO: Check GCC version (if workaround not needed for modern GCC). */ +# if defined(__GNUC__) +# define AO_GCC_BARRIER() AO_compiler_barrier() +# else +# define AO_GCC_BARRIER() (void)0 +# endif +# define AO_HAVE_GCC_BARRIER +#endif + +AO_INLINE unsigned/**/short +AO_short_load_acquire(const volatile unsigned/**/short *addr) +{ + unsigned/**/short result = *addr; + + /* A normal volatile load generates an ld.acq (on IA-64). */ + AO_GCC_BARRIER(); + return result; +} +#define AO_HAVE_short_load_acquire + +AO_INLINE void +AO_short_store_release(volatile unsigned/**/short *addr, unsigned/**/short new_val) +{ + AO_GCC_BARRIER(); + /* A normal volatile store generates an st.rel (on IA-64). */ + *addr = new_val; +} +#define AO_HAVE_short_store_release diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_atomic_load.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_atomic_load.h new file mode 100644 index 000000000..2370540ba --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_atomic_load.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which loads of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE unsigned/**/short +AO_short_load(const volatile unsigned/**/short *addr) +{ +# ifdef AO_ACCESS_short_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + /* Cast away the volatile for architectures like IA64 where */ + /* volatile adds barrier (fence) semantics. */ + return *(const unsigned/**/short *)addr; +} +#define AO_HAVE_short_load diff --git a/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_atomic_store.h b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_atomic_store.h new file mode 100644 index 000000000..b9a9dc6e8 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/loadstore/short_atomic_store.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Definitions for architectures on which stores of given type are */ +/* atomic (either for suitably aligned data only or for any legal */ +/* alignment). */ + +AO_INLINE void +AO_short_store(volatile unsigned/**/short *addr, unsigned/**/short new_val) +{ +# ifdef AO_ACCESS_short_CHECK_ALIGNED + AO_ASSERT_ADDR_ALIGNED(addr); +# endif + *(unsigned/**/short *)addr = new_val; +} +#define AO_HAVE_short_store diff --git a/libatomic_ops/src/atomic_ops/sysdeps/msftc/arm.h b/libatomic_ops/src/atomic_ops/sysdeps/msftc/arm.h new file mode 100644 index 000000000..0458cddcb --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/msftc/arm.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Some ARM slide set, if it has been read correctly, claims that Loads */ +/* followed by either a Load or a Store are ordered, but nothing else. */ +/* It is assumed that Windows interrupt handlers clear the LL/SC flag. */ +/* Unaligned accesses are not guaranteed to be atomic. */ +#include "../all_aligned_atomic_load_store.h" + +#define AO_T_IS_INT + +#ifndef AO_ASSUME_WINDOWS98 + /* CAS is always available */ +# define AO_ASSUME_WINDOWS98 +#endif +#include "common32_defs.h" + +/* If only a single processor is used, we can define AO_UNIPROCESSOR. */ +#ifdef AO_UNIPROCESSOR + AO_INLINE void AO_nop_full(void) + { + AO_compiler_barrier(); + } +# define AO_HAVE_nop_full +#else + /* AO_nop_full() is emulated using AO_test_and_set_full(). */ +#endif + +#ifndef AO_HAVE_test_and_set_full +# include "../test_and_set_t_is_ao_t.h" + /* AO_test_and_set_full() is emulated. */ +#endif + +#if _M_ARM >= 7 && !defined(AO_NO_DOUBLE_CAS) + +# include "../standard_ao_double_t.h" + +/* These intrinsics are supposed to use LDREXD/STREXD. */ +# pragma intrinsic (_InterlockedCompareExchange64) +# pragma intrinsic (_InterlockedCompareExchange64_acq) +# pragma intrinsic (_InterlockedCompareExchange64_nf) +# pragma intrinsic (_InterlockedCompareExchange64_rel) + + AO_INLINE int + AO_double_compare_and_swap(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_ASSERT_ADDR_ALIGNED(addr); + return (double_ptr_storage)_InterlockedCompareExchange64_nf( + (__int64 volatile *)addr, + new_val.AO_whole /* exchange */, + old_val.AO_whole) == old_val.AO_whole; + } +# define AO_HAVE_double_compare_and_swap + + AO_INLINE int + AO_double_compare_and_swap_acquire(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_ASSERT_ADDR_ALIGNED(addr); + return (double_ptr_storage)_InterlockedCompareExchange64_acq( + (__int64 volatile *)addr, + new_val.AO_whole /* exchange */, + old_val.AO_whole) == old_val.AO_whole; + } +# define AO_HAVE_double_compare_and_swap_acquire + + AO_INLINE int + AO_double_compare_and_swap_release(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_ASSERT_ADDR_ALIGNED(addr); + return (double_ptr_storage)_InterlockedCompareExchange64_rel( + (__int64 volatile *)addr, + new_val.AO_whole /* exchange */, + old_val.AO_whole) == old_val.AO_whole; + } +# define AO_HAVE_double_compare_and_swap_release + + AO_INLINE int + AO_double_compare_and_swap_full(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_ASSERT_ADDR_ALIGNED(addr); + return (double_ptr_storage)_InterlockedCompareExchange64( + (__int64 volatile *)addr, + new_val.AO_whole /* exchange */, + old_val.AO_whole) == old_val.AO_whole; + } +# define AO_HAVE_double_compare_and_swap_full + +#endif /* _M_ARM >= 7 && !AO_NO_DOUBLE_CAS */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/msftc/arm64.h b/libatomic_ops/src/atomic_ops/sysdeps/msftc/arm64.h new file mode 100644 index 000000000..367042af6 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/msftc/arm64.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2021 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "../all_aligned_atomic_load_store.h" + +#ifndef AO_ASSUME_WINDOWS98 +# define AO_ASSUME_WINDOWS98 +#endif +#ifndef AO_USE_INTERLOCKED_INTRINSICS +# define AO_USE_INTERLOCKED_INTRINSICS +#endif +#include "common32_defs.h" + +#ifndef AO_HAVE_test_and_set_full +# include "../test_and_set_t_is_ao_t.h" + /* AO_test_and_set_full() is emulated using word-wide CAS. */ +#endif + +#ifndef AO_NO_DOUBLE_CAS + +# include "../standard_ao_double_t.h" + +# pragma intrinsic (_InterlockedCompareExchange128) +# pragma intrinsic (_InterlockedCompareExchange128_acq) +# pragma intrinsic (_InterlockedCompareExchange128_nf) +# pragma intrinsic (_InterlockedCompareExchange128_rel) + + AO_INLINE int + AO_compare_double_and_swap_double(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + __int64 comparandResult[2]; + + AO_ASSERT_ADDR_ALIGNED(addr); + comparandResult[0] = old_val1; /* low */ + comparandResult[1] = old_val2; /* high */ + return _InterlockedCompareExchange128_nf((volatile __int64 *)addr, + new_val2 /* high */, + new_val1 /* low */, + comparandResult); + } +# define AO_HAVE_compare_double_and_swap_double + + AO_INLINE int + AO_compare_double_and_swap_double_acquire(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + __int64 comparandResult[2]; + + AO_ASSERT_ADDR_ALIGNED(addr); + comparandResult[0] = old_val1; /* low */ + comparandResult[1] = old_val2; /* high */ + return _InterlockedCompareExchange128_acq((volatile __int64 *)addr, + new_val2 /* high */, + new_val1 /* low */, + comparandResult); + } +# define AO_HAVE_compare_double_and_swap_double_acquire + + AO_INLINE int + AO_compare_double_and_swap_double_release(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + __int64 comparandResult[2]; + + AO_ASSERT_ADDR_ALIGNED(addr); + comparandResult[0] = old_val1; /* low */ + comparandResult[1] = old_val2; /* high */ + return _InterlockedCompareExchange128_rel((volatile __int64 *)addr, + new_val2 /* high */, + new_val1 /* low */, + comparandResult); + } +# define AO_HAVE_compare_double_and_swap_double_release + + AO_INLINE int + AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + __int64 comparandResult[2]; + + AO_ASSERT_ADDR_ALIGNED(addr); + comparandResult[0] = old_val1; /* low */ + comparandResult[1] = old_val2; /* high */ + return _InterlockedCompareExchange128((volatile __int64 *)addr, + new_val2 /* high */, + new_val1 /* low */, + comparandResult); + } +# define AO_HAVE_compare_double_and_swap_double_full + +#endif /* !AO_NO_DOUBLE_CAS */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/msftc/common32_defs.h b/libatomic_ops/src/atomic_ops/sysdeps/msftc/common32_defs.h new file mode 100644 index 000000000..ece41f743 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/msftc/common32_defs.h @@ -0,0 +1,1197 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This file contains AO primitives based on VC++ built-in intrinsic */ +/* functions commonly available across 32- and 64-bit architectures. */ + +/* This file should be included from arch-specific header files. */ +/* Define AO_USE_INTERLOCKED_INTRINSICS if _Interlocked primitives */ +/* (used below) are available as intrinsic ones for a target arch */ +/* (otherwise "Interlocked" functions family is used instead). */ +/* Define AO_ASSUME_WINDOWS98 if CAS is available. */ + +#if _MSC_VER <= 1400 || !defined(AO_USE_INTERLOCKED_INTRINSICS) \ + || defined(_WIN32_WCE) +# include + /* Seems like over-kill, but that's what MSDN recommends. */ + /* And apparently winbase.h is not always self-contained. */ + /* Optionally, client could define WIN32_LEAN_AND_MEAN before */ + /* include atomic_ops.h to reduce amount of Windows internal */ + /* headers included by windows.h one. */ +#endif + +#if _MSC_VER < 1310 || !defined(AO_USE_INTERLOCKED_INTRINSICS) + +# define _InterlockedIncrement InterlockedIncrement +# define _InterlockedDecrement InterlockedDecrement +# define _InterlockedExchangeAdd InterlockedExchangeAdd +# define _InterlockedCompareExchange InterlockedCompareExchange + +# define AO_INTERLOCKED_VOLATILE /**/ + +#else /* elif _MSC_VER >= 1310 */ + +# if _MSC_VER >= 1400 +# ifndef _WIN32_WCE +# include +# endif + +# else /* elif _MSC_VER < 1400 */ +# ifdef __cplusplus + extern "C" { +# endif + LONG __cdecl _InterlockedIncrement(LONG volatile *); + LONG __cdecl _InterlockedDecrement(LONG volatile *); + LONG __cdecl _InterlockedExchangeAdd(LONG volatile *, LONG); + LONG __cdecl _InterlockedCompareExchange(LONG volatile *, + LONG /* Exchange */, LONG /* Comp */); +# ifdef __cplusplus + } /* extern "C" */ +# endif +# endif /* _MSC_VER < 1400 */ + +# if !defined(AO_PREFER_GENERALIZED) || !defined(AO_ASSUME_WINDOWS98) +# pragma intrinsic (_InterlockedIncrement) +# pragma intrinsic (_InterlockedDecrement) +# pragma intrinsic (_InterlockedExchangeAdd) +# ifndef AO_T_IS_INT +# pragma intrinsic (_InterlockedIncrement64) +# pragma intrinsic (_InterlockedDecrement64) +# pragma intrinsic (_InterlockedExchangeAdd64) +# endif +# endif /* !AO_PREFER_GENERALIZED */ + +# pragma intrinsic (_InterlockedCompareExchange) +# ifndef AO_T_IS_INT +# pragma intrinsic (_InterlockedCompareExchange64) +# endif + +# define AO_INTERLOCKED_VOLATILE volatile + +#endif /* _MSC_VER >= 1310 */ + +#if !defined(AO_PREFER_GENERALIZED) || !defined(AO_ASSUME_WINDOWS98) + AO_INLINE AO_t + AO_fetch_and_add_full(volatile AO_t *p, AO_t incr) + { +# ifdef AO_T_IS_INT + return _InterlockedExchangeAdd((long AO_INTERLOCKED_VOLATILE *)p, incr); +# else + return _InterlockedExchangeAdd64((__int64 volatile *)p, incr); +# endif + } +# define AO_HAVE_fetch_and_add_full + + AO_INLINE AO_t + AO_fetch_and_add1_full(volatile AO_t *p) + { +# ifdef AO_T_IS_INT + return _InterlockedIncrement((long AO_INTERLOCKED_VOLATILE *)p) - 1; +# else + return _InterlockedIncrement64((__int64 volatile *)p) - 1; +# endif + } +# define AO_HAVE_fetch_and_add1_full + + AO_INLINE AO_t + AO_fetch_and_sub1_full(volatile AO_t *p) + { +# ifdef AO_T_IS_INT + return _InterlockedDecrement((long AO_INTERLOCKED_VOLATILE *)p) + 1; +# else + return _InterlockedDecrement64((__int64 volatile *)p) + 1; +# endif + } +# define AO_HAVE_fetch_and_sub1_full + +# ifndef AO_T_IS_INT + AO_INLINE unsigned int + AO_int_fetch_and_add_full(volatile unsigned int *p, unsigned int incr) + { + return _InterlockedExchangeAdd((long volatile *)p, incr); + } +# define AO_HAVE_int_fetch_and_add_full + + AO_INLINE unsigned int + AO_int_fetch_and_add1_full(volatile unsigned int *p) + { + return _InterlockedIncrement((long volatile *)p) - 1; + } +# define AO_HAVE_int_fetch_and_add1_full + + AO_INLINE unsigned int + AO_int_fetch_and_sub1_full(volatile unsigned int *p) + { + return _InterlockedDecrement((long volatile *)p) + 1; + } +# define AO_HAVE_int_fetch_and_sub1_full +# endif /* !AO_T_IS_INT */ +#endif /* !AO_PREFER_GENERALIZED */ + +#ifdef AO_ASSUME_WINDOWS98 + AO_INLINE AO_t + AO_fetch_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { +# ifndef AO_T_IS_INT + return (AO_t)_InterlockedCompareExchange64((__int64 volatile *)addr, + new_val, old_val); +# elif defined(AO_OLD_STYLE_INTERLOCKED_COMPARE_EXCHANGE) + return (AO_t)_InterlockedCompareExchange( + (void *AO_INTERLOCKED_VOLATILE *)addr, + (void *)new_val, (void *)old_val); +# else + return _InterlockedCompareExchange((long AO_INTERLOCKED_VOLATILE *)addr, + new_val, old_val); +# endif + } +# define AO_HAVE_fetch_compare_and_swap_full + +# ifndef AO_T_IS_INT + AO_INLINE unsigned int + AO_int_fetch_compare_and_swap_full(volatile unsigned int *addr, + unsigned int old_val, + unsigned int new_val) + { + return _InterlockedCompareExchange((long volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_int_fetch_compare_and_swap_full +# endif /* !AO_T_IS_INT */ +#endif /* AO_ASSUME_WINDOWS98 */ + +#if (_MSC_VER > 1400) && (!defined(_M_ARM) || _MSC_VER >= 1800) + +# if _MSC_VER < 1800 || !defined(AO_PREFER_GENERALIZED) +# pragma intrinsic (_InterlockedAnd8) +# pragma intrinsic (_InterlockedOr8) +# pragma intrinsic (_InterlockedXor8) + + AO_INLINE void + AO_char_and_full(volatile unsigned char *p, unsigned char value) + { + _InterlockedAnd8((char volatile *)p, value); + } +# define AO_HAVE_char_and_full + + AO_INLINE void + AO_char_or_full(volatile unsigned char *p, unsigned char value) + { + _InterlockedOr8((char volatile *)p, value); + } +# define AO_HAVE_char_or_full + + AO_INLINE void + AO_char_xor_full(volatile unsigned char *p, unsigned char value) + { + _InterlockedXor8((char volatile *)p, value); + } +# define AO_HAVE_char_xor_full +# endif /* _MSC_VER < 1800 || !AO_PREFER_GENERALIZED */ + +# pragma intrinsic (_InterlockedCompareExchange16) + + AO_INLINE unsigned short + AO_short_fetch_compare_and_swap_full(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) + { + return _InterlockedCompareExchange16((short volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_short_fetch_compare_and_swap_full + +# ifndef AO_PREFER_GENERALIZED +# pragma intrinsic (_InterlockedIncrement16) +# pragma intrinsic (_InterlockedDecrement16) + + AO_INLINE unsigned short + AO_short_fetch_and_add1_full(volatile unsigned short *p) + { + return _InterlockedIncrement16((short volatile *)p) - 1; + } +# define AO_HAVE_short_fetch_and_add1_full + + AO_INLINE unsigned short + AO_short_fetch_and_sub1_full(volatile unsigned short *p) + { + return _InterlockedDecrement16((short volatile *)p) + 1; + } +# define AO_HAVE_short_fetch_and_sub1_full +# endif /* !AO_PREFER_GENERALIZED */ + +#endif /* _MSC_VER > 1400 */ + +#if _MSC_VER >= 1800 /* Visual Studio 2013+ */ + +# ifndef AO_PREFER_GENERALIZED +# pragma intrinsic (_InterlockedAnd16) +# pragma intrinsic (_InterlockedOr16) +# pragma intrinsic (_InterlockedXor16) + + AO_INLINE void + AO_short_and_full(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedAnd16((short volatile *)p, value); + } +# define AO_HAVE_short_and_full + + AO_INLINE void + AO_short_or_full(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedOr16((short volatile *)p, value); + } +# define AO_HAVE_short_or_full + + AO_INLINE void + AO_short_xor_full(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedXor16((short volatile *)p, value); + } +# define AO_HAVE_short_xor_full + +# pragma intrinsic (_InterlockedAnd) +# pragma intrinsic (_InterlockedOr) +# pragma intrinsic (_InterlockedXor) + +# ifndef AO_T_IS_INT + AO_INLINE void + AO_int_and_full(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedAnd((long volatile *)p, value); + } +# define AO_HAVE_int_and_full + + AO_INLINE void + AO_int_or_full(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedOr((long volatile *)p, value); + } +# define AO_HAVE_int_or_full + + AO_INLINE void + AO_int_xor_full(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedXor((long volatile *)p, value); + } +# define AO_HAVE_int_xor_full + +# pragma intrinsic (_InterlockedAnd64) +# pragma intrinsic (_InterlockedOr64) +# pragma intrinsic (_InterlockedXor64) +# endif /* !AO_T_IS_INT */ + + AO_INLINE void + AO_and_full(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedAnd((long volatile *)p, value); +# else + (void)_InterlockedAnd64((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_and_full + + AO_INLINE void + AO_or_full(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedOr((long volatile *)p, value); +# else + (void)_InterlockedOr64((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_or_full + + AO_INLINE void + AO_xor_full(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedXor((long volatile *)p, value); +# else + (void)_InterlockedXor64((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_xor_full +# endif /* !AO_PREFER_GENERALIZED */ + +# if !defined(AO_PREFER_GENERALIZED) && (defined(_M_ARM) || defined(_M_ARM64)) +# pragma intrinsic (_InterlockedAnd8_acq) +# pragma intrinsic (_InterlockedAnd8_nf) +# pragma intrinsic (_InterlockedAnd8_rel) +# pragma intrinsic (_InterlockedOr8_acq) +# pragma intrinsic (_InterlockedOr8_nf) +# pragma intrinsic (_InterlockedOr8_rel) +# pragma intrinsic (_InterlockedXor8_acq) +# pragma intrinsic (_InterlockedXor8_nf) +# pragma intrinsic (_InterlockedXor8_rel) + + AO_INLINE void + AO_char_and(volatile unsigned char *p, unsigned char value) + { + _InterlockedAnd8_nf((char volatile *)p, value); + } +# define AO_HAVE_char_and + + AO_INLINE void + AO_char_or(volatile unsigned char *p, unsigned char value) + { + _InterlockedOr8_nf((char volatile *)p, value); + } +# define AO_HAVE_char_or + + AO_INLINE void + AO_char_xor(volatile unsigned char *p, unsigned char value) + { + _InterlockedXor8_nf((char volatile *)p, value); + } +# define AO_HAVE_char_xor + + AO_INLINE void + AO_char_and_acquire(volatile unsigned char *p, unsigned char value) + { + _InterlockedAnd8_acq((char volatile *)p, value); + } +# define AO_HAVE_char_and_acquire + + AO_INLINE void + AO_char_or_acquire(volatile unsigned char *p, unsigned char value) + { + _InterlockedOr8_acq((char volatile *)p, value); + } +# define AO_HAVE_char_or_acquire + + AO_INLINE void + AO_char_xor_acquire(volatile unsigned char *p, unsigned char value) + { + _InterlockedXor8_acq((char volatile *)p, value); + } +# define AO_HAVE_char_xor_acquire + + AO_INLINE void + AO_char_and_release(volatile unsigned char *p, unsigned char value) + { + _InterlockedAnd8_rel((char volatile *)p, value); + } +# define AO_HAVE_char_and_release + + AO_INLINE void + AO_char_or_release(volatile unsigned char *p, unsigned char value) + { + _InterlockedOr8_rel((char volatile *)p, value); + } +# define AO_HAVE_char_or_release + + AO_INLINE void + AO_char_xor_release(volatile unsigned char *p, unsigned char value) + { + _InterlockedXor8_rel((char volatile *)p, value); + } +# define AO_HAVE_char_xor_release + +# pragma intrinsic (_InterlockedAnd16_acq) +# pragma intrinsic (_InterlockedAnd16_nf) +# pragma intrinsic (_InterlockedAnd16_rel) +# pragma intrinsic (_InterlockedOr16_acq) +# pragma intrinsic (_InterlockedOr16_nf) +# pragma intrinsic (_InterlockedOr16_rel) +# pragma intrinsic (_InterlockedXor16_acq) +# pragma intrinsic (_InterlockedXor16_nf) +# pragma intrinsic (_InterlockedXor16_rel) + + AO_INLINE void + AO_short_and(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedAnd16_nf((short volatile *)p, value); + } +# define AO_HAVE_short_and + + AO_INLINE void + AO_short_or(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedOr16_nf((short volatile *)p, value); + } +# define AO_HAVE_short_or + + AO_INLINE void + AO_short_xor(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedXor16_nf((short volatile *)p, value); + } +# define AO_HAVE_short_xor + + AO_INLINE void + AO_short_and_acquire(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedAnd16_acq((short volatile *)p, value); + } +# define AO_HAVE_short_and_acquire + + AO_INLINE void + AO_short_or_acquire(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedOr16_acq((short volatile *)p, value); + } +# define AO_HAVE_short_or_acquire + + AO_INLINE void + AO_short_xor_acquire(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedXor16_acq((short volatile *)p, value); + } +# define AO_HAVE_short_xor_acquire + + AO_INLINE void + AO_short_and_release(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedAnd16_rel((short volatile *)p, value); + } +# define AO_HAVE_short_and_release + + AO_INLINE void + AO_short_or_release(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedOr16_rel((short volatile *)p, value); + } +# define AO_HAVE_short_or_release + + AO_INLINE void + AO_short_xor_release(volatile unsigned short *p, unsigned short value) + { + (void)_InterlockedXor16_rel((short volatile *)p, value); + } +# define AO_HAVE_short_xor_release + +# pragma intrinsic (_InterlockedAnd_acq) +# pragma intrinsic (_InterlockedAnd_nf) +# pragma intrinsic (_InterlockedAnd_rel) +# pragma intrinsic (_InterlockedOr_acq) +# pragma intrinsic (_InterlockedOr_nf) +# pragma intrinsic (_InterlockedOr_rel) +# pragma intrinsic (_InterlockedXor_acq) +# pragma intrinsic (_InterlockedXor_nf) +# pragma intrinsic (_InterlockedXor_rel) + +# ifndef AO_T_IS_INT + AO_INLINE void + AO_int_and(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedAnd_nf((long volatile *)p, value); + } +# define AO_HAVE_int_and + + AO_INLINE void + AO_int_or(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedOr_nf((long volatile *)p, value); + } +# define AO_HAVE_int_or + + AO_INLINE void + AO_int_xor(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedXor_nf((long volatile *)p, value); + } +# define AO_HAVE_int_xor + + AO_INLINE void + AO_int_and_acquire(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedAnd_acq((long volatile *)p, value); + } +# define AO_HAVE_int_and_acquire + + AO_INLINE void + AO_int_or_acquire(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedOr_acq((long volatile *)p, value); + } +# define AO_HAVE_int_or_acquire + + AO_INLINE void + AO_int_xor_acquire(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedXor_acq((long volatile *)p, value); + } +# define AO_HAVE_int_xor_acquire + + AO_INLINE void + AO_int_and_release(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedAnd_rel((long volatile *)p, value); + } +# define AO_HAVE_int_and_release + + AO_INLINE void + AO_int_or_release(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedOr_rel((long volatile *)p, value); + } +# define AO_HAVE_int_or_release + + AO_INLINE void + AO_int_xor_release(volatile unsigned int *p, unsigned int value) + { + (void)_InterlockedXor_rel((long volatile *)p, value); + } +# define AO_HAVE_int_xor_release + +# pragma intrinsic (_InterlockedAnd64_acq) +# pragma intrinsic (_InterlockedAnd64_nf) +# pragma intrinsic (_InterlockedAnd64_rel) +# pragma intrinsic (_InterlockedOr64_acq) +# pragma intrinsic (_InterlockedOr64_nf) +# pragma intrinsic (_InterlockedOr64_rel) +# pragma intrinsic (_InterlockedXor64_acq) +# pragma intrinsic (_InterlockedXor64_nf) +# pragma intrinsic (_InterlockedXor64_rel) +# endif /* !AO_T_IS_INT */ + + AO_INLINE void + AO_and(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedAnd_nf((long volatile *)p, value); +# else + (void)_InterlockedAnd64_nf((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_and + + AO_INLINE void + AO_or(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedOr_nf((long volatile *)p, value); +# else + (void)_InterlockedOr64_nf((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_or + + AO_INLINE void + AO_xor(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedXor_nf((long volatile *)p, value); +# else + (void)_InterlockedXor64_nf((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_xor + + AO_INLINE void + AO_and_acquire(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedAnd_acq((long volatile *)p, value); +# else + (void)_InterlockedAnd64_acq((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_and_acquire + + AO_INLINE void + AO_or_acquire(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedOr_acq((long volatile *)p, value); +# else + (void)_InterlockedOr64_acq((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_or_acquire + + AO_INLINE void + AO_xor_acquire(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedXor_acq((long volatile *)p, value); +# else + (void)_InterlockedXor64_acq((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_xor_acquire + + AO_INLINE void + AO_and_release(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedAnd_rel((long volatile *)p, value); +# else + (void)_InterlockedAnd64_rel((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_and_release + + AO_INLINE void + AO_or_release(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedOr_rel((long volatile *)p, value); +# else + (void)_InterlockedOr64_rel((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_or_release + + AO_INLINE void + AO_xor_release(volatile AO_t *p, AO_t value) + { +# ifdef AO_T_IS_INT + (void)_InterlockedXor_rel((long volatile *)p, value); +# else + (void)_InterlockedXor64_rel((__int64 volatile *)p, value); +# endif + } +# define AO_HAVE_xor_release + +# pragma intrinsic (_InterlockedDecrement16_acq) +# pragma intrinsic (_InterlockedDecrement16_nf) +# pragma intrinsic (_InterlockedDecrement16_rel) +# pragma intrinsic (_InterlockedIncrement16_acq) +# pragma intrinsic (_InterlockedIncrement16_nf) +# pragma intrinsic (_InterlockedIncrement16_rel) + + AO_INLINE unsigned short + AO_short_fetch_and_add1(volatile unsigned short *p) + { + return _InterlockedIncrement16_nf((short volatile *)p) - 1; + } +# define AO_HAVE_short_fetch_and_add1 + + AO_INLINE unsigned short + AO_short_fetch_and_sub1(volatile unsigned short *p) + { + return _InterlockedDecrement16_nf((short volatile *)p) + 1; + } +# define AO_HAVE_short_fetch_and_sub1 + + AO_INLINE unsigned short + AO_short_fetch_and_add1_acquire(volatile unsigned short *p) + { + return _InterlockedIncrement16_acq((short volatile *)p) - 1; + } +# define AO_HAVE_short_fetch_and_add1_acquire + + AO_INLINE unsigned short + AO_short_fetch_and_sub1_acquire(volatile unsigned short *p) + { + return _InterlockedDecrement16_acq((short volatile *)p) + 1; + } +# define AO_HAVE_short_fetch_and_sub1_acquire + + AO_INLINE unsigned short + AO_short_fetch_and_add1_release(volatile unsigned short *p) + { + return _InterlockedIncrement16_rel((short volatile *)p) - 1; + } +# define AO_HAVE_short_fetch_and_add1_release + + AO_INLINE unsigned short + AO_short_fetch_and_sub1_release(volatile unsigned short *p) + { + return _InterlockedDecrement16_rel((short volatile *)p) + 1; + } +# define AO_HAVE_short_fetch_and_sub1_release + +# pragma intrinsic (_InterlockedExchangeAdd_acq) +# pragma intrinsic (_InterlockedExchangeAdd_nf) +# pragma intrinsic (_InterlockedExchangeAdd_rel) + +# pragma intrinsic (_InterlockedDecrement_acq) +# pragma intrinsic (_InterlockedDecrement_nf) +# pragma intrinsic (_InterlockedDecrement_rel) +# pragma intrinsic (_InterlockedIncrement_acq) +# pragma intrinsic (_InterlockedIncrement_nf) +# pragma intrinsic (_InterlockedIncrement_rel) + +# ifndef AO_T_IS_INT +# pragma intrinsic (_InterlockedExchangeAdd64_acq) +# pragma intrinsic (_InterlockedExchangeAdd64_nf) +# pragma intrinsic (_InterlockedExchangeAdd64_rel) + +# pragma intrinsic (_InterlockedDecrement64_acq) +# pragma intrinsic (_InterlockedDecrement64_nf) +# pragma intrinsic (_InterlockedDecrement64_rel) +# pragma intrinsic (_InterlockedIncrement64_acq) +# pragma intrinsic (_InterlockedIncrement64_nf) +# pragma intrinsic (_InterlockedIncrement64_rel) +# endif + + AO_INLINE AO_t + AO_fetch_and_add(volatile AO_t *p, AO_t incr) + { +# ifdef AO_T_IS_INT + return _InterlockedExchangeAdd_nf((long volatile *)p, incr); +# else + return _InterlockedExchangeAdd64_nf((__int64 volatile *)p, incr); +# endif + } +# define AO_HAVE_fetch_and_add + + AO_INLINE AO_t + AO_fetch_and_add1(volatile AO_t *p) + { +# ifdef AO_T_IS_INT + return _InterlockedIncrement_nf((long volatile *)p) - 1; +# else + return _InterlockedIncrement64_nf((__int64 volatile *)p) - 1; +# endif + } +# define AO_HAVE_fetch_and_add1 + + AO_INLINE AO_t + AO_fetch_and_sub1(volatile AO_t *p) + { +# ifdef AO_T_IS_INT + return _InterlockedDecrement_nf((long volatile *)p) + 1; +# else + return _InterlockedDecrement64_nf((__int64 volatile *)p) + 1; +# endif + } +# define AO_HAVE_fetch_and_sub1 + + AO_INLINE AO_t + AO_fetch_and_add_acquire(volatile AO_t *p, AO_t incr) + { +# ifdef AO_T_IS_INT + return _InterlockedExchangeAdd_acq((long volatile *)p, incr); +# else + return _InterlockedExchangeAdd64_acq((__int64 volatile *)p, incr); +# endif + } +# define AO_HAVE_fetch_and_add_acquire + + AO_INLINE AO_t + AO_fetch_and_add1_acquire(volatile AO_t *p) + { +# ifdef AO_T_IS_INT + return _InterlockedIncrement_acq((long volatile *)p) - 1; +# else + return _InterlockedIncrement64_acq((__int64 volatile *)p) - 1; +# endif + } +# define AO_HAVE_fetch_and_add1_acquire + + AO_INLINE AO_t + AO_fetch_and_sub1_acquire(volatile AO_t *p) + { +# ifdef AO_T_IS_INT + return _InterlockedDecrement_acq((long volatile *)p) + 1; +# else + return _InterlockedDecrement64_acq((__int64 volatile *)p) + 1; +# endif + } +# define AO_HAVE_fetch_and_sub1_acquire + + AO_INLINE AO_t + AO_fetch_and_add_release(volatile AO_t *p, AO_t incr) + { +# ifdef AO_T_IS_INT + return _InterlockedExchangeAdd_rel((long volatile *)p, incr); +# else + return _InterlockedExchangeAdd64_rel((__int64 volatile *)p, incr); +# endif + } +# define AO_HAVE_fetch_and_add_release + + AO_INLINE AO_t + AO_fetch_and_add1_release(volatile AO_t *p) + { +# ifdef AO_T_IS_INT + return _InterlockedIncrement_rel((long volatile *)p) - 1; +# else + return _InterlockedIncrement64_rel((__int64 volatile *)p) - 1; +# endif + } +# define AO_HAVE_fetch_and_add1_release + + AO_INLINE AO_t + AO_fetch_and_sub1_release(volatile AO_t *p) + { +# ifdef AO_T_IS_INT + return _InterlockedDecrement_rel((long volatile *)p) + 1; +# else + return _InterlockedDecrement64_rel((__int64 volatile *)p) + 1; +# endif + } +# define AO_HAVE_fetch_and_sub1_release + +# ifndef AO_T_IS_INT + AO_INLINE unsigned int + AO_int_fetch_and_add(volatile unsigned int *p, unsigned int incr) + { + return _InterlockedExchangeAdd_nf((long volatile *)p, incr); + } +# define AO_HAVE_int_fetch_and_add + + AO_INLINE unsigned int + AO_int_fetch_and_add1(volatile unsigned int *p) + { + return _InterlockedIncrement_nf((long volatile *)p) - 1; + } +# define AO_HAVE_int_fetch_and_add1 + + AO_INLINE unsigned int + AO_int_fetch_and_sub1(volatile unsigned int *p) + { + return _InterlockedDecrement_nf((long volatile *)p) + 1; + } +# define AO_HAVE_int_fetch_and_sub1 + + AO_INLINE unsigned int + AO_int_fetch_and_add_acquire(volatile unsigned int *p, + unsigned int incr) + { + return _InterlockedExchangeAdd_acq((long volatile *)p, incr); + } +# define AO_HAVE_int_fetch_and_add_acquire + + AO_INLINE unsigned int + AO_int_fetch_and_add1_acquire(volatile unsigned int *p) + { + return _InterlockedIncrement_acq((long volatile *)p) - 1; + } +# define AO_HAVE_int_fetch_and_add1_acquire + + AO_INLINE unsigned int + AO_int_fetch_and_sub1_acquire(volatile unsigned int *p) + { + return _InterlockedDecrement_acq((long volatile *)p) + 1; + } +# define AO_HAVE_int_fetch_and_sub1_acquire + + AO_INLINE unsigned int + AO_int_fetch_and_add_release(volatile unsigned int *p, + unsigned int incr) + { + return _InterlockedExchangeAdd_rel((long volatile *)p, incr); + } +# define AO_HAVE_int_fetch_and_add_release + + AO_INLINE unsigned int + AO_int_fetch_and_add1_release(volatile unsigned int *p) + { + return _InterlockedIncrement_rel((long volatile *)p) - 1; + } +# define AO_HAVE_int_fetch_and_add1_release + + AO_INLINE unsigned int + AO_int_fetch_and_sub1_release(volatile unsigned int *p) + { + return _InterlockedDecrement_rel((long volatile *)p) + 1; + } +# define AO_HAVE_int_fetch_and_sub1_release +# endif /* !AO_T_IS_INT */ + +# endif /* !AO_PREFER_GENERALIZED && (_M_ARM || _M_ARM64) */ + +# pragma intrinsic (_InterlockedCompareExchange8) + + AO_INLINE unsigned char + AO_char_fetch_compare_and_swap_full(volatile unsigned char *addr, + unsigned char old_val, + unsigned char new_val) + { + return _InterlockedCompareExchange8((char volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_char_fetch_compare_and_swap_full + +# if defined(_M_ARM) || defined(_M_ARM64) + +# pragma intrinsic (_InterlockedCompareExchange_acq) +# pragma intrinsic (_InterlockedCompareExchange_nf) +# pragma intrinsic (_InterlockedCompareExchange_rel) +# ifndef AO_T_IS_INT +# pragma intrinsic (_InterlockedCompareExchange64_acq) +# pragma intrinsic (_InterlockedCompareExchange64_nf) +# pragma intrinsic (_InterlockedCompareExchange64_rel) +# endif + + AO_INLINE AO_t + AO_fetch_compare_and_swap(volatile AO_t *addr, AO_t old_val, AO_t new_val) + { +# ifdef AO_T_IS_INT + return _InterlockedCompareExchange_nf((long volatile *)addr, + new_val, old_val); +# else + return (AO_t)_InterlockedCompareExchange64_nf( + (__int64 volatile *)addr, new_val, old_val); +# endif + } +# define AO_HAVE_fetch_compare_and_swap + + AO_INLINE AO_t + AO_fetch_compare_and_swap_acquire(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { +# ifdef AO_T_IS_INT + return _InterlockedCompareExchange_acq((long volatile *)addr, + new_val, old_val); +# else + return (AO_t)_InterlockedCompareExchange64_acq( + (__int64 volatile *)addr, new_val, old_val); +# endif + } +# define AO_HAVE_fetch_compare_and_swap_acquire + + AO_INLINE AO_t + AO_fetch_compare_and_swap_release(volatile AO_t *addr, AO_t old_val, + AO_t new_val) + { +# ifdef AO_T_IS_INT + return _InterlockedCompareExchange_rel((long volatile *)addr, + new_val, old_val); +# else + return (AO_t)_InterlockedCompareExchange64_rel( + (__int64 volatile *)addr, new_val, old_val); +# endif + } +# define AO_HAVE_fetch_compare_and_swap_release + +# ifndef AO_T_IS_INT + AO_INLINE unsigned int + AO_int_fetch_compare_and_swap(volatile unsigned int *addr, + unsigned int old_val, + unsigned int new_val) + { + return _InterlockedCompareExchange_nf((long volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_int_fetch_compare_and_swap + + AO_INLINE unsigned int + AO_int_fetch_compare_and_swap_acquire(volatile unsigned int *addr, + unsigned int old_val, + unsigned int new_val) + { + return _InterlockedCompareExchange_acq((long volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_int_fetch_compare_and_swap_acquire + + AO_INLINE unsigned int + AO_int_fetch_compare_and_swap_release(volatile unsigned int *addr, + unsigned int old_val, + unsigned int new_val) + { + return _InterlockedCompareExchange_rel((long volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_int_fetch_compare_and_swap_release +# endif /* !AO_T_IS_INT */ + +# pragma intrinsic (_InterlockedCompareExchange16_acq) +# pragma intrinsic (_InterlockedCompareExchange16_nf) +# pragma intrinsic (_InterlockedCompareExchange16_rel) +# pragma intrinsic (_InterlockedCompareExchange8_acq) +# pragma intrinsic (_InterlockedCompareExchange8_nf) +# pragma intrinsic (_InterlockedCompareExchange8_rel) + + AO_INLINE unsigned short + AO_short_fetch_compare_and_swap(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) + { + return _InterlockedCompareExchange16_nf((short volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_short_fetch_compare_and_swap + + AO_INLINE unsigned short + AO_short_fetch_compare_and_swap_acquire(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) + { + return _InterlockedCompareExchange16_acq((short volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_short_fetch_compare_and_swap_acquire + + AO_INLINE unsigned short + AO_short_fetch_compare_and_swap_release(volatile unsigned short *addr, + unsigned short old_val, + unsigned short new_val) + { + return _InterlockedCompareExchange16_rel((short volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_short_fetch_compare_and_swap_release + + AO_INLINE unsigned char + AO_char_fetch_compare_and_swap(volatile unsigned char *addr, + unsigned char old_val, + unsigned char new_val) + { + return _InterlockedCompareExchange8_nf((char volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_char_fetch_compare_and_swap + + AO_INLINE unsigned char + AO_char_fetch_compare_and_swap_acquire(volatile unsigned char *addr, + unsigned char old_val, + unsigned char new_val) + { + return _InterlockedCompareExchange8_acq((char volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_char_fetch_compare_and_swap_acquire + + AO_INLINE unsigned char + AO_char_fetch_compare_and_swap_release(volatile unsigned char *addr, + unsigned char old_val, + unsigned char new_val) + { + return _InterlockedCompareExchange8_rel((char volatile *)addr, + new_val, old_val); + } +# define AO_HAVE_char_fetch_compare_and_swap_release +# endif /* _M_ARM || _M_ARM64 */ + +# if !defined(AO_PREFER_GENERALIZED) && !defined(_M_ARM) +# pragma intrinsic (_InterlockedExchangeAdd16) +# pragma intrinsic (_InterlockedExchangeAdd8) + + AO_INLINE unsigned char + AO_char_fetch_and_add_full(volatile unsigned char *p, unsigned char incr) + { + return _InterlockedExchangeAdd8((char volatile *)p, incr); + } +# define AO_HAVE_char_fetch_and_add_full + + AO_INLINE unsigned short + AO_short_fetch_and_add_full(volatile unsigned short *p, + unsigned short incr) + { + return _InterlockedExchangeAdd16((short volatile *)p, incr); + } +# define AO_HAVE_short_fetch_and_add_full + +# if defined(_M_ARM64) +# pragma intrinsic (_InterlockedExchangeAdd16_acq) +# pragma intrinsic (_InterlockedExchangeAdd16_nf) +# pragma intrinsic (_InterlockedExchangeAdd16_rel) +# pragma intrinsic (_InterlockedExchangeAdd8_acq) +# pragma intrinsic (_InterlockedExchangeAdd8_nf) +# pragma intrinsic (_InterlockedExchangeAdd8_rel) + + AO_INLINE unsigned char + AO_char_fetch_and_add(volatile unsigned char *p, unsigned char incr) + { + return _InterlockedExchangeAdd8_nf((char volatile *)p, incr); + } +# define AO_HAVE_char_fetch_and_add + + AO_INLINE unsigned short + AO_short_fetch_and_add(volatile unsigned short *p, unsigned short incr) + { + return _InterlockedExchangeAdd16_nf((short volatile *)p, incr); + } +# define AO_HAVE_short_fetch_and_add + + AO_INLINE unsigned char + AO_char_fetch_and_add_acquire(volatile unsigned char *p, + unsigned char incr) + { + return _InterlockedExchangeAdd8_acq((char volatile *)p, incr); + } +# define AO_HAVE_char_fetch_and_add_acquire + + AO_INLINE unsigned short + AO_short_fetch_and_add_acquire(volatile unsigned short *p, + unsigned short incr) + { + return _InterlockedExchangeAdd16_acq((short volatile *)p, incr); + } +# define AO_HAVE_short_fetch_and_add_acquire + + AO_INLINE unsigned char + AO_char_fetch_and_add_release(volatile unsigned char *p, + unsigned char incr) + { + return _InterlockedExchangeAdd8_rel((char volatile *)p, incr); + } +# define AO_HAVE_char_fetch_and_add_release + + AO_INLINE unsigned short + AO_short_fetch_and_add_release(volatile unsigned short *p, + unsigned short incr) + { + return _InterlockedExchangeAdd16_rel((short volatile *)p, incr); + } +# define AO_HAVE_short_fetch_and_add_release +# endif /* _M_ARM64 */ + +# endif /* !AO_PREFER_GENERALIZED && !_M_ARM */ + +# if !defined(_M_ARM) || _M_ARM >= 6 +# include "../test_and_set_t_is_char.h" + +# pragma intrinsic (_InterlockedExchange8) + + AO_INLINE AO_TS_VAL_t + AO_test_and_set_full(volatile AO_TS_t *addr) + { + return (AO_TS_VAL_t)(_InterlockedExchange8((char volatile *)addr, + (AO_TS_t)AO_TS_SET) & 0xff); + /* Note: bitwise "and 0xff" is applied to the result because cast */ + /* to unsigned char does not work properly (for a reason) if /J */ + /* option is passed to the MS VC compiler. */ + } +# define AO_HAVE_test_and_set_full +# endif /* !_M_ARM || _M_ARM >= 6 */ + +# if _M_ARM >= 6 || defined(_M_ARM64) +# pragma intrinsic (_InterlockedExchange8_acq) +# pragma intrinsic (_InterlockedExchange8_nf) +# pragma intrinsic (_InterlockedExchange8_rel) + + AO_INLINE AO_TS_VAL_t + AO_test_and_set(volatile AO_TS_t *addr) + { + return (AO_TS_VAL_t)(_InterlockedExchange8_nf((char volatile *)addr, + (AO_TS_t)AO_TS_SET) & 0xff); + } +# define AO_HAVE_test_and_set + + AO_INLINE AO_TS_VAL_t + AO_test_and_set_acquire(volatile AO_TS_t *addr) + { + return (AO_TS_VAL_t)(_InterlockedExchange8_acq((char volatile *)addr, + (AO_TS_t)AO_TS_SET) & 0xff); + } +# define AO_HAVE_test_and_set_acquire + + AO_INLINE AO_TS_VAL_t + AO_test_and_set_release(volatile AO_TS_t *addr) + { + return (AO_TS_VAL_t)(_InterlockedExchange8_rel((char volatile *)addr, + (AO_TS_t)AO_TS_SET) & 0xff); + } +# define AO_HAVE_test_and_set_release +# endif /* _M_ARM >= 6 || _M_ARM64 */ + +#endif /* _MSC_VER >= 1800 */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/msftc/x86.h b/libatomic_ops/src/atomic_ops/sysdeps/msftc/x86.h new file mode 100644 index 000000000..ba2400a50 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/msftc/x86.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "../all_aligned_atomic_load_store.h" + +#if !defined(AO_ASSUME_VISTA) && _MSC_VER >= 1910 + /* Visual Studio 2017 (15.0) discontinued support of Windows XP. */ + /* We assume Windows Server 2003, Vista or later. */ +# define AO_ASSUME_VISTA +#endif + +#if !defined(AO_ASSUME_WINDOWS98) \ + && (defined(AO_ASSUME_VISTA) || _MSC_VER >= 1400) + /* Visual Studio 2005 (MS VC++ 8.0) discontinued support of Windows 95. */ +# define AO_ASSUME_WINDOWS98 +#endif + +#if !defined(AO_USE_PENTIUM4_INSTRS) && _M_IX86_FP >= 2 /* SSE2 */ + /* "mfence" is a part of SSE2 set (introduced on Intel Pentium 4). */ +# define AO_USE_PENTIUM4_INSTRS +#endif + +#define AO_T_IS_INT + +#ifndef AO_USE_INTERLOCKED_INTRINSICS + /* _Interlocked primitives (Inc, Dec, Xchg, Add) are always available */ +# define AO_USE_INTERLOCKED_INTRINSICS +#endif +#include "common32_defs.h" + +/* As far as we can tell, the lfence and sfence instructions are not */ +/* currently needed or useful for cached memory accesses. */ + +/* Unfortunately mfence doesn't exist everywhere. */ +/* IsProcessorFeaturePresent(PF_COMPARE_EXCHANGE128) is */ +/* probably a conservative test for it? */ + +#if defined(AO_USE_PENTIUM4_INSTRS) + +AO_INLINE void +AO_nop_full(void) +{ + __asm { mfence } +} +#define AO_HAVE_nop_full + +#else + +/* We could use the cpuid instruction. But that seems to be slower */ +/* than the default implementation based on test_and_set_full. Thus */ +/* we omit that bit of misinformation here. */ + +#endif + +#if !defined(AO_NO_ASM_XADD) && !defined(AO_HAVE_char_fetch_and_add_full) + AO_INLINE unsigned char + AO_char_fetch_and_add_full(volatile unsigned char *p, unsigned char incr) + { + __asm + { + mov al, incr + mov ebx, p + lock xadd byte ptr [ebx], al + } + /* Ignore possible "missing return value" warning here. */ + } +# define AO_HAVE_char_fetch_and_add_full + + AO_INLINE unsigned short + AO_short_fetch_and_add_full(volatile unsigned short *p, unsigned short incr) + { + __asm + { + mov ax, incr + mov ebx, p + lock xadd word ptr [ebx], ax + } + /* Ignore possible "missing return value" warning here. */ + } +# define AO_HAVE_short_fetch_and_add_full +#endif /* !AO_NO_ASM_XADD */ + +#ifndef AO_HAVE_test_and_set_full +# include "../test_and_set_t_is_char.h" + + AO_INLINE AO_TS_VAL_t + AO_test_and_set_full(volatile AO_TS_t *addr) + { + __asm + { + mov eax,0xff ; /* AO_TS_SET */ + mov ebx,addr ; + xchg byte ptr [ebx],al ; + } + /* Ignore possible "missing return value" warning here. */ + } +# define AO_HAVE_test_and_set_full +#endif + +#if defined(_WIN64) && !defined(CPPCHECK) +# error wrong architecture +#endif + +#ifdef AO_ASSUME_VISTA +# include "../standard_ao_double_t.h" + + /* Reading or writing a quadword aligned on a 64-bit boundary is */ + /* always carried out atomically (requires at least a Pentium). */ +# define AO_ACCESS_double_CHECK_ALIGNED +# include "../loadstore/double_atomic_load_store.h" + + /* Whenever we run on a Pentium class machine, we have that certain */ + /* function. */ +# pragma intrinsic (_InterlockedCompareExchange64) + + /* Returns nonzero if the comparison succeeded. */ + AO_INLINE int + AO_double_compare_and_swap_full(volatile AO_double_t *addr, + AO_double_t old_val, AO_double_t new_val) + { + AO_ASSERT_ADDR_ALIGNED(addr); + return (double_ptr_storage)_InterlockedCompareExchange64( + (__int64 volatile *)addr, + new_val.AO_whole /* exchange */, + old_val.AO_whole) == old_val.AO_whole; + } +# define AO_HAVE_double_compare_and_swap_full +#endif /* AO_ASSUME_VISTA */ + +/* Real X86 implementations, except for some old WinChips, appear */ +/* to enforce ordering between memory operations, EXCEPT that a later */ +/* read can pass earlier writes, presumably due to the visible */ +/* presence of store buffers. */ +/* We ignore both the WinChips, and the fact that the official specs */ +/* seem to be much weaker (and arguably too weak to be usable). */ +#include "../ordered_except_wr.h" diff --git a/libatomic_ops/src/atomic_ops/sysdeps/msftc/x86_64.h b/libatomic_ops/src/atomic_ops/sysdeps/msftc/x86_64.h new file mode 100644 index 000000000..ad98f0b19 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/msftc/x86_64.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2009-2021 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "../all_aligned_atomic_load_store.h" + +/* Real X86 implementations appear */ +/* to enforce ordering between memory operations, EXCEPT that a later */ +/* read can pass earlier writes, presumably due to the visible */ +/* presence of store buffers. */ +/* We ignore the fact that the official specs */ +/* seem to be much weaker (and arguably too weak to be usable). */ +#include "../ordered_except_wr.h" + +#ifndef AO_ASSUME_WINDOWS98 + /* CAS is always available */ +# define AO_ASSUME_WINDOWS98 +#endif +#ifndef AO_USE_INTERLOCKED_INTRINSICS +# define AO_USE_INTERLOCKED_INTRINSICS +#endif +#include "common32_defs.h" + +#ifdef AO_ASM_X64_AVAILABLE + +#if _MSC_VER < 1800 + AO_INLINE unsigned char + AO_char_fetch_and_add_full(volatile unsigned char *p, unsigned char incr) + { + __asm + { + mov al, incr + mov rbx, p + lock xadd byte ptr [rbx], al + } + } +# define AO_HAVE_char_fetch_and_add_full + + AO_INLINE unsigned short + AO_short_fetch_and_add_full(volatile unsigned short *p, unsigned short incr) + { + __asm + { + mov ax, incr + mov rbx, p + lock xadd word ptr [rbx], ax + } + } +# define AO_HAVE_short_fetch_and_add_full +#endif /* _MSC_VER < 1800 */ + + /* As far as we can tell, the lfence and sfence instructions are not */ + /* currently needed or useful for cached memory accesses. */ + + AO_INLINE void + AO_nop_full(void) + { + /* Note: "mfence" (SSE2) is supported on all x86_64/amd64 chips. */ + __asm { mfence } + } +# define AO_HAVE_nop_full + +# ifndef AO_HAVE_test_and_set_full +# include "../test_and_set_t_is_char.h" + + AO_INLINE AO_TS_VAL_t + AO_test_and_set_full(volatile AO_TS_t *addr) + { + __asm + { + mov rax,AO_TS_SET ; + mov rbx,addr ; + xchg byte ptr [rbx],al ; + } + } +# define AO_HAVE_test_and_set_full +# endif + +#endif /* AO_ASM_X64_AVAILABLE */ + +#ifndef AO_HAVE_test_and_set_full +# include "../test_and_set_t_is_ao_t.h" + /* AO_test_and_set_full() is emulated using word-wide CAS. */ +#endif + +#ifdef AO_CMPXCHG16B_AVAILABLE + +# if _MSC_VER >= 1500 +# include "../standard_ao_double_t.h" +# pragma intrinsic (_InterlockedCompareExchange128) + + AO_INLINE int + AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + __int64 comparandResult[2]; + + AO_ASSERT_ADDR_ALIGNED(addr); + comparandResult[0] = old_val1; /* low */ + comparandResult[1] = old_val2; /* high */ + return _InterlockedCompareExchange128((volatile __int64 *)addr, + new_val2 /* high */, + new_val1 /* low */, + comparandResult); + } +# define AO_HAVE_compare_double_and_swap_double_full + +# elif defined(AO_ASM_X64_AVAILABLE) +# include "../standard_ao_double_t.h" + + /* If there is no intrinsic _InterlockedCompareExchange128 then we */ + /* need basically what's given below. */ + AO_INLINE int + AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + __asm + { + mov rdx,QWORD PTR [old_val2] ; + mov rax,QWORD PTR [old_val1] ; + mov rcx,QWORD PTR [new_val2] ; + mov rbx,QWORD PTR [new_val1] ; + lock cmpxchg16b [addr] ; + setz rax ; + } + } +# define AO_HAVE_compare_double_and_swap_double_full +# endif /* AO_ASM_X64_AVAILABLE && (_MSC_VER < 1500) */ + +#endif /* AO_CMPXCHG16B_AVAILABLE */ diff --git a/libatomic_ops/src/atomic_ops/sysdeps/ordered.h b/libatomic_ops/src/atomic_ops/sysdeps/ordered.h new file mode 100644 index 000000000..ba9822d3f --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/ordered.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2003 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* These are common definitions for architectures that provide */ +/* processor ordered memory operations. */ + +#include "ordered_except_wr.h" + +AO_INLINE void +AO_nop_full(void) +{ + AO_compiler_barrier(); +} +#define AO_HAVE_nop_full diff --git a/libatomic_ops/src/atomic_ops/sysdeps/ordered_except_wr.h b/libatomic_ops/src/atomic_ops/sysdeps/ordered_except_wr.h new file mode 100644 index 000000000..78f447175 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/ordered_except_wr.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * These are common definitions for architectures that provide processor + * ordered memory operations except that a later read may pass an + * earlier write. Real x86 implementations seem to be in this category, + * except apparently for some IDT WinChips, which we ignore. + */ + +#include "read_ordered.h" + +AO_INLINE void +AO_nop_write(void) +{ + /* AO_nop_write implementation is the same as of AO_nop_read. */ + AO_compiler_barrier(); + /* sfence according to Intel docs. Pentium 3 and up. */ + /* Unnecessary for cached accesses? */ +} +#define AO_HAVE_nop_write + +#include "loadstore/ordered_stores_only.h" diff --git a/libatomic_ops/src/atomic_ops/sysdeps/read_ordered.h b/libatomic_ops/src/atomic_ops/sysdeps/read_ordered.h new file mode 100644 index 000000000..420cd9705 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/read_ordered.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * These are common definitions for architectures that provide processor + * ordered memory operations except that a later read may pass an + * earlier write. Real x86 implementations seem to be in this category, + * except apparently for some IDT WinChips, which we ignore. + */ + +AO_INLINE void +AO_nop_read(void) +{ + AO_compiler_barrier(); +} +#define AO_HAVE_nop_read + +#include "loadstore/ordered_loads_only.h" diff --git a/libatomic_ops/src/atomic_ops/sysdeps/standard_ao_double_t.h b/libatomic_ops/src/atomic_ops/sysdeps/standard_ao_double_t.h new file mode 100644 index 000000000..757fe79ec --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/standard_ao_double_t.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2004-2011 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2012-2021 Ivan Maidanski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* For 64-bit systems, we expect the double type to hold two int64's. */ + +#if ((defined(__x86_64__) && defined(AO_GCC_ATOMIC_TEST_AND_SET)) \ + || defined(__aarch64__)) && !defined(__ILP32__) + /* x86-64: __m128 is not applicable to atomic intrinsics. */ +# if AO_GNUC_PREREQ(4, 7) || AO_CLANG_PREREQ(3, 6) +# pragma GCC diagnostic push + /* Suppress warning about __int128 type. */ +# if defined(__clang__) || AO_GNUC_PREREQ(6, 0) +# pragma GCC diagnostic ignored "-Wpedantic" +# else + /* GCC before ~4.8 does not accept "-Wpedantic" quietly. */ +# pragma GCC diagnostic ignored "-pedantic" +# endif + typedef unsigned __int128 double_ptr_storage; +# pragma GCC diagnostic pop +# else /* pragma diagnostic is not supported */ + typedef unsigned __int128 double_ptr_storage; +# endif +#elif defined(_M_ARM64) && defined(_MSC_VER) + /* __int128 does not seem to be available. */ + typedef __declspec(align(16)) unsigned __int64 double_ptr_storage[2]; +#elif ((defined(__x86_64__) && AO_GNUC_PREREQ(4, 0)) || defined(_WIN64)) \ + && !defined(__ILP32__) + /* x86-64 (except for x32): __m128 serves as a placeholder which also */ + /* requires the compiler to align it on 16-byte boundary (as required */ + /* by cmpxchg16b). */ + /* Similar things could be done for PPC 64-bit using a VMX data type. */ +# include + typedef __m128 double_ptr_storage; +#elif defined(_WIN32) && !defined(__GNUC__) + typedef unsigned __int64 double_ptr_storage; +#elif defined(__i386__) && defined(__GNUC__) + typedef unsigned long long double_ptr_storage + __attribute__((__aligned__(8))); +#else + typedef unsigned long long double_ptr_storage; +#endif +# define AO_HAVE_DOUBLE_PTR_STORAGE + +typedef union { + struct { AO_t AO_v1; AO_t AO_v2; } AO_parts; + /* Note that AO_v1 corresponds to the low or the high part of */ + /* AO_whole depending on the machine endianness. */ + double_ptr_storage AO_whole; + /* AO_whole is now (starting from v7.3alpha3) the 2nd element */ + /* of this union to make AO_DOUBLE_T_INITIALIZER portable */ + /* (because __m128 definition could vary from a primitive type */ + /* to a structure or array/vector). */ +} AO_double_t; +#define AO_HAVE_double_t + +/* Note: AO_double_t volatile variables are not intended to be local */ +/* ones (at least those which are passed to AO double-wide primitives */ +/* as the first argument), otherwise it is the client responsibility to */ +/* ensure they have double-word alignment. */ + +/* Dummy declaration as a compile-time assertion for AO_double_t size. */ +struct AO_double_t_size_static_assert { + char dummy[sizeof(AO_double_t) == 2 * sizeof(AO_t) ? 1 : -1]; +}; + +#define AO_DOUBLE_T_INITIALIZER { { (AO_t)0, (AO_t)0 } } + +#define AO_val1 AO_parts.AO_v1 +#define AO_val2 AO_parts.AO_v2 diff --git a/libatomic_ops/src/atomic_ops/sysdeps/sunc/sparc.S b/libatomic_ops/src/atomic_ops/sysdeps/sunc/sparc.S new file mode 100644 index 000000000..4001a6762 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/sunc/sparc.S @@ -0,0 +1,5 @@ + .seg "text" + .globl AO_test_and_set_full +AO_test_and_set_full: + retl + ldstub [%o0],%o0 diff --git a/libatomic_ops/src/atomic_ops/sysdeps/sunc/sparc.h b/libatomic_ops/src/atomic_ops/sysdeps/sunc/sparc.h new file mode 100644 index 000000000..5530fd0ee --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/sunc/sparc.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "../all_atomic_load_store.h" + +/* Real SPARC code uses TSO: */ +#include "../ordered_except_wr.h" + +/* Test_and_set location is just a byte. */ +#include "../test_and_set_t_is_char.h" + +#ifdef __cplusplus + extern "C" { +#endif + +extern AO_TS_VAL_t +AO_test_and_set_full(volatile AO_TS_t *addr); +/* Implemented in separate .S file, for now. */ +#define AO_HAVE_test_and_set_full + +/* TODO: Like the gcc version, extend this for V8 and V9. */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif diff --git a/libatomic_ops/src/atomic_ops/sysdeps/sunc/x86.h b/libatomic_ops/src/atomic_ops/sysdeps/sunc/x86.h new file mode 100644 index 000000000..5efda2503 --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/sunc/x86.h @@ -0,0 +1,240 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2009-2016 Ivan Maidanski + * + * 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. + * + * Some of the machine specific code was borrowed from our GC distribution. + */ + +/* The following really assume we have a 486 or better. */ + +#include "../all_aligned_atomic_load_store.h" + +#include "../test_and_set_t_is_char.h" + +#if !defined(AO_USE_PENTIUM4_INSTRS) && !defined(__i386) + /* "mfence" (SSE2) is supported on all x86_64/amd64 chips. */ +# define AO_USE_PENTIUM4_INSTRS +#endif + +#if defined(AO_USE_PENTIUM4_INSTRS) + AO_INLINE void + AO_nop_full(void) + { + __asm__ __volatile__ ("mfence" : : : "memory"); + } +# define AO_HAVE_nop_full + +#else + /* We could use the cpuid instruction. But that seems to be slower */ + /* than the default implementation based on test_and_set_full. Thus */ + /* we omit that bit of misinformation here. */ +#endif /* !AO_USE_PENTIUM4_INSTRS */ + +/* As far as we can tell, the lfence and sfence instructions are not */ +/* currently needed or useful for cached memory accesses. */ + +/* Really only works for 486 and later */ +#ifndef AO_PREFER_GENERALIZED + AO_INLINE AO_t + AO_fetch_and_add_full (volatile AO_t *p, AO_t incr) + { + AO_t result; + + __asm__ __volatile__ ("lock; xadd %0, %1" + : "=r" (result), "+m" (*p) + : "0" (incr) + : "memory"); + return result; + } +# define AO_HAVE_fetch_and_add_full +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE unsigned char +AO_char_fetch_and_add_full (volatile unsigned char *p, unsigned char incr) +{ + unsigned char result; + + __asm__ __volatile__ ("lock; xaddb %0, %1" + : "=q" (result), "+m" (*p) + : "0" (incr) + : "memory"); + return result; +} +#define AO_HAVE_char_fetch_and_add_full + +AO_INLINE unsigned short +AO_short_fetch_and_add_full (volatile unsigned short *p, unsigned short incr) +{ + unsigned short result; + + __asm__ __volatile__ ("lock; xaddw %0, %1" + : "=r" (result), "+m" (*p) + : "0" (incr) + : "memory"); + return result; +} +#define AO_HAVE_short_fetch_and_add_full + +#ifndef AO_PREFER_GENERALIZED + AO_INLINE void + AO_and_full (volatile AO_t *p, AO_t value) + { + __asm__ __volatile__ ("lock; and %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_and_full + + AO_INLINE void + AO_or_full (volatile AO_t *p, AO_t value) + { + __asm__ __volatile__ ("lock; or %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_or_full + + AO_INLINE void + AO_xor_full (volatile AO_t *p, AO_t value) + { + __asm__ __volatile__ ("lock; xor %1, %0" + : "+m" (*p) + : "r" (value) + : "memory"); + } +# define AO_HAVE_xor_full +#endif /* !AO_PREFER_GENERALIZED */ + +AO_INLINE AO_TS_VAL_t +AO_test_and_set_full (volatile AO_TS_t *addr) +{ + AO_TS_t oldval; + /* Note: the "xchg" instruction does not need a "lock" prefix */ + __asm__ __volatile__ ("xchg %b0, %1" + : "=q" (oldval), "+m" (*addr) + : "0" (0xff) + : "memory"); + return (AO_TS_VAL_t)oldval; +} +#define AO_HAVE_test_and_set_full + +#ifndef AO_GENERALIZE_ASM_BOOL_CAS + /* Returns nonzero if the comparison succeeded. */ + AO_INLINE int + AO_compare_and_swap_full(volatile AO_t *addr, AO_t old, AO_t new_val) + { + char result; + __asm__ __volatile__ ("lock; cmpxchg %2, %0; setz %1" + : "+m" (*addr), "=a" (result) + : "r" (new_val), "a" (old) + : "memory"); + return (int) result; + } +# define AO_HAVE_compare_and_swap_full +#endif /* !AO_GENERALIZE_ASM_BOOL_CAS */ + +AO_INLINE AO_t +AO_fetch_compare_and_swap_full(volatile AO_t *addr, AO_t old_val, + AO_t new_val) +{ + AO_t fetched_val; + __asm__ __volatile__ ("lock; cmpxchg %2, %0" + : "+m" (*addr), "=a" (fetched_val) + : "r" (new_val), "a" (old_val) + : "memory"); + return fetched_val; +} +#define AO_HAVE_fetch_compare_and_swap_full + +#if defined(__i386) + +# ifndef AO_NO_CMPXCHG8B +# include "../standard_ao_double_t.h" + + /* Reading or writing a quadword aligned on a 64-bit boundary is */ + /* always carried out atomically (requires at least a Pentium). */ +# define AO_ACCESS_double_CHECK_ALIGNED +# include "../loadstore/double_atomic_load_store.h" + + /* Returns nonzero if the comparison succeeded. */ + /* Really requires at least a Pentium. */ + AO_INLINE int + AO_compare_double_and_swap_double_full(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + AO_t dummy; /* an output for clobbered edx */ + char result; + + __asm__ __volatile__ ("lock; cmpxchg8b %0; setz %1" + : "+m" (*addr), "=a" (result), "=d" (dummy) + : "d" (old_val2), "a" (old_val1), + "c" (new_val2), "b" (new_val1) + : "memory"); + return (int) result; + } +# define AO_HAVE_compare_double_and_swap_double_full +# endif /* !AO_NO_CMPXCHG8B */ + +# define AO_T_IS_INT + +#else /* x64 */ + + AO_INLINE unsigned int + AO_int_fetch_and_add_full (volatile unsigned int *p, unsigned int incr) + { + unsigned int result; + + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "=r" (result), "+m" (*p) + : "0" (incr) + : "memory"); + return result; + } +# define AO_HAVE_int_fetch_and_add_full + +# ifdef AO_CMPXCHG16B_AVAILABLE +# include "../standard_ao_double_t.h" + + /* Older AMD Opterons are missing this instruction (SIGILL should */ + /* be thrown in this case). */ + AO_INLINE int + AO_compare_double_and_swap_double_full (volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) + { + AO_t dummy; + char result; + + __asm__ __volatile__ ("lock; cmpxchg16b %0; setz %1" + : "+m" (*addr), "=a" (result), "=d" (dummy) + : "d" (old_val2), "a" (old_val1), + "c" (new_val2), "b" (new_val1) + : "memory"); + return (int) result; + } +# define AO_HAVE_compare_double_and_swap_double_full +# endif /* !AO_CMPXCHG16B_AVAILABLE */ + +#endif /* x64 */ + +/* Real X86 implementations, except for some old 32-bit WinChips, */ +/* appear to enforce ordering between memory operations, EXCEPT that */ +/* a later read can pass earlier writes, presumably due to the visible */ +/* presence of store buffers. */ +/* We ignore both the WinChips and the fact that the official specs */ +/* seem to be much weaker (and arguably too weak to be usable). */ +#include "../ordered_except_wr.h" diff --git a/libatomic_ops/src/atomic_ops/sysdeps/test_and_set_t_is_ao_t.h b/libatomic_ops/src/atomic_ops/sysdeps/test_and_set_t_is_ao_t.h new file mode 100644 index 000000000..606f7ac6b --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/test_and_set_t_is_ao_t.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * These are common definitions for architectures on which test_and_set + * operates on pointer-sized quantities, the "clear" value contains + * all zeroes, and the "set" value contains only one lowest bit set. + * This can be used if test_and_set is synthesized from compare_and_swap. + */ +typedef enum {AO_TS_clear = 0, AO_TS_set = 1} AO_TS_val; +#define AO_TS_VAL_t AO_TS_val +#define AO_TS_CLEAR AO_TS_clear +#define AO_TS_SET AO_TS_set + +#define AO_TS_t AO_t + +#define AO_AO_TS_T 1 diff --git a/libatomic_ops/src/atomic_ops/sysdeps/test_and_set_t_is_char.h b/libatomic_ops/src/atomic_ops/sysdeps/test_and_set_t_is_char.h new file mode 100644 index 000000000..9eb250ccd --- /dev/null +++ b/libatomic_ops/src/atomic_ops/sysdeps/test_and_set_t_is_char.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * These are common definitions for architectures on which test_and_set + * operates on byte sized quantities, the "clear" value contains + * all zeroes, and the "set" value contains all ones typically. + */ + +#ifndef AO_GCC_ATOMIC_TEST_AND_SET +# define AO_TS_SET_TRUEVAL 0xff +#elif defined(__GCC_ATOMIC_TEST_AND_SET_TRUEVAL) \ + && !defined(AO_PREFER_GENERALIZED) +# define AO_TS_SET_TRUEVAL __GCC_ATOMIC_TEST_AND_SET_TRUEVAL +#else +# define AO_TS_SET_TRUEVAL 1 /* true */ +#endif + +typedef enum { + AO_BYTE_TS_clear = 0, + AO_BYTE_TS_set = AO_TS_SET_TRUEVAL +} AO_BYTE_TS_val; + +#define AO_TS_VAL_t AO_BYTE_TS_val +#define AO_TS_CLEAR AO_BYTE_TS_clear +#define AO_TS_SET AO_BYTE_TS_set + +#define AO_TS_t unsigned char + +#define AO_CHAR_TS_T 1 + +#undef AO_TS_SET_TRUEVAL diff --git a/libatomic_ops/src/atomic_ops_malloc.c b/libatomic_ops/src/atomic_ops_malloc.c new file mode 100644 index 000000000..23d2518fa --- /dev/null +++ b/libatomic_ops/src/atomic_ops_malloc.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if defined(HAVE_CONFIG_H) +# include "config.h" +#endif + +#ifdef DONT_USE_MMAP /* for testing */ +# undef HAVE_MMAP +#endif + +#ifndef AO_BUILD +# define AO_BUILD +#endif + +#define AO_REQUIRE_CAS +#include "atomic_ops_malloc.h" + +#include /* for ffs, which is assumed reentrant. */ +#include +#include + +#ifdef AO_TRACE_MALLOC +# include +# include +#endif + +#if defined(AO_ADDRESS_SANITIZER) && !defined(AO_NO_MALLOC_POISON) + /* #include "sanitizer/asan_interface.h" */ + void __asan_poison_memory_region(void *, size_t); + void __asan_unpoison_memory_region(void *, size_t); +# define ASAN_POISON_MEMORY_REGION(addr, size) \ + __asan_poison_memory_region(addr, size) +# define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ + __asan_unpoison_memory_region(addr, size) +#else +# define ASAN_POISON_MEMORY_REGION(addr, size) (void)0 +# define ASAN_UNPOISON_MEMORY_REGION(addr, size) (void)0 +#endif /* !AO_ADDRESS_SANITIZER */ + +#if (defined(_WIN32_WCE) || defined(__MINGW32CE__)) && !defined(AO_HAVE_abort) +# define abort() _exit(-1) /* there is no abort() in WinCE */ +#endif + +/* + * We round up each allocation request to the next power of two + * minus one word. + * We keep one stack of free objects for each size. Each object + * has an initial word (offset -sizeof(AO_t) from the visible pointer) + * which contains either + * The binary log of the object size in bytes (small objects) + * The object size (a multiple of CHUNK_SIZE) for large objects. + * The second case only arises if mmap-based allocation is supported. + * We align the user-visible part of each object on an ALIGNMENT + * byte boundary. That means that the actual (hidden) start of + * the object starts a word before this boundary. + */ + +#ifndef LOG_MAX_SIZE +# define LOG_MAX_SIZE 16 + /* We assume that 2**LOG_MAX_SIZE is a multiple of page size. */ +#endif + +#ifndef ALIGNMENT +# define ALIGNMENT 16 + /* Assumed to be at least sizeof(AO_t). */ +#endif + +#define CHUNK_SIZE (1 << LOG_MAX_SIZE) + +#ifndef AO_INITIAL_HEAP_SIZE +# define AO_INITIAL_HEAP_SIZE (2*(LOG_MAX_SIZE+1)*CHUNK_SIZE) +#endif + +char AO_initial_heap[AO_INITIAL_HEAP_SIZE]; + +static volatile AO_t initial_heap_ptr = (AO_t)AO_initial_heap; + +#if defined(HAVE_MMAP) + +#include +#include +#include +#include + +#if defined(MAP_ANONYMOUS) || defined(MAP_ANON) +# define USE_MMAP_ANON +#endif + +#ifdef USE_MMAP_FIXED +# define GC_MMAP_FLAGS (MAP_FIXED | MAP_PRIVATE) + /* Seems to yield better performance on Solaris 2, but can */ + /* be unreliable if something is already mapped at the address. */ +#else +# define GC_MMAP_FLAGS MAP_PRIVATE +#endif + +#ifdef USE_MMAP_ANON +# if defined(CPPCHECK) +# define OPT_MAP_ANON 0x20 /* taken from linux */ +# elif defined(MAP_ANONYMOUS) +# define OPT_MAP_ANON MAP_ANONYMOUS +# else +# define OPT_MAP_ANON MAP_ANON +# endif +#else +# include /* for close() */ +# define OPT_MAP_ANON 0 +#endif + +static volatile AO_t mmap_enabled = 0; + +AO_API void +AO_malloc_enable_mmap(void) +{ +# if defined(__sun) + AO_store_release(&mmap_enabled, 1); + /* Workaround for Sun CC */ +# else + AO_store(&mmap_enabled, 1); +# endif +} + +static char *get_mmaped(size_t sz) +{ + char * result; +# ifdef USE_MMAP_ANON +# define zero_fd -1 +# else + int zero_fd; +# endif + + assert(!(sz & (CHUNK_SIZE - 1))); + if (!mmap_enabled) + return 0; + +# ifndef USE_MMAP_ANON + zero_fd = open("/dev/zero", O_RDONLY); + if (zero_fd == -1) + return 0; +# endif + result = (char *)mmap(0, sz, PROT_READ | PROT_WRITE, + GC_MMAP_FLAGS | OPT_MAP_ANON, + zero_fd, 0 /* offset */); +# ifndef USE_MMAP_ANON + close(zero_fd); +# endif + if (AO_EXPECT_FALSE(result == MAP_FAILED)) + result = NULL; + return result; +} + +#ifndef SIZE_MAX +# include +#endif +#if defined(SIZE_MAX) && !defined(CPPCHECK) +# define AO_SIZE_MAX ((size_t)SIZE_MAX) + /* Extra cast to workaround some buggy SIZE_MAX definitions. */ +#else +# define AO_SIZE_MAX (~(size_t)0) +#endif + +/* Saturated addition of size_t values. Used to avoid value wrap */ +/* around on overflow. The arguments should have no side effects. */ +#define SIZET_SAT_ADD(a, b) \ + (AO_EXPECT_FALSE((a) >= AO_SIZE_MAX - (b)) ? AO_SIZE_MAX : (a) + (b)) + +/* Allocate an object of size (incl. header) of size > CHUNK_SIZE. */ +/* sz includes space for an AO_t-sized header. */ +static char * +AO_malloc_large(size_t sz) +{ + void *result; + + /* The header will force us to waste ALIGNMENT bytes, incl. header. */ + /* Round to multiple of CHUNK_SIZE. */ + sz = SIZET_SAT_ADD(sz, ALIGNMENT + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1); + assert(sz > LOG_MAX_SIZE); + result = get_mmaped(sz); + if (AO_EXPECT_FALSE(NULL == result)) + return NULL; + + result = (AO_t *)result + ALIGNMENT / sizeof(AO_t); + ((AO_t *)result)[-1] = (AO_t)sz; + return (char *)result; +} + +static void +AO_free_large(void *p) +{ + AO_t sz = ((AO_t *)p)[-1]; + if (munmap((AO_t *)p - ALIGNMENT / sizeof(AO_t), (size_t)sz) != 0) + abort(); /* Programmer error. Not really async-signal-safe, but ... */ +} + + +#else /* No MMAP */ + +AO_API void +AO_malloc_enable_mmap(void) +{ +} + +#define get_mmaped(sz) ((char*)0) +#define AO_malloc_large(sz) ((char*)0) +#define AO_free_large(p) abort() + /* Programmer error. Not really async-signal-safe, but ... */ + +#endif /* No MMAP */ + +static char * +get_chunk(void) +{ + char *my_chunk_ptr; + + for (;;) { + char *initial_ptr = (char *)AO_load(&initial_heap_ptr); + + my_chunk_ptr = (char *)(((AO_t)initial_ptr + (ALIGNMENT - 1)) + & ~(ALIGNMENT - 1)); + if (initial_ptr != my_chunk_ptr) + { + /* Align correctly. If this fails, someone else did it for us. */ + (void)AO_compare_and_swap_acquire(&initial_heap_ptr, + (AO_t)initial_ptr, (AO_t)my_chunk_ptr); + } + + if (AO_EXPECT_FALSE((AO_t)my_chunk_ptr - (AO_t)AO_initial_heap + > (size_t)(AO_INITIAL_HEAP_SIZE - CHUNK_SIZE))) { + /* We failed. The initial heap is used up. */ + my_chunk_ptr = get_mmaped(CHUNK_SIZE); +# if !defined(CPPCHECK) + assert(((AO_t)my_chunk_ptr & (ALIGNMENT-1)) == 0); +# endif + break; + } + if (AO_compare_and_swap(&initial_heap_ptr, (AO_t)my_chunk_ptr, + (AO_t)(my_chunk_ptr + CHUNK_SIZE))) { + break; + } + } + return my_chunk_ptr; +} + +/* Object free lists. Ith entry corresponds to objects */ +/* of total size 2**i bytes. */ +static AO_stack_t AO_free_list[LOG_MAX_SIZE+1]; + +/* Break up the chunk, and add it to the object free list for */ +/* the given size. We have exclusive access to chunk. */ +static void add_chunk_as(void * chunk, unsigned log_sz) +{ + size_t ofs, limit; + size_t sz = (size_t)1 << log_sz; + + assert((size_t)CHUNK_SIZE >= sz); + assert(sz % sizeof(AO_t) == 0); + limit = (size_t)CHUNK_SIZE - sz; + for (ofs = ALIGNMENT - sizeof(AO_t); ofs <= limit; ofs += sz) { + ASAN_POISON_MEMORY_REGION((char *)chunk + ofs + sizeof(AO_t), + sz - sizeof(AO_t)); + AO_stack_push(&AO_free_list[log_sz], (AO_t *)chunk + ofs / sizeof(AO_t)); + } +} + +static const unsigned char msbs[16] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 +}; + +/* Return the position of the most significant set bit in the */ +/* argument. */ +/* We follow the conventions of ffs(), i.e. the least */ +/* significant bit is number one. */ +static unsigned msb(size_t s) +{ + unsigned result = 0; + if ((s & 0xff) != s) { +# if (__SIZEOF_SIZE_T__ == 8) && !defined(CPPCHECK) + unsigned v = (unsigned)(s >> 32); + if (AO_EXPECT_FALSE(v != 0)) + { + s = v; + result += 32; + } +# elif __SIZEOF_SIZE_T__ == 4 + /* No op. */ +# else + unsigned v; + /* The following is a tricky code ought to be equivalent to */ + /* "(v = s >> 32) != 0" but suppresses warnings on 32-bit arch's. */ +# define SIZEOF_SIZE_T_GT_4 (sizeof(size_t) > 4) + if (SIZEOF_SIZE_T_GT_4 + && (v = (unsigned)(s >> (SIZEOF_SIZE_T_GT_4 ? 32 : 0))) != 0) + { + s = v; + result += 32; + } +# endif /* !defined(__SIZEOF_SIZE_T__) */ + if (AO_EXPECT_FALSE((s >> 16) != 0)) + { + s >>= 16; + result += 16; + } + if ((s >> 8) != 0) + { + s >>= 8; + result += 8; + } + } + if (s > 15) + { + s >>= 4; + result += 4; + } + result += msbs[s]; + return result; +} + +AO_API AO_ATTR_MALLOC AO_ATTR_ALLOC_SIZE(1) +void * +AO_malloc(size_t sz) +{ + AO_t *result; + unsigned log_sz; + + if (AO_EXPECT_FALSE(sz > CHUNK_SIZE - sizeof(AO_t))) + return AO_malloc_large(sz); + log_sz = msb(sz + (sizeof(AO_t) - 1)); + assert(log_sz <= LOG_MAX_SIZE); + assert(((size_t)1 << log_sz) >= sz + sizeof(AO_t)); + result = AO_stack_pop(AO_free_list+log_sz); + while (AO_EXPECT_FALSE(NULL == result)) { + void * chunk = get_chunk(); + + if (AO_EXPECT_FALSE(NULL == chunk)) + return NULL; + add_chunk_as(chunk, log_sz); + result = AO_stack_pop(AO_free_list+log_sz); + } + *result = log_sz; +# ifdef AO_TRACE_MALLOC + fprintf(stderr, "%p: AO_malloc(%lu) = %p\n", + (void *)pthread_self(), (unsigned long)sz, (void *)(result + 1)); +# endif + ASAN_UNPOISON_MEMORY_REGION(result + 1, sz); + return result + 1; +} + +AO_API void +AO_free(void *p) +{ + AO_t *base; + int log_sz; + + if (AO_EXPECT_FALSE(NULL == p)) + return; + + base = (AO_t *)p - 1; + log_sz = (int)(*base); +# ifdef AO_TRACE_MALLOC + fprintf(stderr, "%p: AO_free(%p sz:%lu)\n", (void *)pthread_self(), p, + log_sz > LOG_MAX_SIZE ? (unsigned)log_sz : 1UL << log_sz); +# endif + if (AO_EXPECT_FALSE(log_sz > LOG_MAX_SIZE)) { + AO_free_large(p); + } else { + ASAN_POISON_MEMORY_REGION(base + 1, ((size_t)1 << log_sz) - sizeof(AO_t)); + AO_stack_push(AO_free_list + log_sz, base); + } +} diff --git a/libatomic_ops/src/atomic_ops_malloc.h b/libatomic_ops/src/atomic_ops_malloc.h new file mode 100644 index 000000000..be997f5bb --- /dev/null +++ b/libatomic_ops/src/atomic_ops_malloc.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Almost lock-free malloc implementation based on stack implementation. */ +/* See README_malloc.txt file for detailed usage rules. */ + +#ifndef AO_MALLOC_H +#define AO_MALLOC_H + +#include "atomic_ops_stack.h" + +#include /* for size_t */ + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef AO_STACK_IS_LOCK_FREE +# define AO_MALLOC_IS_LOCK_FREE +#endif + +#ifndef AO_ATTR_MALLOC +# if AO_GNUC_PREREQ(3, 1) +# define AO_ATTR_MALLOC __attribute__((__malloc__)) +# elif defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(__EDG__) +# define AO_ATTR_MALLOC \ + __declspec(allocator) __declspec(noalias) __declspec(restrict) +# elif defined(_MSC_VER) && _MSC_VER >= 1400 +# define AO_ATTR_MALLOC __declspec(noalias) __declspec(restrict) +# else +# define AO_ATTR_MALLOC /* empty */ +# endif +#endif + +#ifndef AO_ATTR_ALLOC_SIZE +# ifdef __clang__ +# if __has_attribute(__alloc_size__) +# define AO_ATTR_ALLOC_SIZE(argnum) \ + __attribute__((__alloc_size__(argnum))) +# else +# define AO_ATTR_ALLOC_SIZE(argnum) /* empty */ +# endif +# elif AO_GNUC_PREREQ(4, 3) && !defined(__ICC) +# define AO_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum))) +# else +# define AO_ATTR_ALLOC_SIZE(argnum) /* empty */ +# endif +#endif + +AO_API void AO_free(void *); + +AO_API AO_ATTR_MALLOC AO_ATTR_ALLOC_SIZE(1) +void * AO_malloc(size_t); + +/* Allow use of mmap to grow the heap. No-op on some platforms. */ +AO_API void AO_malloc_enable_mmap(void); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* !AO_MALLOC_H */ diff --git a/libatomic_ops/src/atomic_ops_stack.c b/libatomic_ops/src/atomic_ops_stack.c new file mode 100644 index 000000000..3a8c1f8e2 --- /dev/null +++ b/libatomic_ops/src/atomic_ops_stack.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if defined(HAVE_CONFIG_H) +# include "config.h" +#endif + +#include +#include +#include + +#ifndef AO_BUILD +# define AO_BUILD +#endif + +#ifndef AO_REAL_PTR_AS_MACRO +# define AO_REAL_PTR_AS_MACRO +#endif + +#define AO_REQUIRE_CAS +#include "atomic_ops_stack.h" + +AO_API void AO_stack_init(AO_stack_t *list) +{ + memset(list, 0, sizeof(AO_stack_t)); +} + +AO_API int AO_stack_is_lock_free(void) +{ +# ifdef AO_USE_ALMOST_LOCK_FREE + return 0; +# else + return 1; +# endif +} + +AO_API AO_t *AO_stack_head_ptr(const AO_stack_t *list) +{ + return AO_REAL_HEAD_PTR(*list); +} + +AO_API AO_t *AO_stack_next_ptr(AO_t next) +{ + return AO_REAL_NEXT_PTR(next); +} + +/* This function call must be a part of a do-while loop with a CAS */ +/* designating the condition of the loop (see the use cases below). */ +#ifdef AO_THREAD_SANITIZER + AO_ATTR_NO_SANITIZE_THREAD + static void store_before_cas(AO_t *addr, AO_t value) + { + *addr = value; + } +#else +# define store_before_cas(addr, value) (void)(*(addr) = (value)) +#endif + +#ifdef AO_USE_ALMOST_LOCK_FREE + +# ifdef __cplusplus + extern "C" { +# endif + AO_API void AO_pause(int); /* defined in atomic_ops.c */ +# ifdef __cplusplus + } /* extern "C" */ +# endif + +# if defined(__alpha__) && (__GNUC__ == 4) + /* Workaround __builtin_expect bug found in */ + /* gcc-4.6.3/alpha causing test_stack failure. */ +# undef AO_EXPECT_FALSE +# define AO_EXPECT_FALSE(expr) (expr) +# endif + + /* LIFO linked lists based on compare-and-swap. We need to avoid */ + /* the case of a node deletion and reinsertion while I'm deleting */ + /* it, since that may cause my CAS to succeed eventhough the next */ + /* pointer is now wrong. Our solution is not fully lock-free, but it */ + /* is good enough for signal handlers, provided we have a suitably */ + /* low bound on the number of recursive signal handler reentries. */ + /* A list consists of a first pointer and a blacklist of pointer */ + /* values that are currently being removed. No list element on */ + /* the blacklist may be inserted. If we would otherwise do so, we */ + /* are allowed to insert a variant that differs only in the least */ + /* significant, ignored, bits. If the list is full, we wait. */ + + /* Crucial observation: A particular padded pointer x (i.e. pointer */ + /* plus arbitrary low order bits) can never be newly inserted into */ + /* a list while it's in the corresponding auxiliary data structure. */ + + /* The second argument is a pointer to the link field of the element */ + /* to be inserted. */ + /* Both list headers and link fields contain "perturbed" pointers, */ + /* i.e. pointers with extra bits or'ed into the low order bits. */ + AO_API void AO_stack_push_explicit_aux_release(volatile AO_t *list, AO_t *x, + AO_stack_aux *a) + { + AO_t x_bits = (AO_t)x; + AO_t next; + + /* No deletions of x can start here, since x is not */ + /* currently in the list. */ + retry: + do { + next = AO_load_acquire(list); + store_before_cas(x, next); + { +# if AO_BL_SIZE == 2 + /* Start all loads as close to concurrently as possible. */ + AO_t entry1 = AO_load(&a->AO_stack_bl[0]); + AO_t entry2 = AO_load(&a->AO_stack_bl[1]); + if (AO_EXPECT_FALSE(entry1 == x_bits || entry2 == x_bits)) +# else + int i; + for (i = 0; i < AO_BL_SIZE; ++i) + if (AO_EXPECT_FALSE(AO_load(&a->AO_stack_bl[i]) == x_bits)) +# endif + { + /* Entry is currently being removed. Change it a little. */ + ++x_bits; + if ((x_bits & AO_BIT_MASK) == 0) + /* Version count overflowed; EXTREMELY unlikely, but possible. */ + x_bits = (AO_t)x; + goto retry; + } + } + + /* x_bits value is not currently being deleted. */ + } while (AO_EXPECT_FALSE(!AO_compare_and_swap_release(list, next, + x_bits))); + } + + /* I concluded experimentally that checking a value first before */ + /* performing a compare-and-swap is usually beneficial on x86, but */ + /* slows things down appreciably with contention on Itanium. */ + /* Since the Itanium behavior makes more sense to me (more cache line */ + /* movement unless we're mostly reading, but back-off should guard */ + /* against that), we take Itanium as the default. Measurements on */ + /* other multiprocessor architectures would be useful. */ + /* (On a uniprocessor, the initial check is almost certainly a very */ + /* small loss.) - HB */ +# ifdef __i386__ +# define PRECHECK(a) (a) == 0 && +# else +# define PRECHECK(a) +# endif + + /* This function is used before CAS in the below AO_stack_pop and the */ + /* data race (reported by TSan) is OK because it results in a retry. */ +# ifdef AO_THREAD_SANITIZER + AO_ATTR_NO_SANITIZE_THREAD + static AO_t AO_load_next(const volatile AO_t *first_ptr) + { + /* Assuming an architecture on which loads of word type are */ + /* atomic. AO_load cannot be used here because it cannot be */ + /* instructed to suppress the warning about the race. */ + return *first_ptr; + } +# else +# define AO_load_next AO_load +# endif + + AO_API AO_t *AO_stack_pop_explicit_aux_acquire(volatile AO_t *list, + AO_stack_aux *a) + { + unsigned i; + int j = 0; + AO_t first; + AO_t * first_ptr; + AO_t next; + + retry: + first = AO_load(list); + if (0 == first) return 0; + /* Insert first into aux black list. */ + /* This may spin if more than AO_BL_SIZE removals using auxiliary */ + /* structure a are currently in progress. */ + for (i = 0; ; ) + { + if (PRECHECK(a -> AO_stack_bl[i]) + AO_compare_and_swap_acquire(a->AO_stack_bl+i, 0, first)) + break; + if (++i >= AO_BL_SIZE) + { + i = 0; + AO_pause(++j); + } + } +# ifndef AO_THREAD_SANITIZER + assert(a -> AO_stack_bl[i] == first); + /* No actual race with the above CAS. */ +# endif + /* first is on the auxiliary black list. It may be removed by */ + /* another thread before we get to it, but a new insertion of x */ + /* cannot be started here. Only we can remove it from the black */ + /* list. We need to make sure that first is still the first entry */ + /* on the list. Otherwise it is possible that a reinsertion of it */ + /* was already started before we added the black list entry. */ + if (AO_EXPECT_FALSE(first != AO_load_acquire(list))) + /* Workaround test failure on AIX, at least, by */ + /* using acquire ordering semantics for this */ + /* load. Probably, it is not the right fix. */ + { + AO_store_release(a->AO_stack_bl+i, 0); + goto retry; + } + first_ptr = AO_REAL_NEXT_PTR(first); + next = AO_load_next(first_ptr); + if (AO_EXPECT_FALSE(!AO_compare_and_swap_release(list, first, next))) + { + AO_store_release(a->AO_stack_bl+i, 0); + goto retry; + } +# ifndef AO_THREAD_SANITIZER + assert(*list != first); /* No actual race with the above CAS. */ +# endif + /* Since we never insert an entry on the black list, this cannot */ + /* have succeeded unless first remained on the list while we were */ + /* running. Thus, its next link cannot have changed out from under */ + /* us, and we removed exactly one entry and preserved the rest of */ + /* the list. Note that it is quite possible that an additional */ + /* entry was inserted and removed while we were running; this is OK */ + /* since the part of the list following first must have remained */ + /* unchanged, and first must again have been at the head of the */ + /* list when the compare_and_swap succeeded. */ + AO_store_release(a->AO_stack_bl+i, 0); + return first_ptr; + } + + AO_API void AO_stack_push_release(AO_stack_t *list, AO_t *x) + { + AO_stack_push_explicit_aux_release(&list->AO_pa.AO_ptr, x, + &list->AO_pa.AO_aux); + } + + AO_API AO_t *AO_stack_pop_acquire(AO_stack_t *list) + { + return AO_stack_pop_explicit_aux_acquire(&list->AO_pa.AO_ptr, + &list->AO_pa.AO_aux); + } + +#else /* !AO_USE_ALMOST_LOCK_FREE */ + + /* The functionality is the same as of AO_load_next but the atomicity */ + /* is not needed. The usage is similar to that of store_before_cas. */ +# if defined(AO_THREAD_SANITIZER) \ + && (defined(AO_HAVE_compare_and_swap_double) \ + || defined(AO_HAVE_compare_double_and_swap_double)) + /* TODO: If compiled by Clang (as of clang-4.0) with -O3 flag, */ + /* no_sanitize attribute is ignored unless the argument is volatile.*/ +# if defined(__clang__) +# define LOAD_BEFORE_CAS_VOLATILE volatile +# else +# define LOAD_BEFORE_CAS_VOLATILE /* empty */ +# endif + AO_ATTR_NO_SANITIZE_THREAD + static AO_t load_before_cas(const LOAD_BEFORE_CAS_VOLATILE AO_t *addr) + { + return *addr; + } +# else +# define load_before_cas(addr) (*(addr)) +# endif /* !AO_THREAD_SANITIZER */ + + /* Better names for fields in AO_stack_t. */ +# define version AO_vp.AO_val1 +# define ptr AO_vp.AO_val2 + +# if defined(AO_HAVE_compare_double_and_swap_double) \ + && !(defined(AO_STACK_PREFER_CAS_DOUBLE) \ + && defined(AO_HAVE_compare_and_swap_double)) + +# ifdef LINT2 + volatile /* non-static */ AO_t AO_noop_sink; +# endif + + AO_API void AO_stack_push_release(AO_stack_t *list, AO_t *element) + { + AO_t next; + + do { + next = AO_load(&list->ptr); + store_before_cas(element, next); + } while (AO_EXPECT_FALSE(!AO_compare_and_swap_release(&list->ptr, next, + (AO_t)element))); + /* This uses a narrow CAS here, an old optimization suggested */ + /* by Treiber. Pop is still safe, since we run into the ABA */ + /* problem only if there were both intervening pops and pushes. */ + /* In that case we still see a change in the version number. */ +# ifdef LINT2 + /* Instruct static analyzer that element is not lost. */ + AO_noop_sink = (AO_t)element; +# endif + } + + AO_API AO_t *AO_stack_pop_acquire(AO_stack_t *list) + { +# if defined(__clang__) && !AO_CLANG_PREREQ(3, 5) + AO_t *volatile cptr; + /* Use volatile to workaround a bug in */ + /* clang-1.1/x86 causing test_stack failure. */ +# else + AO_t *cptr; +# endif + AO_t next; + AO_t cversion; + + do { + /* Version must be loaded first. */ + cversion = AO_load_acquire(&list->version); + cptr = (AO_t *)AO_load(&list->ptr); + if (NULL == cptr) + return NULL; + next = load_before_cas((/* no volatile */ AO_t *)cptr); + } while (AO_EXPECT_FALSE(!AO_compare_double_and_swap_double_release( + &list->AO_vp, cversion, (AO_t)cptr, + cversion+1, next))); + return cptr; + } + +# elif defined(AO_HAVE_compare_and_swap_double) + + /* Needed for future IA64 processors. No current clients? */ + /* TODO: Not tested thoroughly. */ + + /* We have a wide CAS, but only does an AO_t-wide comparison. */ + /* We cannot use the Treiber optimization, since we only check */ + /* for an unchanged version number, not an unchanged pointer. */ + AO_API void AO_stack_push_release(AO_stack_t *list, AO_t *element) + { + AO_t cversion; + + do { + AO_t next_ptr; + + /* Again version must be loaded first, for different reason. */ + cversion = AO_load_acquire(&list->version); + next_ptr = AO_load(&list->ptr); + store_before_cas(element, next_ptr); + } while (!AO_compare_and_swap_double_release(&list->AO_vp, cversion, + cversion+1, (AO_t)element)); + } + + AO_API AO_t *AO_stack_pop_acquire(AO_stack_t *list) + { + AO_t *cptr; + AO_t next; + AO_t cversion; + + do { + cversion = AO_load_acquire(&list->version); + cptr = (AO_t *)AO_load(&list->ptr); + if (NULL == cptr) + return NULL; + next = load_before_cas(cptr); + } while (!AO_compare_double_and_swap_double_release(&list->AO_vp, + cversion, (AO_t)cptr, cversion+1, next)); + return cptr; + } +# endif /* AO_HAVE_compare_and_swap_double */ + +# undef ptr +# undef version +#endif /* !AO_USE_ALMOST_LOCK_FREE */ diff --git a/libatomic_ops/src/atomic_ops_stack.h b/libatomic_ops/src/atomic_ops_stack.h new file mode 100644 index 000000000..5efea4e67 --- /dev/null +++ b/libatomic_ops/src/atomic_ops_stack.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * The implementation of the routines described here is covered by the GPL. + * This header file is covered by the above license. + */ + +/* Almost lock-free LIFO linked lists (linked stacks). */ +#ifndef AO_STACK_H +#define AO_STACK_H + +#include "atomic_ops.h" + +#ifndef AO_HAVE_double_t + /* Can happen if we are using CAS emulation, since we do not want to */ + /* force that here, in case other atomic_ops clients do not want it. */ +# include "atomic_ops/sysdeps/standard_ao_double_t.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef AO_USE_ALMOST_LOCK_FREE + /* Use the almost-non-blocking implementation regardless of the */ + /* double-word CAS availability. */ +#elif !defined(AO_HAVE_compare_double_and_swap_double) \ + && !defined(AO_HAVE_compare_and_swap_double) \ + && defined(AO_HAVE_compare_and_swap) +# define AO_USE_ALMOST_LOCK_FREE +#else + /* If we have no compare-and-swap operation defined, we assume */ + /* that we will actually be using CAS emulation. If we do that, */ + /* it's cheaper to use the version-based implementation. */ +# define AO_STACK_IS_LOCK_FREE +#endif + +/* + * These are not guaranteed to be completely lock-free. + * List insertion may spin under extremely unlikely conditions. + * It cannot deadlock due to recursive reentry unless AO_list_remove + * is called while at least AO_BL_SIZE activations of + * AO_list_remove are currently active in the same thread, i.e. + * we must have at least AO_BL_SIZE recursive signal handler + * invocations. + * + * All operations take an AO_list_aux argument. It is safe to + * share a single AO_list_aux structure among all lists, but that + * may increase contention. Any given list must always be accessed + * with the same AO_list_aux structure. + * + * We make some machine-dependent assumptions: + * - We have a compare-and-swap operation. + * - At least AO_N_BITS low order bits in pointers are + * zero and normally unused. + * - size_t and pointers have the same size. + * + * We do use a fully lock-free implementation if double-width + * compare-and-swap operations are available. + */ + +/* AO_stack_aux should be treated as opaque. It is fully defined */ +/* here, so it can be allocated, and also to facilitate debugging. */ +/* Note: changing the value of AO_BL_SIZE leads to the ABI change. */ +#ifndef AO_BL_SIZE +# define AO_BL_SIZE 2 +#endif + +/* The number of low order pointer bits we can use for a small */ +/* version number. */ +#if defined(__LP64__) || defined(_LP64) || defined(_WIN64) +# define AO_N_BITS 3 +#else +# define AO_N_BITS 2 +#endif + +#define AO_BIT_MASK ((1 << AO_N_BITS) - 1) + +#if AO_BL_SIZE > (1 << AO_N_BITS) +# error AO_BL_SIZE too big +#endif + +#ifndef AO_STACK_ATTR_ALLIGNED + /* Enforce proper alignment of AO_stack_t.AO_pa to avoid the */ + /* structure value to cross the CPU cache line boundary. */ + /* A workaround for almost-lock-free push/pop test failures */ + /* on aarch64, at least. */ +# if AO_BL_SIZE == 1 + /* AO_vp is double-word aligned, so no extra align of AO_pa is needed. */ +# define AO_STACK_ATTR_ALLIGNED /* empty */ +# elif AO_GNUC_PREREQ(3, 1) +# define AO_STACK_LOG_BL_SZP1 (AO_BL_SIZE > 7 ? 4 : AO_BL_SIZE > 3 ? 3 : 2) +# define AO_STACK_ATTR_ALLIGNED \ + __attribute__((__aligned__(sizeof(AO_t) << AO_STACK_LOG_BL_SZP1))) +# elif defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio 2005+ */ + /* MS compiler accepts only a literal number in align, not expression. */ + /* AO_STACK_ALLIGN_N is 1 << (AO_N_BITS + AO_STACK_LOG_BL_SZP1). */ +# if AO_N_BITS > 2 && AO_BL_SIZE > 7 +# define AO_STACK_ALLIGN_N 128 +# elif (AO_N_BITS > 2 && AO_BL_SIZE > 3) || AO_BL_SIZE > 7 +# define AO_STACK_ALLIGN_N 64 +# elif AO_N_BITS > 2 || AO_BL_SIZE > 3 +# define AO_STACK_ALLIGN_N 32 +# else +# define AO_STACK_ALLIGN_N 16 +# endif +# define AO_STACK_ATTR_ALLIGNED __declspec(align(AO_STACK_ALLIGN_N)) +# else +# define AO_STACK_ATTR_ALLIGNED /* TODO: alignment is not enforced */ +# endif +#endif /* !AO_STACK_ATTR_ALLIGNED */ + +typedef struct AO__stack_aux { + volatile AO_t AO_stack_bl[AO_BL_SIZE]; +} AO_stack_aux; + +struct AO__stack_ptr_aux { + volatile AO_t AO_ptr; + AO_stack_aux AO_aux; +}; + +/* The AO stack type. Should be treated as opaque. */ +/* Note: AO_stack_t variables are not intended to be local ones, */ +/* otherwise it is the client responsibility to ensure they have */ +/* double-word alignment. */ +typedef union AO__stack { + AO_STACK_ATTR_ALLIGNED struct AO__stack_ptr_aux AO_pa; + volatile AO_double_t AO_vp; +} AO_stack_t; + +/* The static initializer of the AO stack type. */ +#define AO_STACK_INITIALIZER { /* .AO_pa= */ { 0, { {0} } } } + +#ifdef AO_USE_ALMOST_LOCK_FREE + /* The following two routines should not normally be used directly. */ + /* We make them visible here for the rare cases in which it makes */ + /* sense to share the AO_stack_aux between stacks. */ + + AO_API void + AO_stack_push_explicit_aux_release(volatile AO_t *list, AO_t *new_element, + AO_stack_aux *); + + AO_API AO_t * + AO_stack_pop_explicit_aux_acquire(volatile AO_t *list, AO_stack_aux *); +#endif /* AO_USE_ALMOST_LOCK_FREE */ + +#ifndef AO_REAL_PTR_AS_MACRO + /* The stack implementation knows only about the location of */ + /* link fields in nodes, and nothing about the rest of the */ + /* stack elements. Link fields hold an AO_t, which is not */ + /* necessarily a real pointer. This converts the AO_t to a */ + /* real (AO_t *) which is either NULL, or points at the link */ + /* field in the next node. */ +# define AO_REAL_NEXT_PTR(x) AO_stack_next_ptr(x) + + /* Convert an AO_stack_t to a pointer to the link field in */ + /* the first element. */ +# define AO_REAL_HEAD_PTR(x) AO_stack_head_ptr(&(x)) + +#elif defined(AO_USE_ALMOST_LOCK_FREE) +# define AO_REAL_NEXT_PTR(x) (AO_t *)((x) & ~AO_BIT_MASK) +# define AO_REAL_HEAD_PTR(x) AO_REAL_NEXT_PTR((x).AO_pa.AO_ptr) +#else +# define AO_REAL_NEXT_PTR(x) (AO_t *)(x) +# define AO_REAL_HEAD_PTR(x) (AO_t *)((x).AO_vp.AO_val2 /* ptr */) +#endif /* AO_REAL_PTR_AS_MACRO && !AO_USE_ALMOST_LOCK_FREE */ + +AO_API void AO_stack_push_release(AO_stack_t *list, AO_t *new_element); +#define AO_HAVE_stack_push_release + +#define AO_stack_push(l, e) AO_stack_push_release(l, e) +#define AO_HAVE_stack_push + +AO_API AO_t *AO_stack_pop_acquire(AO_stack_t *list); +#define AO_HAVE_stack_pop_acquire + +#define AO_stack_pop(l) AO_stack_pop_acquire(l) +#define AO_HAVE_stack_pop + +AO_API void AO_stack_init(AO_stack_t *list); +AO_API int AO_stack_is_lock_free(void); + +AO_API AO_t *AO_stack_head_ptr(const AO_stack_t *list); +AO_API AO_t *AO_stack_next_ptr(AO_t /* next */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* !AO_STACK_H */ diff --git a/libatomic_ops/src/atomic_ops_sysdeps.S b/libatomic_ops/src/atomic_ops_sysdeps.S new file mode 100644 index 000000000..f586f23ba --- /dev/null +++ b/libatomic_ops/src/atomic_ops_sysdeps.S @@ -0,0 +1,9 @@ +/* + * Include the appropriate system-dependent assembly file, if any. + * This is used only if the platform supports neither inline assembly + * code, nor appropriate compiler intrinsics. + */ + +#if !defined(__GNUC__) && (defined(sparc) || defined(__sparc)) +# include "atomic_ops/sysdeps/sunc/sparc.S" +#endif diff --git a/libatomic_ops/tests/Makefile.am b/libatomic_ops/tests/Makefile.am new file mode 100644 index 000000000..60b739be7 --- /dev/null +++ b/libatomic_ops/tests/Makefile.am @@ -0,0 +1,148 @@ +EXTRA_DIST=test_atomic_include.template list_atomic.template run_parallel.h \ + test_atomic_include.h list_atomic.c +# We distribute test_atomic_include.h and list_atomic.c, since it is hard +# to regenerate them on Windows without sed. + +BUILT_SOURCES = test_atomic_include.h list_atomic.i list_atomic.o +CLEANFILES = list_atomic.i list_atomic.o + +AM_CPPFLAGS = \ + -I$(top_builddir)/src -I$(top_srcdir)/src \ + -I$(top_builddir)/tests -I$(top_srcdir)/tests + +CFLAGS += $(CFLAGS_EXTRA) + +TESTS = test_atomic$(EXEEXT) test_atomic_generalized$(EXEEXT) +TEST_OBJS = test_atomic.o test_atomic_generalized-test_atomic.o +check_PROGRAMS = test_atomic test_atomic_generalized + +if HAVE_PTHREAD_H +TESTS += test_atomic_pthreads$(EXEEXT) +TEST_OBJS += test_atomic_pthreads-test_atomic.o +check_PROGRAMS += test_atomic_pthreads +test_atomic_pthreads_SOURCES=$(test_atomic_SOURCES) +test_atomic_pthreads_CPPFLAGS=-DAO_USE_PTHREAD_DEFS $(AM_CPPFLAGS) +test_atomic_pthreads_LDADD=$(test_atomic_LDADD) +endif + +test_atomic_SOURCES=test_atomic.c +test_atomic_LDADD = $(THREADDLLIBS) $(top_builddir)/src/libatomic_ops.la + +test_atomic_generalized_SOURCES=$(test_atomic_SOURCES) +test_atomic_generalized_CPPFLAGS= \ + -DAO_PREFER_GENERALIZED -DAO_TEST_EMULATION $(AM_CPPFLAGS) +test_atomic_generalized_LDADD=$(test_atomic_LDADD) + +if ENABLE_GPL + +TESTS += test_malloc$(EXEEXT) test_stack$(EXEEXT) +TEST_OBJS += test_malloc.o test_stack.o +check_PROGRAMS += test_malloc test_stack + +test_stack_SOURCES=test_stack.c +test_stack_LDADD = $(THREADDLLIBS) \ + $(top_builddir)/src/libatomic_ops_gpl.la + +test_malloc_SOURCES=test_malloc.c +test_malloc_LDADD = $(THREADDLLIBS) \ + $(top_builddir)/src/libatomic_ops_gpl.la + +## In case of static libraries build, libatomic_ops.a is already referenced +## in dependency_libs attribute of libatomic_ops_gpl.la file. +if ENABLE_SHARED +test_malloc_LDADD += $(top_builddir)/src/libatomic_ops.la +test_stack_LDADD += $(top_builddir)/src/libatomic_ops.la +endif + +check-gpl-without-test-driver: test_malloc$(EXEEXT) test_stack$(EXEEXT) + ./test_stack$(EXEEXT) + ./test_malloc$(EXEEXT) + +else + +# Nothing to do. +check-gpl-without-test-driver: + +endif + +.PHONY: check-gpl-without-test-driver + +# Run the tests directly (without test-driver): +.PHONY: check-without-test-driver +check-without-test-driver: $(TESTS) check-gpl-without-test-driver + @echo "The following will print some 'Missing ...' messages" + ./test_atomic$(EXEEXT) + ./test_atomic_generalized$(EXEEXT) + test ! -f test_atomic_pthreads$(EXEEXT) || ./test_atomic_pthreads$(EXEEXT) + +test_atomic_include.h: test_atomic_include.template + mkdir -p `dirname $@` + sed -e s:XX::g $? > $@ + sed -e s:XX:_release:g $? >> $@ + sed -e s:XX:_acquire:g $? >> $@ + sed -e s:XX:_read:g $? >> $@ + sed -e s:XX:_write:g $? >> $@ + sed -e s:XX:_full:g $? >> $@ + sed -e s:XX:_release_write:g $? >> $@ + sed -e s:XX:_acquire_read:g $? >> $@ + sed -e s:XX:_dd_acquire_read:g $? >> $@ + +list_atomic.c: list_atomic.template + mkdir -p `dirname $@` + echo "#include \"atomic_ops.h\"" > $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX::g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX:_release:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX:_acquire:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX:_read:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX:_write:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX:_full:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX:_release_write:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX:_acquire_read:g $? >> $@ + sed -e s:XSIZE_::g -e s:XCTYPE:AO_t:g -e s:XX:_dd_acquire_read:g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX::g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX:_release:g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX:_acquire:g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX:_read:g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX:_write:g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX:_full:g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX:_release_write:g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX:_acquire_read:g $? >> $@ + sed -e s:XSIZE:char:g -e s:XCTYPE:unsigned/**/char:g -e s:XX:_dd_acquire_read:g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX::g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX:_release:g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX:_acquire:g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX:_read:g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX:_write:g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX:_full:g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX:_release_write:g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX:_acquire_read:g $? >> $@ + sed -e s:XSIZE:short:g -e s:XCTYPE:unsigned/**/short:g -e s:XX:_dd_acquire_read:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX::g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX:_release:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX:_acquire:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX:_read:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX:_write:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX:_full:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX:_release_write:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX:_acquire_read:g $? >> $@ + sed -e s:XSIZE:int:g -e s:XCTYPE:unsigned:g -e s:XX:_dd_acquire_read:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX::g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX:_release:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX:_acquire:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX:_read:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX:_write:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX:_full:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX:_release_write:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX:_acquire_read:g $? >> $@ + sed -e s:XSIZE:double:g -e s:XCTYPE:AO_double_t:g -e s:XX:_dd_acquire_read:g $? >> $@ + +list_atomic.i: list_atomic.c + mkdir -p `dirname $@` + $(COMPILE) $? -E > $@ + +# Verify list_atomic.c syntax: +list_atomic.o: list_atomic.c + $(COMPILE) -c -o $@ $? + +# Just compile all tests (without linking and execution): +check-nolink-local: $(TEST_OBJS) diff --git a/libatomic_ops/tests/list_atomic.template b/libatomic_ops/tests/list_atomic.template new file mode 100644 index 000000000..dbdc231cf --- /dev/null +++ b/libatomic_ops/tests/list_atomic.template @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* This generates a compilable program. But it is really meant to be */ +/* be used only with cc -E, to inspect the expensions generated by */ +/* primitives. */ + +/* The result will not link or run. */ + +#include /* for exit() */ + +void XSIZE_list_atomicXX(void) +{ +# if defined(AO_HAVE_XSIZE_loadXX) || defined(AO_HAVE_XSIZE_storeXX) \ + || defined(AO_HAVE_XSIZE_fetch_and_addXX) \ + || defined(AO_HAVE_XSIZE_fetch_and_add1XX) \ + || defined(AO_HAVE_XSIZE_andXX) \ + || defined(AO_HAVE_XSIZE_compare_and_swapXX) \ + || defined(AO_HAVE_XSIZE_fetch_compare_and_swapXX) + static volatile XCTYPE val /* = 0 */; +# endif +# if defined(AO_HAVE_XSIZE_compare_and_swapXX) \ + || defined(AO_HAVE_XSIZE_fetch_compare_and_swapXX) + static XCTYPE oldval /* = 0 */; +# endif +# if defined(AO_HAVE_XSIZE_storeXX) \ + || defined(AO_HAVE_XSIZE_compare_and_swapXX) \ + || defined(AO_HAVE_XSIZE_fetch_compare_and_swapXX) + static XCTYPE newval /* = 0 */; +# endif +# if defined(AO_HAVE_test_and_setXX) + AO_TS_t ts = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_XSIZE_fetch_and_addXX) || defined(AO_HAVE_XSIZE_andXX) \ + || defined(AO_HAVE_XSIZE_orXX) || defined(AO_HAVE_XSIZE_xorXX) + static XCTYPE incr /* = 0 */; +# endif + +# if defined(AO_HAVE_nopXX) + (void)"AO_nopXX(): "; + AO_nopXX(); +# else + (void)"No AO_nopXX"; +# endif + +# ifdef AO_HAVE_XSIZE_loadXX + (void)"AO_XSIZE_loadXX(&val):"; + (void)AO_XSIZE_loadXX(&val); +# else + (void)"No AO_XSIZE_loadXX"; +# endif +# ifdef AO_HAVE_XSIZE_storeXX + (void)"AO_XSIZE_storeXX(&val, newval):"; + AO_XSIZE_storeXX(&val, newval); +# else + (void)"No AO_XSIZE_storeXX"; +# endif +# ifdef AO_HAVE_XSIZE_fetch_and_addXX + (void)"AO_XSIZE_fetch_and_addXX(&val, incr):"; + (void)AO_XSIZE_fetch_and_addXX(&val, incr); +# else + (void)"No AO_XSIZE_fetch_and_addXX"; +# endif +# ifdef AO_HAVE_XSIZE_fetch_and_add1XX + (void)"AO_XSIZE_fetch_and_add1XX(&val):"; + (void)AO_XSIZE_fetch_and_add1XX(&val); +# else + (void)"No AO_XSIZE_fetch_and_add1XX"; +# endif +# ifdef AO_HAVE_XSIZE_fetch_and_sub1XX + (void)"AO_XSIZE_fetch_and_sub1XX(&val):"; + (void)AO_XSIZE_fetch_and_sub1XX(&val); +# else + (void)"No AO_XSIZE_fetch_and_sub1XX"; +# endif +# ifdef AO_HAVE_XSIZE_andXX + (void)"AO_XSIZE_andXX(&val, incr):"; + AO_XSIZE_andXX(&val, incr); +# else + (void)"No AO_XSIZE_andXX"; +# endif +# ifdef AO_HAVE_XSIZE_orXX + (void)"AO_XSIZE_orXX(&val, incr):"; + AO_XSIZE_orXX(&val, incr); +# else + (void)"No AO_XSIZE_orXX"; +# endif +# ifdef AO_HAVE_XSIZE_xorXX + (void)"AO_XSIZE_xorXX(&val, incr):"; + AO_XSIZE_xorXX(&val, incr); +# else + (void)"No AO_XSIZE_xorXX"; +# endif +# ifdef AO_HAVE_XSIZE_compare_and_swapXX + (void)"AO_XSIZE_compare_and_swapXX(&val, oldval, newval):"; + if (!AO_XSIZE_compare_and_swapXX(&val, oldval, newval)) + exit(1); +# else + (void)"No AO_XSIZE_compare_and_swapXX"; +# endif + /* TODO: Add AO_compare_double_and_swap_doubleXX */ + /* TODO: Add AO_compare_and_swap_doubleXX */ +# ifdef AO_HAVE_XSIZE_fetch_compare_and_swapXX + (void)"AO_XSIZE_fetch_compare_and_swapXX(&val, oldval, newval):"; + if (AO_XSIZE_fetch_compare_and_swapXX(&val, oldval, newval) != oldval) + exit(1); +# else + (void)"No AO_XSIZE_fetch_compare_and_swapXX"; +# endif + +# if defined(AO_HAVE_test_and_setXX) + (void)"AO_test_and_setXX(&ts):"; + (void)AO_test_and_setXX(&ts); +# else + (void)"No AO_test_and_setXX"; +# endif +} diff --git a/libatomic_ops/tests/run_parallel.h b/libatomic_ops/tests/run_parallel.h new file mode 100644 index 000000000..97e29cbe6 --- /dev/null +++ b/libatomic_ops/tests/run_parallel.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if defined(_MSC_VER) || \ + defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) || \ + defined(_WIN32_WINCE) +# define USE_WINTHREADS +#elif defined(__vxworks) +# define USE_VXTHREADS +#else +# define USE_PTHREADS +#endif + +#include +#include + +#ifdef USE_PTHREADS +# include +#endif + +#ifdef USE_VXTHREADS +# include +# include +#endif + +#ifdef USE_WINTHREADS +# include +#endif + +#include "atomic_ops.h" + +#if !defined(AO_ATOMIC_OPS_H) && !defined(CPPCHECK) +# error Wrong atomic_ops.h included. +#endif + +#if (defined(_WIN32_WCE) || defined(__MINGW32CE__)) && !defined(AO_HAVE_abort) +# define abort() _exit(-1) /* there is no abort() in WinCE */ +#endif + +#ifndef AO_PTRDIFF_T +# define AO_PTRDIFF_T ptrdiff_t +#endif + +#ifndef MAX_NTHREADS +# define MAX_NTHREADS 100 +#endif + +typedef void * (* thr_func)(void *); + +typedef int (* test_func)(void); /* Returns != 0 on success */ + +void * run_parallel(int nthreads, thr_func f1, test_func t, const char *name); + +#ifdef USE_PTHREADS +void * run_parallel(int nthreads, thr_func f1, test_func t, const char *name) +{ + pthread_attr_t attr; + pthread_t thr[MAX_NTHREADS]; + int i; + + printf("Testing %s\n", name); + if (nthreads > MAX_NTHREADS) + { + fprintf(stderr, "run_parallel: requested too many threads\n"); + abort(); + } + +# ifdef _HPUX_SOURCE + /* Default stack size is too small, especially with the 64 bit ABI */ + /* Increase it. */ + if (pthread_default_stacksize_np(1024*1024, 0) != 0) + { + fprintf(stderr, "pthread_default_stacksize_np failed. " + "OK after first call.\n"); + } +# endif + + pthread_attr_init(&attr); + + for (i = 0; i < nthreads; ++i) + { + int code = pthread_create(thr + i, &attr, f1, (void *)(long)i); + if (code != 0) + { + fprintf(stderr, "pthread_create returned %d, thread %d\n", code, i); + abort(); + } + } + for (i = 0; i < nthreads; ++i) + { + int code = pthread_join(thr[i], NULL); + if (code != 0) + { + fprintf(stderr, "pthread_join returned %d, thread %d\n", code, i); + abort(); + } + } + if (t()) + { + printf("Succeeded\n"); + } + else + { + fprintf(stderr, "Failed\n"); + abort(); + } + return 0; +} +#endif /* USE_PTHREADS */ + +#ifdef USE_VXTHREADS +void * run_parallel(int nthreads, thr_func f1, test_func t, const char *name) +{ + int thr[MAX_NTHREADS]; + int i; + + printf("Testing %s\n", name); + if (nthreads > MAX_NTHREADS) + { + fprintf(stderr, "run_parallel: requested too many threads\n"); + taskSuspend(0); + } + + for (i = 0; i < nthreads; ++i) + { + thr[i] = taskSpawn((char*) name, 180, 0, 32768, (FUNCPTR) f1, i, + 1, 2, 3, 4, 5, 6, 7, 8, 9); + if (thr[i] == ERROR) + { + fprintf(stderr, "taskSpawn failed with %d, thread %d\n", + errno, i); + taskSuspend(0); + } + } + for (i = 0; i < nthreads; ++i) + { + while (taskIdVerify(thr[i]) == OK) + taskDelay(60); + } + if (t()) + { + printf("Succeeded\n"); + } + else + { + fprintf(stderr, "Failed\n"); + taskSuspend(0); + } + return 0; +} +#endif /* USE_VXTHREADS */ + +#ifdef USE_WINTHREADS + +struct tramp_args { + thr_func fn; + long arg; +}; + +DWORD WINAPI tramp(LPVOID param) +{ + struct tramp_args *args = (struct tramp_args *)param; + + return (DWORD)(AO_PTRDIFF_T)(*args->fn)((LPVOID)(AO_PTRDIFF_T)args->arg); +} + +void * run_parallel(int nthreads, thr_func f1, test_func t, const char *name) +{ + HANDLE thr[MAX_NTHREADS]; + struct tramp_args args[MAX_NTHREADS]; + int i; + + printf("Testing %s\n", name); + if (nthreads > MAX_NTHREADS) + { + fprintf(stderr, "run_parallel: requested too many threads\n"); + abort(); + } + + for (i = 0; i < nthreads; ++i) + { + args[i].fn = f1; + args[i].arg = i; + if ((thr[i] = CreateThread(NULL, 0, tramp, (LPVOID)(args+i), 0, NULL)) + == NULL) + { + fprintf(stderr, "CreateThread failed with %lu, thread %d\n", + (unsigned long)GetLastError(), i); + abort(); + } + } + for (i = 0; i < nthreads; ++i) + { + DWORD code = WaitForSingleObject(thr[i], INFINITE); + if (code != WAIT_OBJECT_0) + { + fprintf(stderr, "WaitForSingleObject returned %lu, thread %d\n", + (unsigned long)code, i); + abort(); + } + } + if (t()) + { + printf("Succeeded\n"); + } + else + { + fprintf(stderr, "Failed\n"); + abort(); + } + return 0; +} +#endif /* USE_WINTHREADS */ diff --git a/libatomic_ops/tests/test_atomic.c b/libatomic_ops/tests/test_atomic.c new file mode 100644 index 000000000..e773b266a --- /dev/null +++ b/libatomic_ops/tests/test_atomic.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if defined(HAVE_CONFIG_H) +# include "config.h" +#endif + +#if (defined(AO_NO_PTHREADS) || defined(__MINGW32__)) \ + && defined(AO_USE_PTHREAD_DEFS) +# include + + int main(void) + { + printf("test skipped\n"); + return 0; + } + +#else + +#include "run_parallel.h" + +#include "test_atomic_include.h" + +#if defined(AO_USE_PTHREAD_DEFS) || defined(AO_PREFER_GENERALIZED) +# define NITERS 100000 +#else +# define NITERS 10000000 +#endif + +void * add1sub1_thr(void * id); +int add1sub1_test(void); +void * acqrel_thr(void *id); +int acqrel_test(void); +void * test_and_set_thr(void * id); +int test_and_set_test(void); + +#if defined(AO_HAVE_fetch_and_add1) && defined(AO_HAVE_fetch_and_sub1) + +AO_t counter = 0; + +void * add1sub1_thr(void * id) +{ + int me = (int)(AO_PTRDIFF_T)id; + + int i; + + for (i = 0; i < NITERS; ++i) + if ((me & 1) != 0) { + (void)AO_fetch_and_sub1(&counter); + } else { + (void)AO_fetch_and_add1(&counter); + } + return 0; +} + +int add1sub1_test(void) +{ + return counter == 0; +} + +#endif /* defined(AO_HAVE_fetch_and_add1) && defined(AO_HAVE_fetch_and_sub1) */ + +#if defined(AO_HAVE_store_release_write) && defined(AO_HAVE_load_acquire_read) + +/* Invariant: counter1 >= counter2 */ +AO_t counter1 = 0; +AO_t counter2 = 0; + +void * acqrel_thr(void *id) +{ + int me = (int)(AO_PTRDIFF_T)id; + + int i; + + for (i = 0; i < NITERS; ++i) + if (me & 1) + { + AO_t my_counter1; + if (me != 1) + { + fprintf(stderr, "acqrel test: too many threads\n"); + abort(); + } + my_counter1 = AO_load(&counter1); + AO_store(&counter1, my_counter1 + 1); + AO_store_release_write(&counter2, my_counter1 + 1); + } + else + { + AO_t my_counter1a, my_counter2a; + AO_t my_counter1b, my_counter2b; + + my_counter2a = AO_load_acquire_read(&counter2); + my_counter1a = AO_load(&counter1); + /* Redo this, to make sure that the second load of counter1 */ + /* is not viewed as a common subexpression. */ + my_counter2b = AO_load_acquire_read(&counter2); + my_counter1b = AO_load(&counter1); + if (my_counter1a < my_counter2a) + { + fprintf(stderr, "Saw release store out of order: %lu < %lu\n", + (unsigned long)my_counter1a, (unsigned long)my_counter2a); + abort(); + } + if (my_counter1b < my_counter2b) + { + fprintf(stderr, + "Saw release store out of order (bad CSE?): %lu < %lu\n", + (unsigned long)my_counter1b, (unsigned long)my_counter2b); + abort(); + } + } + + return 0; +} + +int acqrel_test(void) +{ + return counter1 == NITERS && counter2 == NITERS; +} + +#endif /* AO_HAVE_store_release_write && AO_HAVE_load_acquire_read */ + +#if defined(AO_HAVE_test_and_set_acquire) + +AO_TS_t lock = AO_TS_INITIALIZER; + +unsigned long locked_counter; +volatile unsigned long junk = 13; + +AO_ATTR_NO_SANITIZE_THREAD +void do_junk(void) +{ + junk *= 17; + junk *= 19; +} + +void * test_and_set_thr(void * id) +{ + unsigned long i; + + for (i = 0; i < NITERS/10; ++i) + { + while (AO_test_and_set_acquire(&lock) != AO_TS_CLEAR); + ++locked_counter; + if (locked_counter != 1) + { + fprintf(stderr, "Test and set failure 1, counter = %ld, id = %d\n", + (long)locked_counter, (int)(AO_PTRDIFF_T)id); + abort(); + } + locked_counter *= 2; + locked_counter -= 1; + locked_counter *= 5; + locked_counter -= 4; + if (locked_counter != 1) + { + fprintf(stderr, "Test and set failure 2, counter = %ld, id = %d\n", + (long)locked_counter, (int)(AO_PTRDIFF_T)id); + abort(); + } + --locked_counter; + AO_CLEAR(&lock); + /* Spend a bit of time outside the lock. */ + do_junk(); + } + return 0; +} + +int test_and_set_test(void) +{ + return locked_counter == 0; +} + +#endif /* defined(AO_HAVE_test_and_set_acquire) */ + +#if (!defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__) \ + || defined(AO_USE_NO_SIGNALS) || defined(AO_USE_WIN32_PTHREADS)) \ + && defined(AO_TEST_EMULATION) + +# ifdef __cplusplus + extern "C" { +# endif + + AO_API void AO_store_full_emulation(volatile AO_t *addr, AO_t val); + AO_API AO_t AO_fetch_compare_and_swap_emulation(volatile AO_t *addr, + AO_t old_val, AO_t new_val); +# ifdef AO_HAVE_double_t + AO_API int + AO_compare_double_and_swap_double_emulation(volatile AO_double_t *, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2); +# endif + +# ifdef __cplusplus + } /* extern "C" */ +# endif + + void test_atomic_emulation(void) + { + AO_t x; +# ifdef AO_HAVE_double_t + AO_double_t w; /* double-word alignment not needed */ + + w.AO_val1 = 0; + w.AO_val2 = 0; + TA_assert(!AO_compare_double_and_swap_double_emulation(&w, 4116, 2121, + 8537, 6410)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_emulation(&w, 0, 0, + 8537, 6410)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); +# endif + AO_store_full_emulation(&x, 1314); + TA_assert(x == 1314); + TA_assert(AO_fetch_compare_and_swap_emulation(&x, 14, 13117) == 1314); + TA_assert(x == 1314); + TA_assert(AO_fetch_compare_and_swap_emulation(&x, 1314, 14117) == 1314); + TA_assert(x == 14117); + } + +#else +# define test_atomic_emulation() (void)0 +#endif /* _MSC_VER && !AO_USE_NO_SIGNALS || !AO_TEST_EMULATION */ + +int main(void) +{ + test_atomic(); + test_atomic_acquire(); + test_atomic_release(); + test_atomic_read(); + test_atomic_write(); + test_atomic_full(); + test_atomic_release_write(); + test_atomic_acquire_read(); + test_atomic_dd_acquire_read(); +# if defined(AO_HAVE_fetch_and_add1) && defined(AO_HAVE_fetch_and_sub1) + run_parallel(4, add1sub1_thr, add1sub1_test, "add1/sub1"); +# endif +# if defined(AO_HAVE_store_release_write) && defined(AO_HAVE_load_acquire_read) + run_parallel(3, acqrel_thr, acqrel_test, + "store_release_write/load_acquire_read"); +# endif +# if defined(AO_HAVE_test_and_set_acquire) + run_parallel(5, test_and_set_thr, test_and_set_test, + "test_and_set"); +# endif + test_atomic_emulation(); + return 0; +} + +#endif /* !AO_NO_PTHREADS || !AO_USE_PTHREAD_DEFS */ diff --git a/libatomic_ops/tests/test_atomic_include.h b/libatomic_ops/tests/test_atomic_include.h new file mode 100644 index 000000000..a60bbb975 --- /dev/null +++ b/libatomic_ops/tests/test_atomic_include.h @@ -0,0 +1,5364 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: )\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "") + +#if defined(CPPCHECK) + void list_atomic(void); + void char_list_atomic(void); + void short_list_atomic(void); + void int_list_atomic(void); + void double_list_atomic(void); +#endif + +void test_atomic(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap) \ + || defined(AO_HAVE_double_load) \ + || defined(AO_HAVE_double_store) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double) \ + || defined(AO_HAVE_compare_double_and_swap_double) \ + || defined(AO_HAVE_double_compare_and_swap) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic(); + char_list_atomic(); + short_list_atomic(); + int_list_atomic(); + double_list_atomic(); +# endif +# if defined(AO_HAVE_nop) + AO_nop(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load) + TA_assert(AO_load(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set) + TA_assert(AO_test_and_set(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set(&z) == AO_TS_SET); + TA_assert(AO_test_and_set(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add) + TA_assert(AO_fetch_and_add(&x, 42) == 13); + TA_assert(AO_fetch_and_add(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1) + TA_assert(AO_fetch_and_add1(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1) + TA_assert(AO_fetch_and_sub1(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add) + TA_assert(AO_short_fetch_and_add(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1) + TA_assert(AO_short_fetch_and_add1(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1) + TA_assert(AO_short_fetch_and_sub1(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add) + TA_assert(AO_char_fetch_and_add(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1) + TA_assert(AO_char_fetch_and_add1(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1) + TA_assert(AO_char_fetch_and_sub1(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add) + TA_assert(AO_int_fetch_and_add(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1) + TA_assert(AO_int_fetch_and_add1(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1) + TA_assert(AO_int_fetch_and_sub1(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap) + TA_assert(!AO_compare_and_swap(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or) + AO_or(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor) + AO_xor(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and) + AO_and(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap) + TA_assert(AO_fetch_compare_and_swap(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap) + TA_assert(!AO_short_compare_and_swap(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or) + AO_short_or(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor) + AO_short_xor(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and) + AO_short_and(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap) + TA_assert(AO_short_fetch_compare_and_swap(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap) + TA_assert(!AO_char_compare_and_swap(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or) + AO_char_or(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor) + AO_char_xor(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and) + AO_char_and(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap) + TA_assert(AO_char_fetch_compare_and_swap(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap) + TA_assert(!AO_int_compare_and_swap(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or) + AO_int_or(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor) + AO_int_xor(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and) + AO_int_and(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap) + TA_assert(AO_int_fetch_compare_and_swap(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load) || defined(AO_HAVE_double_store) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load) + new_w = AO_double_load(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double) + TA_assert(!AO_compare_double_and_swap_double(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double) + TA_assert(!AO_compare_and_swap_double(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: _release)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "_release") + +#if defined(CPPCHECK) + void list_atomic_release(void); + void char_list_atomic_release(void); + void short_list_atomic_release(void); + void int_list_atomic_release(void); + void double_list_atomic_release(void); +#endif + +void test_atomic_release(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set_release) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap_release) \ + || defined(AO_HAVE_double_load_release) \ + || defined(AO_HAVE_double_store_release) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double_release) \ + || defined(AO_HAVE_compare_double_and_swap_double_release) \ + || defined(AO_HAVE_double_compare_and_swap_release) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic_release(); + char_list_atomic_release(); + short_list_atomic_release(); + int_list_atomic_release(); + double_list_atomic_release(); +# endif +# if defined(AO_HAVE_nop_release) + AO_nop_release(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store_release) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store_release(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load_release) + TA_assert(AO_load_release(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set_release) + TA_assert(AO_test_and_set_release(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_release(&z) == AO_TS_SET); + TA_assert(AO_test_and_set_release(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add_release) + TA_assert(AO_fetch_and_add_release(&x, 42) == 13); + TA_assert(AO_fetch_and_add_release(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1_release) + TA_assert(AO_fetch_and_add1_release(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1_release) + TA_assert(AO_fetch_and_sub1_release(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store_release) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store_release(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load_release) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add_release) + TA_assert(AO_short_fetch_and_add_release(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add_release(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1_release) + TA_assert(AO_short_fetch_and_add1_release(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1_release) + TA_assert(AO_short_fetch_and_sub1_release(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store_release) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store_release(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load_release) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add_release) + TA_assert(AO_char_fetch_and_add_release(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add_release(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1_release) + TA_assert(AO_char_fetch_and_add1_release(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1_release) + TA_assert(AO_char_fetch_and_sub1_release(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store_release) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store_release(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load_release) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add_release) + TA_assert(AO_int_fetch_and_add_release(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add_release(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1_release) + TA_assert(AO_int_fetch_and_add1_release(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1_release) + TA_assert(AO_int_fetch_and_sub1_release(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap_release) + TA_assert(!AO_compare_and_swap_release(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_release(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or_release) + AO_or_release(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor_release) + AO_xor_release(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and_release) + AO_and_release(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap_release) + TA_assert(AO_fetch_compare_and_swap_release(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap_release(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap_release) + TA_assert(!AO_short_compare_and_swap_release(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap_release(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or_release) + AO_short_or_release(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor_release) + AO_short_xor_release(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and_release) + AO_short_and_release(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap_release) + TA_assert(AO_short_fetch_compare_and_swap_release(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap_release(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap_release) + TA_assert(!AO_char_compare_and_swap_release(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap_release(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or_release) + AO_char_or_release(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor_release) + AO_char_xor_release(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and_release) + AO_char_and_release(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap_release) + TA_assert(AO_char_fetch_compare_and_swap_release(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap_release(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap_release) + TA_assert(!AO_int_compare_and_swap_release(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap_release(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or_release) + AO_int_or_release(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor_release) + AO_int_xor_release(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and_release) + AO_int_and_release(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap_release) + TA_assert(AO_int_fetch_compare_and_swap_release(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap_release(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load_release) || defined(AO_HAVE_double_store_release) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load_release) + new_w = AO_double_load_release(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store_release) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store_release(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store_release(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store_release(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double_release) + TA_assert(!AO_compare_double_and_swap_double_release(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_release(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_release(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_release(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_release(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double_release(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double_release(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double_release) + TA_assert(!AO_compare_and_swap_double_release(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double_release(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_release(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_release(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double_release(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double_release(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap_release) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap_release(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap_release(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap_release(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap_release(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap_release(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap_release(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap_release(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: _acquire)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "_acquire") + +#if defined(CPPCHECK) + void list_atomic_acquire(void); + void char_list_atomic_acquire(void); + void short_list_atomic_acquire(void); + void int_list_atomic_acquire(void); + void double_list_atomic_acquire(void); +#endif + +void test_atomic_acquire(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set_acquire) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap_acquire) \ + || defined(AO_HAVE_double_load_acquire) \ + || defined(AO_HAVE_double_store_acquire) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double_acquire) \ + || defined(AO_HAVE_compare_double_and_swap_double_acquire) \ + || defined(AO_HAVE_double_compare_and_swap_acquire) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic_acquire(); + char_list_atomic_acquire(); + short_list_atomic_acquire(); + int_list_atomic_acquire(); + double_list_atomic_acquire(); +# endif +# if defined(AO_HAVE_nop_acquire) + AO_nop_acquire(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store_acquire) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store_acquire(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load_acquire) + TA_assert(AO_load_acquire(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set_acquire) + TA_assert(AO_test_and_set_acquire(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_acquire(&z) == AO_TS_SET); + TA_assert(AO_test_and_set_acquire(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add_acquire) + TA_assert(AO_fetch_and_add_acquire(&x, 42) == 13); + TA_assert(AO_fetch_and_add_acquire(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1_acquire) + TA_assert(AO_fetch_and_add1_acquire(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1_acquire) + TA_assert(AO_fetch_and_sub1_acquire(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store_acquire) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store_acquire(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load_acquire) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add_acquire) + TA_assert(AO_short_fetch_and_add_acquire(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add_acquire(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1_acquire) + TA_assert(AO_short_fetch_and_add1_acquire(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1_acquire) + TA_assert(AO_short_fetch_and_sub1_acquire(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store_acquire) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store_acquire(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load_acquire) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add_acquire) + TA_assert(AO_char_fetch_and_add_acquire(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add_acquire(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1_acquire) + TA_assert(AO_char_fetch_and_add1_acquire(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1_acquire) + TA_assert(AO_char_fetch_and_sub1_acquire(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store_acquire) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store_acquire(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load_acquire) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add_acquire) + TA_assert(AO_int_fetch_and_add_acquire(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add_acquire(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1_acquire) + TA_assert(AO_int_fetch_and_add1_acquire(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1_acquire) + TA_assert(AO_int_fetch_and_sub1_acquire(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap_acquire) + TA_assert(!AO_compare_and_swap_acquire(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_acquire(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or_acquire) + AO_or_acquire(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor_acquire) + AO_xor_acquire(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and_acquire) + AO_and_acquire(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap_acquire) + TA_assert(AO_fetch_compare_and_swap_acquire(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap_acquire(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap_acquire) + TA_assert(!AO_short_compare_and_swap_acquire(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap_acquire(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or_acquire) + AO_short_or_acquire(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor_acquire) + AO_short_xor_acquire(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and_acquire) + AO_short_and_acquire(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap_acquire) + TA_assert(AO_short_fetch_compare_and_swap_acquire(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap_acquire(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap_acquire) + TA_assert(!AO_char_compare_and_swap_acquire(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap_acquire(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or_acquire) + AO_char_or_acquire(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor_acquire) + AO_char_xor_acquire(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and_acquire) + AO_char_and_acquire(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap_acquire) + TA_assert(AO_char_fetch_compare_and_swap_acquire(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap_acquire(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap_acquire) + TA_assert(!AO_int_compare_and_swap_acquire(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap_acquire(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or_acquire) + AO_int_or_acquire(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor_acquire) + AO_int_xor_acquire(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and_acquire) + AO_int_and_acquire(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap_acquire) + TA_assert(AO_int_fetch_compare_and_swap_acquire(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap_acquire(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load_acquire) || defined(AO_HAVE_double_store_acquire) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load_acquire) + new_w = AO_double_load_acquire(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store_acquire) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store_acquire(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store_acquire(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store_acquire(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double_acquire) + TA_assert(!AO_compare_double_and_swap_double_acquire(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_acquire(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_acquire(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_acquire(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_acquire(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double_acquire(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double_acquire(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double_acquire) + TA_assert(!AO_compare_and_swap_double_acquire(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double_acquire(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_acquire(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_acquire(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double_acquire(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double_acquire(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap_acquire) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap_acquire(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap_acquire(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap_acquire(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap_acquire(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap_acquire(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap_acquire(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap_acquire(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: _read)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "_read") + +#if defined(CPPCHECK) + void list_atomic_read(void); + void char_list_atomic_read(void); + void short_list_atomic_read(void); + void int_list_atomic_read(void); + void double_list_atomic_read(void); +#endif + +void test_atomic_read(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set_read) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap_read) \ + || defined(AO_HAVE_double_load_read) \ + || defined(AO_HAVE_double_store_read) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double_read) \ + || defined(AO_HAVE_compare_double_and_swap_double_read) \ + || defined(AO_HAVE_double_compare_and_swap_read) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic_read(); + char_list_atomic_read(); + short_list_atomic_read(); + int_list_atomic_read(); + double_list_atomic_read(); +# endif +# if defined(AO_HAVE_nop_read) + AO_nop_read(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store_read(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load_read) + TA_assert(AO_load_read(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set_read) + TA_assert(AO_test_and_set_read(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_read(&z) == AO_TS_SET); + TA_assert(AO_test_and_set_read(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add_read) + TA_assert(AO_fetch_and_add_read(&x, 42) == 13); + TA_assert(AO_fetch_and_add_read(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1_read) + TA_assert(AO_fetch_and_add1_read(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1_read) + TA_assert(AO_fetch_and_sub1_read(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store_read(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load_read) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add_read) + TA_assert(AO_short_fetch_and_add_read(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add_read(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1_read) + TA_assert(AO_short_fetch_and_add1_read(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1_read) + TA_assert(AO_short_fetch_and_sub1_read(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store_read(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load_read) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add_read) + TA_assert(AO_char_fetch_and_add_read(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add_read(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1_read) + TA_assert(AO_char_fetch_and_add1_read(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1_read) + TA_assert(AO_char_fetch_and_sub1_read(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store_read(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load_read) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add_read) + TA_assert(AO_int_fetch_and_add_read(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add_read(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1_read) + TA_assert(AO_int_fetch_and_add1_read(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1_read) + TA_assert(AO_int_fetch_and_sub1_read(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap_read) + TA_assert(!AO_compare_and_swap_read(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_read(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or_read) + AO_or_read(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor_read) + AO_xor_read(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and_read) + AO_and_read(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap_read) + TA_assert(AO_fetch_compare_and_swap_read(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap_read(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap_read) + TA_assert(!AO_short_compare_and_swap_read(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap_read(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or_read) + AO_short_or_read(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor_read) + AO_short_xor_read(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and_read) + AO_short_and_read(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap_read) + TA_assert(AO_short_fetch_compare_and_swap_read(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap_read(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap_read) + TA_assert(!AO_char_compare_and_swap_read(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap_read(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or_read) + AO_char_or_read(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor_read) + AO_char_xor_read(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and_read) + AO_char_and_read(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap_read) + TA_assert(AO_char_fetch_compare_and_swap_read(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap_read(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap_read) + TA_assert(!AO_int_compare_and_swap_read(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap_read(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or_read) + AO_int_or_read(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor_read) + AO_int_xor_read(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and_read) + AO_int_and_read(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap_read) + TA_assert(AO_int_fetch_compare_and_swap_read(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap_read(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load_read) || defined(AO_HAVE_double_store_read) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load_read) + new_w = AO_double_load_read(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store_read) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double_read) + TA_assert(!AO_compare_double_and_swap_double_read(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_read(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_read(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_read(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_read(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double_read(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double_read(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double_read) + TA_assert(!AO_compare_and_swap_double_read(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double_read(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_read(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_read(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double_read(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double_read(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap_read) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap_read(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: _write)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "_write") + +#if defined(CPPCHECK) + void list_atomic_write(void); + void char_list_atomic_write(void); + void short_list_atomic_write(void); + void int_list_atomic_write(void); + void double_list_atomic_write(void); +#endif + +void test_atomic_write(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set_write) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap_write) \ + || defined(AO_HAVE_double_load_write) \ + || defined(AO_HAVE_double_store_write) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double_write) \ + || defined(AO_HAVE_compare_double_and_swap_double_write) \ + || defined(AO_HAVE_double_compare_and_swap_write) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic_write(); + char_list_atomic_write(); + short_list_atomic_write(); + int_list_atomic_write(); + double_list_atomic_write(); +# endif +# if defined(AO_HAVE_nop_write) + AO_nop_write(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store_write) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store_write(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load_write) + TA_assert(AO_load_write(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set_write) + TA_assert(AO_test_and_set_write(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_write(&z) == AO_TS_SET); + TA_assert(AO_test_and_set_write(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add_write) + TA_assert(AO_fetch_and_add_write(&x, 42) == 13); + TA_assert(AO_fetch_and_add_write(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1_write) + TA_assert(AO_fetch_and_add1_write(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1_write) + TA_assert(AO_fetch_and_sub1_write(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store_write) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store_write(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load_write) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add_write) + TA_assert(AO_short_fetch_and_add_write(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add_write(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1_write) + TA_assert(AO_short_fetch_and_add1_write(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1_write) + TA_assert(AO_short_fetch_and_sub1_write(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store_write) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store_write(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load_write) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add_write) + TA_assert(AO_char_fetch_and_add_write(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add_write(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1_write) + TA_assert(AO_char_fetch_and_add1_write(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1_write) + TA_assert(AO_char_fetch_and_sub1_write(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store_write) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store_write(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load_write) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add_write) + TA_assert(AO_int_fetch_and_add_write(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add_write(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1_write) + TA_assert(AO_int_fetch_and_add1_write(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1_write) + TA_assert(AO_int_fetch_and_sub1_write(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap_write) + TA_assert(!AO_compare_and_swap_write(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_write(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or_write) + AO_or_write(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor_write) + AO_xor_write(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and_write) + AO_and_write(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap_write) + TA_assert(AO_fetch_compare_and_swap_write(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap_write(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap_write) + TA_assert(!AO_short_compare_and_swap_write(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap_write(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or_write) + AO_short_or_write(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor_write) + AO_short_xor_write(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and_write) + AO_short_and_write(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap_write) + TA_assert(AO_short_fetch_compare_and_swap_write(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap_write(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap_write) + TA_assert(!AO_char_compare_and_swap_write(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap_write(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or_write) + AO_char_or_write(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor_write) + AO_char_xor_write(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and_write) + AO_char_and_write(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap_write) + TA_assert(AO_char_fetch_compare_and_swap_write(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap_write(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap_write) + TA_assert(!AO_int_compare_and_swap_write(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap_write(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or_write) + AO_int_or_write(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor_write) + AO_int_xor_write(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and_write) + AO_int_and_write(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap_write) + TA_assert(AO_int_fetch_compare_and_swap_write(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap_write(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load_write) || defined(AO_HAVE_double_store_write) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load_write) + new_w = AO_double_load_write(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store_write) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store_write(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store_write(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store_write(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double_write) + TA_assert(!AO_compare_double_and_swap_double_write(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_write(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_write(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_write(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_write(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double_write(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double_write(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double_write) + TA_assert(!AO_compare_and_swap_double_write(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double_write(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_write(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_write(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double_write(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double_write(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap_write) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap_write(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: _full)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "_full") + +#if defined(CPPCHECK) + void list_atomic_full(void); + void char_list_atomic_full(void); + void short_list_atomic_full(void); + void int_list_atomic_full(void); + void double_list_atomic_full(void); +#endif + +void test_atomic_full(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set_full) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap_full) \ + || defined(AO_HAVE_double_load_full) \ + || defined(AO_HAVE_double_store_full) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double_full) \ + || defined(AO_HAVE_compare_double_and_swap_double_full) \ + || defined(AO_HAVE_double_compare_and_swap_full) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic_full(); + char_list_atomic_full(); + short_list_atomic_full(); + int_list_atomic_full(); + double_list_atomic_full(); +# endif +# if defined(AO_HAVE_nop_full) + AO_nop_full(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store_full) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store_full(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load_full) + TA_assert(AO_load_full(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set_full) + TA_assert(AO_test_and_set_full(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_full(&z) == AO_TS_SET); + TA_assert(AO_test_and_set_full(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add_full) + TA_assert(AO_fetch_and_add_full(&x, 42) == 13); + TA_assert(AO_fetch_and_add_full(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1_full) + TA_assert(AO_fetch_and_add1_full(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1_full) + TA_assert(AO_fetch_and_sub1_full(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store_full) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store_full(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load_full) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add_full) + TA_assert(AO_short_fetch_and_add_full(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add_full(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1_full) + TA_assert(AO_short_fetch_and_add1_full(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1_full) + TA_assert(AO_short_fetch_and_sub1_full(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store_full) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store_full(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load_full) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add_full) + TA_assert(AO_char_fetch_and_add_full(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add_full(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1_full) + TA_assert(AO_char_fetch_and_add1_full(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1_full) + TA_assert(AO_char_fetch_and_sub1_full(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store_full) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store_full(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load_full) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add_full) + TA_assert(AO_int_fetch_and_add_full(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add_full(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1_full) + TA_assert(AO_int_fetch_and_add1_full(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1_full) + TA_assert(AO_int_fetch_and_sub1_full(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap_full) + TA_assert(!AO_compare_and_swap_full(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_full(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or_full) + AO_or_full(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor_full) + AO_xor_full(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and_full) + AO_and_full(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap_full) + TA_assert(AO_fetch_compare_and_swap_full(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap_full(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap_full) + TA_assert(!AO_short_compare_and_swap_full(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap_full(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or_full) + AO_short_or_full(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor_full) + AO_short_xor_full(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and_full) + AO_short_and_full(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap_full) + TA_assert(AO_short_fetch_compare_and_swap_full(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap_full(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap_full) + TA_assert(!AO_char_compare_and_swap_full(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap_full(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or_full) + AO_char_or_full(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor_full) + AO_char_xor_full(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and_full) + AO_char_and_full(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap_full) + TA_assert(AO_char_fetch_compare_and_swap_full(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap_full(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap_full) + TA_assert(!AO_int_compare_and_swap_full(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap_full(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or_full) + AO_int_or_full(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor_full) + AO_int_xor_full(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and_full) + AO_int_and_full(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap_full) + TA_assert(AO_int_fetch_compare_and_swap_full(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap_full(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load_full) || defined(AO_HAVE_double_store_full) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load_full) + new_w = AO_double_load_full(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store_full) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store_full(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store_full(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store_full(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double_full) + TA_assert(!AO_compare_double_and_swap_double_full(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_full(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_full(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_full(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_full(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double_full(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double_full(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double_full) + TA_assert(!AO_compare_and_swap_double_full(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double_full(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_full(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_full(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double_full(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double_full(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap_full) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap_full(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap_full(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap_full(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap_full(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap_full(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap_full(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap_full(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: _release_write)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "_release_write") + +#if defined(CPPCHECK) + void list_atomic_release_write(void); + void char_list_atomic_release_write(void); + void short_list_atomic_release_write(void); + void int_list_atomic_release_write(void); + void double_list_atomic_release_write(void); +#endif + +void test_atomic_release_write(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set_release_write) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap_release_write) \ + || defined(AO_HAVE_double_load_release_write) \ + || defined(AO_HAVE_double_store_release_write) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double_release_write) \ + || defined(AO_HAVE_compare_double_and_swap_double_release_write) \ + || defined(AO_HAVE_double_compare_and_swap_release_write) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic_release_write(); + char_list_atomic_release_write(); + short_list_atomic_release_write(); + int_list_atomic_release_write(); + double_list_atomic_release_write(); +# endif +# if defined(AO_HAVE_nop_release_write) + AO_nop_release_write(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store_release_write) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store_release_write(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load_release_write) + TA_assert(AO_load_release_write(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set_release_write) + TA_assert(AO_test_and_set_release_write(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_release_write(&z) == AO_TS_SET); + TA_assert(AO_test_and_set_release_write(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add_release_write) + TA_assert(AO_fetch_and_add_release_write(&x, 42) == 13); + TA_assert(AO_fetch_and_add_release_write(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1_release_write) + TA_assert(AO_fetch_and_add1_release_write(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1_release_write) + TA_assert(AO_fetch_and_sub1_release_write(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store_release_write) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store_release_write(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load_release_write) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add_release_write) + TA_assert(AO_short_fetch_and_add_release_write(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add_release_write(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1_release_write) + TA_assert(AO_short_fetch_and_add1_release_write(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1_release_write) + TA_assert(AO_short_fetch_and_sub1_release_write(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store_release_write) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store_release_write(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load_release_write) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add_release_write) + TA_assert(AO_char_fetch_and_add_release_write(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add_release_write(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1_release_write) + TA_assert(AO_char_fetch_and_add1_release_write(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1_release_write) + TA_assert(AO_char_fetch_and_sub1_release_write(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store_release_write) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store_release_write(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load_release_write) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add_release_write) + TA_assert(AO_int_fetch_and_add_release_write(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add_release_write(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1_release_write) + TA_assert(AO_int_fetch_and_add1_release_write(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1_release_write) + TA_assert(AO_int_fetch_and_sub1_release_write(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap_release_write) + TA_assert(!AO_compare_and_swap_release_write(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_release_write(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or_release_write) + AO_or_release_write(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor_release_write) + AO_xor_release_write(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and_release_write) + AO_and_release_write(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap_release_write) + TA_assert(AO_fetch_compare_and_swap_release_write(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap_release_write(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap_release_write) + TA_assert(!AO_short_compare_and_swap_release_write(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap_release_write(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or_release_write) + AO_short_or_release_write(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor_release_write) + AO_short_xor_release_write(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and_release_write) + AO_short_and_release_write(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap_release_write) + TA_assert(AO_short_fetch_compare_and_swap_release_write(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap_release_write(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap_release_write) + TA_assert(!AO_char_compare_and_swap_release_write(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap_release_write(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or_release_write) + AO_char_or_release_write(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor_release_write) + AO_char_xor_release_write(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and_release_write) + AO_char_and_release_write(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap_release_write) + TA_assert(AO_char_fetch_compare_and_swap_release_write(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap_release_write(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap_release_write) + TA_assert(!AO_int_compare_and_swap_release_write(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap_release_write(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or_release_write) + AO_int_or_release_write(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor_release_write) + AO_int_xor_release_write(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and_release_write) + AO_int_and_release_write(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap_release_write) + TA_assert(AO_int_fetch_compare_and_swap_release_write(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap_release_write(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load_release_write) || defined(AO_HAVE_double_store_release_write) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load_release_write) + new_w = AO_double_load_release_write(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store_release_write) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store_release_write(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store_release_write(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store_release_write(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double_release_write) + TA_assert(!AO_compare_double_and_swap_double_release_write(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_release_write(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_release_write(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_release_write(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_release_write(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double_release_write(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double_release_write(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double_release_write) + TA_assert(!AO_compare_and_swap_double_release_write(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double_release_write(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_release_write(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_release_write(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double_release_write(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double_release_write(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap_release_write) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap_release_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap_release_write(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap_release_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap_release_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap_release_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap_release_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap_release_write(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: _acquire_read)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "_acquire_read") + +#if defined(CPPCHECK) + void list_atomic_acquire_read(void); + void char_list_atomic_acquire_read(void); + void short_list_atomic_acquire_read(void); + void int_list_atomic_acquire_read(void); + void double_list_atomic_acquire_read(void); +#endif + +void test_atomic_acquire_read(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set_acquire_read) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap_acquire_read) \ + || defined(AO_HAVE_double_load_acquire_read) \ + || defined(AO_HAVE_double_store_acquire_read) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double_acquire_read) \ + || defined(AO_HAVE_compare_double_and_swap_double_acquire_read) \ + || defined(AO_HAVE_double_compare_and_swap_acquire_read) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic_acquire_read(); + char_list_atomic_acquire_read(); + short_list_atomic_acquire_read(); + int_list_atomic_acquire_read(); + double_list_atomic_acquire_read(); +# endif +# if defined(AO_HAVE_nop_acquire_read) + AO_nop_acquire_read(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store_acquire_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store_acquire_read(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load_acquire_read) + TA_assert(AO_load_acquire_read(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set_acquire_read) + TA_assert(AO_test_and_set_acquire_read(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_acquire_read(&z) == AO_TS_SET); + TA_assert(AO_test_and_set_acquire_read(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add_acquire_read) + TA_assert(AO_fetch_and_add_acquire_read(&x, 42) == 13); + TA_assert(AO_fetch_and_add_acquire_read(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1_acquire_read) + TA_assert(AO_fetch_and_add1_acquire_read(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1_acquire_read) + TA_assert(AO_fetch_and_sub1_acquire_read(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store_acquire_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store_acquire_read(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load_acquire_read) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add_acquire_read) + TA_assert(AO_short_fetch_and_add_acquire_read(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add_acquire_read(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1_acquire_read) + TA_assert(AO_short_fetch_and_add1_acquire_read(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1_acquire_read) + TA_assert(AO_short_fetch_and_sub1_acquire_read(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store_acquire_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store_acquire_read(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load_acquire_read) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add_acquire_read) + TA_assert(AO_char_fetch_and_add_acquire_read(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add_acquire_read(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1_acquire_read) + TA_assert(AO_char_fetch_and_add1_acquire_read(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1_acquire_read) + TA_assert(AO_char_fetch_and_sub1_acquire_read(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store_acquire_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store_acquire_read(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load_acquire_read) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add_acquire_read) + TA_assert(AO_int_fetch_and_add_acquire_read(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add_acquire_read(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1_acquire_read) + TA_assert(AO_int_fetch_and_add1_acquire_read(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1_acquire_read) + TA_assert(AO_int_fetch_and_sub1_acquire_read(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap_acquire_read) + TA_assert(!AO_compare_and_swap_acquire_read(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_acquire_read(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or_acquire_read) + AO_or_acquire_read(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor_acquire_read) + AO_xor_acquire_read(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and_acquire_read) + AO_and_acquire_read(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap_acquire_read) + TA_assert(AO_fetch_compare_and_swap_acquire_read(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap_acquire_read(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap_acquire_read) + TA_assert(!AO_short_compare_and_swap_acquire_read(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap_acquire_read(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or_acquire_read) + AO_short_or_acquire_read(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor_acquire_read) + AO_short_xor_acquire_read(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and_acquire_read) + AO_short_and_acquire_read(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap_acquire_read) + TA_assert(AO_short_fetch_compare_and_swap_acquire_read(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap_acquire_read(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap_acquire_read) + TA_assert(!AO_char_compare_and_swap_acquire_read(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap_acquire_read(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or_acquire_read) + AO_char_or_acquire_read(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor_acquire_read) + AO_char_xor_acquire_read(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and_acquire_read) + AO_char_and_acquire_read(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap_acquire_read) + TA_assert(AO_char_fetch_compare_and_swap_acquire_read(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap_acquire_read(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap_acquire_read) + TA_assert(!AO_int_compare_and_swap_acquire_read(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap_acquire_read(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or_acquire_read) + AO_int_or_acquire_read(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor_acquire_read) + AO_int_xor_acquire_read(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and_acquire_read) + AO_int_and_acquire_read(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap_acquire_read) + TA_assert(AO_int_fetch_compare_and_swap_acquire_read(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap_acquire_read(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load_acquire_read) || defined(AO_HAVE_double_store_acquire_read) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load_acquire_read) + new_w = AO_double_load_acquire_read(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store_acquire_read) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store_acquire_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store_acquire_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store_acquire_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double_acquire_read) + TA_assert(!AO_compare_double_and_swap_double_acquire_read(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_acquire_read(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_acquire_read(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_acquire_read(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_acquire_read(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double_acquire_read(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double_acquire_read(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double_acquire_read) + TA_assert(!AO_compare_and_swap_double_acquire_read(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double_acquire_read(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_acquire_read(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_acquire_read(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double_acquire_read(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double_acquire_read(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap_acquire_read) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap_acquire_read(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: _dd_acquire_read)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "_dd_acquire_read") + +#if defined(CPPCHECK) + void list_atomic_dd_acquire_read(void); + void char_list_atomic_dd_acquire_read(void); + void short_list_atomic_dd_acquire_read(void); + void int_list_atomic_dd_acquire_read(void); + void double_list_atomic_dd_acquire_read(void); +#endif + +void test_atomic_dd_acquire_read(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_set_dd_acquire_read) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swap_dd_acquire_read) \ + || defined(AO_HAVE_double_load_dd_acquire_read) \ + || defined(AO_HAVE_double_store_dd_acquire_read) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_double_dd_acquire_read) \ + || defined(AO_HAVE_compare_double_and_swap_double_dd_acquire_read) \ + || defined(AO_HAVE_double_compare_and_swap_dd_acquire_read) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomic_dd_acquire_read(); + char_list_atomic_dd_acquire_read(); + short_list_atomic_dd_acquire_read(); + int_list_atomic_dd_acquire_read(); + double_list_atomic_dd_acquire_read(); +# endif +# if defined(AO_HAVE_nop_dd_acquire_read) + AO_nop_dd_acquire_read(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_store_dd_acquire_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_store_dd_acquire_read(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_load_dd_acquire_read) + TA_assert(AO_load_dd_acquire_read(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_set_dd_acquire_read) + TA_assert(AO_test_and_set_dd_acquire_read(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_set_dd_acquire_read(&z) == AO_TS_SET); + TA_assert(AO_test_and_set_dd_acquire_read(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_add_dd_acquire_read) + TA_assert(AO_fetch_and_add_dd_acquire_read(&x, 42) == 13); + TA_assert(AO_fetch_and_add_dd_acquire_read(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1_dd_acquire_read) + TA_assert(AO_fetch_and_add1_dd_acquire_read(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1_dd_acquire_read) + TA_assert(AO_fetch_and_sub1_dd_acquire_read(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_store_dd_acquire_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_store_dd_acquire_read(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_load_dd_acquire_read) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_add_dd_acquire_read) + TA_assert(AO_short_fetch_and_add_dd_acquire_read(&s, 42) == 13); + TA_assert(AO_short_fetch_and_add_dd_acquire_read(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1_dd_acquire_read) + TA_assert(AO_short_fetch_and_add1_dd_acquire_read(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1_dd_acquire_read) + TA_assert(AO_short_fetch_and_sub1_dd_acquire_read(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_store_dd_acquire_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_store_dd_acquire_read(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_load_dd_acquire_read) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_add_dd_acquire_read) + TA_assert(AO_char_fetch_and_add_dd_acquire_read(&b, 42) == 13); + TA_assert(AO_char_fetch_and_add_dd_acquire_read(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1_dd_acquire_read) + TA_assert(AO_char_fetch_and_add1_dd_acquire_read(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1_dd_acquire_read) + TA_assert(AO_char_fetch_and_sub1_dd_acquire_read(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_store_dd_acquire_read) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_store_dd_acquire_read(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_load_dd_acquire_read) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_add_dd_acquire_read) + TA_assert(AO_int_fetch_and_add_dd_acquire_read(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_add_dd_acquire_read(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1_dd_acquire_read) + TA_assert(AO_int_fetch_and_add1_dd_acquire_read(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1_dd_acquire_read) + TA_assert(AO_int_fetch_and_sub1_dd_acquire_read(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swap_dd_acquire_read) + TA_assert(!AO_compare_and_swap_dd_acquire_read(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swap_dd_acquire_read(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_or_dd_acquire_read) + AO_or_dd_acquire_read(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xor_dd_acquire_read) + AO_xor_dd_acquire_read(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_and_dd_acquire_read) + AO_and_dd_acquire_read(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swap_dd_acquire_read) + TA_assert(AO_fetch_compare_and_swap_dd_acquire_read(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swap_dd_acquire_read(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swap_dd_acquire_read) + TA_assert(!AO_short_compare_and_swap_dd_acquire_read(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swap_dd_acquire_read(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_or_dd_acquire_read) + AO_short_or_dd_acquire_read(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xor_dd_acquire_read) + AO_short_xor_dd_acquire_read(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_and_dd_acquire_read) + AO_short_and_dd_acquire_read(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swap_dd_acquire_read) + TA_assert(AO_short_fetch_compare_and_swap_dd_acquire_read(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swap_dd_acquire_read(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swap_dd_acquire_read) + TA_assert(!AO_char_compare_and_swap_dd_acquire_read(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swap_dd_acquire_read(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_or_dd_acquire_read) + AO_char_or_dd_acquire_read(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xor_dd_acquire_read) + AO_char_xor_dd_acquire_read(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_and_dd_acquire_read) + AO_char_and_dd_acquire_read(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swap_dd_acquire_read) + TA_assert(AO_char_fetch_compare_and_swap_dd_acquire_read(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swap_dd_acquire_read(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swap_dd_acquire_read) + TA_assert(!AO_int_compare_and_swap_dd_acquire_read(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swap_dd_acquire_read(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_or_dd_acquire_read) + AO_int_or_dd_acquire_read(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xor_dd_acquire_read) + AO_int_xor_dd_acquire_read(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_and_dd_acquire_read) + AO_int_and_dd_acquire_read(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swap_dd_acquire_read) + TA_assert(AO_int_fetch_compare_and_swap_dd_acquire_read(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swap_dd_acquire_read(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_load_dd_acquire_read) || defined(AO_HAVE_double_store_dd_acquire_read) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_load_dd_acquire_read) + new_w = AO_double_load_dd_acquire_read(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_store_dd_acquire_read) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_store_dd_acquire_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_store_dd_acquire_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_store_dd_acquire_read(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_double_dd_acquire_read) + TA_assert(!AO_compare_double_and_swap_double_dd_acquire_read(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_double_dd_acquire_read(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_dd_acquire_read(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_dd_acquire_read(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_double_dd_acquire_read(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_double_dd_acquire_read(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_double_dd_acquire_read(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_double_dd_acquire_read) + TA_assert(!AO_compare_and_swap_double_dd_acquire_read(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_double_dd_acquire_read(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_dd_acquire_read(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_double_dd_acquire_read(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_double_dd_acquire_read(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_double_dd_acquire_read(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swap_dd_acquire_read) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swap_dd_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swap_dd_acquire_read(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swap_dd_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swap_dd_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swap_dd_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swap_dd_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swap_dd_acquire_read(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} diff --git a/libatomic_ops/tests/test_atomic_include.template b/libatomic_ops/tests/test_atomic_include.template new file mode 100644 index 000000000..fc2c470ca --- /dev/null +++ b/libatomic_ops/tests/test_atomic_include.template @@ -0,0 +1,596 @@ +/* + * Copyright (c) 2003 by Hewlett-Packard Company. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Some basic sanity tests. These do not test the barrier semantics. */ + +#undef TA_assert +#define TA_assert(e) \ + if (!(e)) { fprintf(stderr, "Assertion failed %s:%d (barrier: XX)\n", \ + __FILE__, __LINE__), exit(1); } + +#undef MISSING +#define MISSING(name) \ + printf("Missing: %s\n", #name "XX") + +#if defined(CPPCHECK) + void list_atomicXX(void); + void char_list_atomicXX(void); + void short_list_atomicXX(void); + void int_list_atomicXX(void); + void double_list_atomicXX(void); +#endif + +void test_atomicXX(void) +{ + AO_t x; + unsigned char b; + unsigned short s; + unsigned int zz; +# if defined(AO_HAVE_test_and_setXX) + AO_TS_t z = AO_TS_INITIALIZER; +# endif +# if defined(AO_HAVE_double_compare_and_swapXX) \ + || defined(AO_HAVE_double_loadXX) \ + || defined(AO_HAVE_double_storeXX) + static AO_double_t old_w; /* static to avoid misalignment */ + AO_double_t new_w; +# endif +# if defined(AO_HAVE_compare_and_swap_doubleXX) \ + || defined(AO_HAVE_compare_double_and_swap_doubleXX) \ + || defined(AO_HAVE_double_compare_and_swapXX) + static AO_double_t w; /* static to avoid misalignment */ + w.AO_val1 = 0; + w.AO_val2 = 0; +# endif + +# if defined(CPPCHECK) + list_atomicXX(); + char_list_atomicXX(); + short_list_atomicXX(); + int_list_atomicXX(); + double_list_atomicXX(); +# endif +# if defined(AO_HAVE_nopXX) + AO_nopXX(); +# elif !defined(AO_HAVE_nop) || !defined(AO_HAVE_nop_full) \ + || !defined(AO_HAVE_nop_read) || !defined(AO_HAVE_nop_write) + MISSING(AO_nop); +# endif +# if defined(AO_HAVE_storeXX) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile AO_t *)&x = 0; /* initialize to avoid false warning */ +# endif + AO_storeXX(&x, 13); + TA_assert(x == 13); +# else +# if !defined(AO_HAVE_store) || !defined(AO_HAVE_store_full) \ + || !defined(AO_HAVE_store_release) \ + || !defined(AO_HAVE_store_release_write) \ + || !defined(AO_HAVE_store_write) + MISSING(AO_store); +# endif + x = 13; +# endif +# if defined(AO_HAVE_loadXX) + TA_assert(AO_loadXX(&x) == 13); +# elif !defined(AO_HAVE_load) || !defined(AO_HAVE_load_acquire) \ + || !defined(AO_HAVE_load_acquire_read) \ + || !defined(AO_HAVE_load_dd_acquire_read) \ + || !defined(AO_HAVE_load_full) || !defined(AO_HAVE_load_read) + MISSING(AO_load); +# endif +# if defined(AO_HAVE_test_and_setXX) + TA_assert(AO_test_and_setXX(&z) == AO_TS_CLEAR); + TA_assert(AO_test_and_setXX(&z) == AO_TS_SET); + TA_assert(AO_test_and_setXX(&z) == AO_TS_SET); + AO_CLEAR(&z); +# else + MISSING(AO_test_and_set); +# endif +# if defined(AO_HAVE_fetch_and_addXX) + TA_assert(AO_fetch_and_addXX(&x, 42) == 13); + TA_assert(AO_fetch_and_addXX(&x, (AO_t)(-42)) == 55); +# else + MISSING(AO_fetch_and_add); +# endif +# if defined(AO_HAVE_fetch_and_add1XX) + TA_assert(AO_fetch_and_add1XX(&x) == 13); +# else + MISSING(AO_fetch_and_add1); + ++x; +# endif +# if defined(AO_HAVE_fetch_and_sub1XX) + TA_assert(AO_fetch_and_sub1XX(&x) == 14); +# else + MISSING(AO_fetch_and_sub1); + --x; +# endif +# if defined(AO_HAVE_short_storeXX) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile short *)&s = 0; +# endif + AO_short_storeXX(&s, 13); +# else +# if !defined(AO_HAVE_short_store) || !defined(AO_HAVE_short_store_full) \ + || !defined(AO_HAVE_short_store_release) \ + || !defined(AO_HAVE_short_store_release_write) \ + || !defined(AO_HAVE_short_store_write) + MISSING(AO_short_store); +# endif + s = 13; +# endif +# if defined(AO_HAVE_short_loadXX) + TA_assert(AO_short_load(&s) == 13); +# elif !defined(AO_HAVE_short_load) || !defined(AO_HAVE_short_load_acquire) \ + || !defined(AO_HAVE_short_load_acquire_read) \ + || !defined(AO_HAVE_short_load_dd_acquire_read) \ + || !defined(AO_HAVE_short_load_full) \ + || !defined(AO_HAVE_short_load_read) + MISSING(AO_short_load); +# endif +# if defined(AO_HAVE_short_fetch_and_addXX) + TA_assert(AO_short_fetch_and_addXX(&s, 42) == 13); + TA_assert(AO_short_fetch_and_addXX(&s, (unsigned short)-42) == 55); +# else + MISSING(AO_short_fetch_and_add); +# endif +# if defined(AO_HAVE_short_fetch_and_add1XX) + TA_assert(AO_short_fetch_and_add1XX(&s) == 13); +# else + MISSING(AO_short_fetch_and_add1); + ++s; +# endif +# if defined(AO_HAVE_short_fetch_and_sub1XX) + TA_assert(AO_short_fetch_and_sub1XX(&s) == 14); +# else + MISSING(AO_short_fetch_and_sub1); + --s; +# endif + TA_assert(*(volatile short *)&s == 13); +# if defined(AO_HAVE_char_storeXX) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile char *)&b = 0; +# endif + AO_char_storeXX(&b, 13); +# else +# if !defined(AO_HAVE_char_store) || !defined(AO_HAVE_char_store_full) \ + || !defined(AO_HAVE_char_store_release) \ + || !defined(AO_HAVE_char_store_release_write) \ + || !defined(AO_HAVE_char_store_write) + MISSING(AO_char_store); +# endif + b = 13; +# endif +# if defined(AO_HAVE_char_loadXX) + TA_assert(AO_char_load(&b) == 13); +# elif !defined(AO_HAVE_char_load) || !defined(AO_HAVE_char_load_acquire) \ + || !defined(AO_HAVE_char_load_acquire_read) \ + || !defined(AO_HAVE_char_load_dd_acquire_read) \ + || !defined(AO_HAVE_char_load_full) || !defined(AO_HAVE_char_load_read) + MISSING(AO_char_load); +# endif +# if defined(AO_HAVE_char_fetch_and_addXX) + TA_assert(AO_char_fetch_and_addXX(&b, 42) == 13); + TA_assert(AO_char_fetch_and_addXX(&b, (unsigned char)-42) == 55); +# else + MISSING(AO_char_fetch_and_add); +# endif +# if defined(AO_HAVE_char_fetch_and_add1XX) + TA_assert(AO_char_fetch_and_add1XX(&b) == 13); +# else + MISSING(AO_char_fetch_and_add1); + ++b; +# endif +# if defined(AO_HAVE_char_fetch_and_sub1XX) + TA_assert(AO_char_fetch_and_sub1XX(&b) == 14); +# else + MISSING(AO_char_fetch_and_sub1); + --b; +# endif + TA_assert(*(volatile char *)&b == 13); +# if defined(AO_HAVE_int_storeXX) +# if (defined(AO_MEMORY_SANITIZER) || defined(LINT2)) \ + && defined(AO_PREFER_GENERALIZED) + *(volatile int *)&zz = 0; +# endif + AO_int_storeXX(&zz, 13); +# else +# if !defined(AO_HAVE_int_store) || !defined(AO_HAVE_int_store_full) \ + || !defined(AO_HAVE_int_store_release) \ + || !defined(AO_HAVE_int_store_release_write) \ + || !defined(AO_HAVE_int_store_write) + MISSING(AO_int_store); +# endif + zz = 13; +# endif +# if defined(AO_HAVE_int_loadXX) + TA_assert(AO_int_load(&zz) == 13); +# elif !defined(AO_HAVE_int_load) || !defined(AO_HAVE_int_load_acquire) \ + || !defined(AO_HAVE_int_load_acquire_read) \ + || !defined(AO_HAVE_int_load_dd_acquire_read) \ + || !defined(AO_HAVE_int_load_full) || !defined(AO_HAVE_int_load_read) + MISSING(AO_int_load); +# endif +# if defined(AO_HAVE_int_fetch_and_addXX) + TA_assert(AO_int_fetch_and_addXX(&zz, 42) == 13); + TA_assert(AO_int_fetch_and_addXX(&zz, (unsigned int)-42) == 55); +# else + MISSING(AO_int_fetch_and_add); +# endif +# if defined(AO_HAVE_int_fetch_and_add1XX) + TA_assert(AO_int_fetch_and_add1XX(&zz) == 13); +# else + MISSING(AO_int_fetch_and_add1); + ++zz; +# endif +# if defined(AO_HAVE_int_fetch_and_sub1XX) + TA_assert(AO_int_fetch_and_sub1XX(&zz) == 14); +# else + MISSING(AO_int_fetch_and_sub1); + --zz; +# endif + TA_assert(*(volatile int *)&zz == 13); +# if defined(AO_HAVE_compare_and_swapXX) + TA_assert(!AO_compare_and_swapXX(&x, 14, 42)); + TA_assert(x == 13); + TA_assert(AO_compare_and_swapXX(&x, 13, 42)); + TA_assert(x == 42); +# else + MISSING(AO_compare_and_swap); + if (*(volatile AO_t *)&x == 13) x = 42; +# endif +# if defined(AO_HAVE_orXX) + AO_orXX(&x, 66); + TA_assert(x == 106); +# else +# if !defined(AO_HAVE_or) || !defined(AO_HAVE_or_acquire) \ + || !defined(AO_HAVE_or_acquire_read) || !defined(AO_HAVE_or_full) \ + || !defined(AO_HAVE_or_read) || !defined(AO_HAVE_or_release) \ + || !defined(AO_HAVE_or_release_write) || !defined(AO_HAVE_or_write) + MISSING(AO_or); +# endif + x |= 66; +# endif +# if defined(AO_HAVE_xorXX) + AO_xorXX(&x, 181); + TA_assert(x == 223); +# else +# if !defined(AO_HAVE_xor) || !defined(AO_HAVE_xor_acquire) \ + || !defined(AO_HAVE_xor_acquire_read) || !defined(AO_HAVE_xor_full) \ + || !defined(AO_HAVE_xor_read) || !defined(AO_HAVE_xor_release) \ + || !defined(AO_HAVE_xor_release_write) || !defined(AO_HAVE_xor_write) + MISSING(AO_xor); +# endif + x ^= 181; +# endif +# if defined(AO_HAVE_andXX) + AO_andXX(&x, 57); + TA_assert(x == 25); +# else +# if !defined(AO_HAVE_and) || !defined(AO_HAVE_and_acquire) \ + || !defined(AO_HAVE_and_acquire_read) || !defined(AO_HAVE_and_full) \ + || !defined(AO_HAVE_and_read) || !defined(AO_HAVE_and_release) \ + || !defined(AO_HAVE_and_release_write) || !defined(AO_HAVE_and_write) + MISSING(AO_and); +# endif + x &= 57; +# endif +# if defined(AO_HAVE_fetch_compare_and_swapXX) + TA_assert(AO_fetch_compare_and_swapXX(&x, 14, 117) == 25); + TA_assert(x == 25); + TA_assert(AO_fetch_compare_and_swapXX(&x, 25, 117) == 25); +# else + MISSING(AO_fetch_compare_and_swap); + if (x == 25) x = 117; +# endif + TA_assert(x == 117); +# if defined(AO_HAVE_short_compare_and_swapXX) + TA_assert(!AO_short_compare_and_swapXX(&s, 14, 42)); + TA_assert(s == 13); + TA_assert(AO_short_compare_and_swapXX(&s, 13, 42)); + TA_assert(s == 42); +# else + MISSING(AO_short_compare_and_swap); + if (*(volatile short *)&s == 13) s = 42; +# endif +# if defined(AO_HAVE_short_orXX) + AO_short_orXX(&s, 66); + TA_assert(s == 106); +# else +# if !defined(AO_HAVE_short_or) || !defined(AO_HAVE_short_or_acquire) \ + || !defined(AO_HAVE_short_or_acquire_read) \ + || !defined(AO_HAVE_short_or_full) || !defined(AO_HAVE_short_or_read) \ + || !defined(AO_HAVE_short_or_release) \ + || !defined(AO_HAVE_short_or_release_write) \ + || !defined(AO_HAVE_short_or_write) + MISSING(AO_short_or); +# endif + s |= 66; +# endif +# if defined(AO_HAVE_short_xorXX) + AO_short_xorXX(&s, 181); + TA_assert(s == 223); +# else +# if !defined(AO_HAVE_short_xor) || !defined(AO_HAVE_short_xor_acquire) \ + || !defined(AO_HAVE_short_xor_acquire_read) \ + || !defined(AO_HAVE_short_xor_full) \ + || !defined(AO_HAVE_short_xor_read) \ + || !defined(AO_HAVE_short_xor_release) \ + || !defined(AO_HAVE_short_xor_release_write) \ + || !defined(AO_HAVE_short_xor_write) + MISSING(AO_short_xor); +# endif + s ^= 181; +# endif +# if defined(AO_HAVE_short_andXX) + AO_short_andXX(&s, 57); + TA_assert(s == 25); +# else +# if !defined(AO_HAVE_short_and) || !defined(AO_HAVE_short_and_acquire) \ + || !defined(AO_HAVE_short_and_acquire_read) \ + || !defined(AO_HAVE_short_and_full) \ + || !defined(AO_HAVE_short_and_read) \ + || !defined(AO_HAVE_short_and_release) \ + || !defined(AO_HAVE_short_and_release_write) \ + || !defined(AO_HAVE_short_and_write) + MISSING(AO_short_and); +# endif + s &= 57; +# endif +# if defined(AO_HAVE_short_fetch_compare_and_swapXX) + TA_assert(AO_short_fetch_compare_and_swapXX(&s, 14, 117) == 25); + TA_assert(s == 25); + TA_assert(AO_short_fetch_compare_and_swapXX(&s, 25, 117) == 25); +# else + MISSING(AO_short_fetch_compare_and_swap); + if (s == 25) s = 117; +# endif + TA_assert(s == 117); +# if defined(AO_HAVE_char_compare_and_swapXX) + TA_assert(!AO_char_compare_and_swapXX(&b, 14, 42)); + TA_assert(b == 13); + TA_assert(AO_char_compare_and_swapXX(&b, 13, 42)); + TA_assert(b == 42); +# else + MISSING(AO_char_compare_and_swap); + if (*(volatile char *)&b == 13) b = 42; +# endif +# if defined(AO_HAVE_char_orXX) + AO_char_orXX(&b, 66); + TA_assert(b == 106); +# else +# if !defined(AO_HAVE_char_or) || !defined(AO_HAVE_char_or_acquire) \ + || !defined(AO_HAVE_char_or_acquire_read) \ + || !defined(AO_HAVE_char_or_full) || !defined(AO_HAVE_char_or_read) \ + || !defined(AO_HAVE_char_or_release) \ + || !defined(AO_HAVE_char_or_release_write) \ + || !defined(AO_HAVE_char_or_write) + MISSING(AO_char_or); +# endif + b |= 66; +# endif +# if defined(AO_HAVE_char_xorXX) + AO_char_xorXX(&b, 181); + TA_assert(b == 223); +# else +# if !defined(AO_HAVE_char_xor) || !defined(AO_HAVE_char_xor_acquire) \ + || !defined(AO_HAVE_char_xor_acquire_read) \ + || !defined(AO_HAVE_char_xor_full) || !defined(AO_HAVE_char_xor_read) \ + || !defined(AO_HAVE_char_xor_release) \ + || !defined(AO_HAVE_char_xor_release_write) \ + || !defined(AO_HAVE_char_xor_write) + MISSING(AO_char_xor); +# endif + b ^= 181; +# endif +# if defined(AO_HAVE_char_andXX) + AO_char_andXX(&b, 57); + TA_assert(b == 25); +# else +# if !defined(AO_HAVE_char_and) || !defined(AO_HAVE_char_and_acquire) \ + || !defined(AO_HAVE_char_and_acquire_read) \ + || !defined(AO_HAVE_char_and_full) || !defined(AO_HAVE_char_and_read) \ + || !defined(AO_HAVE_char_and_release) \ + || !defined(AO_HAVE_char_and_release_write) \ + || !defined(AO_HAVE_char_and_write) + MISSING(AO_char_and); +# endif + b &= 57; +# endif +# if defined(AO_HAVE_char_fetch_compare_and_swapXX) + TA_assert(AO_char_fetch_compare_and_swapXX(&b, 14, 117) == 25); + TA_assert(b == 25); + TA_assert(AO_char_fetch_compare_and_swapXX(&b, 25, 117) == 25); +# else + MISSING(AO_char_fetch_compare_and_swap); + if (b == 25) b = 117; +# endif + TA_assert(b == 117); +# if defined(AO_HAVE_int_compare_and_swapXX) + TA_assert(!AO_int_compare_and_swapXX(&zz, 14, 42)); + TA_assert(zz == 13); + TA_assert(AO_int_compare_and_swapXX(&zz, 13, 42)); + TA_assert(zz == 42); +# else + MISSING(AO_int_compare_and_swap); + if (*(volatile int *)&zz == 13) zz = 42; +# endif +# if defined(AO_HAVE_int_orXX) + AO_int_orXX(&zz, 66); + TA_assert(zz == 106); +# else +# if !defined(AO_HAVE_int_or) || !defined(AO_HAVE_int_or_acquire) \ + || !defined(AO_HAVE_int_or_acquire_read) \ + || !defined(AO_HAVE_int_or_full) || !defined(AO_HAVE_int_or_read) \ + || !defined(AO_HAVE_int_or_release) \ + || !defined(AO_HAVE_int_or_release_write) \ + || !defined(AO_HAVE_int_or_write) + MISSING(AO_int_or); +# endif + zz |= 66; +# endif +# if defined(AO_HAVE_int_xorXX) + AO_int_xorXX(&zz, 181); + TA_assert(zz == 223); +# else +# if !defined(AO_HAVE_int_xor) || !defined(AO_HAVE_int_xor_acquire) \ + || !defined(AO_HAVE_int_xor_acquire_read) \ + || !defined(AO_HAVE_int_xor_full) || !defined(AO_HAVE_int_xor_read) \ + || !defined(AO_HAVE_int_xor_release) \ + || !defined(AO_HAVE_int_xor_release_write) \ + || !defined(AO_HAVE_int_xor_write) + MISSING(AO_int_xor); +# endif + zz ^= 181; +# endif +# if defined(AO_HAVE_int_andXX) + AO_int_andXX(&zz, 57); + TA_assert(zz == 25); +# else +# if !defined(AO_HAVE_int_and) || !defined(AO_HAVE_int_and_acquire) \ + || !defined(AO_HAVE_int_and_acquire_read) \ + || !defined(AO_HAVE_int_and_full) || !defined(AO_HAVE_int_and_read) \ + || !defined(AO_HAVE_int_and_release) \ + || !defined(AO_HAVE_int_and_release_write) \ + || !defined(AO_HAVE_int_and_write) + MISSING(AO_int_and); +# endif + zz &= 57; +# endif +# if defined(AO_HAVE_int_fetch_compare_and_swapXX) + TA_assert(AO_int_fetch_compare_and_swapXX(&zz, 14, 117) == 25); + TA_assert(zz == 25); + TA_assert(AO_int_fetch_compare_and_swapXX(&zz, 25, 117) == 25); +# else + MISSING(AO_int_fetch_compare_and_swap); + if (zz == 25) zz = 117; +# endif + TA_assert(zz == 117); +# if defined(AO_HAVE_double_loadXX) || defined(AO_HAVE_double_storeXX) + /* Initialize old_w even for store to workaround MSan warning. */ + old_w.AO_val1 = 3316; + old_w.AO_val2 = 2921; +# endif +# if defined(AO_HAVE_double_loadXX) + new_w = AO_double_loadXX(&old_w); + TA_assert(new_w.AO_val1 == 3316 && new_w.AO_val2 == 2921); +# elif !defined(AO_HAVE_double_load) \ + || !defined(AO_HAVE_double_load_acquire) \ + || !defined(AO_HAVE_double_load_acquire_read) \ + || !defined(AO_HAVE_double_load_dd_acquire_read) \ + || !defined(AO_HAVE_double_load_full) \ + || !defined(AO_HAVE_double_load_read) + MISSING(AO_double_load); +# endif +# if defined(AO_HAVE_double_storeXX) + new_w.AO_val1 = 1375; + new_w.AO_val2 = 8243; + AO_double_storeXX(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + AO_double_storeXX(&old_w, new_w); + TA_assert(old_w.AO_val1 == 1375 && old_w.AO_val2 == 8243); + new_w.AO_val1 ^= old_w.AO_val1; + new_w.AO_val2 ^= old_w.AO_val2; + AO_double_storeXX(&old_w, new_w); + TA_assert(old_w.AO_val1 == 0 && old_w.AO_val2 == 0); +# elif !defined(AO_HAVE_double_store) \ + || !defined(AO_HAVE_double_store_full) \ + || !defined(AO_HAVE_double_store_release) \ + || !defined(AO_HAVE_double_store_release_write) \ + || !defined(AO_HAVE_double_store_write) + MISSING(AO_double_store); +# endif +# if defined(AO_HAVE_compare_double_and_swap_doubleXX) + TA_assert(!AO_compare_double_and_swap_doubleXX(&w, 17, 42, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_double_and_swap_doubleXX(&w, 0, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_doubleXX(&w, 12, 14, 64, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_doubleXX(&w, 11, 13, 85, 82)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_double_and_swap_doubleXX(&w, 13, 12, 17, 42)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_double_and_swap_doubleXX(&w, 12, 13, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_double_and_swap_doubleXX(&w, 17, 42, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_double_and_swap_double); +# endif +# if defined(AO_HAVE_compare_and_swap_doubleXX) + TA_assert(!AO_compare_and_swap_doubleXX(&w, 17, 12, 13)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_compare_and_swap_doubleXX(&w, 0, 12, 13)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_doubleXX(&w, 13, 12, 33)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(!AO_compare_and_swap_doubleXX(&w, 1213, 48, 86)); + TA_assert(w.AO_val1 == 12 && w.AO_val2 == 13); + TA_assert(AO_compare_and_swap_doubleXX(&w, 12, 17, 42)); + TA_assert(w.AO_val1 == 17 && w.AO_val2 == 42); + TA_assert(AO_compare_and_swap_doubleXX(&w, 17, 0, 0)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_compare_and_swap_double); +# endif +# if defined(AO_HAVE_double_compare_and_swapXX) + old_w.AO_val1 = 4116; + old_w.AO_val2 = 2121; + new_w.AO_val1 = 8537; + new_w.AO_val2 = 6410; + TA_assert(!AO_double_compare_and_swapXX(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); + TA_assert(AO_double_compare_and_swapXX(&w, w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = 29; + new_w.AO_val1 = 820; + new_w.AO_val2 = 5917; + TA_assert(!AO_double_compare_and_swapXX(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = 11; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 3552; + new_w.AO_val2 = 1746; + TA_assert(!AO_double_compare_and_swapXX(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 8537; + new_w.AO_val1 = 4116; + new_w.AO_val2 = 2121; + TA_assert(!AO_double_compare_and_swapXX(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 8537 && w.AO_val2 == 6410); + old_w.AO_val1 = old_w.AO_val2; + old_w.AO_val2 = 6410; + new_w.AO_val1 = 1; + TA_assert(AO_double_compare_and_swapXX(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 1 && w.AO_val2 == 2121); + old_w.AO_val1 = new_w.AO_val1; + old_w.AO_val2 = w.AO_val2; + new_w.AO_val1--; + new_w.AO_val2 = 0; + TA_assert(AO_double_compare_and_swapXX(&w, old_w, new_w)); + TA_assert(w.AO_val1 == 0 && w.AO_val2 == 0); +# else + MISSING(AO_double_compare_and_swap); +# endif +} diff --git a/libatomic_ops/tests/test_malloc.c b/libatomic_ops/tests/test_malloc.c new file mode 100644 index 000000000..7a0b421f5 --- /dev/null +++ b/libatomic_ops/tests/test_malloc.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if defined(HAVE_CONFIG_H) +# include "config.h" +#endif + +#ifdef DONT_USE_MMAP +# undef HAVE_MMAP +#endif + +#include "run_parallel.h" + +#include +#include +#include "atomic_ops_malloc.h" + +#ifndef DEFAULT_NTHREADS +# ifdef HAVE_MMAP +# define DEFAULT_NTHREADS 16 /* must be <= MAX_NTHREADS */ +# else +# define DEFAULT_NTHREADS 3 +# endif +#endif + +#ifndef N_REVERSALS +# ifdef AO_USE_PTHREAD_DEFS +# define N_REVERSALS 4 +# else +# define N_REVERSALS 1000 /* must be even */ +# endif +#endif + +#ifndef LIST_LENGTH +# ifdef HAVE_MMAP +# define LIST_LENGTH 1000 +# else +# define LIST_LENGTH 100 +# endif +#endif + +#ifndef LARGE_OBJ_SIZE +# ifdef HAVE_MMAP +# define LARGE_OBJ_SIZE 200000 +# else +# define LARGE_OBJ_SIZE 20000 +# endif +#endif + +#ifdef USE_STANDARD_MALLOC +# define AO_malloc(n) malloc(n) +# define AO_free(p) free(p) +# define AO_malloc_enable_mmap() +#endif + +typedef struct list_node { + struct list_node *next; + int data; +} ln; + +ln *cons(int d, ln *tail) +{ +# ifdef AO_HAVE_fetch_and_add1 + static volatile AO_t extra = 0; + size_t my_extra = (size_t)AO_fetch_and_add1(&extra) % 101; +# else + static size_t extra = 0; /* data race in extra is OK */ + size_t my_extra = (extra++) % 101; +# endif + ln *result; + char *extras; + unsigned i; + + result = (ln *)AO_malloc(sizeof(ln) + sizeof(int)*my_extra); + if (result == 0) + { + fprintf(stderr, "Out of memory\n"); + /* Normal for more than about 10 threads without mmap? */ + exit(2); + } + + result -> data = d; + result -> next = tail; + extras = (char *)(result+1); + for (i = 0; i < my_extra; ++i) + extras[i*sizeof(int)] = 42; + return result; +} + +#ifdef DEBUG_RUN_ONE_TEST +void print_list(ln *l) +{ + ln *p; + + for (p = l; p != 0; p = p -> next) + { + printf("%d, ", p -> data); + } + printf("\n"); +} +#endif /* DEBUG_RUN_ONE_TEST */ + +/* Check that l contains numbers from m to n inclusive in ascending order */ +void check_list(ln *l, int m, int n) +{ + ln *p; + int i; + + for (p = l, i = m; p != 0 && i <= n; p = p -> next, ++i) + { + if (i != p -> data) + { + fprintf(stderr, "Found %d, expected %d\n", p -> data, i); + abort(); + } + } + if (i <= n) + { + fprintf(stderr, "Number not found: %d\n", i); + abort(); + } + if (p != 0) + { + fprintf(stderr, "Found unexpected number: %d\n", i); + abort(); + } +} + +/* Create a list of integers from m to n */ +ln * +make_list(int m, int n) +{ + if (m > n) return 0; + return cons(m, make_list(m+1, n)); +} + +void free_list(ln *x) +{ + while (x != NULL) { + ln *next = x -> next; + AO_free(x); + x = next; + } +} + +/* Reverse list x, and concatenate it to y, deallocating no longer needed */ +/* nodes in x. */ +ln * +reverse(ln *x, ln *y) +{ + ln * result; + + if (x == 0) return y; + result = reverse(x -> next, cons(x -> data, y)); + AO_free(x); + return result; +} + +int dummy_test(void) { return 1; } + +void * run_one_test(void * arg) { + ln * x = make_list(1, LIST_LENGTH); + int i; + char *p = (char *)AO_malloc(LARGE_OBJ_SIZE); + char *q; + char a = 'a' + ((int)((AO_PTRDIFF_T)arg) * 2) % ('z' - 'a' + 1); + char b = a + 1; + + if (0 == p) { +# ifdef HAVE_MMAP + fprintf(stderr, "AO_malloc(%d) failed\n", LARGE_OBJ_SIZE); + abort(); +# else + fprintf(stderr, "AO_malloc(%d) failed: This is normal without mmap\n", + LARGE_OBJ_SIZE); +# endif + } else { + p[0] = p[LARGE_OBJ_SIZE/2] = p[LARGE_OBJ_SIZE-1] = a; + q = (char *)AO_malloc(LARGE_OBJ_SIZE); + if (q == 0) + { + fprintf(stderr, "Out of memory\n"); + /* Normal for more than about 10 threads without mmap? */ + exit(2); + } + q[0] = q[LARGE_OBJ_SIZE/2] = q[LARGE_OBJ_SIZE-1] = b; + if (p[0] != a || p[LARGE_OBJ_SIZE/2] != a || p[LARGE_OBJ_SIZE-1] != a) { + fprintf(stderr, "First large allocation smashed\n"); + abort(); + } + AO_free(p); + if (q[0] != b || q[LARGE_OBJ_SIZE/2] != b || q[LARGE_OBJ_SIZE-1] != b) { + fprintf(stderr, "Second large allocation smashed\n"); + abort(); + } + AO_free(q); + } +# ifdef DEBUG_RUN_ONE_TEST + x = reverse(x, 0); + print_list(x); + x = reverse(x, 0); + print_list(x); +# endif + for (i = 0; i < N_REVERSALS; ++i) { + x = reverse(x, 0); + } + check_list(x, 1, LIST_LENGTH); + free_list(x); + return NULL; +} + +#ifndef LOG_MAX_SIZE +# define LOG_MAX_SIZE 16 +#endif + +#define CHUNK_SIZE (1 << LOG_MAX_SIZE) + +int main(int argc, char **argv) { + int nthreads; + + if (1 == argc) { + nthreads = DEFAULT_NTHREADS; + } else if (2 == argc) { + nthreads = atoi(argv[1]); + if (nthreads < 1 || nthreads > MAX_NTHREADS) { + fprintf(stderr, "Invalid # of threads argument\n"); + exit(1); + } + } else { + fprintf(stderr, "Usage: %s [# of threads]\n", argv[0]); + exit(1); + } + printf("Performing %d reversals of %d element lists in %d threads\n", + N_REVERSALS, LIST_LENGTH, nthreads); + AO_malloc_enable_mmap(); + + /* Test various corner cases. */ + AO_free(NULL); + AO_free(AO_malloc(0)); +# ifdef HAVE_MMAP + AO_free(AO_malloc(CHUNK_SIZE - (sizeof(AO_t)-1))); /* large alloc */ +# endif + + run_parallel(nthreads, run_one_test, dummy_test, "AO_malloc/AO_free"); + return 0; +} diff --git a/libatomic_ops/tests/test_stack.c b/libatomic_ops/tests/test_stack.c new file mode 100644 index 000000000..7231aeb64 --- /dev/null +++ b/libatomic_ops/tests/test_stack.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if defined(HAVE_CONFIG_H) +# include "config.h" +#endif + +#include + +#if defined(__vxworks) + + int main(void) + { + printf("test skipped\n"); + return 0; + } + +#else + +#if ((defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__)) \ + || defined(_MSC_VER) || defined(_WIN32_WINCE)) \ + && !defined(AO_USE_WIN32_PTHREADS) +# define USE_WINTHREADS +#endif + +#ifdef USE_WINTHREADS +# include +#else +# include +#endif + +#include +#include + +#include "atomic_ops_stack.h" /* includes atomic_ops.h as well */ + +#if (defined(_WIN32_WCE) || defined(__MINGW32CE__)) && !defined(AO_HAVE_abort) +# define abort() _exit(-1) /* there is no abort() in WinCE */ +#endif + +#ifndef MAX_NTHREADS +# define MAX_NTHREADS 100 +#endif + +#ifndef DEFAULT_NTHREADS +# define DEFAULT_NTHREADS 16 /* must be <= MAX_NTHREADS */ +#endif + +#ifdef NO_TIMES +# define get_msecs() 0 +#elif (defined(USE_WINTHREADS) || defined(AO_USE_WIN32_PTHREADS)) \ + && !defined(CPPCHECK) +# include + unsigned long get_msecs(void) + { + struct timeb tb; + + ftime(&tb); + return (unsigned long)tb.time * 1000 + tb.millitm; + } +#else /* Unix */ +# include +# include + unsigned long get_msecs(void) + { + struct timeval tv; + + gettimeofday(&tv, 0); + return (unsigned long)tv.tv_sec * 1000 + tv.tv_usec/1000; + } +#endif /* !NO_TIMES */ + +struct le { + AO_t next; /* must be the first field */ + int data; +}; + +typedef union le_u { + AO_t next; + struct le e; +} list_element; + +#if defined(CPPCHECK) + AO_stack_t the_list; /* to test AO_stack_init() */ +#else + AO_stack_t the_list = AO_STACK_INITIALIZER; +#endif + +/* Add elements from 1 to n to the list (1 is pushed first). */ +/* This is called from a single thread only. */ +void add_elements(int n) +{ + list_element * le; + if (n == 0) return; + add_elements(n-1); + le = (list_element *)malloc(sizeof(list_element)); + if (le == 0) + { + fprintf(stderr, "Out of memory\n"); + exit(2); + } +# if defined(CPPCHECK) + le->e.next = 0; /* mark field as used */ +# endif + le->e.data = n; + AO_stack_push(&the_list, &le->next); +} + +#ifdef VERBOSE_STACK +void print_list(void) +{ + AO_t *p; + + for (p = AO_REAL_HEAD_PTR(the_list); + p != 0; p = AO_REAL_NEXT_PTR(*p)) + printf("%d\n", ((list_element*)p)->e.data); +} +#endif /* VERBOSE_STACK */ + +/* Check that the list contains only values from 1 to n, in any order, */ +/* w/o duplications. Executed when the list mutation is finished. */ +void check_list(int n) +{ + AO_t *p; + int i; + int err_cnt = 0; + char *marks = (char*)calloc(n + 1, 1); + + if (0 == marks) + { + fprintf(stderr, "Out of memory (marks)\n"); + exit(2); + } + + for (p = AO_REAL_HEAD_PTR(the_list); + p != 0; p = AO_REAL_NEXT_PTR(*p)) + { + i = ((list_element*)p)->e.data; + if (i > n || i <= 0) + { + fprintf(stderr, "Found erroneous list element %d\n", i); + err_cnt++; + } + else if (marks[i] != 0) + { + fprintf(stderr, "Found duplicate list element %d\n", i); + abort(); + } + else marks[i] = 1; + } + + for (i = 1; i <= n; ++i) + if (marks[i] != 1) + { + fprintf(stderr, "Missing list element %d\n", i); + err_cnt++; + } + + free(marks); + if (err_cnt > 0) abort(); +} + +volatile AO_t ops_performed = 0; + +#ifndef LIMIT + /* Total number of push/pop ops in all threads per test. */ +# ifdef AO_USE_PTHREAD_DEFS +# define LIMIT 20000 +# else +# define LIMIT 1000000 +# endif +#endif + +#ifdef AO_HAVE_fetch_and_add +# define fetch_then_add(addr, val) AO_fetch_and_add(addr, val) +#else + /* OK to perform it in two atomic steps, but really quite */ + /* unacceptable for timing purposes. */ + AO_INLINE AO_t fetch_then_add(volatile AO_t * addr, AO_t val) + { + AO_t result = AO_load(addr); + AO_store(addr, result + val); + return result; + } +#endif + +#ifdef USE_WINTHREADS + DWORD WINAPI run_one_test(LPVOID arg) +#else + void * run_one_test(void * arg) +#endif +{ + AO_t *t[MAX_NTHREADS + 1]; + unsigned index = (unsigned)(size_t)arg; + unsigned i; +# ifdef VERBOSE_STACK + unsigned j = 0; + + printf("starting thread %u\n", index); +# endif + assert(index <= MAX_NTHREADS); + while (fetch_then_add(&ops_performed, index + 1) + index + 1 < LIMIT) + { + /* Pop index+1 elements (where index is the thread's one), then */ + /* push them back (in the same order of operations). */ + /* Note that this is done in parallel by many threads. */ + for (i = 0; i <= index; ++i) + { + t[i] = AO_stack_pop(&the_list); + if (0 == t[i]) + { + /* This should not happen as at most n*(n+1)/2 elements */ + /* could be popped off at a time. */ + fprintf(stderr, "Failed - nothing to pop\n"); + abort(); + } + } + for (i = 0; i <= index; ++i) + { + AO_stack_push(&the_list, t[i]); + } +# ifdef VERBOSE_STACK + j += index + 1; +# endif + } + /* Repeat until LIMIT push/pop operations are performed (by all */ + /* the threads simultaneously). */ +# ifdef VERBOSE_STACK + printf("finished thread %u: %u total ops\n", index, j); +# endif + return 0; +} + +#ifndef N_EXPERIMENTS +# define N_EXPERIMENTS 1 +#endif + +unsigned long times[MAX_NTHREADS + 1][N_EXPERIMENTS]; + +void run_one_experiment(int max_nthreads, int exper_n) +{ + int nthreads; + + assert(max_nthreads <= MAX_NTHREADS); + assert(exper_n < N_EXPERIMENTS); + for (nthreads = 1; nthreads <= max_nthreads; ++nthreads) { + unsigned i; +# ifdef USE_WINTHREADS + DWORD thread_id; + HANDLE thread[MAX_NTHREADS]; +# else + pthread_t thread[MAX_NTHREADS]; +# endif + int list_length = nthreads*(nthreads+1)/2; + unsigned long start_time; + AO_t *le; + +# ifdef VERBOSE_STACK + printf("Before add_elements: exper_n=%d, nthreads=%d," + " max_nthreads=%d, list_length=%d\n", + exper_n, nthreads, max_nthreads, list_length); +# endif + /* Create a list with n*(n+1)/2 elements. */ + assert(0 == AO_REAL_HEAD_PTR(the_list)); + add_elements(list_length); +# ifdef VERBOSE_STACK + printf("Initial list (nthreads = %d):\n", nthreads); + print_list(); +# endif + ops_performed = 0; + start_time = get_msecs(); + /* Start n-1 threads to run_one_test in parallel. */ + for (i = 1; (int)i < nthreads; ++i) { + int code; + +# ifdef USE_WINTHREADS + thread[i] = CreateThread(NULL, 0, run_one_test, (LPVOID)(size_t)i, + 0, &thread_id); + code = thread[i] != NULL ? 0 : (int)GetLastError(); +# else + code = pthread_create(&thread[i], 0, run_one_test, + (void *)(size_t)i); +# endif + if (code != 0) { + fprintf(stderr, "Thread creation failed %u\n", (unsigned)code); + exit(3); + } + } + /* We use the main thread to run one test. This allows */ + /* gprof profiling to work, for example. */ + run_one_test(0); + /* Wait for all the threads to complete. */ + for (i = 1; (int)i < nthreads; ++i) { + int code; + +# ifdef USE_WINTHREADS + code = WaitForSingleObject(thread[i], INFINITE) == WAIT_OBJECT_0 ? + 0 : (int)GetLastError(); +# else + code = pthread_join(thread[i], 0); +# endif + if (code != 0) { + fprintf(stderr, "Thread join failed %u\n", (unsigned)code); + abort(); + } + } + times[nthreads][exper_n] = get_msecs() - start_time; +# ifdef VERBOSE_STACK + printf("nthreads=%d, time_ms=%lu\n", + nthreads, times[nthreads][exper_n]); + printf("final list (should be reordered initial list):\n"); + print_list(); +# endif + /* Ensure that no element is lost or duplicated. */ + check_list(list_length); + /* And, free the entire list. */ + while ((le = AO_stack_pop(&the_list)) != 0) + free(le); + /* Retry with larger n values. */ + } +} + +void run_all_experiments(int max_nthreads) +{ + int exper_n; + + for (exper_n = 0; exper_n < N_EXPERIMENTS; ++exper_n) + run_one_experiment(max_nthreads, exper_n); +} + +/* Output the performance statistic. */ +void output_stat(int max_nthreads) +{ + int nthreads; + + assert(max_nthreads <= MAX_NTHREADS); + for (nthreads = 1; nthreads <= max_nthreads; ++nthreads) { +# ifndef NO_TIMES + int exper_n; + unsigned long sum = 0; +# endif + + printf("About %d pushes + %d pops in %d threads:", + LIMIT, LIMIT, nthreads); +# ifndef NO_TIMES + for (exper_n = 0; exper_n < N_EXPERIMENTS; ++exper_n) { +# ifdef VERBOSE_STACK + printf(" [%lums]", times[nthreads][exper_n]); +# endif + sum += times[nthreads][exper_n]; + } + printf(" %lu msecs\n", (sum + N_EXPERIMENTS/2)/N_EXPERIMENTS); +# else + printf(" completed\n"); +# endif + } +} + +int main(int argc, char **argv) +{ + int max_nthreads = DEFAULT_NTHREADS; + + if (2 == argc) + { + max_nthreads = atoi(argv[1]); + if (max_nthreads < 1 || max_nthreads > MAX_NTHREADS) + { + fprintf(stderr, "Invalid max # of threads argument\n"); + exit(1); + } + } + else if (argc > 2) + { + fprintf(stderr, "Usage: %s [max # of threads]\n", argv[0]); + exit(1); + } + if (!AO_stack_is_lock_free()) + printf("Use almost-lock-free implementation\n"); +# if defined(CPPCHECK) + AO_stack_init(&the_list); +# endif + run_all_experiments(max_nthreads); + output_stat(max_nthreads); + return 0; +} + +#endif From b5a6fd02a63debff8830884154f8ea7a8b26ef59 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sun, 22 Dec 2024 02:09:25 -0700 Subject: [PATCH 05/24] REPO: Remove libatomic_ops from ignore list. --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9bec32009..d15cb8096 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ __pycache__ ### Compressed packages and Garbage Collector *.tar.gz *.tgz -libatomic* ### Binaries asy From 92a60fbc9190e8bbdf3c4129f3da9baeaaf39205 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sun, 22 Dec 2024 02:09:39 -0700 Subject: [PATCH 06/24] VCPKG: Remove gc from vcpkg install list. --- cmake-scripts/vcpkg-features.cmake | 4 ---- vcpkg.json | 4 ---- 2 files changed, 8 deletions(-) diff --git a/cmake-scripts/vcpkg-features.cmake b/cmake-scripts/vcpkg-features.cmake index 8e6380313..a0aa72598 100644 --- a/cmake-scripts/vcpkg-features.cmake +++ b/cmake-scripts/vcpkg-features.cmake @@ -1,7 +1,3 @@ -if (ENABLE_GC) - list(APPEND VCPKG_MANIFEST_FEATURES gc) -endif() - if (ENABLE_READLINE) list(APPEND VCPKG_MANIFEST_FEATURES readline) endif() diff --git a/vcpkg.json b/vcpkg.json index 08b299aeb..b3c43933b 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -31,10 +31,6 @@ } ] }, - "gc": { - "description": "Enable garbage collection support", - "dependencies": ["bdwgc"] - }, "curl": { "description": "Enable curl support", "dependencies": ["curl"] From a02ebe837ea4977b7b56f05a6c955e02438c7c12 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sun, 22 Dec 2024 02:09:55 -0700 Subject: [PATCH 07/24] CMAKE: Use subrepo-cloned libatomic_ops and bdwgc for asymptote. --- cmake-scripts/external-libs.cmake | 34 ----------------- cmake-scripts/subrepo-projects.cmake | 55 ++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/cmake-scripts/external-libs.cmake b/cmake-scripts/external-libs.cmake index f1708a8ac..7f567ac43 100644 --- a/cmake-scripts/external-libs.cmake +++ b/cmake-scripts/external-libs.cmake @@ -60,40 +60,6 @@ else() message(FATAL_ERROR "glm not found; will not use glm") endif() -# -------- not required, but highly recommend if your system can build it --------- -# these options are (mostly) on by default - -# boehm gc - -if (ENABLE_GC) - find_package(BDWgc CONFIG) - if (BDWgc_FOUND) - list(APPEND ASY_STATIC_LIBARIES BDWgc::gc BDWgc::gccpp) - - # We use #include as opposed to (and also for other gc include files) to allow - # linking directly to the compiled source for testing different GC versions. - - # In GC tarballs downloaded from https://www.hboehm.info/gc/, the header files are in include/gc.h, and not - # include/gc/gc.h, hence we need a way to allow inclusion of "gc.h". In vcpkg gc distributions, the include - # files are provided in include/gc/gc.h (and other files). Hence we append "/gc" to the include directories. - list(APPEND - ASYMPTOTE_INCLUDES - $,APPEND,/gc> - $,APPEND,/gc> - ) - - if (WIN32) - list(APPEND ASY_STATIC_LIBARIES BDWgc::gctba) - endif() - list(APPEND ASY_MACROS USEGC) - else() - message(FATAL_ERROR "BDWgc not found") - endif() -else() - message(STATUS "Disabling gc support") -endif() - - if (ENABLE_READLINE) # curses if (UNIX) diff --git a/cmake-scripts/subrepo-projects.cmake b/cmake-scripts/subrepo-projects.cmake index c8b368945..846b8184e 100644 --- a/cmake-scripts/subrepo-projects.cmake +++ b/cmake-scripts/subrepo-projects.cmake @@ -1,4 +1,47 @@ -set(LSP_REPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/LspCpp) +set(ASY_SUBREPO_CLONE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) + +set(LSP_REPO_ROOT ${ASY_SUBREPO_CLONE_ROOT}/LspCpp) +set(TINYEXR_SUBREPO_ROOT ${ASY_SUBREPO_CLONE_ROOT}/tinyexr) +set(BOEHM_GC_ROOT ${ASY_SUBREPO_CLONE_ROOT}/gc) +set(LIBATOMIC_OPS_ROOT ${ASY_SUBREPO_CLONE_ROOT}/libatomic_ops) + +# boehm gc +if (ENABLE_GC) + set(enable_gpl OFF CACHE INTERNAL "libatomicops gpl libs option") + add_subdirectory(${LIBATOMIC_OPS_ROOT}) + + set(OLD_CFLAG_EXTRA ${CFLAG_EXTRA}) + set(CFLAGS_EXTRA -I${LIBATOMIC_OPS_ROOT}/src) # for bdwgc + + set(OLD_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "bdwgc shared libs flag") + set(enable_cplusplus ON CACHE INTERNAL "bdwgc enable C++") + set(without_libatomic_ops ON CACHE INTERNAL "bdwgc use libatomic ops") + add_subdirectory(${BOEHM_GC_ROOT}) + + set(CFLAG_EXTRA ${OLD_CFLAG_EXTRA}) + unset(BUILD_SHARED_LIBS CACHE) + set(BUILD_SHARED_LIBS ${OLD_BUILD_SHARED_LIBS}) + + list(APPEND ASY_STATIC_LIBARIES gc gccpp atomic_ops) + + if (WIN32) + list(APPEND ASY_MACROS GC_NOT_DLL) + endif() + # We use #include as opposed to (and also for other gc include files) to allow + # linking directly to the compiled source for testing different GC versions. + + # In GC tarballs downloaded from https://www.hboehm.info/gc/, the header files are in include/gc.h, and not + # include/gc/gc.h, hence we need a way to allow inclusion of "gc.h". In vcpkg gc distributions, the include + # files are provided in include/gc/gc.h (and other files). Hence we append "/gc" to the include directories. + + if (WIN32) + list(APPEND ASY_STATIC_LIBARIES gctba) + endif() + list(APPEND ASY_MACROS USEGC) +else() + message(STATUS "Disabling gc support") +endif() if (ENABLE_LSP) message(STATUS "LSP Enabled.") @@ -6,12 +49,18 @@ if (ENABLE_LSP) set(Boost_NO_WARN_NEW_VERSIONS 1) set(USE_SYSTEM_RAPIDJSON ON CACHE INTERNAL "Use system rapidjson") set(LSPCPP_USE_CPP17 ON CACHE INTERNAL "C++17 mode") - set(LSPCPP_SUPPORT_BOEHM_GC ON CACHE INTERNAL "Use boehm GC") # For transitive URI dependency set(Uri_BUILD_DOCS OFF CACHE INTERNAL "build docs for uri") set(Uri_BUILD_TESTS OFF CACHE INTERNAL "build tests for uri") + if (ENABLE_GC) + set(LSPCPP_SUPPORT_BOEHM_GC ON CACHE INTERNAL "Use boehm GC") + set(LSPCPP_GC_DOWNLOADED_ROOT ${BOEHM_GC_ROOT} CACHE INTERNAL "gc root for lsp") + set(LSPCPP_GC_STATIC ON CACHE INTERNAL "lsp use static gc") + endif() + add_subdirectory(${LSP_REPO_ROOT}) + list(APPEND ASY_STATIC_LIBARIES lspcpp) list(APPEND ASY_MACROS HAVE_LSP=1) else() @@ -19,5 +68,3 @@ else() message(STATUS "LSP Disabled. Will not have language server protocol support.") list(APPEND ASYMPTOTE_INCLUDES ${LSP_REPO_ROOT}/include) endif() - -set(TINYEXR_SUBREPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/tinyexr) From 203ea75d0c9a7b460309f54296aba105cced8f27 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sun, 22 Dec 2024 02:50:59 -0700 Subject: [PATCH 08/24] git subrepo pull LspCpp subrepo: subdir: "LspCpp" merged: "80e204a4f" upstream: origin: "git@github.com:vectorgraphics/LspCpp" branch: "master" commit: "80e204a4f" git-subrepo: version: "0.4.9" origin: "https://github.com/ingydotnet/git-subrepo" commit: "cce3d93" --- LspCpp/.gitrepo | 4 ++-- LspCpp/CMakeLists.txt | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/LspCpp/.gitrepo b/LspCpp/.gitrepo index 9b4d69a1b..ab1429b8d 100644 --- a/LspCpp/.gitrepo +++ b/LspCpp/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:vectorgraphics/LspCpp branch = master - commit = d12eab466ee2c9eb8bb8925ec8482e374a880fcc - parent = 2c34fbcaf9d487f6fd7a9299649dbc9f76860e83 + commit = 80e204a4fc9f088b9732e66affa114ca459d3151 + parent = 91cae6a300d7bf25381f9630f190a0531e6f107d method = merge cmdver = 0.4.9 diff --git a/LspCpp/CMakeLists.txt b/LspCpp/CMakeLists.txt index 7b11cd297..114b38441 100644 --- a/LspCpp/CMakeLists.txt +++ b/LspCpp/CMakeLists.txt @@ -54,6 +54,7 @@ where the tar.gz file is extracted to. This directory must be an absolute path. If this setting is set, LspCpp will use downloaded GC regardless of whether GC from find_package or pkg_config is available or not. ") +option_if_not_defined(LSPCPP_GC_STATIC "Compiling with static gc library. Only used if a custom GC root is given" OFF) ########################################################### # Boehm GC @@ -187,6 +188,10 @@ function(lspcpp_set_target_options target) if (LSPCPP_GC_DOWNLOADED_ROOT) message(STATUS "Using manually downloaded GC") target_include_directories(${target} PUBLIC ${LSPCPP_GC_DOWNLOADED_ROOT}/include) + + if (LSPCPP_GC_STATIC) + target_compile_definitions(${target} PUBLIC GC_NOT_DLL) + endif() else() if (NOT GC_USE_PKGCONFIG) message(STATUS "Using cmake config for locating gc") From 2b0f0c33f127b9cdea7d496cf6cf7b720e727607 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sun, 22 Dec 2024 14:52:27 -0700 Subject: [PATCH 09/24] GUI: Remove click from buildtool. --- GUI/buildtool.py | 93 +++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/GUI/buildtool.py b/GUI/buildtool.py index e0f12e70f..92d815e77 100644 --- a/GUI/buildtool.py +++ b/GUI/buildtool.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 +import argparse import pathlib import sys import subprocess import shutil from typing import Optional -import click from PyQt5.uic import compileUiDir import os @@ -21,16 +21,7 @@ PY_VERSION_MODULE_DIR = BUILD_ROOT_DIRECTORY / "xasyversion" -def add_version_override_arg(cmd_fn): - return click.option( - "--version-override", - default=None, - type=str, - help="Version to use. If not given, uses information from configure.ac.", - )(cmd_fn) - - -def _mapUiFile(_: str, fileName: str): +def _map_ui_file(_: str, fileName: str): return str(PY_UI_FILE_DIR), fileName @@ -38,16 +29,17 @@ def make_init_py_at_dir(dir_name: pathlib.Path): (dir_name / "__init__.py").touch(exist_ok=True) -@click.command() -def buildUi(): +def build_ui(): compileUiDir( - "windows", map=_mapUiFile, from_imports=True, import_from=XASY_ICONS_MODULE_NAME + "windows", + map=_map_ui_file, + from_imports=True, + import_from=XASY_ICONS_MODULE_NAME, ) make_init_py_at_dir(PY_UI_FILE_DIR) -@click.command() -def buildIcons(): +def build_icons(): PY_ICONS_FILE_DIR.mkdir(exist_ok=True) make_init_py_at_dir(PY_ICONS_FILE_DIR) subprocess.run( @@ -60,7 +52,7 @@ def buildIcons(): ) -def determineAsyVersion() -> str: +def determine_asy_version() -> str: version_base = determine_pkg_info.determine_asy_pkg_info( BUILD_ROOT_DIRECTORY.parent / "configure.ac" ).get("version-base") @@ -69,24 +61,17 @@ def determineAsyVersion() -> str: return version_base -def buildVersionModuleInternal(version_override: Optional[str] = None): +def build_verison_module(version_override: Optional[str] = None): PY_VERSION_MODULE_DIR.mkdir(exist_ok=True) make_init_py_at_dir(PY_VERSION_MODULE_DIR) if version_override is not None: version = version_override else: - version = determineAsyVersion() + version = determine_asy_version() with open(PY_VERSION_MODULE_DIR / "version.py", "w", encoding="utf-8") as f: f.write(f'VERSION="{version}"\n') -@click.command() -@add_version_override_arg -def buildVersionModule(version_override: Optional[str]): - buildVersionModuleInternal(version_override) - - -@click.command() def clean(): if PY_UI_FILE_DIR.exists(): shutil.rmtree(PY_UI_FILE_DIR) @@ -98,28 +83,40 @@ def clean(): shutil.rmtree(PY_VERSION_MODULE_DIR) -@click.command() -@click.pass_context -@add_version_override_arg -def build(ctx: click.Context, version_override: Optional[str] = None): - ctx.invoke(buildUi) - ctx.invoke(buildIcons) - buildVersionModuleInternal(version_override) - - -@click.group(invoke_without_command=True) -@click.pass_context -def cli(ctx: click.Context): - if ctx.invoked_subcommand is None: - ctx.invoke(build) - - -cli.add_command(buildUi) -cli.add_command(buildIcons) -cli.add_command(buildVersionModule) -cli.add_command(build) -cli.add_command(clean) +def parse_args(): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(help="subcommands", dest="subcommand") + version_parser = subparsers.add_parser( + "buildversionmodule", help="build version module" + ) + build_parser = subparsers.add_parser("build", help="build command") + for subparser in [build_parser, version_parser]: + subparser.add_argument("--version-override", required=False, type=str) + + subparsers.add_parser("clean", help="clean command") + subparsers.add_parser("buildicons", help="build icons") + subparsers.add_parser("buildui", help="build ui files") + + return parser.parse_args() + + +def main(): + args = parse_args() + if args.subcommand == "buildui": + build_ui() + elif args.subcommand == "buildicons": + build_icons() + elif args.subcommand == "buildversionmodule": + build_verison_module(args.version_override) + elif args.subcommand == "build": + build_ui() + build_icons() + build_verison_module(args.version_override) + elif args.subcommand == "clean": + clean() + else: + raise RuntimeError("Unknown subcommand") if __name__ == "__main__": - cli() + main() From 889ba00a5ab95526c1ee44fa614bc6e640b255fa Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sun, 22 Dec 2024 14:52:37 -0700 Subject: [PATCH 10/24] GUI: Remove click from dev dependency list. --- GUI/requirements.dev.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/GUI/requirements.dev.txt b/GUI/requirements.dev.txt index 6558ee69c..503ef71bd 100644 --- a/GUI/requirements.dev.txt +++ b/GUI/requirements.dev.txt @@ -1,2 +1 @@ -click~=8.1.7 setuptools~=70.0.0 From 54253b0425e88d3ea2b8cb9bdeac2b5831e21f98 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Mon, 23 Dec 2024 18:14:03 -0700 Subject: [PATCH 11/24] MAKE: Support local gc in build process. --- Makefile.in | 48 +++++++++------------------------ configure.ac | 75 ++++++++-------------------------------------------- 2 files changed, 23 insertions(+), 100 deletions(-) diff --git a/Makefile.in b/Makefile.in index d2d3ff6be..7b221647a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -5,8 +5,8 @@ POLL = poll ASYGLVERSION = @ASYGLVERSION@ GCVERSION = @GCVERSION@ -GC = gc-$(GCVERSION) -LIBATOMIC = libatomic_ops-@ATOMICVERSION@ +GC = gc +LIBATOMIC = libatomic_ops GCOPTIONS = @GCOPTIONS@ GCLIB = @GCLIB@ GCPPLIB = @GCPPLIB@ @@ -176,34 +176,15 @@ $(LSP_BUILD_ROOT)/liblspcpp.a: all: asy sty man faq asy-keywords.el -$(GCLIB): $(GC).tar.gz - gunzip -c $(GC).tar.gz > $(GC).tar - tar -xf $(GC).tar - rm -f $(GC).tar - if test -r $(LIBATOMIC).tar.gz; then \ - gunzip -c $(LIBATOMIC).tar.gz > $(LIBATOMIC).tar; \ - tar -xf $(LIBATOMIC).tar; \ - rm -f $(LIBATOMIC).tar; \ - mv $(LIBATOMIC) $(GC)/libatomic_ops; \ - fi - if test "$(GC)" = "gc-7.0"; then \ - cd $(GC)/include/private && \ - patch < ../../../patches/gc-7.0nomem.patch; \ - fi - if test "$(GC)" = "gc-7.2b"; then \ - mv gc-7.2 gc-7.2b; \ - fi - if test "$(GC)" = "gc-7.2c"; then \ - mv gc-7.2 gc-7.2c; \ - fi - if test "$(GC)" = "gc-7.2d"; then \ - mv gc-7.2 gc-7.2d; \ - fi - cd $(GC) && \ - ./configure CC="$(CC)" CXX="$(CXX)" $(GCOPTIONS); \ - $(MAKE) check +$(GC)/libatomic_ops: $(LIBATOMIC) + ln -sf ../$< $@ + +$(GC)/Makefile: $(GC)/libatomic_ops + cd $(GC) && ./autogen.sh + cd $(GC) && ./configure CC="$(CC)" CXX="$(CXX)" $(GCOPTIONS) -$(GCPPLIB): $(GCLIB) +$(GCLIB): $(GC)/Makefile $(GC)/libatomic_ops + $(MAKE) -C $(GC) check sty: doc/version.texi cd doc && $(MAKE) asy-latex.pdf @@ -395,16 +376,11 @@ clean: FORCE -cd GUI && $(PYTHON) buildtool.py clean gc-clean: FORCE clean - -if test -d $(GC); then \ - $(MAKE) -C $(GC) clean; \ - fi + -$(MAKE) -C $(GC) clean cleaner: FORCE clean - -if test -d $(GC); then \ - rm -rf $(GC); \ - fi + -$(MAKE) -C $(GC) distclean -rm -f Makefile config.h config.log config.status errors.temp - -rm -rf acextlibs cd doc && $(MAKE) clean cd tests && $(MAKE) distclean diff --git a/configure.ac b/configure.ac index cfb9f6c86..4bfdb569b 100644 --- a/configure.ac +++ b/configure.ac @@ -205,7 +205,7 @@ AC_ARG_WITH(vcpkg-target-triplet, If left blank, will try to determine the triplet ] ), [ # given - if test "x$with_vcpkg" != "xno" -a test "x$withval" != "xno"; then + if test "x$with_vcpkg" != "xno" -a "x$withval" != "xno"; then vcpkg_triplet=$withval fi ],[ # not given @@ -263,7 +263,7 @@ AC_ARG_WITH(vcpkg-host-triplet, If left blank, will try to determine the triplet ] ), [ # given - if test "x$with_vcpkg" != "xno" -a test "x$withval" != "xno"; then + if test "x$with_vcpkg" != "xno" -a "x$withval" != "xno"; then VCPKG_INSTALL_ARGUMENTS=$VCPKG_INSTALL_ARGUMENTS"--host-triplet=$withval " fi ], @@ -274,14 +274,10 @@ AC_ARG_WITH(vcpkg-host-triplet, ATOMICVERSION=7.6.12 GCVERSION=8.2.4 GCFILE=gc-$GCVERSION -ac_cv_use_gc="system" +ac_cv_use_gc="yes" -AC_CHECK_FILE($GCFILE.tar.gz, -ac_cv_use_gc=$GCVERSION) AC_ARG_ENABLE(gc, - [AS_HELP_STRING(--enable-gc[[[=system]]], enable system Boehm garbage collector)] - [ [[=VERSION]] enable local VERSION of Boehm garbage collector] - [ [[=PREFIX]] use Boehm garbage collector installed in PREFIX], + [AS_HELP_STRING(--enable-gc[[[=yes]]], enable system Boehm garbage collector)], [ # if true if test "x$enableval" != "xyes" ; then # system, no or else ac_cv_use_gc=$enableval @@ -312,55 +308,22 @@ AC_ARG_ENABLE(gc-full-debug, fi ]) -GCLIB= -GCPPLIB= + GCNAME="Boehm Garbage Collector" if test "x$ac_cv_use_gc" != "xno" ; then AC_DEFINE(USEGC,1,[GC Enabled]) - if test "x$with_vcpkg" != "xno"; then # managed by vcpkg - ADD_VCPKG_FEATURE(gc) - # The reason we are not using AC_CHECK_LIB is because - # we are not installing the dependencies until we collect all the features, - # hence the library files will not be available - ADD_VCPKG_LIB_FOR_PKGCONFIG(bdw-gc) - INCL=$INCL" -Ivcpkg_installed/$vcpkg_triplet/include/gc " - LIBS=$LIBS"-lgccpp " - else # else, determine by prefix or system case _$ac_cv_use_gc in _|_system|_*[[\\/]]*) - if test "x$ac_cv_use_gc" = "xsystem" ; then - INCL=$INCL" -I$prefix/include/gc -I/usr/include/gc " - LIBS=$LIBS"-L$prefix/lib " - else - INCL=$INCL" -I$ac_cv_use_gc/include/gc " - LIBS=$LIBS"-L$ac_cv_use_gc/lib " - fi - CPPFLAGS_SAVE=$CPPFLAGS - CPPFLAGS=$CPPFLAGS" $INCL" - AC_CHECK_HEADER(gc.h, - AC_CHECK_LIB([gc],[GC_malloc],[ - LIBS=$LIBS"-lgc " - AC_MSG_NOTICE([enabling system $GCNAME])],[ - GCDIR=$GCFILE - INCL=$INCL" -I\$(GC)/include " - GCLIB="\$(GC)/.libs/libgc.a" - AC_MSG_NOTICE($GCNAME library not found)]), - GCDIR=$GCFILE - GCLIB="\$(GC)/.libs/libgc.a" - INCL=$INCL" -I\$(GC)/include " - AC_MSG_NOTICE($GCNAME header file not found)) - CPPFLAGS=$CPPFLAGS_SAVE + AC_MSG_ERROR([*** System gc is not supported ***]) + # for some reason, if this line is removed, configure produces an error + AC_CHECK_HEADER(_.h,AC_CHECK_LIB([_],[__],[],[]),) ;; *) - GCVERSION=$ac_cv_use_gc - GCFILE=gc-$GCVERSION - GCDIR=$GCFILE - AC_MSG_NOTICE([enabling local $GCNAME $GCDIR]) + AC_MSG_NOTICE([enabling local gc]) GCLIB="\$(GC)/.libs/libgc.a" INCL=$INCL" -I\$(GC)/include " ;; esac - fi else AC_MSG_NOTICE([disabling the $GCNAME]) fi @@ -437,7 +400,7 @@ if test "x$enable_lsp" != "xno" -a "x$enable_threads" != "xno"; then if test "x$ac_cv_use_gc" != "xno"; then LSP_CMAKE_OPTIONS=$LSP_CMAKE_OPTIONS"-DLSPCPP_SUPPORT_BOEHM_GC=ON " - if test "x$with_vcpkg" = "xno" -a test "x$ac_cv_use_gc" != "xsystem"; then + if test "x$with_vcpkg" = "xno"; then LSP_CMAKE_OPTIONS=$LSP_CMAKE_OPTIONS"-DLSPCPP_GC_DOWNLOADED_ROOT=$ROOT_DIR_RELATIVE_TO_SRC/\$(GC) " fi fi @@ -699,7 +662,7 @@ AC_SUBST(TINYEXR_ROOT) if test "x$enable_gl" != "xno"; then if test "x$with_vcpkg" != "xno"; then # managed by vcpkg ADD_VCPKG_FEATURE(opengl) - AC_DEFINE(HAVE_GL,1,[OpenGL is enabled]) + AC_DEFINE(HAVE_LIBGL,1,[libgl is enabled]) AC_DEFINE(FREEGLUT,1,[Freeglut is enabled]) AC_DEFINE(HAVE_LIBGLUT,1,[Freeglut library is available]) INCL=$INCL" -Ibackports/glew/include " @@ -707,7 +670,6 @@ if test "x$with_vcpkg" != "xno"; then # managed by vcpkg ADD_VCPKG_LIB_FOR_PKGCONFIG(glut) else # managed by the system AC_CHECK_HEADERS([ncurses/curses.h ncurses.h curses.h],[break]) - AC_CHECK_HEADERS([ncurses/curses.h ncurses.h curses.h],[break]) case "$OSTYPE" in darwin*) @@ -723,7 +685,6 @@ case "$OSTYPE" in AC_MSG_NOTICE([*** Could not find GLUT: will compile without OpenGLLUT support ***])) ;; *) - AC_CHECK_LIB([gccpp],[GC_throw_bad_alloc]) AC_CHECK_LIB([glut], [glutMainLoop],, AC_MSG_NOTICE([*** Could not find libglut: will compile without OpenGL support ***])) AC_CHECK_LIB([GL], [glDepthMask], @@ -821,17 +782,3 @@ AC_CHECK_FUNCS(strnlen) AC_CONFIG_FILES([Makefile doc/Makefile doc/png/Makefile]) AC_CONFIG_HEADERS([config.h]) AC_OUTPUT - -if test "x$with_vcpkg" = "xno" -a "x$GCDIR" != "x" ; then - AC_CHECK_FILE($GCDIR.tar.gz,,[ - echo - echo Please put the Boehm garbage collector tar.gz files in the asymptote directory. - echo FOR EXAMPLE, USE THE COMMANDS: - echo - echo wget https://github.com/ivmai/bdwgc/releases/download/v$GCVERSION/$GCFILE.tar.gz - echo wget https://github.com/ivmai/libatomic_ops/releases/download/v$ATOMICVERSION/libatomic_ops-$ATOMICVERSION.tar.gz - - echo - exit 1 - ]) -fi From 3c5828d9b66f487b13f6f96dd71e9203a7b0565e Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Mon, 23 Dec 2024 19:38:53 -0700 Subject: [PATCH 12/24] GC: Set +x on autogen.sh. --- gc/autogen.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gc/autogen.sh diff --git a/gc/autogen.sh b/gc/autogen.sh old mode 100644 new mode 100755 From e2af319e39aeb939b67edd0c81c9bab80d8276c2 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Mon, 23 Dec 2024 22:05:54 -0700 Subject: [PATCH 13/24] CMAKE: Change HAVE_GL to HAVE_LIBGL during configuration. --- cmake-scripts/external-libs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake-scripts/external-libs.cmake b/cmake-scripts/external-libs.cmake index 7f567ac43..4d64b58fa 100644 --- a/cmake-scripts/external-libs.cmake +++ b/cmake-scripts/external-libs.cmake @@ -189,7 +189,7 @@ if (ENABLE_OPENGL) endif() if (OPENGL_GLU_FOUND) - list(APPEND ASY_MACROS HAVE_GL) + list(APPEND ASY_MACROS HAVE_LIBGL) else() message(FATAL_ERROR "GL components incomplete; will not use OpenGL") endif() From 814739609d95ac1475fc86f0db3161045491ae1a Mon Sep 17 00:00:00 2001 From: John Bowman Date: Tue, 24 Dec 2024 09:35:38 -0800 Subject: [PATCH 14/24] git subrepo clone --force --branch=v8.2.8 https://github.com/ivmai/bdwgc gc subrepo: subdir: "gc" merged: "ee59af372" upstream: origin: "https://github.com/ivmai/bdwgc" branch: "v8.2.8" commit: "ee59af372" git-subrepo: version: "0.4.9" origin: "???" commit: "???" --- gc/.github/workflows/cmake-build.yml | 78 ++++ gc/.gitignore | 5 +- gc/.gitrepo | 6 +- gc/.travis.yml | 72 ++-- gc/AUTHORS | 8 + gc/CMakeLists.txt | 90 +++-- gc/ChangeLog | 243 ++++++++++++- gc/Makefile.am | 2 +- gc/Makefile.direct | 5 +- gc/README.md | 2 +- gc/WCC_MAKEFILE | 24 +- gc/allchblk.c | 2 +- gc/alloc.c | 15 +- gc/backgraph.c | 16 +- gc/checksums.c | 8 +- gc/configure.ac | 16 +- gc/cord/cord.am | 2 +- gc/cord/cordxtra.c | 45 ++- gc/cord/tests/cordtest.c | 10 +- gc/cord/tests/de_win.c | 2 + gc/cord/tests/de_win.h | 2 +- gc/darwin_stop_world.c | 17 +- gc/dbg_mlc.c | 71 ++-- gc/doc/README.cmake | 2 +- gc/doc/gc.man | 4 +- gc/doc/tree.md | 49 +-- gc/dyn_load.c | 25 +- gc/extra/symbian/init_global_static_roots.cpp | 4 +- gc/finalize.c | 17 +- gc/gcj_mlc.c | 14 +- gc/headers.c | 10 +- gc/include/cord_pos.h | 6 +- gc/include/gc.h | 8 +- gc/include/gc_allocator.h | 134 +++---- gc/include/gc_backptr.h | 3 +- gc/include/gc_config_macros.h | 10 +- gc/include/gc_mark.h | 12 +- gc/include/gc_pthread_redirects.h | 7 +- gc/include/gc_version.h | 2 +- gc/include/leak_detector.h | 3 + gc/include/private/dbg_mlc.h | 13 +- gc/include/private/gc_locks.h | 3 +- gc/include/private/gc_pmark.h | 20 +- gc/include/private/gc_priv.h | 110 ++++-- gc/include/private/gcconfig.h | 146 +++++--- gc/mach_dep.c | 3 +- gc/malloc.c | 33 +- gc/mallocx.c | 21 +- gc/mark.c | 43 +-- gc/misc.c | 31 +- gc/os_dep.c | 333 +++++++++++------- gc/pthread_stop_world.c | 27 +- gc/pthread_support.c | 36 +- gc/ptr_chck.c | 8 +- gc/reclaim.c | 6 +- gc/specific.c | 6 +- gc/tests/disclaim_bench.c | 9 +- gc/tests/disclaim_test.c | 31 +- gc/tests/disclaim_weakmap_test.c | 25 +- gc/tests/huge_test.c | 4 +- gc/tests/initsecondarythread.c | 12 +- gc/tests/leak_test.c | 15 + gc/tests/middle.c | 22 +- gc/tests/realloc_test.c | 20 +- gc/tests/smash_test.c | 36 +- gc/tests/staticrootslib.c | 38 +- gc/tests/subthread_create.c | 8 +- gc/tests/test.c | 169 +++++---- gc/tests/thread_leak_test.c | 10 + gc/tests/threadkey_test.c | 2 +- gc/tests/trace_test.c | 20 +- gc/tools/callprocs.sh | 0 gc/tools/setjmp_t.c | 7 +- gc/typd_mlc.c | 8 +- gc/win32_threads.c | 38 +- 75 files changed, 1549 insertions(+), 815 deletions(-) create mode 100644 gc/.github/workflows/cmake-build.yml mode change 100644 => 100755 gc/tools/callprocs.sh diff --git a/gc/.github/workflows/cmake-build.yml b/gc/.github/workflows/cmake-build.yml new file mode 100644 index 000000000..c38ef931c --- /dev/null +++ b/gc/.github/workflows/cmake-build.yml @@ -0,0 +1,78 @@ +# This workflow is for CMake-based build/test running on multiple platforms. +name: cmake build + +on: [push, pull_request] + +jobs: + build: + name: ${{ matrix.os }} ${{ matrix.c_compiler }} ${{ matrix.build_type }} thr:${{ matrix.enable_threads }} dll:${{ matrix.shared_libs }} + runs-on: ${{ matrix.os }} + + strategy: + # Deliver the feedback for all matrix combinations. + fail-fast: false + + matrix: + os: [ macos-latest, ubuntu-latest, windows-latest ] + c_compiler: [ cl, clang, gcc ] + build_type: [ Debug, Release ] + gc_assertions: [ off, on ] + enable_threads: [ off, on ] + shared_libs: [ off, on ] + exclude: + - os: macos-latest + c_compiler: cl + - os: macos-latest + c_compiler: gcc + - os: ubuntu-latest + c_compiler: cl + - build_type: Debug + gc_assertions: off + - build_type: Release + gc_assertions: on + - os: windows-latest # TODO: support dependency on libatomic_ops + c_compiler: cl + enable_threads: on + include: + - os: windows-latest + c_compiler: gcc + cmake_generator_opt: '-G "Unix Makefiles"' + - os: windows-latest + c_compiler: clang + cmake_generator_opt: '-G "Unix Makefiles"' + + steps: + - uses: actions/checkout@v4 + + - name: Set reusable strings + # Turn repeated input strings into step outputs. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + ${{ matrix.cmake_generator_opt }} + -DBUILD_SHARED_LIBS=${{ matrix.shared_libs }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -Dbuild_tests=ON + -Denable_gc_assertions=${{ matrix.gc_assertions }} + -Denable_threads=${{ matrix.enable_threads }} + -Denable_werror=ON + -Werror=dev + -S ${{ github.workspace }} + + - name: Build + # Build the code with the given configuration. + run: > + cmake --build ${{ steps.strings.outputs.build-output-dir }} + --config ${{ matrix.build_type }} --verbose --parallel + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. + run: ctest --build-config ${{ matrix.build_type }} --verbose --parallel 8 diff --git a/gc/.gitignore b/gc/.gitignore index a38ed0411..5bd225463 100644 --- a/gc/.gitignore +++ b/gc/.gitignore @@ -1,7 +1,9 @@ # Ignored files in bdwgc Git repo. # Binary files (in root dir, cord, tests): +*.a *.dll +*.dylib *.exe *.gcda *.gch @@ -11,10 +13,10 @@ *.lo *.o *.obj +lib*.so *.gc.log .dirstamp -/*.a /*_bench.log /*_bench.trs /*_test @@ -58,7 +60,6 @@ /if_not_there /initfromthreadtest /leaktest -/lib*.so /libtool /middletest /realloctest diff --git a/gc/.gitrepo b/gc/.gitrepo index a44eceed9..e49b57f9d 100644 --- a/gc/.gitrepo +++ b/gc/.gitrepo @@ -5,8 +5,8 @@ ; [subrepo] remote = https://github.com/ivmai/bdwgc - branch = d1ff06cc503a74dca0150d5e988f2c93158b0cdf - commit = d1ff06cc503a74dca0150d5e988f2c93158b0cdf - parent = 5cf1ed4457f398b1d0c71d6b4b7b12599b685f86 + branch = v8.2.8 + commit = ee59af3722e56de8404de6cd0c21c2493cc4d855 + parent = e2af319e39aeb939b67edd0c81c9bab80d8276c2 method = merge cmdver = 0.4.9 diff --git a/gc/.travis.yml b/gc/.travis.yml index 39c686f8c..ce7ed9485 100644 --- a/gc/.travis.yml +++ b/gc/.travis.yml @@ -47,7 +47,9 @@ jobs: - CPPCHECK_OUT_FILTER="Z" - NO_CLONE_LIBATOMIC_OPS=true - env: - - CPPCHECK_ENABLE="-j4 --enable=information,performance,portability,style,warning --force -U GC_PRIVATE_H -I libatomic_ops/src *.c" + - CPPCHECK_ENABLE="-j4 --enable=information,performance,portability,style,warning --force -U GC_PRIVATE_H -I libatomic_ops/src a*.c b*.c c*.c d*.c f*.c g*.c h*.c m*.c" + - env: + - CPPCHECK_ENABLE="-j4 --enable=information,performance,portability,style,warning --force -U GC_PRIVATE_H -I libatomic_ops/src n*.c o*.c p*.c r*.c s*.c t*.c w*.c" - env: - CPPCHECK_ENABLE="-j4 --enable=information,performance,portability,style,warning --force -U GC_PRIVATE_H -I libatomic_ops/src *.cc cord/*.c cord/tests/*.c extra/AmigaOS.c extra/MacOS.c extra/msvc_dbg.c extra/symbian.cpp tests/*.c tests/*.cc tools/*.c" - arch: arm64 @@ -67,15 +69,9 @@ jobs: - CFLAGS_EXTRA="-O3" - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --disable-shared" - NO_CLONE_LIBATOMIC_OPS=true - - addons: - apt: - packages: - - gcc-11 - sources: - - ubuntu-toolchain-r-test - arch: arm64 - compiler: gcc-11 - dist: bionic + - arch: arm64 + compiler: gcc + dist: focal env: - CFLAGS_EXTRA="-O3 -march=native" - CONF_OPTIONS="--enable-cplusplus --disable-gcj-support" @@ -133,42 +129,28 @@ jobs: - MAKEFILE_TARGETS="check cord/de" - arch: ppc64le compiler: clang + dist: focal - arch: ppc64le compiler: gcc - - addons: - apt: - packages: - - clang-12 - sources: - - ubuntu-toolchain-r-test - arch: ppc64le - compiler: clang-12 + dist: focal + env: + - CONF_OPTIONS="--disable-shared --disable-threads" + - arch: ppc64le + compiler: clang dist: focal env: - CFLAGS_EXTRA="-O3" - CONF_OPTIONS="--enable-gc-assertions --enable-cplusplus --enable-static" - NO_CLONE_LIBATOMIC_OPS=true - - addons: - apt: - packages: - - gcc-11 - sources: - - ubuntu-toolchain-r-test - arch: ppc64le - compiler: gcc-11 - dist: bionic + - arch: ppc64le + compiler: gcc + dist: focal env: - CFLAGS_EXTRA="-O3 -D NO_MPROTECT_VDB" - CONF_OPTIONS="--enable-cplusplus" - NO_CLONE_LIBATOMIC_OPS=true - - addons: - apt: - packages: - - clang-12 - sources: - - ubuntu-toolchain-r-test - arch: ppc64le - compiler: clang-12 + - arch: ppc64le + compiler: clang dist: focal language: c env: @@ -178,12 +160,14 @@ jobs: - TESTS_CUSTOM_RUN=true - arch: ppc64le compiler: clang + dist: focal env: - CMAKE_CONFIG="Release" - CMAKE_OPTIONS="-Dbuild_tests=ON -Denable_cplusplus=ON -Denable_gc_assertions=ON" - NO_CLONE_LIBATOMIC_OPS=true - arch: ppc64le compiler: clang + dist: focal env: - MAKEFILE_NAME=Makefile.direct - MAKEFILE_TARGETS="check cord/de" @@ -191,6 +175,8 @@ jobs: compiler: clang - arch: s390x compiler: gcc + env: + - CONF_OPTIONS="--disable-disclaim" - addons: apt: packages: @@ -248,14 +234,8 @@ jobs: - MAKEFILE_NAME=Makefile.direct - MAKEFILE_TARGETS="check cord/de" - MAKE_NPROC=8 - - addons: - apt: - packages: - - gcc-11 - sources: - - ubuntu-toolchain-r-test - compiler: gcc-11 - dist: bionic + - compiler: gcc + dist: jammy env: - CFLAGS_EXTRA="-O3 -march=native" - CONF_OPTIONS="--enable-cplusplus" @@ -776,6 +756,12 @@ after_success: coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info; fi +before_deploy: +- yes | gem update --system --force +- gem install bundler +- gem install uri +- gem install logger + deploy: provider: releases edge: true diff --git a/gc/AUTHORS b/gc/AUTHORS index 35ca61cda..f5dbbaa2b 100644 --- a/gc/AUTHORS +++ b/gc/AUTHORS @@ -120,9 +120,11 @@ Daniel R. Grayson Danny Smith Darrell Schiebel Dave Barrett +Dave Barton Dave Detlefs Dave Korn Dave Love +David A. Holland David Ayers David Brownlee David Butenhof @@ -157,6 +159,7 @@ Emmanual Stumpf Eric Benson Eric Holk Erik M. Bray +Fabian Ruffy Fabian Thylman Fabrice Fontaine Fergus Henderson @@ -211,6 +214,7 @@ Jay Krell Jean-Baptiste Nivois Jean-Claude Beaudoin Jean-Daniel Fekete +Jeaye Wilkerson Jeff Sturm Jeffrey Hsu Jeffrey Mark Siskind @@ -283,6 +287,7 @@ Marcos Dione Marcus Herbert Marek Vasut Margaret Fleck +Marius Gerbershagen Mark Boulter Mark Mitchell Mark Reichert @@ -360,6 +365,7 @@ Phillip Musumeci Phong Vo Pierre de Rop Pontus Rydin +Qing Guo Radek Polak Rainer Orth Ranjit Mathew @@ -376,6 +382,7 @@ Richard Sandiford Richard Zidlicky Rob Haack Robert Brazile +Robert Hensing Rodrigo Kumpera Roger Sayle Roland McGrath @@ -392,6 +399,7 @@ Samuel Thibault Scott Ananian Scott Ferguson Scott Schwartz +Seonghyun Kim Shawn Wagner Shea Levy Shiro Kawai diff --git a/gc/CMakeLists.txt b/gc/CMakeLists.txt index ddb04eece..faca593e4 100644 --- a/gc/CMakeLists.txt +++ b/gc/CMakeLists.txt @@ -22,16 +22,16 @@ # this will generate gc.sln # -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) -set(PACKAGE_VERSION 8.2.4) +set(PACKAGE_VERSION 8.2.8) # Version must match that in AC_INIT of configure.ac and that in README. # Version must conform to: [0-9]+[.][0-9]+[.][0-9]+ # Info (current:revision:age) for the Libtool versioning system. # These values should match those in cord/cord.am and Makefile.am. -set(LIBCORD_VER_INFO 6:0:5) -set(LIBGC_VER_INFO 6:2:5) +set(LIBCORD_VER_INFO 6:1:5) +set(LIBGC_VER_INFO 6:4:5) set(LIBGCCPP_VER_INFO 6:0:5) option(enable_cplusplus "C++ support" OFF) @@ -143,9 +143,6 @@ elseif (MSVC) # All warnings but ignoring "unreferenced formal parameter" and # "conditional expression is constant" ones. add_compile_options(/W4 /wd4100 /wd4127) - # Disable crt security warnings, since unfortunately they warn about all - # sorts of safe uses of strncpy. - add_definitions("-D_CRT_SECURE_NO_DEPRECATE") elseif (WATCOM) add_compile_options(/wx) if (NOT enable_threads) @@ -157,6 +154,11 @@ else() add_compile_options(-Wall -Wextra) endif() +if (WIN32) + # Disable MS crt security warnings reported e.g. for getenv, strcpy. + add_definitions("-D_CRT_SECURE_NO_DEPRECATE") +endif() + include_directories(include) set(SRC alloc.c reclaim.c allchblk.c misc.c mach_dep.c os_dep.c @@ -196,11 +198,14 @@ set(ATOMIC_OPS_LIBS "") # TODO: Assume libatomic_ops library is not needed. # Thread support detection. if (CMAKE_USE_PTHREADS_INIT) set(SRC ${SRC} gc_dlopen.c) - if (CYGWIN OR MSYS) + if (CYGWIN OR WIN32) set(SRC ${SRC} win32_threads.c) else() set(SRC ${SRC} pthread_start.c pthread_stop_world.c pthread_support.c) endif() + if (APPLE) + set(SRC ${SRC} darwin_stop_world.c) + endif() if (HOST MATCHES .*-.*-hpux10.*) message(FATAL_ERROR "HP/UX 10 POSIX threads are not supported.") endif() @@ -223,12 +228,8 @@ if (CMAKE_USE_PTHREADS_INIT) add_definitions("-D_PTHREADS") set(NEED_LIB_RT ON) endif() - if (MSYS) + if (WIN32) # AND NOT CYGWIN # Does not provide process fork functionality. - elseif (APPLE) - # The incremental mode conflicts with fork handling (for now). - # Thus, HANDLE_FORK is not defined. - set(SRC ${SRC} darwin_stop_world.c) elseif (enable_handle_fork AND NOT disable_handle_fork) add_definitions("-DHANDLE_FORK") endif() @@ -321,7 +322,11 @@ if (enable_redirect_malloc) else() add_definitions("-DREDIRECT_MALLOC=GC_malloc") endif() - add_definitions("-DGC_USE_DLOPEN_WRAP") + if (WIN32) + add_definitions("-DREDIRECT_MALLOC_IN_HEADER") + else() + add_definitions("-DGC_USE_DLOPEN_WRAP") + endif() endif(enable_redirect_malloc) if (enable_munmap) @@ -384,10 +389,10 @@ endif(enable_werror) if (enable_single_obj_compilation OR BUILD_SHARED_LIBS) set(SRC extra/gc.c) # override SRC - if (CMAKE_USE_PTHREADS_INIT) + if (CMAKE_USE_PTHREADS_INIT AND NOT (CYGWIN OR WIN32)) add_definitions("-DGC_PTHREAD_START_STANDALONE") set(SRC ${SRC} pthread_start.c) - endif(CMAKE_USE_PTHREADS_INIT) + endif() elseif (BORLAND) # Suppress "GC_push_contents_hdr() is declared but never used" warning. add_compile_options(/w-use) @@ -398,6 +403,18 @@ if (MSVC) set(SRC ${SRC} extra/msvc_dbg.c) endif() +# Declare that the libraries do not refer to external symbols. +if (BUILD_SHARED_LIBS AND NOT (APPLE OR ${CMAKE_SYSTEM_NAME} MATCHES "BSD")) + # Note: performed before CMAKE_REQUIRED_FLAGS is updated with "-c". + if (${CMAKE_VERSION} VERSION_LESS "3.18.0") + set(WL_NO_UNDEFINED_OPT "-Wl,--no-undefined") + check_c_compiler_flag(${WL_NO_UNDEFINED_OPT} HAVE_FLAG_WL_NO_UNDEFINED) + else() + set(WL_NO_UNDEFINED_OPT "LINKER:--no-undefined") + check_linker_flag(C "${WL_NO_UNDEFINED_OPT}" HAVE_FLAG_WL_NO_UNDEFINED) + endif() +endif() + # Instruct check_c_source_compiles to skip linking. # Alternatively, we could set CMAKE_REQUIRED_LIBRARIES properly. SET(CMAKE_REQUIRED_FLAGS "-c") @@ -407,7 +424,14 @@ SET(CMAKE_REQUIRED_FLAGS "-c") if (NOT BORLAND AND NOT MSVC AND NOT WATCOM) check_c_compiler_flag(-Werror HAVE_FLAG_WERROR) if (HAVE_FLAG_WERROR) + check_c_compiler_flag(-Wno-unused-command-line-argument + HAVE_FLAG_WNO_UNUSED_CMDLINE_ARG) SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") + # Prevent "linker input unused" error in further check_c_compiler_flag. + if (HAVE_FLAG_WNO_UNUSED_CMDLINE_ARG) + SET(CMAKE_REQUIRED_FLAGS + "${CMAKE_REQUIRED_FLAGS} -Wno-unused-command-line-argument") + endif() endif(HAVE_FLAG_WERROR) endif() @@ -421,13 +445,6 @@ if (BUILD_SHARED_LIBS) else() add_definitions("-DGC_NO_VISIBILITY") endif() - if (${CMAKE_VERSION} VERSION_LESS "3.18.0") - set(WL_NO_UNDEFINED_OPT "-Wl,--no-undefined") - check_c_compiler_flag(${WL_NO_UNDEFINED_OPT} HAVE_FLAG_WL_NO_UNDEFINED) - else() - set(WL_NO_UNDEFINED_OPT "LINKER:--no-undefined") - check_linker_flag(C "${WL_NO_UNDEFINED_OPT}" HAVE_FLAG_WL_NO_UNDEFINED) - endif() else() add_definitions("-DGC_NOT_DLL") if (WIN32) @@ -443,6 +460,14 @@ if (HAVE_FLAG_F_NO_STRICT_ALIASING) add_compile_options(-fno-strict-aliasing) endif() +# Prevent "__builtin_return_address with a nonzero argument is unsafe" warning. +if (NOT BORLAND AND NOT MSVC AND NOT WATCOM) + check_c_compiler_flag(-Wno-frame-address HAVE_FLAG_WNO_FRAME_ADDRESS) + if (HAVE_FLAG_WNO_FRAME_ADDRESS) + add_compile_options(-Wno-frame-address) + endif(HAVE_FLAG_WNO_FRAME_ADDRESS) +endif() + # Extra user-defined flags to pass both to C and C++ compilers. if (DEFINED CFLAGS_EXTRA) separate_arguments(CFLAGS_EXTRA_LIST UNIX_COMMAND "${CFLAGS_EXTRA}") @@ -457,6 +482,17 @@ endif() # Check for getcontext (uClibc can be configured without it, for example). check_function_exists(getcontext HAVE_GETCONTEXT) +if (HAVE_GETCONTEXT AND NOT APPLE) + # Double check getcontext() is available (needed at least on OpenBSD 7.3). + # Note: OS X is excluded here because the header filename differs. + check_c_source_compiles(" +#include \n +int main(void) { ucontext_t ctxt; (void)getcontext(&ctxt); return 0; }" + HAVE_GETCONTEXT_FUNC) + if (NOT HAVE_GETCONTEXT_FUNC) + set(HAVE_GETCONTEXT OFF) + endif() +endif() if (NOT HAVE_GETCONTEXT) add_definitions("-DNO_GETCONTEXT") endif() @@ -504,7 +540,7 @@ int main(void) {\n add_definitions("-DHAVE_PTHREAD_SETNAME_NP_WITH_TID") endif() endif(HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG) - endif (HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) + endif(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) endif() # Check for dladdr (used for debugging). @@ -692,8 +728,12 @@ if (build_tests) add_test(NAME cordtest COMMAND cordtest) if (WIN32 AND NOT CYGWIN) + if (NOT (CMAKE_C_COMPILER_ID STREQUAL "Clang")) + # Workaround MS Clang failure to compile a resource file. + set(DE_WIN_RC cord/tests/de_win.rc) + endif() add_executable(de cord/tests/de.c cord/tests/de_win.c - cord/tests/de_win.rc) + ${DE_WIN_RC}) set_target_properties(de PROPERTIES WIN32_EXECUTABLE TRUE) target_link_libraries(de PRIVATE cord gc gdi32) endif() diff --git a/gc/ChangeLog b/gc/ChangeLog index 0266d4e6f..bc381d0d7 100644 --- a/gc/ChangeLog +++ b/gc/ChangeLog @@ -1,4 +1,116 @@ +== [8.2.8] 2024-09-08 == + +* Allow GC_size() argument to be null +* Disable backtrace saving at garbage collections if DONT_SAVE_TO_LAST_STACK +* Eliminate 'cast signed to bigger unsigned' CSA warnings in GC_find_limit +* Eliminate 'x might be clobbered by longjmp' gcc warning in setjmp_t.c +* Fix 'un-mprotect vdb failed' abort with out-of-memory reason on Linux +* Fix ADD_CALL_CHAIN() placement to follow GC_store_debug_info_inner call +* Fix GC_debug_realloc to support custom kind +* Fix GC_is_visible for case of arg pointing exactly to object upper bound +* Fix GC_print_trace_inner to print the last element of the circular buffer +* Fix cordtst2.tmp file deletion in cordtest on Windows +* Fix double lock in GC_malloc called from backtrace() +* Fix handling of page-unaligned boundaries in soft_set_grungy_pages +* Fix heap blocks size computation by GC_get_memory_use +* Fix indent of a closing curly braces in GC_apply_to_all_blocks +* Fix infinite resend lost signals if a thread is restarted by SIGQUIT +* Fix null pointer dereference in GC_is_visible if type_descr is null +* Fix per_object_helper() after changing hb_sz units +* Fix pointer relational comparison in GC_do_enumerate_reachable_objects +* Fix poor thread-local allocation performance because of double EXTRA_BYTES +* Fix potential GC_add_roots_inner call with an overflowed pointer (Win32) +* Fix potential address overflow in GC_add_to_heap +* Fix potential buffer overrun during read in GC_text_mapping +* Fix various typos in comments +* Prevent GC_noop_sink from scanning by the collector +* Prevent redirected malloc call from a garbage collection routine +* Redirect malloc_usable_size() in leak_detector.h +* Remove redundant dirty/reachable_here calls in GC_malloc_explicitly_typed +* Update and fix diagrams describing the tree structure for pointer lookups +* Use atomic store to set GC_first_nonempty in GC_do_parallel_mark +* Use atomic store to set entry id and update cache_ptr in slow_getspecific +* Workaround '.obj file not found' error reported by watcom wlib + + +== [8.2.6] 2024-02-04 == + +* Avoid unexpected heap growth in gctest for the case of VERY_SMALL_CONFIG +* Change gc.man to include gc/gc.h +* Check for out-of-memory on every memory allocation in tests +* Do not compile pthread_start.c on Cygwin even if build shared libs (CMake) +* Eliminate 'alloc_small declared but unused' gcc warning in gctest +* Eliminate 'make_key is defined but unused' gcc warning in threadkeytest +* Eliminate 'old_segv_handler is defined but unused' gcc warning on OpenBSD +* Eliminate 'rand() may return deterministic values' warning +* Eliminate 'unused parameter' compiler warnings reported by MS clang +* Eliminate 'unused parameter' gcc warning in free() if IGNORE_FREE +* Eliminate 'unused value' gcc warnings in init_global_static_roots (Symbian) +* Eliminate GCC warning of unsafe __builtin_return_address(1) in Cmake script +* Eliminate compiler warning of missing cast in LONG_MULT after shift +* Eliminate warning of unused expression result in GC_FreeBSDGetDataStart +* Ensure _GNU_SOURCE is defined if HAVE_DLADDR is defined by configure +* Fix 'g++ not found' error on OpenBSD (Makefile.direct) +* Fix 'implicit declaration of function pthread_atfork' gcc error on MinGW +* Fix 'implicit declaration of function sbrk' gcc error on Symbian +* Fix 'implicit declaration of iscntrl()' warning in cord/de_win (MinGW) +* Fix 'info' buffer potential overrun in GC_save_callers +* Fix 'l-value specifies const object' MSVC error in GC_push_many_regs +* Fix 'linker input unused' error inside check_c_compiler_flag (CMake) +* Fix 'missing binary operator before token' gcc error in gcconfig.h +* Fix 'missing sysconf() prototype' gcc error in setjmp_t tool (OpenBSD) +* Fix 'sigset_t undeclared' MS VC error if pthreads-w32 is used +* Fix 'undefined reference' linker errors if shared build on OpenBSD (CMake) +* Fix 'unused GC_set_and_save_fault_handler' warning on OS X +* Fix GC_push_stack_for() to push also Xmm registers on Windows/x64 +* Fix GC_set_handle_fork(1) on Darwin when MPROTECT_VDB but no threads +* Fix MACH_TYPE macro redefinition on Symbian/arm +* Fix SVR4 macro definition order +* Fix asm constraint in LONG_MULT for gcc/x86 +* Fix assertion violation in GC_get_maps on Linux if malloc redirection +* Fix back graph and checksums support in WCC_MAKEFILE +* Fix bitwise negation and rounding direction in setjmp_t tool +* Fix checksums GC_record_fault invocation on Darwin +* Fix closing bracket placement for case statement in configure +* Fix deprecation warning about support of CMake older than v3.5 +* Fix extra 'extern C' for include signal.h in gcconfig.h +* Fix getcontext() detection by CMake on OpenBSD +* Fix handling of GC_gc_no counter wrap in GC_clear_stack +* Fix handling of GC_gc_no counter wrap in GC_notify_or_invoke_finalizers +* Fix indent of a closing curly brace in GC_forward_exception +* Fix lock assertion violation in GC_try_to_collect_inner on OS X +* Fix missing GC_pthread_sigmask on FreeBSD and NetBSD +* Fix missing _setjmp() on djgpp +* Fix missing atomic barriers in CORD_from_file_lazy +* Fix missing outermost parentheses in CORD_pos_cur_char_addr and hugetest +* Fix missing redirect and implementation of pthread_sigmask() on OpenBSD +* Fix missing type widening before left shift in GC_MAKE_PROC +* Fix misspelled GC_HEADERS_H macro in gc_priv.h +* Fix null dereference in check_finalizer_nested if redirect malloc on Linux +* Fix posix_memalign() to overwrite pointer storage only on success +* Fix race in init_lib_bounds on Linux with glibc v2.34+ if redirect malloc +* Fix skipped removal of page protection in case of address hash collision +* Fix typos in comments +* Fix undefined GC_real_pthread_sigmask if redirect malloc on OS X +* Fix update of last_back_trace_gc_no if KEEP_BACK_PTRS is not defined +* Handle GC_gc_no counter overflow properly in GC_print_trace +* Include Darwin CoreFoundation.h only if 32-bit ARM +* Make gc_allocator members public +* Re-enable incremental mode on OS X (arm64) +* Remove .log and cordtest .tmp files by 'make clean' (Makefile.direct) +* Remove a redundant check of HOST_ANDROID in gcconfig.h +* Remove duplication of random numbers generator formula +* Specify constexpr in GC allocators if C++20 or later +* Support NetBSD/riscv64 +* Support non-msys MinGW build by CMake +* Turn on handle fork by default on Darwin (multi-threaded only) +* Use AO primitives in GC_RAND_NEXT instead of no_sanitize attribute +* Workaround 'malloc inconsistent dll linkage' MS VC error in CMake script +* Workaround MS Clang failure to compile de_win.rc +* Workaround mark stack overflow in GC_push_finalizer_structures on MinGW + + == [8.2.4] 2023-05-26 == * Abort with appropriate message if first call of mmap fails with EPERM @@ -6,7 +118,7 @@ * Adjust WoW64 workaround to work on UWP/WinRT * Adjust naming of Win32/64 and x86/64 words in comments and documentation * Avoid potential race between realloc and GC_block_was_dirty -* Do not double-clear first two words of object in GC_generic_malloc_aligned +* Do not double-clear first two words of object in GC_generic_malloc * Do not mention FASTLOCK in comment * Do not mix debug and non-debug allocations in disclaim tests * Do not prohibit threaded builds with malloc redirection on non-Linux @@ -446,6 +558,27 @@ * Workaround various cppcheck false positives +== [8.0.14] 2024-09-07 == + +* Disable backtrace saving at garbage collections if DONT_SAVE_TO_LAST_STACK +* Fix infinite resend lost signals if a thread is restarted by SIGQUIT + +Also, includes 7.6.22 changes + + +== [8.0.12] 2024-02-04 == + +* Eliminate 'make_key is defined but unused' gcc warning in threadkeytest +* Ensure _GNU_SOURCE is defined if HAVE_DLADDR is defined by configure +* Fix 'implicit declaration of function pthread_atfork' gcc error on MinGW +* Fix 'missing binary operator before token' gcc error in gcconfig.h +* Fix GC_set_handle_fork(1) on Darwin when MPROTECT_VDB but no threads +* Fix checksums GC_record_fault invocation on Darwin +* Fix extra 'extern C' for include signal.h in gcconfig.h + +Also, includes 7.6.20 changes + + == [8.0.10] 2023-05-26 == * Abort with appropriate message if first call of mmap fails with EPERM @@ -962,6 +1095,28 @@ Also, includes 7.6.18 changes * Workaround Thread Sanitizer (TSan) false positive warnings +== [7.6.22] 2024-09-07 == + +* Eliminate 'cast signed to bigger unsigned' CSA warnings in GC_find_limit +* Fix GC_debug_realloc to support custom kind +* Fix heap blocks size computation by GC_get_memory_use +* Fix pointer relational comparison in GC_do_enumerate_reachable_objects +* Prevent GC_noop_sink from scanning by the collector + +Also, includes 7.4.28 changes + + +== [7.6.20] 2024-02-03 == + +* Eliminate compiler warning of missing cast in LONG_MULT after shift +* Fix 'sigset_t undeclared' MS VC error if pthreads-w32 is used +* Fix lock assertion violation in GC_try_to_collect_inner on OS X +* Fix missing outermost parentheses in macro definitions in huge test +* Fix undefined GC_real_pthread_sigmask if redirect malloc on OS X + +Also, includes 7.4.26 changes + + == [7.6.18] 2023-05-26 == * Fix IRIX5 defined wrongly on FreeBSD/mips @@ -1531,6 +1686,41 @@ Also, includes 7.4.6 changes Also, includes 7.4.4 changes +== [7.4.28] 2024-09-07 == + +* Avoid gcc stringop-overflow warning for intended overflow in smashtest +* Fix ADD_CALL_CHAIN() placement to follow GC_store_debug_info_inner call +* Fix GC_is_visible for case of arg pointing exactly to object upper bound +* Fix GC_print_trace_inner to print the last element of the circular buffer +* Fix cordtst2.tmp file deletion in cordtest on Windows +* Fix double lock in GC_malloc called from backtrace() +* Fix indent of a closing curly braces in GC_apply_to_all_blocks +* Fix null pointer dereference in GC_is_visible if type_descr is null +* Fix per_object_helper() after changing hb_sz units +* Fix poor thread-local allocation performance because of double EXTRA_BYTES +* Fix potential GC_add_roots_inner call with an overflowed pointer (Win32) +* Fix potential address overflow in GC_add_to_heap +* Fix potential buffer overrun during read in GC_text_mapping +* Fix typos in comments +* Prevent redirected malloc call from a garbage collection routine +* Remove redundant dirty/reachable_here calls in GC_malloc_explicitly_typed +* Update and fix diagrams describing the tree structure for pointer lookups + + +== [7.4.26] 2024-02-03 == + +* Eliminate 'unused parameter' gcc warning in free() if IGNORE_FREE +* Eliminate 'unused value' gcc warnings in init_global_static_roots (Symbian) +* Fix 'implicit declaration of function sbrk' gcc error on Symbian +* Fix 'unused GC_set_and_save_fault_handler' warning on OS X +* Fix GC_push_stack_for() to push also Xmm registers on Windows/x64 +* Fix MACH_TYPE macro redefinition on Symbian/arm +* Fix missing GC_pthread_sigmask on FreeBSD and NetBSD +* Fix missing redirect and implementation of pthread_sigmask() on OpenBSD + +Also, includes 7.2r changes. + + == [7.4.24] 2023-05-25 == * Adjust CORD_ec comment placement in ec.h @@ -2091,6 +2281,57 @@ Also, includes 7.2e, 7.2d, 7.2c, 7.2b changes Also, includes 7.2 changes +== [7.2s] 2024-09-06 == + +* Avoid gcc stringop-overflow warning for intended overflow in smashtest +* Eliminate 'type defaults to int in declaration' warning (REDIRECT_MALLOC) +* Eliminate 'value exceeds maximum object size' GCC warning in huge_test +* Eliminate warning and simplify expression in GC_init_explicit_typing +* Fix 'GetVersion deprecated' compiler warning in os_dep (MS VC) +* Fix 'value truncated' compiler warning in CORD_cat (MS VC) +* Fix ADD_CALL_CHAIN() placement to follow GC_store_debug_info_inner call +* Fix GC_is_visible for case of arg pointing exactly to object upper bound +* Fix GC_jmp_buf multiple definition +* Fix GC_print_trace_inner to print the last element of the circular buffer +* Fix double lock in GC_malloc called from backtrace() +* Fix indent of a closing curly braces in GC_apply_to_all_blocks +* Fix null pointer dereference in GC_is_visible if type_descr is null +* Fix per_object_helper() after changing hb_sz units +* Fix poor thread-local allocation performance because of double EXTRA_BYTES +* Fix potential GC_add_roots_inner call with an overflowed pointer (Win32) +* Fix potential address overflow in GC_add_to_heap +* Fix potential buffer overrun during read in GC_text_mapping +* Fix typos in comments +* Prevent compiler warnings regarding unused argument and printf in extra +* Prevent redirected malloc call from a garbage collection routine +* Remove barrett_diagram file duplicated by tree.html +* Remove non-existing DISCARD_WORDS from GC data structure ASCII diagram +* Resolve GCC warning in setjmp_t.c +* Update and fix diagrams describing the tree structure for pointer lookups + + +== [7.2r] 2024-02-03 == + +* Fix 'g++ not found' error on OpenBSD (Makefile.direct) +* Fix 'implicit declaration of iscntrl()' warning in cord/de_win (MinGW) +* Fix 'info' buffer potential overrun in GC_save_callers +* Fix 'missing sysconf() prototype' gcc error in setjmp_t tool (OpenBSD) +* Fix SVR4 macro definition order +* Fix asm constraint in LONG_MULT for gcc/x86 +* Fix assertion violation in GC_get_maps on Linux if malloc redirection +* Fix bitwise negation and rounding direction in setjmp_t tool +* Fix closing bracket placement for case statement in configure +* Fix indent of a closing curly brace in GC_forward_exception +* Fix missing outermost parentheses in CORD_pos_cur_char_addr +* Fix missing type widening before left shift in GC_MAKE_PROC +* Fix misspelled GC_HEADERS_H macro in gc_priv.h +* Fix null dereference in check_finalizer_nested if redirect malloc on Linux +* Fix posix_memalign() to overwrite pointer storage only on success +* Fix skipped removal of page protection in case of address hash collision +* Fix typos in comments +* Fix update of last_back_trace_gc_no if KEEP_BACK_PTRS is not defined + + == [7.2q] 2023-05-25 == * Fix CORD_next() indent inside loop in test_basics() of cordtest diff --git a/gc/Makefile.am b/gc/Makefile.am index ea5d7796e..1cf6dc562 100644 --- a/gc/Makefile.am +++ b/gc/Makefile.am @@ -14,7 +14,7 @@ # Info (current:revision:age) for the Libtool versioning system. # These numbers should be updated at most once just before the release, # and, optionally, at most once during the development (after the release). -LIBGC_VER_INFO = 6:2:5 +LIBGC_VER_INFO = 6:4:5 LIBGCCPP_VER_INFO = 6:0:5 ## FIXME: `make distcheck' in this directory will not currently work. diff --git a/gc/Makefile.direct b/gc/Makefile.direct index 10fe88111..27612cb99 100644 --- a/gc/Makefile.direct +++ b/gc/Makefile.direct @@ -24,7 +24,7 @@ AS_ABI_FLAG=$(ABI_FLAG) CC=cc $(ABI_FLAG) # Compiler executable name. For EMX, replace to "gcc". -CXX=g++ $(ABI_FLAG) +CXX= c++ $(ABI_FLAG) # Needed only for "make c++", which builds the C++ interface. AS=as $(AS_ABI_FLAG) @@ -379,7 +379,8 @@ if_not_there$(EXEEXT): $(srcdir)/tools/if_not_there.c clean: rm -f *.a *.i *.o *.exe tests/*.o gctest gctest_dyn_link test_cpp \ setjmp_test mon.out gmon.out a.out core if_not_there if_mach \ - base_lib c++ $(CORD_OBJS) cordtest de cords dont_ar_* threadlibs + base_lib c++ $(CORD_OBJS) cordtest de cords dont_ar_* \ + threadlibs *.log cordtst*.tmp -rm -f *~ gctest$(EXEEXT): tests/test.o base_lib $(UTILS) diff --git a/gc/README.md b/gc/README.md index eeb1450c6..794582fdd 100644 --- a/gc/README.md +++ b/gc/README.md @@ -1,6 +1,6 @@ # Boehm-Demers-Weiser Garbage Collector -This is version 8.2.4 of a conservative garbage +This is version 8.2.8 of a conservative garbage collector for C and C++. diff --git a/gc/WCC_MAKEFILE b/gc/WCC_MAKEFILE index 3de104806..1e53da98d 100644 --- a/gc/WCC_MAKEFILE +++ b/gc/WCC_MAKEFILE @@ -87,32 +87,32 @@ check: gctest.exe test_cpp.exe cordtest.exe .SYMBOLIC !ifdef ENABLE_STATIC -OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj & - mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj & +OBJS= alloc.obj reclaim.obj allchblk.obj backgraph.obj checksums.obj & + misc.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj & obj_map.obj blacklst.obj finalize.obj new_hblk.obj & dbg_mlc.obj malloc.obj dyn_load.obj & typd_mlc.obj ptr_chck.obj mallocx.obj fnlz_mlc.obj gcj_mlc.obj gc.lib: $(OBJS) @%create $*.lb1 - @for %i in ($(OBJS)) do @%append $*.lb1 +'%i' + @for %i in ($(OBJS)) do @%append $*.lb1 +%i *wlib -b -c -n -p=512 $@ @$*.lb1 cord.lib: $(COBJS) @%create $*.lb1 - @for %i in ($(COBJS)) do @%append $*.lb1 +'%i' + @for %i in ($(COBJS)) do @%append $*.lb1 +%i *wlib -b -c -n -p=512 $@ @$*.lb1 gccpp.lib: gc_badalc.obj gc_cpp.obj @%create $*.lb1 - @%append $*.lb1 +'gc_badalc.obj' - @%append $*.lb1 +'gc_cpp.obj' + @%append $*.lb1 +gc_badalc.obj + @%append $*.lb1 +gc_cpp.obj *wlib -b -c -n -p=512 $@ @$*.lb1 # The same as gccpp.lib but contains only gc_badalc.obj. gctba.lib: gc_badalc.obj @%create $*.lb1 - @%append $*.lb1 +'gc_badalc.obj' + @%append $*.lb1 +gc_badalc.obj *wlib -b -c -n -p=512 $@ @$*.lb1 !else @@ -134,7 +134,7 @@ gc.dll: gc.obj .AUTODEPEND !endif @%append $*.lnk op case @%append $*.lnk name $* - @%append $*.lnk file 'gc.obj' + @%append $*.lnk file gc.obj *wlink @$*.lnk cord.lib: cord.dll @@ -151,7 +151,7 @@ cord.dll: $(COBJS) gc.lib .AUTODEPEND !endif @%append $*.lnk op case @%append $*.lnk name $* - @for %i in ($(COBJS)) do @%append $*.lnk file '%i' + @for %i in ($(COBJS)) do @%append $*.lnk file %i @%append $*.lnk library gc.lib *wlink @$*.lnk @@ -169,8 +169,8 @@ gccpp.dll: gc_badalc.obj gc_cpp.obj gc.lib .AUTODEPEND !endif @%append $*.lnk op case @%append $*.lnk name $* - @%append $*.lnk file 'gc_badalc.obj' - @%append $*.lnk file 'gc_cpp.obj' + @%append $*.lnk file gc_badalc.obj + @%append $*.lnk file gc_cpp.obj @%append $*.lnk library gc.lib @%append $*.lnk library wr7$(CALLING)dll.lib *wlink @$*.lnk @@ -189,7 +189,7 @@ gctba.dll: gc_badalc.obj gc.lib .AUTODEPEND !endif @%append $*.lnk op case @%append $*.lnk name $* - @%append $*.lnk file 'gc_badalc.obj' + @%append $*.lnk file gc_badalc.obj @%append $*.lnk library gc.lib @%append $*.lnk library wr7$(CALLING)dll.lib *wlink @$*.lnk diff --git a/gc/allchblk.c b/gc/allchblk.c index cc4cfd7f8..6d31968b2 100644 --- a/gc/allchblk.c +++ b/gc/allchblk.c @@ -621,7 +621,7 @@ STATIC struct hblk * GC_get_first_part(struct hblk *h, hdr *hhdr, * appropriate free list. * N replaces h in the original free list. * - * Nhdr is not completely filled in, since it is about to allocated. + * Nhdr is not completely filled in, since it is about to be allocated. * It may in fact end up on the wrong free list for its size. * That's not a disaster, since n is about to be allocated * by our caller. diff --git a/gc/alloc.c b/gc/alloc.c index 95b0e666d..9627c79d2 100644 --- a/gc/alloc.c +++ b/gc/alloc.c @@ -530,9 +530,7 @@ STATIC void GC_maybe_gc(void) /* used instead of GC_never_stop_func here. */ if (GC_stopped_mark(GC_time_limit == GC_TIME_UNLIMITED? GC_never_stop_func : GC_timeout_stop_func)) { -# ifdef SAVE_CALL_CHAIN - GC_save_callers(GC_last_stack); -# endif + SAVE_CALLERS_TO_LAST_STACK(); GC_finish_collection(); } else { if (!GC_is_full_gc) { @@ -621,9 +619,7 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func) } GC_invalidate_mark_state(); /* Flush mark stack. */ GC_clear_marks(); -# ifdef SAVE_CALL_CHAIN - GC_save_callers(GC_last_stack); -# endif + SAVE_CALLERS_TO_LAST_STACK(); GC_is_full_gc = TRUE; if (!GC_stopped_mark(stop_func)) { if (!GC_incremental) { @@ -736,9 +732,7 @@ GC_INNER void GC_collect_a_little_inner(int n) if (i < max_deficit && !GC_dont_gc) { /* Need to finish a collection. */ -# ifdef SAVE_CALL_CHAIN - GC_save_callers(GC_last_stack); -# endif + SAVE_CALLERS_TO_LAST_STACK(); # ifdef PARALLEL_MARK if (GC_parallel) GC_wait_for_reclaim(); @@ -1408,7 +1402,7 @@ STATIC void GC_add_to_heap(struct hblk *p, size_t bytes) if (0 == bytes) return; } endp = (word)p + bytes; - if (endp <= (word)p) { + while (endp <= (word)p) { /* Address wrapped. */ bytes -= HBLKSIZE; if (0 == bytes) return; @@ -1474,6 +1468,7 @@ STATIC void GC_add_to_heap(struct hblk *p, size_t bytes) GC_greatest_real_heap_addr = endp; } # endif + GC_handle_protected_regions_limit(); if (old_capacity > 0) { # ifndef GWW_VDB /* Recycling may call GC_add_to_heap() again but should not */ diff --git a/gc/backgraph.c b/gc/backgraph.c index 6c6e8a041..733edea2b 100644 --- a/gc/backgraph.c +++ b/gc/backgraph.c @@ -236,7 +236,7 @@ static void add_edge(ptr_t p, ptr_t q) /* Check whether it was already in the list of predecessors. */ { - back_edges *e = (back_edges *)((word)pred & ~FLAG_MANY); + back_edges *e = (back_edges *)((word)pred & ~(word)FLAG_MANY); word n_edges; word total; int local = 0; @@ -263,7 +263,7 @@ static void add_edge(ptr_t p, ptr_t q) } ensure_struct(q); - be = (back_edges *)((word)GET_OH_BG_PTR(q) & ~FLAG_MANY); + be = (back_edges *)((word)GET_OH_BG_PTR(q) & ~(word)FLAG_MANY); for (i = be -> n_edges, be_cont = be; i > MAX_IN; i -= MAX_IN) be_cont = be_cont -> cont; if (i == MAX_IN) { @@ -294,7 +294,7 @@ static void per_object_helper(struct hblk *h, word fn) do { f((ptr_t)(h -> hb_body + i), sz, descr); i += sz; - } while (i + sz <= BYTES_TO_WORDS(HBLKSIZE)); + } while (i + sz <= HBLKSIZE); } GC_INLINE void GC_apply_to_each_object(per_object_func f) @@ -309,7 +309,7 @@ static void reset_back_edge(ptr_t p, size_t n_bytes GC_ATTR_UNUSED, if (GC_HAS_DEBUG_INFO(p)) { ptr_t old_back_ptr = GET_OH_BG_PTR(p); if ((word)old_back_ptr & FLAG_MANY) { - back_edges *be = (back_edges *)((word)old_back_ptr & ~FLAG_MANY); + back_edges *be = (back_edges *)((word)old_back_ptr & ~(word)FLAG_MANY); if (!(be -> flags & RETAIN)) { deallocate_back_edges(be); SET_OH_BG_PTR(p, 0); @@ -387,7 +387,7 @@ static word backwards_height(ptr_t p) pop_in_progress(p); return result; } - be = (back_edges *)((word)pred & ~FLAG_MANY); + be = (back_edges *)((word)pred & ~(word)FLAG_MANY); if (be -> height >= 0 && be -> height_gc_no == (unsigned short)GC_gc_no) return be -> height; /* Ignore back edges in DFS */ @@ -463,13 +463,13 @@ static void update_max_height(ptr_t p, size_t n_bytes GC_ATTR_UNUSED, /* to p, but it can't have decreased. */ back_ptr = GET_OH_BG_PTR(p); if (0 != back_ptr && ((word)back_ptr & FLAG_MANY)) { - be = (back_edges *)((word)back_ptr & ~FLAG_MANY); + be = (back_edges *)((word)back_ptr & ~(word)FLAG_MANY); if (be -> height != HEIGHT_UNKNOWN) p_height = be -> height; } { ptr_t pred = GET_OH_BG_PTR(p); - back_edges *e = (back_edges *)((word)pred & ~FLAG_MANY); + back_edges *e = (back_edges *)((word)pred & ~(word)FLAG_MANY); word n_edges; word total; int local = 0; @@ -508,7 +508,7 @@ static void update_max_height(ptr_t p, size_t n_bytes GC_ATTR_UNUSED, if (be == 0) { ensure_struct(p); back_ptr = GET_OH_BG_PTR(p); - be = (back_edges *)((word)back_ptr & ~FLAG_MANY); + be = (back_edges *)((word)back_ptr & ~(word)FLAG_MANY); } be -> flags |= RETAIN; be -> height = p_height; diff --git a/gc/checksums.c b/gc/checksums.c index 28e40a887..cf0c13322 100644 --- a/gc/checksums.c +++ b/gc/checksums.c @@ -36,10 +36,10 @@ STATIC word GC_faulted[NSUMS] = { 0 }; STATIC size_t GC_n_faulted = 0; -#if defined(MPROTECT_VDB) && !defined(DARWIN) +#ifdef MPROTECT_VDB void GC_record_fault(struct hblk * h) { - word page = (word)h & ~(GC_page_size - 1); + word page = (word)h & ~(word)(GC_page_size-1); GC_ASSERT(GC_page_size != 0); if (GC_n_faulted >= NSUMS) ABORT("write fault log overflowed"); @@ -50,7 +50,7 @@ STATIC size_t GC_n_faulted = 0; STATIC GC_bool GC_was_faulted(struct hblk *h) { size_t i; - word page = (word)h & ~(GC_page_size - 1); + word page = (word)h & ~(word)(GC_page_size-1); for (i = 0; i < GC_n_faulted; ++i) { if (GC_faulted[i] == page) return TRUE; @@ -118,7 +118,7 @@ STATIC void GC_add_block(struct hblk *h, word dummy GC_ATTR_UNUSED) { hdr * hhdr = HDR(h); - GC_bytes_in_used_blocks += (hhdr->hb_sz + HBLKSIZE-1) & ~(HBLKSIZE-1); + GC_bytes_in_used_blocks += (hhdr->hb_sz + HBLKSIZE-1) & ~(word)(HBLKSIZE-1); } STATIC void GC_check_blocks(void) diff --git a/gc/configure.ac b/gc/configure.ac index 0a6282616..c8deb0840 100644 --- a/gc/configure.ac +++ b/gc/configure.ac @@ -14,7 +14,7 @@ dnl Process this file with autoconf to produce configure. dnl Initialization. -AC_INIT(gc,8.2.4,https://github.com/ivmai/bdwgc/issues) +AC_INIT(gc,8.2.8,https://github.com/ivmai/bdwgc/issues) dnl Version must conform to: [0-9]+[.][0-9]+[.][0-9]+ AC_CONFIG_SRCDIR(gcj_mlc.c) @@ -136,8 +136,7 @@ AC_ARG_ENABLE(parallel-mark, AC_MSG_ERROR([Parallel mark requires --enable-threads=x spec]) fi ;; - esac] -) + esac]) AC_ARG_ENABLE(thread-local-alloc, [AS_HELP_STRING([--disable-thread-local-alloc], @@ -786,8 +785,8 @@ AC_ARG_ENABLE(gc-debug, i[3456]86-*-dgux*) AC_DEFINE(MAKE_BACK_GRAPH) ;; - esac ] - fi) + esac + fi ]) AM_CONDITIONAL([MAKE_BACK_GRAPH], [test x"$enable_gc_debug" = xyes]) AM_CONDITIONAL([KEEP_BACK_PTRS], [test x"$keep_back_ptrs" = xtrue]) @@ -1209,11 +1208,8 @@ elif test "${enable_handle_fork}" != manual -a x$THREADS = xposix; then # If the option is omitted, pthread_atfork handlers are installed # by default for the targets where pthread_atfork is known to work. case "$host" in - *-*-darwin*) - # The incremental mode conflicts with fork handling on Darwin. - ;; - *-*-aix* | *-*-android* | *-*-cygwin* | *-*-freebsd* | *-*-haiku* | \ - *-*-hpux11* | *-*-irix* | *-*-kfreebsd*-gnu | \ + *-*-aix* | *-*-android* | *-*-cygwin* | *-*-darwin* | *-*-freebsd* | \ + *-*-haiku* | *-*-hpux11* | *-*-irix* | *-*-kfreebsd*-gnu | \ *-*-*linux* | *-*-netbsd* | *-*-openbsd* | *-*-osf* | *-*-solaris*) AC_DEFINE(HANDLE_FORK) ;; diff --git a/gc/cord/cord.am b/gc/cord/cord.am index 3deb4043f..466d50d52 100644 --- a/gc/cord/cord.am +++ b/gc/cord/cord.am @@ -3,7 +3,7 @@ # Info (current:revision:age) for the Libtool versioning system. # These numbers should be updated at most once just before the release, # and, optionally, at most once during the development (after the release). -LIBCORD_VER_INFO = 6:0:5 +LIBCORD_VER_INFO = 6:1:5 lib_LTLIBRARIES += libcord.la diff --git a/gc/cord/cordxtra.c b/gc/cord/cordxtra.c index bed55eb26..3cdc4efea 100644 --- a/gc/cord/cordxtra.c +++ b/gc/cord/cordxtra.c @@ -38,13 +38,17 @@ /* of the threads primitives. */ # include "gc.h" -/* For now we assume that pointer reads and writes are atomic, */ -/* i.e. another thread always sees the state before or after */ -/* a write. This might be false on a Motorola M68K with */ -/* pointers that are not 32-bit aligned. But there probably */ -/* aren't too many threads packages running on those. */ -# define ATOMIC_WRITE(x,y) (x) = (y) -# define ATOMIC_READ(x) (*(x)) +/* If available, use GCC built-in atomic load-acquire and store-release */ +/* primitives to access the cache lines safely. Otherwise, fall back */ +/* to using the GC allocation lock even during the cache lines reading. */ +/* Note: for simplicity of libcord building, do not rely on GC_THREADS */ +/* macro, libatomic_ops package presence and private/gc_atomic_ops.h. */ +#if !defined(AO_DISABLE_GCC_ATOMICS) \ + && ((defined(__clang__) && __clang_major__ >= 8) /* clang 8.0+ */ \ + || (defined(__GNUC__) /* gcc 5.4+ */ \ + && (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 4)))) +# define CORD_USE_GCC_ATOMIC +#endif /* The standard says these are in stdio.h, but they aren't always: */ # ifndef SEEK_SET @@ -63,7 +67,7 @@ typedef void (* oom_fn)(void); ABORT("Out of memory"); } # define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } -#if GC_GNUC_PREREQ(3, 4) +#if GC_GNUC_PREREQ(3, 4) || defined(__clang__) # define CORD_ATTR_UNUSED __attribute__((__unused__)) #else # define CORD_ATTR_UNUSED /* empty */ @@ -518,7 +522,7 @@ typedef struct { # define DIV_CACHE_SZ(n) ((n) >> LOG_CACHE_SZ) # define MOD_LINE_SZ(n) ((n) & (LINE_SZ - 1)) # define DIV_LINE_SZ(n) ((n) >> LOG_LINE_SZ) -# define LINE_START(n) ((n) & ~(LINE_SZ - 1)) +# define LINE_START(n) ((n) & ~(size_t)(LINE_SZ - 1)) typedef struct { lf_state * state; @@ -545,20 +549,37 @@ static void * GC_CALLBACK refill_cache(void * client_data) ABORT("fread failed"); } new_cache -> tag = DIV_LINE_SZ(file_pos); - /* Store barrier goes here. */ - ATOMIC_WRITE(state -> lf_cache[line_no], new_cache); +# ifdef CORD_USE_GCC_ATOMIC + __atomic_store_n(&(state -> lf_cache[line_no]), new_cache, + __ATOMIC_RELEASE); +# else + state -> lf_cache[line_no] = new_cache; +# endif GC_END_STUBBORN_CHANGE((/* no volatile */ void *)(state -> lf_cache + line_no)); state -> lf_current = line_start + LINE_SZ; return (void *)((GC_word)new_cache->data[MOD_LINE_SZ(file_pos)]); } +#ifndef CORD_USE_GCC_ATOMIC + static void * GC_CALLBACK get_cache_line(void * client_data) + { + return (void *)(*(cache_line **)client_data); + } +#endif + char CORD_lf_func(size_t i, void * client_data) { lf_state * state = (lf_state *)client_data; cache_line * volatile * cl_addr = &(state -> lf_cache[DIV_LINE_SZ(MOD_CACHE_SZ(i))]); - cache_line * cl = (cache_line *)ATOMIC_READ(cl_addr); +# ifdef CORD_USE_GCC_ATOMIC + cache_line * cl = (cache_line *)__atomic_load_n(cl_addr, + __ATOMIC_ACQUIRE); +# else + cache_line * cl = (cache_line *)GC_call_with_alloc_lock( + get_cache_line, (void *)cl_addr); +# endif if (cl == 0 || cl -> tag != DIV_LINE_SZ(i)) { /* Cache miss */ diff --git a/gc/cord/tests/cordtest.c b/gc/cord/tests/cordtest.c index e81db5c0b..44df3e1b6 100644 --- a/gc/cord/tests/cordtest.c +++ b/gc/cord/tests/cordtest.c @@ -11,7 +11,7 @@ * modified is included with the above copyright notice. */ -# include "gc.h" /* For GC_INIT() only */ +# include "gc.h" # include "cord.h" # include @@ -230,9 +230,6 @@ void test_extras(void) /* But we cannot call fclose as it might lead to double close. */ fprintf(stderr, "WARNING: remove failed: " FNAME1 "\n"); } - if (remove(FNAME2) != 0) { - fprintf(stderr, "WARNING: remove failed: " FNAME2 "\n"); - } } int wrap_vprintf(CORD format, ...) @@ -344,6 +341,11 @@ int main(void) test_basics(); test_extras(); test_printf(); + + GC_gcollect(); /* to close f2 before the file removal */ + if (remove(FNAME2) != 0) { + fprintf(stderr, "WARNING: remove failed: " FNAME2 "\n"); + } CORD_fprintf(stdout, "SUCCEEDED\n"); return(0); } diff --git a/gc/cord/tests/de_win.c b/gc/cord/tests/de_win.c index a162ae417..45425ae6b 100644 --- a/gc/cord/tests/de_win.c +++ b/gc/cord/tests/de_win.c @@ -25,6 +25,8 @@ #define NOSERVICE #include +#include + #include "gc.h" #include "cord.h" #include "de_cmds.h" diff --git a/gc/cord/tests/de_win.h b/gc/cord/tests/de_win.h index 33ecfa256..4a5078082 100644 --- a/gc/cord/tests/de_win.h +++ b/gc/cord/tests/de_win.h @@ -74,7 +74,7 @@ void set_position(int x, int y); void do_command(int); /* Execute an editor command. */ - /* Agument is a command character or one */ + /* Argument is a command character or one */ /* of the IDM_ commands. */ void generic_init(void); diff --git a/gc/darwin_stop_world.c b/gc/darwin_stop_world.c index f421518d4..0605d7819 100644 --- a/gc/darwin_stop_world.c +++ b/gc/darwin_stop_world.c @@ -23,7 +23,10 @@ #include #include -#include + +#if defined(ARM32) && defined(ARM_THREAD_STATE32) +# include +#endif /* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple Page 49: @@ -83,15 +86,16 @@ GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start) # ifdef DEBUG_THREADS_EXTRA GC_log_printf("FindTopOfStack start at sp= %p\n", (void *)frame); # endif - while (frame->savedSP != 0) { - /* if there are no more stack frames, stop */ + while (frame->savedSP != 0) { /* stop if no more stack frames */ + unsigned long maskedLR; frame = (StackFrame*)frame->savedSP; /* we do these next two checks after going to the next frame because the LR for the first stack frame in the loop is not set up on purpose, so we shouldn't check it. */ - if ((frame->savedLR & ~0x3) == 0 || (frame->savedLR & ~0x3) == ~0x3UL) + maskedLR = frame -> savedLR & ~0x3UL; + if (0 == maskedLR || ~0x3UL == maskedLR) break; /* if the next LR is bogus, stop */ } # ifdef DEBUG_THREADS_EXTRA @@ -560,12 +564,13 @@ GC_INNER void GC_stop_world(void) GC_log_printf("Stopping the world from thread %p\n", (void *)(word)my_thread); # endif -# ifdef PARALLEL_MARK - if (GC_parallel) { /* Make sure all free list construction has stopped before we */ /* start. No new construction can start, since free list */ /* construction is required to acquire and release the GC lock */ /* before it starts, and we have the lock. */ + +# ifdef PARALLEL_MARK + if (GC_parallel) { GC_acquire_mark_lock(); GC_ASSERT(GC_fl_builder_count == 0); /* We should have previously waited for it to become zero. */ diff --git a/gc/dbg_mlc.c b/gc/dbg_mlc.c index e555da15c..2c3c35676 100644 --- a/gc/dbg_mlc.c +++ b/gc/dbg_mlc.c @@ -54,32 +54,19 @@ } #endif /* !SHORT_DBG_HDRS */ -#ifdef LINT2 - long GC_random(void) +#ifdef KEEP_BACK_PTRS + + /* Use a custom trivial random() implementation as the standard */ + /* one might lead to crashes (if used from a multi-threaded code) */ + /* or to a compiler warning about the deterministic result. */ + static int GC_rand(void) { - static unsigned seed = 1; /* not thread-safe */ + static GC_RAND_STATE_T seed; - /* Linear congruential pseudo-random numbers generator. */ - seed = (seed * 1103515245U + 12345) & GC_RAND_MAX; /* overflow is ok */ - return (long)seed; + return GC_RAND_NEXT(&seed); } -#endif - -#ifdef KEEP_BACK_PTRS -#ifdef LINT2 -# define RANDOM() GC_random() -#else -# include -# define GC_RAND_MAX RAND_MAX - -# if defined(__GLIBC__) || defined(SOLARIS) \ - || defined(HPUX) || defined(IRIX5) || defined(OSF1) -# define RANDOM() random() -# else -# define RANDOM() (long)rand() -# endif -#endif /* !LINT2 */ +# define RANDOM() (long)GC_rand() /* Store back pointer to source in dest, if that appears to be possible. */ /* This is not completely safe, since we may mistakenly conclude that */ @@ -108,7 +95,6 @@ /* and *offset_p. */ /* source is root ==> *base_p = address, *offset_p = 0 */ /* source is heap object ==> *base_p != 0, *offset_p = offset */ - /* Returns 1 on success, 0 if source couldn't be determined. */ /* Dest can be any address within a heap object. */ GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p) @@ -163,16 +149,17 @@ GC_API void * GC_CALL GC_generate_random_heap_address(void) { size_t i; - word heap_offset = RANDOM(); + word heap_offset = (word)RANDOM(); - if (GC_heapsize > GC_RAND_MAX) { + if (GC_heapsize > (word)GC_RAND_MAX) { heap_offset *= GC_RAND_MAX; - heap_offset += RANDOM(); + heap_offset += (word)RANDOM(); } heap_offset %= GC_heapsize; /* This doesn't yield a uniform distribution, especially if */ - /* e.g. RAND_MAX = 1.5* GC_heapsize. But for typical cases, */ + /* e.g. RAND_MAX is 1.5*GC_heapsize. But for typical cases, */ /* it's not too bad. */ + for (i = 0;; ++i) { size_t size; @@ -312,8 +299,8 @@ static void *store_debug_info(void *p, size_t lb, LOCK(); if (!GC_debugging_started) GC_start_debugging_inner(); - ADD_CALL_CHAIN(p, ra); result = GC_store_debug_info_inner(p, (word)lb, s, i); + ADD_CALL_CHAIN(p, ra); UNLOCK(); return result; } @@ -587,11 +574,11 @@ STATIC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS) /* case, we need to make sure that all objects have debug headers. */ GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k) { - void * result; + void *base, *result; GC_ASSERT(I_HOLD_LOCK()); - result = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), k); - if (NULL == result) { + base = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), k); + if (NULL == base) { GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n", (unsigned long) lb); return(0); @@ -599,19 +586,20 @@ STATIC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS) 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)); + result = GC_store_debug_info_inner(base, (word)lb, "INTERNAL", 0); + ADD_CALL_CHAIN(base, GC_RETURN_ADDR); + return result; } GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, int k) { - void * result; + void *base, *result; GC_ASSERT(I_HOLD_LOCK()); - result = GC_generic_malloc_inner_ignore_off_page( + base = GC_generic_malloc_inner_ignore_off_page( SIZET_SAT_ADD(lb, DEBUG_BYTES), k); - if (NULL == result) { + if (NULL == base) { GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n", (unsigned long) lb); return(0); @@ -619,8 +607,9 @@ STATIC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS) 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)); + result = GC_store_debug_info_inner(base, (word)lb, "INTERNAL", 0); + ADD_CALL_CHAIN_INNER(base); + return result; } #endif /* DBG_HDRS_ALL */ @@ -890,8 +879,8 @@ GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS) break; # endif default: - result = NULL; /* initialized to prevent warning. */ - ABORT_RET("GC_debug_realloc: encountered bad kind"); + result = GC_debug_generic_malloc(lb, hhdr -> hb_obj_kind, OPT_RA s, i); + break; } if (result != NULL) { @@ -986,7 +975,7 @@ STATIC void GC_check_heap_block(struct hblk *hbp, word dummy GC_ATTR_UNUSED) } else { plim = hbp->hb_body + HBLKSIZE - sz; } - /* go through all words in block */ + /* go through all objects in block */ 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)) { diff --git a/gc/doc/README.cmake b/gc/doc/README.cmake index b38f9e5dd..d38ac6ac2 100644 --- a/gc/doc/README.cmake +++ b/gc/doc/README.cmake @@ -55,7 +55,7 @@ HOW TO IMPORT BDWGC Another project could add bdwgc as one of its dependencies with something like this in their CMakeLists.txt: -find_package(BDWgc 8.2.4 REQUIRED) +find_package(BDWgc 8.2.8 REQUIRED) add_executable(Foo foo.c) target_link_libraries(Foo BDWgc::gc) diff --git a/gc/doc/gc.man b/gc/doc/gc.man index 6725d889e..86065b52c 100644 --- a/gc/doc/gc.man +++ b/gc/doc/gc.man @@ -1,11 +1,11 @@ -.TH BDWGC 3 "26 Mar 2019" +.TH BDWGC 3 "23 Aug 2023" .SH NAME GC_malloc, GC_malloc_atomic, GC_free, GC_realloc, GC_enable_incremental, GC_register_finalizer, GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, GC_set_warn_proc \- Garbage collecting malloc replacement .SH SYNOPSIS -#include +#include .br void * GC_malloc(size_t size); .br diff --git a/gc/doc/tree.md b/gc/doc/tree.md index 53551633f..0fd3401d5 100644 --- a/gc/doc/tree.md +++ b/gc/doc/tree.md @@ -94,7 +94,7 @@ contributed originally by Dave Barrett. --- +--------------+ | | | ^ | | | | | | | | | | | - TOP_SZ +--------------+<--+ | | + TOP_SZ +--------------+<--+ | | (items)+-<| [] | * | | | | +--------------+ if 0 < bi< HBLKSIZE | | | | | | then large object | | @@ -104,8 +104,8 @@ contributed originally by Dave Barrett. v | aligned) | bi= |GET_BI(p){->hash_link}->key==hi | | v | | - | (bottom_index) \ scratch_alloc'd | | - | ( struct bi ) / by get_index() | | + | (bottom_index) \ GC_scratch_alloc'd | | + | (struct bi) / by get_index() | | --- +->+--------------+ | | ^ | | | | | | | | | @@ -124,43 +124,48 @@ contributed originally by Dave Barrett. | +--------------+ | +-+-+-----+-+-+-+-+ --- | | |<----MAP_LEN---->| | | =HBLKSIZE/GRANULE_BYTES - HDR(p)| GC_find_header(p) | (1024 on Alpha) - | \ from | (8/16 bits each) + HDR(p)| GC_find_header(p) | (1024 elements on Alpha) + | \ from | (16 bits each) | (hdr) (struct hblkhdr) / alloc_hdr() | +--->+----------------------+ | - GET_HDR(p)| word hb_sz (words) | | + GET_HDR(p)| struct hblk *hb_next | | +----------------------+ | - | struct hblk *hb_next | | + | ... | | +----------------------+ | - | word hb_descr | | + | uchar hb_obj_kind | | +----------------------+ | - | char * hb_map |>------------+ - +----------------------+ - | uchar hb_obj_kind | - +----------------------+ - | uchar hb_flags | + | uchar hb_flags | | + +----------------------+ | + | hb_last_reclaimed | | + +----------------------+ | + | size_t hb_sz | | + +----------------------+ | + | word hb_descr | | + +----------------------+ | + | ushort *hb_map |>------------+ +----------------------+ - | hb_last_reclaimed | + | AO_t hb_n_marks | --- +----------------------+ ^ | | - MARK_BITS_SZ| hb_marks[] | * if hdr is free, hb_sz is the size of - (words) | | a heap chunk (struct hblk) of at least - v | | MININCR*HBLKSIZE bytes (below), - --- +----------------------+ otherwise, size of each object in chunk. + | | | * if hdr is free, hb_sz is the size + MARK_BITS_SZ| char/word hb_marks[] | of a heap chunk (struct hblk) of at + | | | least MINHINCR*HBLKSIZE bytes (below); + v | | otherwise, size of each object in chunk. + --- +----------------------+ Dynamic data structures above are interleaved throughout the heap in blocks -of size `MININCR * HBLKSIZE` bytes as done by `gc_scratch_alloc` which cannot +of size `MINHINCR * HBLKSIZE` bytes as done by `GC_scratch_alloc` which cannot be freed; free lists are used (e.g. `alloc_hdr`). `hblk`'s below are collected. (struct hblk) --- +----------------------+ < HBLKSIZE --- - ^ +-----hb_body----------+ (and WORDSZ) ^ --- --- - | | | aligned | ^ ^ + ^ +-----hb_body----------+ (and WORDSZ- ^ --- --- + | | | aligned) | ^ ^ + | | | | | | | | | | hb_sz | - | | | | (words) | | | Object 0 | | | | | | | i |(word- v | | + - - - - - - - - - - -+ --- (bytes)|aligned) --- | diff --git a/gc/dyn_load.c b/gc/dyn_load.c index bee040c39..c73897e6b 100644 --- a/gc/dyn_load.c +++ b/gc/dyn_load.c @@ -544,7 +544,7 @@ STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info, } else { GC_ASSERT((word)end <= (((word)load_segs[j].end + GC_page_size - 1) & - ~(GC_page_size - 1))); + ~(word)(GC_page_size - 1))); /* Remove from the existing load segment */ load_segs[j].end2 = load_segs[j].end; load_segs[j].end = start; @@ -1003,7 +1003,7 @@ GC_INNER void GC_register_dynamic_libraries(void) DWORD protect; LPVOID p; char * base; - char * limit, * new_limit; + char * limit; # ifdef MSWIN32 if (GC_no_win32_dlls) return; @@ -1015,17 +1015,19 @@ GC_INNER void GC_register_dynamic_libraries(void) # ifdef MSWINCE if (result == 0) { - /* Page is free; advance to the next possible allocation base */ - new_limit = (char *) - (((DWORD) p + GC_sysinfo.dwAllocationGranularity) - & ~(GC_sysinfo.dwAllocationGranularity-1)); + if ((word)p > GC_WORD_MAX - GC_sysinfo.dwAllocationGranularity) + break; /* overflow */ + /* Page is free; advance to the next possible allocation base. */ + p = (LPVOID)(((DWORD)p + GC_sysinfo.dwAllocationGranularity) + & ~(GC_sysinfo.dwAllocationGranularity-1)); } else # endif /* else */ { if (result != sizeof(buf)) { ABORT("Weird VirtualQuery result"); } - new_limit = (char *)p + buf.RegionSize; + if ((word)p > GC_WORD_MAX - buf.RegionSize) break; /* overflow */ + protect = buf.Protect; if (buf.State == MEM_COMMIT && (protect == PAGE_EXECUTE_READWRITE @@ -1051,11 +1053,10 @@ GC_INNER void GC_register_dynamic_libraries(void) GC_cond_add_roots(base, limit); base = (char *)p; } - limit = new_limit; + limit = (char *)p + buf.RegionSize; } + p = (char *)p + buf.RegionSize; } - if ((word)p > (word)new_limit /* overflow */) break; - p = (LPVOID)new_limit; } GC_cond_add_roots(base, limit); } @@ -1445,7 +1446,7 @@ GC_INNER void GC_register_dynamic_libraries(void) /* The _dyld_* functions have an internal lock so no _dyld functions can be called while the world is stopped without the risk of a deadlock. Because of this we MUST setup callbacks BEFORE we ever stop the world. - This should be called BEFORE any thread in created and WITHOUT the + This should be called BEFORE any thread is created and WITHOUT the allocation lock held. */ GC_INNER void GC_init_dyld(void) @@ -1472,7 +1473,7 @@ GC_INNER void GC_init_dyld(void) (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_add); _dyld_register_func_for_remove_image( (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_remove); - /* Structure mach_header64 has the same fields */ + /* Structure mach_header_64 has the same fields */ /* as mach_header except for the reserved one */ /* at the end, so these casts are OK. */ diff --git a/gc/extra/symbian/init_global_static_roots.cpp b/gc/extra/symbian/init_global_static_roots.cpp index 683d4adab..89163c1f7 100644 --- a/gc/extra/symbian/init_global_static_roots.cpp +++ b/gc/extra/symbian/init_global_static_roots.cpp @@ -10,8 +10,8 @@ extern "C" { void GC_init_global_static_roots() { - ptr_t dataStart = NULL; - ptr_t dataEnd = NULL; + ptr_t dataStart; + ptr_t dataEnd; # if defined (__WINS__) extern int winscw_data_start, winscw_data_end; dataStart = ((ptr_t)&winscw_data_start); diff --git a/gc/finalize.c b/gc/finalize.c index 446cc340a..5901ec49e 100644 --- a/gc/finalize.c +++ b/gc/finalize.c @@ -1298,12 +1298,15 @@ GC_INNER void GC_notify_or_invoke_finalizers(void) /* This is a convenient place to generate backtraces if appropriate, */ /* since that code is not callable with the allocation lock. */ # if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) - if (GC_gc_no > last_back_trace_gc_no) { + if (GC_gc_no != last_back_trace_gc_no) { # ifdef KEEP_BACK_PTRS - long i; - /* Stops when GC_gc_no wraps; that's OK. */ - last_back_trace_gc_no = GC_WORD_MAX; /* disable others. */ - for (i = 0; i < GC_backtraces; ++i) { + static GC_bool bt_in_progress = FALSE; + + if (!bt_in_progress) { + long i; + + bt_in_progress = TRUE; /* prevent recursion or parallel usage */ + for (i = 0; i < GC_backtraces; ++i) { /* FIXME: This tolerates concurrent heap mutation, */ /* which may cause occasional mysterious results. */ /* We need to release the GC lock, since GC_print_callers */ @@ -1314,9 +1317,11 @@ GC_INNER void GC_notify_or_invoke_finalizers(void) GC_printf("\n****Chosen address %p in object\n", current); GC_print_backtrace(current); LOCK(); + } + bt_in_progress = FALSE; } - last_back_trace_gc_no = GC_gc_no; # endif + last_back_trace_gc_no = GC_gc_no; # ifdef MAKE_BACK_GRAPH if (GC_print_back_height) { GC_print_back_graph_stats(); diff --git a/gc/gcj_mlc.c b/gc/gcj_mlc.c index 478206fbe..10525d21d 100644 --- a/gc/gcj_mlc.c +++ b/gc/gcj_mlc.c @@ -192,28 +192,28 @@ static void maybe_finalize(void) GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_gcj_malloc(size_t lb, void * ptr_to_struct_containing_descr, GC_EXTRA_PARAMS) { - void * result; + void *base, *result; DCL_LOCK_STATE; /* We're careful to avoid extra calls, which could */ /* confuse the backtrace. */ LOCK(); maybe_finalize(); - result = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), - GC_gcj_debug_kind); - if (result == 0) { + base = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), + GC_gcj_debug_kind); + if (NULL == base) { GC_oom_func oom_fn = GC_oom_fn; UNLOCK(); 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; + *((void **)((ptr_t)base + sizeof(oh))) = ptr_to_struct_containing_descr; if (!GC_debugging_started) { GC_start_debugging_inner(); } - ADD_CALL_CHAIN(result, ra); - result = GC_store_debug_info_inner(result, (word)lb, s, i); + result = GC_store_debug_info_inner(base, (word)lb, s, i); + ADD_CALL_CHAIN(base, ra); UNLOCK(); GC_dirty(result); REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); diff --git a/gc/headers.c b/gc/headers.c index 38ef23827..c7517dba9 100644 --- a/gc/headers.c +++ b/gc/headers.c @@ -344,13 +344,13 @@ void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data), client_data); } j--; - } else if (index_p->index[j] == 0) { + } else if (index_p->index[j] == 0) { j--; - } else { + } else { j -= (signed_word)(index_p->index[j]); - } - } - } + } + } + } } GC_INNER struct hblk * GC_next_block(struct hblk *h, GC_bool allow_free) diff --git a/gc/include/cord_pos.h b/gc/include/cord_pos.h index 3459f2423..45b5261ab 100644 --- a/gc/include/cord_pos.h +++ b/gc/include/cord_pos.h @@ -67,8 +67,8 @@ CORD_API size_t CORD_pos_to_index(CORD_pos p); /* Fetch the character located at the given position: */ CORD_API char CORD_pos_fetch(CORD_pos p); -/* Initialize the position to refer to the give cord and index. */ -/* Note that this is the most expensive function on positions: */ +/* Initialize the position to refer to the given cord and index. */ +/* Note that this is the most expensive function on positions. */ CORD_API void CORD_set_pos(CORD_pos p, CORD x, size_t i); /* Advance the position to the next character. */ @@ -118,7 +118,7 @@ CORD_API void CORD__prev(CORD_pos); /* 0 < n < CORD_pos_chars_left(p). */ #define CORD_pos_cur_char_addr(p) \ - (p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start) + ((p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start)) /* Address of the current character in cache. */ #ifdef __cplusplus diff --git a/gc/include/gc.h b/gc/include/gc.h index a1506f2f9..78c6a4bab 100644 --- a/gc/include/gc.h +++ b/gc/include/gc.h @@ -598,9 +598,11 @@ GC_API void * GC_CALL GC_base(void * /* displaced_pointer */); 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 * /* obj_addr */) GC_ATTR_NONNULL(1); +/* (For small objects this also happens to work from interior pointers, */ +/* but that should not be relied upon.) The returned size may be */ +/* slightly larger than what was originally requested. The argument */ +/* may be NULL (causing 0 to be returned). */ +GC_API size_t GC_CALL GC_size(const void * /* obj_addr */); /* For compatibility with C library. This is occasionally faster than */ /* a malloc followed by a bcopy. But if you rely on that, either here */ diff --git a/gc/include/gc_allocator.h b/gc/include/gc_allocator.h index 597c7f131..f8d069d89 100644 --- a/gc/include/gc_allocator.h +++ b/gc/include/gc_allocator.h @@ -124,38 +124,44 @@ class gc_allocator { typedef gc_allocator other; }; - gc_allocator() GC_NOEXCEPT {} - gc_allocator(const gc_allocator&) GC_NOEXCEPT {} + GC_CONSTEXPR gc_allocator() GC_NOEXCEPT {} + GC_CONSTEXPR gc_allocator(const gc_allocator&) GC_NOEXCEPT {} # ifndef GC_NO_MEMBER_TEMPLATES template GC_ATTR_EXPLICIT - gc_allocator(const gc_allocator&) GC_NOEXCEPT {} + GC_CONSTEXPR gc_allocator(const gc_allocator&) GC_NOEXCEPT {} # endif - ~gc_allocator() GC_NOEXCEPT {} + GC_CONSTEXPR ~gc_allocator() GC_NOEXCEPT {} - pointer address(reference GC_x) const { return &GC_x; } - const_pointer address(const_reference GC_x) const { return &GC_x; } + GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; } + GC_CONSTEXPR const_pointer address(const_reference GC_x) const { + return &GC_x; + } // GC_n is permitted to be 0. The C++ standard says nothing about what // the return value is when GC_n == 0. - GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) { GC_type_traits traits; return static_cast(GC_selective_alloc(GC_n * sizeof(GC_Tp), - traits.GC_is_ptr_free, - false)); + traits.GC_is_ptr_free, false)); } - void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT - { GC_FREE(__p); } + GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT { + GC_FREE(__p); + } - size_type max_size() const GC_NOEXCEPT + GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT { return size_t(-1) / sizeof(GC_Tp); } - void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } - void destroy(pointer __p) { __p->~GC_Tp(); } + GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) { + new(__p) GC_Tp(__val); + } + + GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); } }; template<> class gc_allocator { +public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef void* pointer; @@ -169,16 +175,14 @@ class gc_allocator { template -inline bool operator==(const gc_allocator&, - const gc_allocator&) GC_NOEXCEPT -{ +GC_CONSTEXPR inline bool operator==(const gc_allocator&, + const gc_allocator&) GC_NOEXCEPT { return true; } template -inline bool operator!=(const gc_allocator&, - const gc_allocator&) GC_NOEXCEPT -{ +GC_CONSTEXPR inline bool operator!=(const gc_allocator&, + const gc_allocator&) GC_NOEXCEPT { return false; } @@ -199,40 +203,46 @@ class gc_allocator_ignore_off_page { typedef gc_allocator_ignore_off_page other; }; - gc_allocator_ignore_off_page() GC_NOEXCEPT {} - gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page&) - GC_NOEXCEPT {} + GC_CONSTEXPR gc_allocator_ignore_off_page() GC_NOEXCEPT {} + GC_CONSTEXPR gc_allocator_ignore_off_page( + const gc_allocator_ignore_off_page&) GC_NOEXCEPT {} # ifndef GC_NO_MEMBER_TEMPLATES template GC_ATTR_EXPLICIT - gc_allocator_ignore_off_page(const gc_allocator_ignore_off_page&) - GC_NOEXCEPT {} + GC_CONSTEXPR gc_allocator_ignore_off_page( + const gc_allocator_ignore_off_page&) GC_NOEXCEPT {} # endif - ~gc_allocator_ignore_off_page() GC_NOEXCEPT {} + GC_CONSTEXPR ~gc_allocator_ignore_off_page() GC_NOEXCEPT {} - pointer address(reference GC_x) const { return &GC_x; } - const_pointer address(const_reference GC_x) const { return &GC_x; } + GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; } + GC_CONSTEXPR const_pointer address(const_reference GC_x) const { + return &GC_x; + } // GC_n is permitted to be 0. The C++ standard says nothing about what // the return value is when GC_n == 0. - GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) { GC_type_traits traits; return static_cast(GC_selective_alloc(GC_n * sizeof(GC_Tp), - traits.GC_is_ptr_free, - true)); + traits.GC_is_ptr_free, true)); } - void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT - { GC_FREE(__p); } + GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT { + GC_FREE(__p); + } - size_type max_size() const GC_NOEXCEPT + GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT { return size_t(-1) / sizeof(GC_Tp); } - void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } - void destroy(pointer __p) { __p->~GC_Tp(); } + GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) { + new(__p) GC_Tp(__val); + } + + GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); } }; template<> class gc_allocator_ignore_off_page { +public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef void* pointer; @@ -245,16 +255,14 @@ class gc_allocator_ignore_off_page { }; template -inline bool operator==(const gc_allocator_ignore_off_page&, - const gc_allocator_ignore_off_page&) GC_NOEXCEPT -{ +GC_CONSTEXPR inline bool operator==(const gc_allocator_ignore_off_page&, + const gc_allocator_ignore_off_page&) GC_NOEXCEPT { return true; } template -inline bool operator!=(const gc_allocator_ignore_off_page&, - const gc_allocator_ignore_off_page&) GC_NOEXCEPT -{ +GC_CONSTEXPR inline bool operator!=(const gc_allocator_ignore_off_page&, + const gc_allocator_ignore_off_page&) GC_NOEXCEPT { return false; } @@ -280,38 +288,46 @@ class traceable_allocator { typedef traceable_allocator other; }; - traceable_allocator() GC_NOEXCEPT {} - traceable_allocator(const traceable_allocator&) GC_NOEXCEPT {} + GC_CONSTEXPR traceable_allocator() GC_NOEXCEPT {} + GC_CONSTEXPR traceable_allocator(const traceable_allocator&) GC_NOEXCEPT {} # ifndef GC_NO_MEMBER_TEMPLATES template GC_ATTR_EXPLICIT - traceable_allocator(const traceable_allocator&) GC_NOEXCEPT {} + GC_CONSTEXPR traceable_allocator( + const traceable_allocator&) GC_NOEXCEPT {} # endif - ~traceable_allocator() GC_NOEXCEPT {} + GC_CONSTEXPR ~traceable_allocator() GC_NOEXCEPT {} - pointer address(reference GC_x) const { return &GC_x; } - const_pointer address(const_reference GC_x) const { return &GC_x; } + GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; } + GC_CONSTEXPR const_pointer address(const_reference GC_x) const { + return &GC_x; + } // GC_n is permitted to be 0. The C++ standard says nothing about what // the return value is when GC_n == 0. - GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) { void * obj = GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp)); if (0 == obj) GC_ALLOCATOR_THROW_OR_ABORT(); return static_cast(obj); } - void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT - { GC_FREE(__p); } + GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT { + GC_FREE(__p); + } - size_type max_size() const GC_NOEXCEPT + GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT { return size_t(-1) / sizeof(GC_Tp); } - void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); } - void destroy(pointer __p) { __p->~GC_Tp(); } + GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) { + new(__p) GC_Tp(__val); + } + + GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); } }; template<> class traceable_allocator { +public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef void* pointer; @@ -325,16 +341,14 @@ class traceable_allocator { template -inline bool operator==(const traceable_allocator&, - const traceable_allocator&) GC_NOEXCEPT -{ +GC_CONSTEXPR inline bool operator==(const traceable_allocator&, + const traceable_allocator&) GC_NOEXCEPT { return true; } template -inline bool operator!=(const traceable_allocator&, - const traceable_allocator&) GC_NOEXCEPT -{ +GC_CONSTEXPR inline bool operator!=(const traceable_allocator&, + const traceable_allocator&) GC_NOEXCEPT { return false; } diff --git a/gc/include/gc_backptr.h b/gc/include/gc_backptr.h index 5fd919592..da04f9c95 100644 --- a/gc/include/gc_backptr.h +++ b/gc/include/gc_backptr.h @@ -49,11 +49,10 @@ /* Store information about the object referencing dest in *base_p */ /* and *offset_p. */ /* If multiple objects or roots point to dest, the one reported */ -/* will be the last on used by the garbage collector to trace the */ +/* will be the last one used by the garbage collector to trace the */ /* object. */ /* source is root ==> *base_p = address, *offset_p = 0 */ /* source is heap object ==> *base_p != 0, *offset_p = offset */ -/* Returns 1 on success, 0 if source couldn't be determined. */ /* Dest can be any address within a heap object. */ typedef enum { GC_UNREFERENCED, /* No reference info available. */ diff --git a/gc/include/gc_config_macros.h b/gc/include/gc_config_macros.h index f26f92ce4..ec452c68d 100644 --- a/gc/include/gc_config_macros.h +++ b/gc/include/gc_config_macros.h @@ -385,7 +385,7 @@ # endif # if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \ - || defined(GC_OPENBSD_THREADS) || defined(__native_client__)) \ + || defined(__native_client__)) \ && !defined(GC_NO_PTHREAD_SIGMASK) /* Either there is no pthread_sigmask() or no need to intercept it. */ # define GC_NO_PTHREAD_SIGMASK @@ -453,6 +453,14 @@ # endif #endif +#ifndef GC_CONSTEXPR +# if __cplusplus >= 202002L +# define GC_CONSTEXPR constexpr +# else +# define GC_CONSTEXPR /* empty */ +# endif +#endif + #endif /* __cplusplus */ #endif diff --git a/gc/include/gc_mark.h b/gc/include/gc_mark.h index 90121d890..215e41c5b 100644 --- a/gc/include/gc_mark.h +++ b/gc/include/gc_mark.h @@ -82,14 +82,14 @@ typedef struct GC_ms_entry * (*GC_mark_proc)(GC_word * /* addr */, /* Object descriptors on mark stack or in objects. Low order two */ /* bits are tags distinguishing among the following 4 possibilities */ -/* for the high order 30 bits. */ +/* for the rest (high order) bits. */ #define GC_DS_TAG_BITS 2 #define GC_DS_TAGS ((1 << GC_DS_TAG_BITS) - 1) #define GC_DS_LENGTH 0 /* The entire word is a length in bytes that */ /* must be a multiple of 4. */ -#define GC_DS_BITMAP 1 /* 30 (62) bits are a bitmap describing pointer */ - /* fields. The msb is 1 if the first word */ - /* is a pointer. */ +#define GC_DS_BITMAP 1 /* The high order bits are describing pointer */ + /* fields. The most significant bit is set if */ + /* the first word is a pointer. */ /* (This unconventional ordering sometimes */ /* makes the marker slightly faster.) */ /* Zeroes indicate definite nonpointers. Ones */ @@ -101,7 +101,7 @@ typedef struct GC_ms_entry * (*GC_mark_proc)(GC_word * /* addr */, /* PROC(descr). ENV(descr) is passed as the */ /* last argument. */ #define GC_MAKE_PROC(proc_index, env) \ - (((((env) << GC_LOG_MAX_MARK_PROCS) \ + ((((((GC_word)(env)) << GC_LOG_MAX_MARK_PROCS) \ | (proc_index)) << GC_DS_TAG_BITS) | GC_DS_PROC) #define GC_DS_PER_OBJECT 3 /* The real descriptor is at the */ /* byte displacement from the beginning of the */ @@ -245,7 +245,7 @@ GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL #endif /* !GC_DEBUG */ /* Similar to GC_size but returns object kind. Size is returned too */ -/* if psize is not NULL. */ +/* if psize is not NULL. The object pointer should not be NULL. */ GC_API int GC_CALL GC_get_kind_and_size(const void *, size_t * /* psize */) GC_ATTR_NONNULL(1); diff --git a/gc/include/gc_pthread_redirects.h b/gc/include/gc_pthread_redirects.h index c7e72fe64..1cc0c96cb 100644 --- a/gc/include/gc_pthread_redirects.h +++ b/gc/include/gc_pthread_redirects.h @@ -56,9 +56,10 @@ # endif /* !GC_NO_DLOPEN */ # ifndef GC_NO_PTHREAD_SIGMASK -# if defined(GC_PTHREAD_SIGMASK_NEEDED) \ - || defined(_BSD_SOURCE) || defined(_GNU_SOURCE) \ - || (_POSIX_C_SOURCE >= 199506L) || (_XOPEN_SOURCE >= 500) +# if defined(GC_PTHREAD_SIGMASK_NEEDED) || defined(_BSD_SOURCE) \ + || defined(_GNU_SOURCE) || defined(_NETBSD_SOURCE) \ + || (_POSIX_C_SOURCE >= 199506L) || (_XOPEN_SOURCE >= 500) \ + || (__POSIX_VISIBLE >= 199506) /* xBSD internal macro */ GC_API int GC_pthread_sigmask(int /* how */, const sigset_t *, sigset_t * /* oset */); # else diff --git a/gc/include/gc_version.h b/gc/include/gc_version.h index bdd3dd196..b58d0128d 100644 --- a/gc/include/gc_version.h +++ b/gc/include/gc_version.h @@ -30,7 +30,7 @@ /* it to keep the old-style build process working. */ #define GC_TMP_VERSION_MAJOR 8 #define GC_TMP_VERSION_MINOR 2 -#define GC_TMP_VERSION_MICRO 4 /* 8.2.4 */ +#define GC_TMP_VERSION_MICRO 8 /* 8.2.8 */ #ifdef GC_VERSION_MAJOR # if GC_TMP_VERSION_MAJOR != GC_VERSION_MAJOR \ diff --git a/gc/include/leak_detector.h b/gc/include/leak_detector.h index f8161b27e..c85502c44 100644 --- a/gc/include/leak_detector.h +++ b/gc/include/leak_detector.h @@ -57,6 +57,9 @@ #undef posix_memalign #define posix_memalign(p,a,n) GC_posix_memalign(p,a,n) +#undef malloc_usable_size +#define malloc_usable_size(p) GC_size(p) + #ifndef CHECK_LEAKS # define CHECK_LEAKS() GC_gcollect() /* Note 1: CHECK_LEAKS does not have GC prefix (preserved for */ diff --git a/gc/include/private/dbg_mlc.h b/gc/include/private/dbg_mlc.h index e38475a62..0e510ceef 100644 --- a/gc/include/private/dbg_mlc.h +++ b/gc/include/private/dbg_mlc.h @@ -80,7 +80,7 @@ typedef struct { /* We're careful never to overwrite a value with lsb 0. */ # if ALIGNMENT == 1 /* Fudge back pointer to be even. */ -# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(~1 & (word)(p)) +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(~(word)1 & (word)(p)) # else # define HIDE_BACK_PTR(p) GC_HIDE_POINTER(p) # endif @@ -131,6 +131,12 @@ typedef struct { GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); # define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) +# if defined(REDIRECT_MALLOC) && defined(THREADS) && defined(DBG_HDRS_ALL) \ + && NARGS == 0 && NFRAMES % 2 == 0 && defined(GC_HAVE_BUILTIN_BACKTRACE) + GC_INNER void GC_save_callers_no_unlock(struct callinfo info[NFRAMES]); +# define ADD_CALL_CHAIN_INNER(base) \ + GC_save_callers_no_unlock(((oh *)(base)) -> oh_ci) +# endif # define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) #elif defined(GC_ADD_CALLER) struct callinfo; @@ -142,6 +148,11 @@ typedef struct { # define PRINT_CALL_CHAIN(base) #endif +#if !defined(ADD_CALL_CHAIN_INNER) && defined(DBG_HDRS_ALL) + /* A variant of ADD_CALL_CHAIN() used for internal allocations. */ +# define ADD_CALL_CHAIN_INNER(base) ADD_CALL_CHAIN(base, GC_RETURN_ADDR) +#endif + #ifdef GC_ADD_CALLER # define OPT_RA ra, #else diff --git a/gc/include/private/gc_locks.h b/gc/include/private/gc_locks.h index cb1f45c79..ffad99e09 100644 --- a/gc/include/private/gc_locks.h +++ b/gc/include/private/gc_locks.h @@ -145,8 +145,6 @@ # define USE_SPIN_LOCK GC_EXTERN volatile AO_TS_t GC_allocate_lock; GC_INNER void GC_lock(void); - /* Allocation lock holder. Only set if acquired by client through */ - /* GC_call_with_alloc_lock. */ # ifdef GC_ASSERTIONS # define UNCOND_LOCK() \ { GC_ASSERT(I_DONT_HOLD_LOCK()); \ @@ -193,6 +191,7 @@ # endif /* USE_PTHREAD_LOCKS */ # ifdef GC_ASSERTIONS GC_EXTERN unsigned long GC_lock_holder; + /* The allocator lock holder. */ # define SET_LOCK_HOLDER() \ GC_lock_holder = NUMERIC_THREAD_ID(pthread_self()) # define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD diff --git a/gc/include/private/gc_pmark.h b/gc/include/private/gc_pmark.h index d86af0c67..309c044fc 100644 --- a/gc/include/private/gc_pmark.h +++ b/gc/include/private/gc_pmark.h @@ -83,7 +83,7 @@ GC_EXTERN unsigned GC_n_mark_procs; * The initiating threads holds the GC lock, and sets GC_help_wanted. * * Other threads: - * 1) update helper_count (while holding mark_lock.) + * 1) update helper_count (while holding the mark lock). * 2) allocate a local mark stack * repeatedly: * 3) Steal a global mark stack entry by atomically replacing @@ -94,7 +94,7 @@ GC_EXTERN unsigned GC_n_mark_procs; * 6) If necessary, copy local stack to global one, * holding mark lock. * 7) Stop when the global mark stack is empty. - * 8) decrement helper_count (holding mark_lock). + * 8) decrement helper_count (holding the mark lock). * * This is an experiment to see if we can do something along the lines * of the University of Tokyo SGC in a less intrusive, though probably @@ -234,7 +234,7 @@ GC_INLINE mse * GC_push_obj(ptr_t obj, hdr * hhdr, mse * mark_stack_top, # define LONG_MULT(hprod, lprod, x, y) \ do { \ __asm__ __volatile__("mull %2" : "=a"(lprod), "=d"(hprod) \ - : "g"(y), "0"(x)); \ + : "r"(y), "0"(x)); \ } while (0) #else # if defined(__int64) && !defined(__GNUC__) && !defined(CPPCHECK) @@ -246,7 +246,7 @@ GC_INLINE mse * GC_push_obj(ptr_t obj, hdr * hhdr, mse * mark_stack_top, do { \ ULONG_MULT_T prod = (ULONG_MULT_T)(x) * (ULONG_MULT_T)(y); \ GC_STATIC_ASSERT(sizeof(x) + sizeof(y) <= sizeof(prod)); \ - hprod = prod >> 32; \ + hprod = (unsigned32)(prod >> 32); \ lprod = (unsigned32)prod; \ } while (0) #endif /* !I386 */ @@ -369,7 +369,7 @@ GC_INLINE mse * GC_push_contents_hdr(ptr_t current, mse * mark_stack_top, /* * Push a single value onto mark stack. Mark from the object pointed to by p. - * Invoke FIXUP_POINTER(p) before any further processing. + * Invoke FIXUP_POINTER() before any further processing. * P is considered valid even if it is an interior pointer. * Previously marked objects are not pushed. Hence we make progress even * if the mark stack overflows. @@ -379,14 +379,16 @@ GC_INLINE mse * GC_push_contents_hdr(ptr_t current, mse * mark_stack_top, /* Try both the raw version and the fixed up one. */ # define GC_PUSH_ONE_STACK(p, source) \ do { \ + word pp = (word)(p); \ + \ 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 ((word)(p) >= (word)GC_least_plausible_heap_addr \ - && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \ - PUSH_ONE_CHECKED_STACK(p, source); \ + FIXUP_POINTER(pp); \ + if (pp >= (word)GC_least_plausible_heap_addr \ + && pp < (word)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(pp, source); \ } \ } while (0) #else /* !NEED_FIXUP_POINTER */ diff --git a/gc/include/private/gc_priv.h b/gc/include/private/gc_priv.h index 32b073737..a398e48d5 100644 --- a/gc/include/private/gc_priv.h +++ b/gc/include/private/gc_priv.h @@ -27,7 +27,8 @@ #endif #if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__) \ - || defined(__CYGWIN__) || defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) \ + || defined(__CYGWIN__) || defined(HAVE_DLADDR) \ + || defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) \ || defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG) \ || defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)) && !defined(_GNU_SOURCE) /* Can't test LINUX, since this must be defined before other includes. */ @@ -186,9 +187,7 @@ typedef int GC_bool; # define GC_ATTR_WORD_ALIGNED /* empty */ #endif -#ifndef HEADERS_H -# include "gc_hdrs.h" -#endif +#include "gc_hdrs.h" #ifndef GC_ATTR_NO_SANITIZE_ADDR # ifndef ADDRESS_SANITIZER @@ -224,7 +223,7 @@ typedef int GC_bool; #endif /* !GC_ATTR_NO_SANITIZE_THREAD */ #ifndef GC_ATTR_UNUSED -# if GC_GNUC_PREREQ(3, 4) +# if GC_GNUC_PREREQ(3, 4) || defined(__clang__) # define GC_ATTR_UNUSED __attribute__((__unused__)) # else # define GC_ATTR_UNUSED /* empty */ @@ -991,7 +990,7 @@ EXTERN_C_BEGIN /* Round up allocation size (in bytes) to a multiple of a granule. */ #define ROUNDUP_GRANULE_SIZE(lb) /* lb should have no side-effect */ \ - (SIZET_SAT_ADD(lb, GRANULE_BYTES - 1) & ~(GRANULE_BYTES - 1)) + (SIZET_SAT_ADD(lb, GRANULE_BYTES-1) & ~(size_t)(GRANULE_BYTES-1)) /* Round up byte allocation requests to integral number of words, etc. */ # define ROUNDED_UP_GRANULES(lb) /* lb should have no side-effect */ \ @@ -1098,9 +1097,6 @@ union word_ptr_ao_u { # 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. */ struct hblkhdr { struct hblk * hb_next; /* Link field for hblk free list */ /* and for lists of chunks waiting to be */ @@ -1454,12 +1450,14 @@ struct _GC_arrays { # ifdef USE_MUNMAP # define GC_unmapped_bytes GC_arrays._unmapped_bytes word _unmapped_bytes; -# ifdef COUNT_UNMAPPED_REGIONS -# define GC_num_unmapped_regions GC_arrays._num_unmapped_regions - signed_word _num_unmapped_regions; -# endif # else # define GC_unmapped_bytes 0 +# endif +# if defined(COUNT_UNMAPPED_REGIONS) && defined(USE_MUNMAP) +# define GC_num_unmapped_regions GC_arrays._num_unmapped_regions + signed_word _num_unmapped_regions; +# else +# define GC_num_unmapped_regions 0 # endif bottom_index * _all_nils; # define GC_scan_ptr GC_arrays._scan_ptr @@ -1487,6 +1485,8 @@ struct _GC_arrays { # define GC_trace_addr GC_arrays._trace_addr ptr_t _trace_addr; # endif +# define GC_noop_sink GC_arrays._noop_sink + volatile word _noop_sink; # define GC_capacity_heap_sects GC_arrays._capacity_heap_sects size_t _capacity_heap_sects; # define GC_n_heap_sects GC_arrays._n_heap_sects @@ -1566,13 +1566,17 @@ struct _GC_arrays { # define GC_root_index GC_arrays._root_index struct roots * _root_index[RT_SIZE]; # endif -# ifdef SAVE_CALL_CHAIN -# define GC_last_stack GC_arrays._last_stack +# if defined(SAVE_CALL_CHAIN) && !defined(DONT_SAVE_TO_LAST_STACK) \ + && (!defined(REDIRECT_MALLOC) || !defined(GC_HAVE_BUILTIN_BACKTRACE)) struct callinfo _last_stack[NFRAMES]; /* Stack at last garbage collection. Useful for */ /* debugging mysterious object disappearances. In the */ /* multi-threaded case, we currently only save the */ - /* calling stack. */ + /* calling stack. Not supported in case of malloc */ + /* redirection because backtrace() may call malloc(). */ +# define SAVE_CALLERS_TO_LAST_STACK() GC_save_callers(GC_arrays._last_stack) +# else +# define SAVE_CALLERS_TO_LAST_STACK() (void)0 # endif # ifndef SEPARATE_GLOBALS # define GC_objfreelist GC_arrays._objfreelist @@ -1923,12 +1927,12 @@ GC_INNER void GC_invalidate_mark_state(void); /* ones, and roots may point to */ /* unmarked objects. Reset mark stack. */ GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame); - /* Perform about one pages worth of marking */ + /* Perform about one page of marking */ /* work of whatever kind is needed. Returns */ /* quickly if no collection is in progress. */ - /* Return TRUE if mark phase finished. */ + /* Return TRUE if mark phase is finished. */ GC_INNER void GC_initiate_gc(void); - /* initiate collection. */ + /* Initiate collection. */ /* If the mark state is invalid, this */ /* becomes full collection. Otherwise */ /* it's partial. */ @@ -1936,9 +1940,11 @@ GC_INNER void GC_initiate_gc(void); GC_INNER GC_bool GC_collection_in_progress(void); /* Collection is in progress, or was abandoned. */ -#define GC_PUSH_ALL_SYM(sym) \ - GC_push_all((/* no volatile */ void *)&(sym), \ - (/* no volatile */ void *)(&(sym) + 1)) +/* Push contents of the symbol residing in the static roots area */ +/* excluded from scanning by the the collector for a reason. */ +/* Note: it should be used only for symbols of relatively small size */ +/* (one or several words). */ +#define GC_PUSH_ALL_SYM(sym) GC_push_all_eager(&(sym), &(sym) + 1) GC_INNER void GC_push_all_stack(ptr_t b, ptr_t t); /* As GC_push_all but consider */ @@ -2447,9 +2453,24 @@ GC_EXTERN void (*GC_print_heap_obj)(ptr_t p); GC_EXTERN long GC_backtraces; #endif -#ifdef LINT2 -# define GC_RAND_MAX (~0U >> 1) - GC_API_PRIV long GC_random(void); +/* A trivial (linear congruential) pseudo-random numbers generator, */ +/* safe for the concurrent usage. */ +#define GC_RAND_MAX ((int)(~0U >> 1)) +#if defined(AO_HAVE_store) && defined(THREAD_SANITIZER) +# define GC_RAND_STATE_T volatile AO_t +# define GC_RAND_NEXT(pseed) GC_rand_next(pseed) + GC_INLINE int GC_rand_next(GC_RAND_STATE_T *pseed) + { + AO_t next = (AO_t)((AO_load(pseed) * 1103515245U + 12345) + & (unsigned)GC_RAND_MAX); + AO_store(pseed, next); + return (int)next; + } +#else +# define GC_RAND_STATE_T unsigned +# define GC_RAND_NEXT(pseed) /* overflow and race are OK */ \ + (int)(*(pseed) = (*(pseed) * 1103515245U + 12345) \ + & (unsigned)GC_RAND_MAX) #endif GC_EXTERN GC_bool GC_print_back_height; @@ -2501,7 +2522,7 @@ GC_EXTERN GC_bool GC_print_back_height; /* Compute end address for an unmap operation on the indicated block. */ GC_INLINE ptr_t GC_unmap_end(ptr_t start, size_t bytes) { - return (ptr_t)((word)(start + bytes) & ~(GC_page_size - 1)); + return (ptr_t)((word)(start + bytes) & ~(word)(GC_page_size-1)); } # endif #endif /* USE_MUNMAP */ @@ -2562,7 +2583,8 @@ GC_EXTERN GC_bool GC_print_back_height; # endif # ifdef CAN_HANDLE_FORK -# if defined(PROC_VDB) || defined(SOFT_VDB) +# if defined(PROC_VDB) || defined(SOFT_VDB) \ + || (defined(MPROTECT_VDB) && defined(GC_DARWIN_THREADS)) GC_INNER void GC_dirty_update_child(void); /* Update pid-specific resources (like /proc file */ /* descriptors) needed by the dirty bits implementation */ @@ -2572,6 +2594,19 @@ GC_EXTERN GC_bool GC_print_back_height; # endif # endif /* CAN_HANDLE_FORK */ +# if defined(MPROTECT_VDB) && defined(DARWIN) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# ifdef THREADS + GC_INNER int GC_inner_pthread_create(pthread_t *t, + GC_PTHREAD_CREATE_CONST pthread_attr_t *a, + void *(*fn)(void *), void *arg); +# else +# define GC_inner_pthread_create pthread_create +# endif +# endif /* MPROTECT_VDB && DARWIN */ + GC_INNER GC_bool GC_dirty_init(void); /* Returns true if dirty bits are maintained (otherwise */ /* it is OK to be called again if the client invokes */ @@ -2589,6 +2624,14 @@ GC_EXTERN GC_bool GC_print_back_height; # define REACHABLE_AFTER_DIRTY(p) GC_reachable_here(p) #endif /* !GC_DISABLE_INCREMENTAL */ +#if defined(COUNT_PROTECTED_REGIONS) && defined(MPROTECT_VDB) + /* Do actions on heap growth, if needed, to prevent hitting the */ + /* kernel limit on the VM map regions. */ + GC_INNER void GC_handle_protected_regions_limit(void); +#else +# define GC_handle_protected_regions_limit() (void)0 +#endif + /* 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))) @@ -2763,7 +2806,7 @@ GC_EXTERN signed_word GC_bytes_found; #endif #ifdef CHECKSUMS -# if defined(MPROTECT_VDB) && !defined(DARWIN) +# ifdef MPROTECT_VDB void GC_record_fault(struct hblk * h); # endif void GC_check_dirty(void); @@ -2786,6 +2829,13 @@ GC_INNER void GC_start_debugging_inner(void); /* defined in dbg_mlc.c. */ GC_INNER void *GC_store_debug_info_inner(void *p, word sz, const char *str, int linenum); +#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_MALLOC_IN_HEADER) \ + && defined(GC_LINUX_THREADS) + GC_INNER void GC_init_lib_bounds(void); +#else +# define GC_init_lib_bounds() (void)0 +#endif + #ifdef REDIRECT_MALLOC # ifdef GC_LINUX_THREADS GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp); @@ -2855,7 +2905,7 @@ GC_INNER void *GC_store_debug_info_inner(void *p, word sz, const char *str, void * GC_find_limit(void *, int); #endif -#ifdef UNIX_LIKE +#if defined(UNIX_LIKE) && !defined(NO_DEBUGGING) GC_INNER void GC_set_and_save_fault_handler(void (*handler)(int)); #endif @@ -2993,7 +3043,7 @@ GC_INNER void *GC_store_debug_info_inner(void *p, word sz, const char *str, # elif (defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)) \ && !defined(GC_USESIGRT_SIGNALS) # if defined(SPARC) && !defined(SIGPWR) - /* SPARC/Linux doesn't properly define SIGPWR in . */ + /* Linux/SPARC doesn't properly define SIGPWR in . */ /* It is aliased to SIGLOST in asm/signal.h, though. */ # define SIG_SUSPEND SIGLOST # else diff --git a/gc/include/private/gcconfig.h b/gc/include/private/gcconfig.h index f173d5a13..c1cf80fb1 100644 --- a/gc/include/private/gcconfig.h +++ b/gc/include/private/gcconfig.h @@ -26,6 +26,13 @@ #ifndef GCCONFIG_H #define GCCONFIG_H +#ifndef GC_H +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif +# include "../gc.h" +#endif + #ifdef CPPCHECK # undef CLOCKS_PER_SEC # undef FIXUP_POINTER @@ -156,13 +163,12 @@ EXTERN_C_BEGIN # endif # if defined(__arm) || defined(__arm__) || defined(__thumb__) # define ARM32 -# if defined(NACL) +# if defined(NACL) || defined(SYMBIAN) # define mach_type_known # elif !defined(LINUX) && !defined(NETBSD) && !defined(FREEBSD) \ && !defined(OPENBSD) && !defined(DARWIN) && !defined(_WIN32) \ && !defined(__CEGCC__) && !defined(NN_PLATFORM_CTR) \ - && !defined(GC_NO_NOSYS) && !defined(SN_TARGET_PSP2) \ - && !defined(SYMBIAN) + && !defined(GC_NO_NOSYS) && !defined(SN_TARGET_PSP2) # define NOSYS # define mach_type_known # endif @@ -682,7 +688,7 @@ EXTERN_C_BEGIN # define mach_type_known # endif # if defined(__riscv) && (defined(FREEBSD) || defined(LINUX) \ - || defined(OPENBSD)) + || defined(NETBSD) || defined(OPENBSD)) # define RISCV # define mach_type_known # endif @@ -700,10 +706,6 @@ EXTERN_C_BEGIN # define mach_type_known # endif -# if defined(SYMBIAN) -# define mach_type_known -# endif - # if defined(__EMSCRIPTEN__) || defined(EMSCRIPTEN) # ifndef EMSCRIPTEN # define EMSCRIPTEN @@ -1112,13 +1114,15 @@ EXTERN_C_BEGIN # define STACK_GRAN 0x1000000 # ifdef SYMBIAN -# define MACH_TYPE "SYMBIAN" # define OS_TYPE "SYMBIAN" -# define CPP_WORDSZ 32 # define ALIGNMENT 4 # define DATASTART (ptr_t)ALIGNMENT /* cannot be null */ # define DATAEND (ptr_t)ALIGNMENT -# endif +# ifndef USE_MMAP + /* sbrk() is not available. */ +# define USE_MMAP 1 +# endif +# endif /* SYMBIAN */ # ifdef M68K # define MACH_TYPE "M68K" @@ -1158,7 +1162,7 @@ EXTERN_C_BEGIN # endif /* !GLIBC2 */ # else extern int etext[]; -# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~(word)0xfff)) # endif # endif # ifdef AMIGA @@ -1423,7 +1427,7 @@ EXTERN_C_BEGIN # ifdef SEQUENT # define OS_TYPE "SEQUENT" extern int etext[]; -# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~(word)0xfff)) # define STACKBOTTOM ((ptr_t)0x3ffff000) # endif # ifdef EMSCRIPTEN @@ -1451,7 +1455,7 @@ EXTERN_C_BEGIN # endif # ifdef HAIKU extern int etext[]; -# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~(word)0xfff)) # endif # ifdef SOLARIS # define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext) @@ -1464,7 +1468,8 @@ EXTERN_C_BEGIN # ifdef SCO # define OS_TYPE "SCO" extern int etext[]; -# define DATASTART ((ptr_t)((((word)(etext)) + 0x3fffff) & ~0x3fffff) \ +# define DATASTART ((ptr_t)((((word)(etext)) + 0x3fffff) \ + & ~(word)0x3fffff) \ + ((word)(etext) & 0xfff)) # define STACKBOTTOM ((ptr_t)0x7ffffffc) # endif @@ -1536,7 +1541,7 @@ EXTERN_C_BEGIN # endif # else extern int etext[]; -# define DATASTART ((ptr_t)((((word)(etext)) + 0xfff) & ~0xfff)) +# define DATASTART ((ptr_t)(((word)(etext) + 0xfff) & ~(word)0xfff)) # endif # ifdef USE_I686_PREFETCH # define PREFETCH(x) \ @@ -1617,7 +1622,7 @@ EXTERN_C_BEGIN extern int etext[]; extern int _stklen; extern int __djgpp_stack_limit; -# define DATASTART ((ptr_t)((((word)(etext)) + 0x1ff) & ~0x1ff)) +# define DATASTART ((ptr_t)((((word)(etext)) + 0x1ff) & ~(word)0x1ff)) /* #define STACKBOTTOM ((ptr_t)((word)_stubinfo+_stubinfo->size+_stklen)) */ # define STACKBOTTOM ((ptr_t)((word)__djgpp_stack_limit + _stklen)) /* This may not be right. */ @@ -1756,12 +1761,13 @@ EXTERN_C_BEGIN extern int end[]; # endif extern int _DYNAMIC_LINKING[], _gp[]; -# define DATASTART ((ptr_t)((((word)(etext) + 0x3ffff) & ~0x3ffff) \ +# define DATASTART ((ptr_t)((((word)(etext) + 0x3ffff) \ + & ~(word)0x3ffff) \ + ((word)(etext) & 0xffff))) # define DATAEND ((ptr_t)(edata)) # define GC_HAVE_DATAREGION2 # define DATASTART2 (_DYNAMIC_LINKING \ - ? (ptr_t)(((word)_gp + 0x8000 + 0x3ffff) & ~0x3ffff) \ + ? (ptr_t)(((word)_gp + 0x8000 + 0x3ffff) & ~(word)0x3ffff) \ : (ptr_t)edata) # define DATAEND2 ((ptr_t)(end)) # define ALIGNMENT 4 @@ -1956,7 +1962,8 @@ EXTERN_C_BEGIN /* Hence we give an upper pound. */ /* This is currently unused, since we disabled HEURISTIC2 */ extern int __start[]; -# define HEURISTIC2_LIMIT ((ptr_t)((word)(__start) & ~(getpagesize()-1))) +# define HEURISTIC2_LIMIT ((ptr_t)((word)(__start) \ + & ~(word)(getpagesize()-1))) # ifndef GC_OSF1_THREADS /* Unresolved signal issues with threads. */ # define MPROTECT_VDB @@ -2079,7 +2086,8 @@ EXTERN_C_BEGIN extern int etext[]; # ifdef CX_UX # define OS_TYPE "CX_UX" -# define DATASTART ((ptr_t)((((word)(etext) + 0x3fffff) & ~0x3fffff) \ +# define DATASTART ((ptr_t)((((word)(etext) + 0x3fffff) \ + & ~(word)0x3fffff) \ + 0x10000)) # endif # ifdef DGUX @@ -2160,14 +2168,18 @@ EXTERN_C_BEGIN # endif # endif # ifdef DARWIN - /* iOS */ + /* OS X, iOS */ # define DARWIN_DONT_PARSE_STACK 1 # define STACKBOTTOM ((ptr_t)0x16fdfffff) - /* MPROTECT_VDB causes use of non-public API like exc_server, */ - /* this could be a reason for blocking the client application in */ - /* the store. */ -# if TARGET_OS_IPHONE && !defined(NO_DYLD_BIND_FULLY_IMAGE) -# define NO_DYLD_BIND_FULLY_IMAGE +# if TARGET_OS_IPHONE +# ifndef NO_DYLD_BIND_FULLY_IMAGE +# define NO_DYLD_BIND_FULLY_IMAGE +# endif + /* MPROTECT_VDB causes use of non-public API like exc_server, */ + /* this could be a reason for blocking the client application */ + /* in the store. */ +# elif TARGET_OS_OSX +# define MPROTECT_VDB # endif # endif # ifdef FREEBSD @@ -2279,7 +2291,10 @@ EXTERN_C_BEGIN extern void *__stack_base__; # define STACKBOTTOM ((ptr_t)(__stack_base__)) # endif -#endif +# ifdef SYMBIAN + /* Nothing specific. */ +# endif +# endif /* ARM32 */ # ifdef CRIS # define MACH_TYPE "CRIS" @@ -2547,6 +2562,9 @@ EXTERN_C_BEGIN extern int __data_start[] __attribute__((__weak__)); # define DATASTART ((ptr_t)__data_start) # endif +# ifdef NETBSD + /* Nothing specific. */ +# endif # ifdef OPENBSD /* Nothing specific. */ # endif @@ -2635,7 +2653,14 @@ EXTERN_C_BEGIN # define DATAEND (__end__ != 0 ? (ptr_t)__end__ : (ptr_t)_end) #endif -#if (defined(SVR4) || defined(HOST_ANDROID) || defined(HOST_TIZEN)) \ +#if defined(SOLARIS) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. */ + /* Probably others also qualify. */ +# define SVR4 +#endif + +#if (defined(HOST_ANDROID) || defined(HOST_TIZEN) \ + || (defined(LINUX) && defined(SPARC))) \ && !defined(GETPAGESIZE) EXTERN_C_END # include @@ -2645,7 +2670,8 @@ EXTERN_C_BEGIN #ifndef GETPAGESIZE # if defined(AIX) || defined(IRIX5) || defined(LINUX) || defined(SOLARIS) \ - || defined(NETBSD) || defined(FREEBSD) || defined(HPUX) + || defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD) \ + || defined(HPUX) EXTERN_C_END # include EXTERN_C_BEGIN @@ -2661,12 +2687,6 @@ EXTERN_C_BEGIN # define USE_TKILL_ON_ANDROID #endif -#if defined(SOLARIS) || defined(DRSNX) || defined(UTS4) - /* OS has SVR4 generic features. */ - /* Probably others also qualify. */ -# define SVR4 -#endif - #if defined(MPROTECT_VDB) && defined(__GLIBC__) \ && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)) # error glibc too old? @@ -2698,8 +2718,18 @@ EXTERN_C_BEGIN # define NO_SIGNALS_UNBLOCK_IN_MAIN #endif +#if defined(PARALLEL_MARK) && defined(GC_PTHREADS) \ + && !defined(GC_PTHREADS_PARAMARK) && !defined(__MINGW32__) + /* Use pthread-based parallel mark implementation. */ + /* Except for MinGW 32/64 to workaround a deadlock in */ + /* winpthreads-3.0b internals. */ +# define GC_PTHREADS_PARAMARK +#endif + #if !defined(NO_MARKER_SPECIAL_SIGMASK) \ - && (defined(NACL) || defined(GC_WIN32_PTHREADS)) + && (defined(NACL) || defined(GC_WIN32_PTHREADS) \ + || (defined(GC_PTHREADS_PARAMARK) && defined(GC_WIN32_THREADS)) \ + || defined(GC_NO_PTHREAD_SIGMASK)) /* Either there is no pthread_sigmask(), or GC marker thread cannot */ /* steal and drop user signal calls. */ # define NO_MARKER_SPECIAL_SIGMASK @@ -2794,20 +2824,6 @@ EXTERN_C_BEGIN # define MUNMAP_THRESHOLD 2 #endif -#if defined(USE_MUNMAP) && defined(COUNT_UNMAPPED_REGIONS) \ - && !defined(GC_UNMAPPED_REGIONS_SOFT_LIMIT) - /* The default limit of vm.max_map_count on Linux is ~65530. */ - /* There is approximately one mapped region to every unmapped region. */ - /* Therefore if we aim to use up to half of vm.max_map_count for the */ - /* GC (leaving half for the rest of the process) then the number of */ - /* unmapped regions should be one quarter of vm.max_map_count. */ -# if defined(__DragonFly__) -# define GC_UNMAPPED_REGIONS_SOFT_LIMIT (1000000 / 4) -# else -# define GC_UNMAPPED_REGIONS_SOFT_LIMIT 16384 -# endif -#endif - #if defined(GC_DISABLE_INCREMENTAL) || defined(DEFAULT_VDB) # undef GWW_VDB # undef MPROTECT_VDB @@ -2873,7 +2889,9 @@ EXTERN_C_BEGIN #endif #if defined(MPROTECT_VDB) && !defined(MSWIN32) && !defined(MSWINCE) + EXTERN_C_END # include /* for SA_SIGINFO, SIGBUS */ + EXTERN_C_BEGIN #endif #if defined(SIGBUS) && !defined(HAVE_SIGBUS) && !defined(CPPCHECK) @@ -2902,6 +2920,27 @@ EXTERN_C_BEGIN # define NO_VDB_FOR_STATIC_ROOTS #endif +#if defined(MPROTECT_VDB) && !defined(DONT_COUNT_PROTECTED_REGIONS) \ + && !defined(COUNT_PROTECTED_REGIONS) \ + && (defined(LINUX) || defined(__DragonFly__)) +# define COUNT_PROTECTED_REGIONS +#endif + +#if (defined(COUNT_PROTECTED_REGIONS) || defined(COUNT_UNMAPPED_REGIONS)) \ + && !defined(GC_UNMAPPED_REGIONS_SOFT_LIMIT) + /* The default limit of vm.max_map_count on Linux is ~65530. */ + /* There is approximately one mapped region to every protected or */ + /* unmapped region. Therefore if we aim to use up to half of */ + /* vm.max_map_count for the GC (leaving half for the rest of the */ + /* process) then the number of such regions should be one quarter */ + /* of vm.max_map_count. */ +# if defined(__DragonFly__) +# define GC_UNMAPPED_REGIONS_SOFT_LIMIT (1000000 / 4) +# else +# define GC_UNMAPPED_REGIONS_SOFT_LIMIT 16384 +# endif +#endif + #if ((defined(UNIX_LIKE) && (defined(DARWIN) || defined(HAIKU) \ || defined(HURD) || defined(OPENBSD) \ || defined(ARM32) \ @@ -3053,7 +3092,7 @@ EXTERN_C_BEGIN /* Outline pthread primitives to use in GC_get_[main_]stack_base. */ #if ((defined(FREEBSD) && defined(__GLIBC__)) /* kFreeBSD */ \ - || defined(LINUX) || defined(NETBSD) || defined(HOST_ANDROID)) \ + || defined(LINUX) || defined(NETBSD)) \ && !defined(NO_PTHREAD_GETATTR_NP) # define HAVE_PTHREAD_GETATTR_NP 1 #elif defined(FREEBSD) && !defined(__GLIBC__) \ @@ -3098,7 +3137,8 @@ EXTERN_C_BEGIN && !defined(HAVE_NO_FORK) \ && ((defined(GC_PTHREADS) && !defined(NACL) \ && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC)) \ - || (defined(DARWIN) && defined(MPROTECT_VDB)) || defined(HANDLE_FORK)) + || (defined(DARWIN) && defined(MPROTECT_VDB) /* && !THREADS */) \ + || (defined(HANDLE_FORK) && defined(GC_PTHREADS))) /* 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 diff --git a/gc/mach_dep.c b/gc/mach_dep.c index ee120fd70..b51f4f661 100644 --- a/gc/mach_dep.c +++ b/gc/mach_dep.c @@ -405,7 +405,8 @@ GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), } # if defined(MSWIN32) || defined(MSWINCE) || defined(UTS4) \ || defined(OS2) || defined(CX_UX) || defined(__CC_ARM) \ - || defined(LINUX) || defined(EWS4800) || defined(RTEMS) + || defined(LINUX) || defined(EWS4800) || defined(RTEMS) \ + || defined(DJGPP) (void) setjmp(regs); # else (void) _setjmp(regs); diff --git a/gc/malloc.c b/gc/malloc.c index 77c60ddac..6abf654ee 100644 --- a/gc/malloc.c +++ b/gc/malloc.c @@ -136,14 +136,14 @@ STATIC void GC_extend_size_map(size_t i) /* For these larger sizes, we use an even number of granules. */ /* This makes it easier to, e.g., construct a 16-byte-aligned */ /* allocator even if GRANULE_BYTES is 8. */ - granule_sz = (granule_sz + 1) & ~1; + granule_sz = (granule_sz + 1) & ~(size_t)1; if (granule_sz > MAXOBJGRANULES) granule_sz = MAXOBJGRANULES; /* If we can fit the same number of larger objects in a block, do so. */ number_of_objs = HBLK_GRANULES / granule_sz; GC_ASSERT(number_of_objs != 0); - granule_sz = (HBLK_GRANULES / number_of_objs) & ~1; + granule_sz = (HBLK_GRANULES / number_of_objs) & ~(size_t)1; byte_sz = GRANULES_TO_BYTES(granule_sz) - EXTRA_BYTES; /* We may need one extra byte; do not always */ @@ -470,14 +470,22 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_uncollectable(size_t lb) STATIC ptr_t GC_libpthread_end = 0; STATIC ptr_t GC_libld_start = 0; STATIC ptr_t GC_libld_end = 0; + static GC_bool lib_bounds_set = FALSE; - STATIC void GC_init_lib_bounds(void) + GC_INNER void GC_init_lib_bounds(void) { IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + /* This test does not need to ensure memory visibility, since */ + /* the bounds will be set when/if we create another thread. */ + if (EXPECT(lib_bounds_set, TRUE)) return; - if (GC_libpthread_start != 0) return; DISABLE_CANCEL(cancel_state); GC_init(); /* if not called yet */ +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + LOCK(); /* just to set GC_lock_holder */ +# endif if (!GC_text_mapping("libpthread-", &GC_libpthread_start, &GC_libpthread_end)) { /* Some libc implementations like bionic, musl and glibc 2.34 */ @@ -493,13 +501,15 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_uncollectable(size_t lb) /* This might still work with some versions of libpthread, */ /* so we do not abort. */ # endif - /* Generate message only once: */ - GC_libpthread_start = (ptr_t)1; } if (!GC_text_mapping("ld-", &GC_libld_start, &GC_libld_end)) { WARN("Failed to find ld.so text mapping: Expect crash\n", 0); } +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + UNLOCK(); +# endif RESTORE_CANCEL(cancel_state); + lib_bounds_set = TRUE; } # endif /* GC_LINUX_THREADS */ @@ -512,14 +522,9 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_uncollectable(size_t lb) /* libpthread allocated some memory that is only pointed to by */ /* 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); - /* This test does not need to ensure memory visibility, since */ - /* the bounds will be set when/if we create another thread. */ - if (!EXPECT(lib_bounds_set, TRUE)) { - GC_init_lib_bounds(); - lib_bounds_set = TRUE; - } + + GC_init_lib_bounds(); if (((word)caller >= (word)GC_libpthread_start && (word)caller < (word)GC_libpthread_end) || ((word)caller >= (word)GC_libld_start @@ -698,7 +703,7 @@ GC_API void GC_CALL GC_free(void * p) # define REDIRECT_FREE_F REDIRECT_FREE # endif - void free(void * p) + void free(void * p GC_ATTR_UNUSED) { # ifndef IGNORE_FREE # if defined(GC_LINUX_THREADS) && !defined(USE_PROC_FOR_LIBRARIES) diff --git a/gc/mallocx.c b/gc/mallocx.c index 1f2af0bee..21c35e317 100644 --- a/gc/mallocx.c +++ b/gc/mallocx.c @@ -293,7 +293,8 @@ GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void) /* GC_malloc_many or friends to replenish it. (We do not round up */ /* object sizes, since a call indicates the intention to consume many */ /* objects of exactly this size.) */ -/* We assume that the size is a multiple of GRANULE_BYTES. */ +/* We assume that the size is non-zero and a multiple of */ +/* GRANULE_BYTES, and that it already includes EXTRA_BYTES value. */ /* We return the free-list by assigning it to *result, since it is */ /* not safe to return, e.g. a linked list of pointer-free objects, */ /* since the collector would not retain the entire list if it were */ @@ -315,8 +316,8 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result) /* Currently a single object is always allocated if manual VDB. */ /* TODO: GC_dirty should be called for each linked object (but */ /* the last one) to support multiple objects allocation. */ - if (!SMALL_OBJ(lb) || GC_manual_vdb) { - op = GC_generic_malloc(lb, k); + if (!EXPECT(lb <= MAXOBJBYTES, TRUE) || GC_manual_vdb) { + op = GC_generic_malloc(lb - EXTRA_BYTES, k); if (EXPECT(0 != op, TRUE)) obj_link(op) = 0; *result = op; @@ -334,7 +335,7 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result) if (EXPECT(get_have_errors(), FALSE)) GC_print_all_errors(); GC_INVOKE_FINALIZERS(); - GC_DBG_COLLECT_AT_MALLOC(lb); + GC_DBG_COLLECT_AT_MALLOC(lb - EXTRA_BYTES); if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); LOCK(); /* Do our share of marking work */ @@ -473,7 +474,7 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result) /* As a last attempt, try allocating a single object. Note that */ /* this may trigger a collection or expand the heap. */ - op = GC_generic_malloc_inner(lb, k); + op = GC_generic_malloc_inner(lb - EXTRA_BYTES, k); if (0 != op) obj_link(op) = 0; out: @@ -536,8 +537,10 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_memalign(size_t align, size_t lb) /* 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. */ + void *p; size_t align_minus_one = align - 1; /* to workaround a cppcheck warning */ + + /* Check alignment properly. */ if (align < sizeof(void *) || (align_minus_one & align) != 0) { # ifdef MSWINCE return ERROR_INVALID_PARAMETER; @@ -546,14 +549,16 @@ GC_API int GC_CALL GC_posix_memalign(void **memptr, size_t align, size_t lb) # endif } - if ((*memptr = GC_memalign(align, lb)) == NULL) { + p = GC_memalign(align, lb); + if (EXPECT(NULL == p, FALSE)) { # ifdef MSWINCE return ERROR_NOT_ENOUGH_MEMORY; # else return ENOMEM; # endif } - return 0; + *memptr = p; + return 0; /* success */ } /* provide a version of strdup() that uses the collector to allocate the diff --git a/gc/mark.c b/gc/mark.c index 6ccc599c3..50e6cfd7c 100644 --- a/gc/mark.c +++ b/gc/mark.c @@ -34,8 +34,6 @@ void GC_noop6(word arg1 GC_ATTR_UNUSED, word arg2 GC_ATTR_UNUSED, # endif } -volatile word GC_noop_sink; - /* Single argument version, robust against whole program analysis. */ GC_ATTR_NO_SANITIZE_THREAD GC_API void GC_CALL GC_noop1(word x) @@ -546,7 +544,7 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, /* The following is 0 only for small objects described by a simple */ /* length descriptor. For many applications this is the common */ /* case, so we try to detect it quickly. */ - if (descr & ((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS) - 1)) | GC_DS_TAGS)) { + if (descr & (~(word)(WORDS_TO_BYTES(SPLIT_RANGE_WORDS)-1) | GC_DS_TAGS)) { word tag = descr & GC_DS_TAGS; GC_STATIC_ASSERT(GC_DS_TAGS == 0x3); @@ -613,7 +611,7 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, (unsigned long)descr); } # endif /* ENABLE_TRACE */ - descr &= ~GC_DS_TAGS; + descr &= ~(word)GC_DS_TAGS; credit -= WORDS_TO_BYTES(WORDSZ/2); /* guess */ for (; descr != 0; descr <<= 1, current_p += sizeof(word)) { if ((descr & SIGNB) == 0) continue; @@ -1087,7 +1085,7 @@ STATIC void GC_do_parallel_mark(void) ABORT("Tried to start parallel mark in bad state"); 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; + AO_store(&GC_first_nonempty, (AO_t)GC_mark_stack); GC_active_count = 0; GC_helper_count = 1; GC_help_wanted = TRUE; @@ -1198,8 +1196,8 @@ GC_API void GC_CALL GC_push_all(void *bottom, void *top) { word length; - bottom = (void *)(((word)bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); - top = (void *)((word)top & ~(ALIGNMENT-1)); + bottom = (void *)(((word)bottom + ALIGNMENT-1) & ~(word)(ALIGNMENT-1)); + top = (void *)((word)top & ~(word)(ALIGNMENT-1)); if ((word)bottom >= (word)top) return; GC_mark_stack_top++; @@ -1209,7 +1207,7 @@ GC_API void GC_CALL GC_push_all(void *bottom, void *top) length = (word)top - (word)bottom; # if GC_DS_TAGS > ALIGNMENT - 1 length += GC_DS_TAGS; - length &= ~GC_DS_TAGS; + length &= ~(word)GC_DS_TAGS; # endif GC_mark_stack_top -> mse_start = (ptr_t)bottom; GC_mark_stack_top -> mse_descr.w = length; @@ -1230,8 +1228,8 @@ GC_API void GC_CALL GC_push_all(void *bottom, void *top) { struct hblk * h; - bottom = (ptr_t)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); - top = (ptr_t)(((word) top) & ~(ALIGNMENT-1)); + bottom = (ptr_t)(((word)bottom + ALIGNMENT-1) & ~(word)(ALIGNMENT-1)); + top = (ptr_t)((word)top & ~(word)(ALIGNMENT-1)); if ((word)bottom >= (word)top) return; h = HBLKPTR(bottom + HBLKSIZE); @@ -1431,27 +1429,30 @@ void GC_add_trace_entry(char *kind, word arg1, word arg2) if (GC_trace_buf_ptr >= TRACE_ENTRIES) GC_trace_buf_ptr = 0; } -GC_API void GC_CALL GC_print_trace_inner(word gc_no) +GC_API void GC_CALL GC_print_trace_inner(GC_word gc_no) { int i; - for (i = GC_trace_buf_ptr-1; i != GC_trace_buf_ptr; i--) { + for (i = GC_trace_buf_ptr-1;; i--) { struct trace_entry *p; if (i < 0) i = TRACE_ENTRIES-1; p = GC_trace_buf + i; - if (p -> gc_no < gc_no || p -> kind == 0) { + /* Compare gc_no values (p->gc_no is less than given gc_no) */ + /* taking into account that the counter may overflow. */ + if ((((p -> gc_no) - gc_no) & SIGNB) != 0 || 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, + p -> kind, (unsigned)(p -> gc_no), + (unsigned long)(p -> bytes_allocd), (long)p->arg1 ^ 0x80000000L, (long)p->arg2 ^ 0x80000000L); + if (i == GC_trace_buf_ptr) break; } GC_printf("Trace incomplete\n"); } -GC_API void GC_CALL GC_print_trace(word gc_no) +GC_API void GC_CALL GC_print_trace(GC_word gc_no) { DCL_LOCK_STATE; @@ -1467,8 +1468,8 @@ GC_API void GC_CALL GC_print_trace(word gc_no) GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD GC_API void GC_CALL GC_push_all_eager(void *bottom, void *top) { - word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); - word * t = (word *)(((word) top) & ~(ALIGNMENT-1)); + word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(word)(ALIGNMENT-1)); + word * t = (word *)(((word) top) & ~(word)(ALIGNMENT-1)); REGISTER word *p; REGISTER word *lim; REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; @@ -1515,8 +1516,8 @@ GC_INNER void GC_push_all_stack(ptr_t bottom, ptr_t top) GC_INNER void GC_push_conditional_eager(void *bottom, void *top, GC_bool all) { - word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); - word * t = (word *)(((word) top) & ~(ALIGNMENT-1)); + word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(word)(ALIGNMENT-1)); + word * t = (word *)(((word) top) & ~(word)(ALIGNMENT-1)); REGISTER word *p; REGISTER word *lim; REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; @@ -1798,7 +1799,7 @@ STATIC void GC_push_marked(struct hblk *h, hdr *hhdr) /* the disclaim notifiers. */ /* To determine whether an object has been reclaimed, we require that */ /* any live object has a non-zero as one of the two lowest bits of the */ -/* first word. On the other hand, a reclaimed object is a members of */ +/* first word. On the other hand, a reclaimed object is a member of */ /* free-lists, and thus contains a word-aligned next-pointer as the */ /* first word. */ GC_ATTR_NO_SANITIZE_THREAD diff --git a/gc/misc.c b/gc/misc.c index 824e39b8c..91cb193a0 100644 --- a/gc/misc.c +++ b/gc/misc.c @@ -281,7 +281,8 @@ STATIC void GC_init_size_map(void) # ifdef THREADS # define BIG_CLEAR_SIZE 2048 /* Clear this much now and then. */ # else - STATIC word GC_stack_last_cleared = 0; /* GC_no when we last did this */ + STATIC word GC_stack_last_cleared = 0; + /* GC_gc_no value when we last did this. */ STATIC ptr_t GC_min_sp = NULL; /* Coolest stack pointer value from which */ /* we've already cleared the stack. */ @@ -363,22 +364,22 @@ STATIC void GC_init_size_map(void) ptr_t limit = sp; MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word)); - limit = (ptr_t)((word)limit & ~0xf); + limit = (ptr_t)((word)limit & ~(word)0xf); /* Make it sufficiently aligned for assembly */ /* implementations of GC_clear_stack_inner. */ return GC_clear_stack_inner(arg, limit); } BZERO((void *)dummy, SMALL_CLEAR_SIZE*sizeof(word)); # else - if (GC_gc_no > GC_stack_last_cleared) { - /* Start things over, so we clear the entire stack again */ - if (GC_stack_last_cleared == 0) + if (GC_gc_no != GC_stack_last_cleared) { + /* Start things over, so we clear the entire stack again. */ + if (EXPECT(NULL == GC_high_water, FALSE)) GC_high_water = (ptr_t)GC_stackbottom; GC_min_sp = GC_high_water; GC_stack_last_cleared = GC_gc_no; GC_bytes_allocd_at_reset = GC_bytes_allocd; } - /* Adjust GC_high_water */ + /* Adjust GC_high_water. */ MAKE_COOLER(GC_high_water, WORDS_TO_BYTES(DEGRADE_RATE) + GC_SLOP); if ((word)sp HOTTER_THAN (word)GC_high_water) { GC_high_water = sp; @@ -389,7 +390,7 @@ STATIC void GC_init_size_map(void) MAKE_HOTTER(limit, SLOP); if ((word)sp COOLER_THAN (word)limit) { - limit = (ptr_t)((word)limit & ~0xf); + limit = (ptr_t)((word)limit & ~(word)0xf); /* Make it sufficiently aligned for assembly */ /* implementations of GC_clear_stack_inner. */ GC_min_sp = sp; @@ -434,7 +435,7 @@ GC_API void * GC_CALL GC_base(void * p) } if (HBLK_IS_FREE(candidate_hdr)) return(0); /* Make sure r points to the beginning of the object */ - r = (ptr_t)((word)r & ~(WORDS_TO_BYTES(1) - 1)); + r = (ptr_t)((word)r & ~(word)(WORDS_TO_BYTES(1)-1)); { size_t offset = HBLKDISPL(r); word sz = candidate_hdr -> hb_sz; @@ -461,20 +462,21 @@ GC_API int GC_CALL GC_is_heap_ptr(const void *p) return HDR_FROM_BI(bi, p) != 0; } -/* Return the size of an object, given a pointer to its base. */ -/* (For small objects this also happens to work from interior pointers, */ -/* but that shouldn't be relied upon.) */ GC_API size_t GC_CALL GC_size(const void * p) { - hdr * hhdr = HDR(p); + hdr *hhdr; + /* Accept NULL for compatibility with malloc_usable_size(). */ + if (EXPECT(NULL == p, FALSE)) return 0; + + hhdr = HDR(p); return (size_t)hhdr->hb_sz; } - /* These getters remain unsynchronized for compatibility (since some */ /* clients could call some of them from a GC callback holding the */ /* allocator lock). */ + GC_API size_t GC_CALL GC_get_heap_size(void) { /* ignore the memory space returned to OS (i.e. count only the */ @@ -2351,8 +2353,7 @@ GC_API void * GC_CALL GC_do_blocking(GC_fn_type fn, void * client_data) static void block_add_size(struct hblk *h, word pbytes) { hdr *hhdr = HDR(h); - *(word *)pbytes += (WORDS_TO_BYTES(hhdr->hb_sz) + (HBLKSIZE - 1)) - & ~(word)(HBLKSIZE - 1); + *(word *)pbytes += (hhdr -> hb_sz + (HBLKSIZE - 1)) & ~(word)(HBLKSIZE - 1); } GC_API size_t GC_CALL GC_get_memory_use(void) diff --git a/gc/os_dep.c b/gc/os_dep.c index a42a3f220..e90c77250 100644 --- a/gc/os_dep.c +++ b/gc/os_dep.c @@ -386,7 +386,7 @@ GC_INNER const char * GC_get_maps(void) /* Set p to point just past last slash, if any. */ while (*p != '\0' && *p != '\n' && *p != ' ' && *p != '\t') ++p; - while (*p != '/' && (word)p >= (word)map_path) --p; + while ((word)p >= (word)map_path && *p != '/') --p; ++p; if (strncmp(nm, p, nm_len) == 0) { *startp = my_start; @@ -619,7 +619,7 @@ GC_INNER const char * GC_get_maps(void) result = bound; } else { result += pgsz; /* no overflow expected */ - GC_noop1((word)(*result)); + GC_noop1((word)(unsigned char)(*result)); } } @@ -784,7 +784,7 @@ GC_INNER size_t GC_page_size = 0; result = VirtualQuery(p, &buf, sizeof(buf)); if (result != sizeof(buf)) ABORT("Weird VirtualQuery result"); if (base != 0) *base = (ptr_t)(buf.AllocationBase); - protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE)); + protect = buf.Protect & ~(word)(PAGE_GUARD | PAGE_NOCACHE); if (!is_writable(protect)) { return(0); } @@ -801,7 +801,7 @@ GC_INNER size_t GC_page_size = 0; /* function even before GC is initialized). */ if (!GC_page_size) GC_setpagesize(); - trunc_sp = (ptr_t)((word)GC_approx_sp() & ~(GC_page_size - 1)); + trunc_sp = (ptr_t)((word)GC_approx_sp() & ~(word)(GC_page_size-1)); /* FIXME: This won't work if called from a deeply recursive */ /* client code (and the committed stack space has grown). */ size = GC_get_writable_length(trunc_sp, 0); @@ -880,7 +880,9 @@ GC_INNER size_t GC_page_size = 0; # define GET_MAIN_STACKBASE_SPECIAL # endif /* AMIGA */ -# if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE) \ +# if defined(NEED_FIND_LIMIT) \ + || (defined(UNIX_LIKE) && !defined(NO_DEBUGGING)) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) \ || (defined(WRAP_MARK_SOME) && defined(__GNUC__)) typedef void (*GC_fault_handler_t)(int); @@ -893,7 +895,7 @@ GC_INNER size_t GC_page_size = 0; || defined(HURD) || defined(NETBSD) || defined(FREEBSD) static struct sigaction old_bus_act; # endif -# else +# elif !defined(OPENBSD) static GC_fault_handler_t old_segv_handler; # ifdef HAVE_SIGBUS static GC_fault_handler_t old_bus_handler; @@ -1004,8 +1006,7 @@ GC_INNER size_t GC_page_size = 0; GC_ASSERT(I_HOLD_LOCK()); GC_setup_temporary_fault_handler(); if (SETJMP(GC_jmp_buf) == 0) { - result = (ptr_t)(((word)(p)) - & ~(MIN_PAGE_SIZE-1)); + result = (ptr_t)((word)p & ~(word)(MIN_PAGE_SIZE-1)); for (;;) { if (up) { if ((word)result >= (word)bound - MIN_PAGE_SIZE) { @@ -1025,7 +1026,7 @@ GC_INNER size_t GC_page_size = 0; } result -= MIN_PAGE_SIZE; /* no underflow expected */ } - GC_noop1((word)(*result)); + GC_noop1((word)(unsigned char)(*result)); } } GC_reset_fault_handler(); @@ -1083,7 +1084,7 @@ GC_INNER size_t GC_page_size = 0; /* old way to get the register stackbottom */ return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1) - & ~(BACKING_STORE_ALIGNMENT - 1)); + & ~(word)(BACKING_STORE_ALIGNMENT-1)); } #endif /* HPUX_STACK_BOTTOM */ @@ -1326,9 +1327,10 @@ GC_INNER size_t GC_page_size = 0; # define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) # ifdef STACK_GROWS_DOWN result = (ptr_t)(((word)GC_approx_sp() + STACKBOTTOM_ALIGNMENT_M1) - & ~STACKBOTTOM_ALIGNMENT_M1); + & ~(word)STACKBOTTOM_ALIGNMENT_M1); # else - result = (ptr_t)((word)GC_approx_sp() & ~STACKBOTTOM_ALIGNMENT_M1); + result = (ptr_t)((word)GC_approx_sp() + & ~(word)STACKBOTTOM_ALIGNMENT_M1); # endif # elif defined(HPUX_MAIN_STACKBOTTOM) result = GC_hpux_main_stack_base(); @@ -1825,7 +1827,7 @@ void GC_register_data_segments(void) { MEMORY_BASIC_INFORMATION buf; LPVOID limit = GC_sysinfo.lpMinimumApplicationAddress; - ptr_t p = (ptr_t)((word)start & ~(GC_page_size - 1)); + ptr_t p = (ptr_t)((word)start & ~(word)(GC_page_size-1)); GC_ASSERT(GC_page_size != 0); for (;;) { @@ -1964,25 +1966,21 @@ void GC_register_data_segments(void) p = base = limit = GC_least_described_address(static_root); while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) { size_t result = VirtualQuery(p, &buf, sizeof(buf)); - char * new_limit; DWORD protect; if (result != sizeof(buf) || buf.AllocationBase == 0 || GC_is_heap_base(buf.AllocationBase)) break; - new_limit = (char *)p + buf.RegionSize; + if ((word)p > GC_WORD_MAX - buf.RegionSize) break; /* overflow */ protect = buf.Protect; if (buf.State == MEM_COMMIT && is_writable(protect)) { - if ((char *)p == limit) { - limit = new_limit; - } else { + if ((char *)p != limit) { if (base != limit) GC_add_roots_inner(base, limit, FALSE); base = (char *)p; - limit = new_limit; } + limit = (char *)p + buf.RegionSize; } - if ((word)p > (word)new_limit /* overflow */) break; - p = (LPVOID)new_limit; + p = (char *)p + buf.RegionSize; } if (base != limit) GC_add_roots_inner(base, limit, FALSE); } @@ -2030,10 +2028,11 @@ void GC_register_data_segments(void) } else { GC_reset_fault_handler(); /* We got here via a longjmp. The address is not readable. */ - /* This is known to happen under Solaris 2.4 + gcc, which place */ - /* string constants in the text segment, but after etext. */ - /* Use plan B. Note that we now know there is a gap between */ - /* text and data segments, so plan A brought us something. */ + /* This is known to happen under Solaris 2.4 + gcc, which */ + /* places string constants in the text segment, but after */ + /* etext. Use plan B. Note that we now know there is a gap */ + /* between text and data segments, so plan A brought us */ + /* something. */ result = (char *)GC_find_limit(DATAEND, FALSE); } return (/* no volatile */ ptr_t)result; @@ -2060,7 +2059,7 @@ void GC_register_data_segments(void) /* Try reading at the address. */ /* This should happen before there is another thread. */ for (; next_page < (word)DATAEND; next_page += (word)max_page_size) - *(volatile char *)next_page; + GC_noop1((word)(*(volatile unsigned char *)next_page)); GC_reset_fault_handler(); } else { GC_reset_fault_handler(); @@ -2271,7 +2270,7 @@ void GC_register_data_segments(void) return NULL; } last_addr = (ptr_t)(((word)result + bytes + GC_page_size - 1) - & ~(GC_page_size - 1)); + & ~(word)(GC_page_size - 1)); # if !defined(LINUX) if (last_addr == 0) { /* Oops. We got the end of the address space. This isn't */ @@ -2318,7 +2317,7 @@ STATIC ptr_t GC_unix_sbrk_get_mem(size_t bytes) goto out; } if (lsbs != 0) { - if((ptr_t)sbrk((SBRK_ARG_T)GC_page_size - lsbs) == (ptr_t)(-1)) { + if ((ptr_t)sbrk((SBRK_ARG_T)GC_page_size - lsbs) == (ptr_t)(-1)) { result = 0; goto out; } @@ -2623,7 +2622,7 @@ void * os2_alloc(size_t bytes) STATIC ptr_t GC_unmap_start(ptr_t start, size_t bytes) { ptr_t result = (ptr_t)(((word)start + GC_page_size - 1) - & ~(GC_page_size - 1)); + & ~(word)(GC_page_size - 1)); GC_ASSERT(GC_page_size != 0); if ((word)(result + GC_page_size) > (word)(start + bytes)) return 0; @@ -3140,9 +3139,7 @@ GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void) * to the write-protected heap. Probably the best way to do this is to * 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. + * are not protected. * We assume the page size is a multiple of HBLKSIZE. * We prefer them to be the same. We avoid protecting pointer-free * objects only if they are the same. @@ -3315,7 +3312,8 @@ GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void) # endif if (SIG_OK && CODE_OK) { - struct hblk * h = (struct hblk *)((word)addr & ~(GC_page_size-1)); + struct hblk * h = (struct hblk *)((word)addr + & ~(word)(GC_page_size-1)); GC_bool in_allocd_block; size_t i; @@ -3433,6 +3431,19 @@ GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void) { # if !defined(MSWIN32) && !defined(MSWINCE) struct sigaction act, oldact; +# endif + +# ifdef COUNT_PROTECTED_REGIONS + GC_ASSERT(GC_page_size != 0); + if ((signed_word)(GC_heapsize / (word)GC_page_size) + >= ((signed_word)GC_UNMAPPED_REGIONS_SOFT_LIMIT + - GC_num_unmapped_regions) * 2) { + GC_COND_LOG_PRINTF("Cannot turn on GC incremental mode" + " as heap contains too many pages\n"); + return FALSE; + } +# endif +# if !defined(MSWIN32) && !defined(MSWINCE) act.sa_flags = SA_RESTART | SA_SIGINFO; act.sa_sigaction = GC_write_fault_handler; (void)sigemptyset(&act.sa_mask); @@ -3607,32 +3618,44 @@ STATIC void GC_protect_heap(void) } } -/* - * Acquiring the allocation lock here is dangerous, since this - * can be called from within GC_call_with_alloc_lock, and the cord - * package does so. On systems that allow nested lock acquisition, this - * happens to work. - */ +# if defined(CAN_HANDLE_FORK) && defined(DARWIN) && defined(THREADS) \ + || defined(COUNT_PROTECTED_REGIONS) + /* Remove protection for the entire heap not updating GC_dirty_pages. */ + STATIC void GC_unprotect_all_heap(void) + { + unsigned i; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_auto_incremental); + for (i = 0; i < GC_n_heap_sects; i++) { + UNPROTECT(GC_heap_sects[i].hs_start, GC_heap_sects[i].hs_bytes); + } + } +# endif /* CAN_HANDLE_FORK && DARWIN && THREADS || COUNT_PROTECTED_REGIONS */ -# ifdef THREAD_SANITIZER - /* Used by GC_remove_protection only. Potential data race between */ - /* this function and GC_write_fault_handler should not be harmful */ - /* because it would only result in a double call of UNPROTECT() for */ - /* a region. */ - GC_ATTR_NO_SANITIZE_THREAD - static GC_bool get_pht_entry_from_index_async(volatile page_hash_table db, - size_t index) +# ifdef COUNT_PROTECTED_REGIONS + GC_INNER void GC_handle_protected_regions_limit(void) { - return (GC_bool)get_pht_entry_from_index(db, index); + GC_ASSERT(GC_page_size != 0); + /* To prevent exceeding the limit of vm.max_map_count, the most */ + /* trivial (though highly restrictive) way is to turn off the */ + /* incremental collection mode (based on mprotect) once the */ + /* number of pages in the heap reaches that limit. */ + if (GC_auto_incremental && !GC_GWW_AVAILABLE() + && (signed_word)(GC_heapsize / (word)GC_page_size) + >= ((signed_word)GC_UNMAPPED_REGIONS_SOFT_LIMIT + - GC_num_unmapped_regions) * 2) { + GC_unprotect_all_heap(); +# ifdef DARWIN + GC_task_self = 0; +# endif + GC_incremental = FALSE; + WARN("GC incremental mode is turned off" + " to prevent hitting VM maps limit\n", 0); + } } -# else -# define get_pht_entry_from_index_async(bl, index) \ - get_pht_entry_from_index(bl, index) -# endif +# endif /* COUNT_PROTECTED_REGIONS */ -/* We no longer wrap read by default, since that was causing too many */ -/* problems. It is preferred that the client instead avoids writing */ -/* to the write-protected heap with a system call. */ #endif /* MPROTECT_VDB */ #if !defined(THREADS) && (defined(PROC_VDB) || defined(SOFT_VDB)) @@ -4022,8 +4045,8 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded) ofs = (size_t)(fpos - pagemap_buf_fpos); res = (ssize_t)(pagemap_buf_fpos + pagemap_buf_len - fpos); } else { - off_t aligned_pos = fpos & ~(GC_page_size < VDB_BUF_SZ - ? GC_page_size-1 : VDB_BUF_SZ-1); + off_t aligned_pos = fpos & ~(off_t)(GC_page_size < VDB_BUF_SZ + ? GC_page_size-1 : VDB_BUF_SZ-1); for (;;) { size_t count; @@ -4068,19 +4091,24 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded) return &soft_vdb_buf[ofs / sizeof(pagemap_elem_t)]; } - static void soft_set_grungy_pages(ptr_t vaddr /* start */, ptr_t limit, + static void soft_set_grungy_pages(ptr_t start, ptr_t limit, ptr_t next_start_hint) { + word vaddr = (word)start & ~(word)(GC_page_size-1); + off_t next_fpos_hint; + + GC_ASSERT(modHBLKSZ((word)start) == 0); GC_ASSERT(GC_page_size != 0); - while ((word)vaddr < (word)limit) { + next_fpos_hint = (off_t)((word)next_start_hint / GC_page_size + * sizeof(pagemap_elem_t)); + while (vaddr < (word)limit) { size_t res; word limit_buf; const pagemap_elem_t *bufp = pagemap_buffered_read(&res, - (off_t)((word)vaddr / GC_page_size * sizeof(pagemap_elem_t)), - (size_t)((((word)limit - (word)vaddr + GC_page_size-1) - / GC_page_size) * sizeof(pagemap_elem_t)), - (off_t)((word)next_start_hint / GC_page_size - * sizeof(pagemap_elem_t))); + (off_t)(vaddr / GC_page_size * sizeof(pagemap_elem_t)), + (size_t)(((word)limit - vaddr + GC_page_size-1) / GC_page_size + * sizeof(pagemap_elem_t)), + next_fpos_hint); if (res % sizeof(pagemap_elem_t) != 0) { /* Punt: */ @@ -4089,19 +4117,23 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded) break; } - limit_buf = ((word)vaddr & ~(GC_page_size-1)) - + (res / sizeof(pagemap_elem_t)) * GC_page_size; - for (; (word)vaddr < limit_buf; vaddr += GC_page_size, bufp++) + limit_buf = vaddr + (res / sizeof(pagemap_elem_t)) * GC_page_size; + for (; vaddr < limit_buf; vaddr += GC_page_size, bufp++) if ((*bufp & PM_SOFTDIRTY_MASK) != 0) { struct hblk * h; - ptr_t next_vaddr = vaddr + GC_page_size; + word next_vaddr = vaddr + GC_page_size; + if (EXPECT(next_vaddr > (word)limit, FALSE)) + next_vaddr = (word)limit; /* If the bit is set, the respective PTE was written to */ /* since clearing the soft-dirty bits. */ # ifdef DEBUG_DIRTY_BITS GC_log_printf("dirty page at: %p\n", (void *)vaddr); # endif - for (h = (struct hblk *)vaddr; (word)h < (word)next_vaddr; h++) { + h = (struct hblk *)vaddr; + if (EXPECT(vaddr < (word)start, FALSE)) + h = (struct hblk *)start; + for (; (word)h < next_vaddr; h++) { word index = PHT_HASH(h); set_pht_entry_from_index(GC_grungy_pages, index); } @@ -4137,9 +4169,9 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded) pagemap_buf_len = 0; /* invalidate soft_vdb_buf */ for (i = 0; i != GC_n_heap_sects; ++i) { - ptr_t vaddr = GC_heap_sects[i].hs_start; + ptr_t start = GC_heap_sects[i].hs_start; - soft_set_grungy_pages(vaddr, vaddr + GC_heap_sects[i].hs_bytes, + soft_set_grungy_pages(start, start + GC_heap_sects[i].hs_bytes, i < GC_n_heap_sects-1 ? GC_heap_sects[i+1].hs_start : NULL); } @@ -4149,7 +4181,7 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded) # ifndef NO_VDB_FOR_STATIC_ROOTS for (i = 0; (int)i < n_root_sets; ++i) { - soft_set_grungy_pages(GC_static_roots[i].r_start, + soft_set_grungy_pages((ptr_t)HBLKPTR(GC_static_roots[i].r_start), GC_static_roots[i].r_end, (int)i < n_root_sets-1 ? GC_static_roots[i+1].r_start : NULL); @@ -4347,14 +4379,12 @@ GC_INNER GC_bool GC_dirty_init(void) if (!GC_auto_incremental || GC_GWW_AVAILABLE()) return; GC_ASSERT(GC_page_size != 0); - h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1)); + h_trunc = (struct hblk *)((word)h & ~(word)(GC_page_size-1)); h_end = (struct hblk *)(((word)(h + nblocks) + GC_page_size - 1) - & ~(GC_page_size - 1)); - if (h_end == h_trunc + 1 && - get_pht_entry_from_index_async(GC_dirty_pages, PHT_HASH(h_trunc))) { - /* already marked dirty, and hence unprotected. */ - return; - } + & ~(word)(GC_page_size - 1)); + /* Note that we cannot examine GC_dirty_pages to check */ + /* whether the page at h_trunc has already been marked */ + /* dirty as there could be a hash collision. */ for (current = h_trunc; (word)current < (word)h_end; ++current) { word index = PHT_HASH(current); @@ -4390,7 +4420,6 @@ GC_INNER GC_bool GC_dirty_init(void) #include #include #include -#include EXTERN_C_BEGIN @@ -4568,6 +4597,32 @@ typedef enum { GC_mprotect_thread_notify(ID_RESUME); } +# ifdef CAN_HANDLE_FORK + GC_INNER void GC_dirty_update_child(void) + { + GC_ASSERT(I_HOLD_LOCK()); + if (0 == GC_task_self) return; /* GC incremental mode is off */ + + GC_ASSERT(GC_mprotect_state == GC_MP_NORMAL); + GC_task_self = mach_task_self(); /* needed by UNPROTECT() */ + GC_unprotect_all_heap(); + + /* Restore the old task exception ports. */ + /* TODO: Should we do it in fork_prepare/parent_proc? */ + if (GC_old_exc_ports.count > 0) { + /* TODO: Should we check GC_old_exc_ports.count<=1? */ + if (task_set_exception_ports(GC_task_self, GC_old_exc_ports.masks[0], + GC_old_exc_ports.ports[0], GC_old_exc_ports.behaviors[0], + GC_old_exc_ports.flavors[0]) != KERN_SUCCESS) + ABORT("task_set_exception_ports failed (in child)"); + } + + /* TODO: Re-enable incremental mode in child. */ + GC_task_self = 0; + GC_incremental = FALSE; + } +# endif /* CAN_HANDLE_FORK */ + #else /* The compiler should optimize away any GC_mprotect_state computations */ # define GC_mprotect_state GC_MP_NORMAL @@ -4607,22 +4662,22 @@ STATIC void *GC_mprotect_thread(void *arg) GC_darwin_register_mach_handler_thread(mach_thread_self()); # endif - for(;;) { + for (;;) { r = mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE | - (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0), - 0, sizeof(msg), GC_ports.exception, + (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT + : 0), 0, sizeof(msg), GC_ports.exception, GC_mprotect_state == GC_MP_DISCARDING ? 0 : MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1; # if defined(THREADS) - if(GC_mprotect_state == GC_MP_DISCARDING) { - if(r == MACH_RCV_TIMED_OUT) { + if (GC_mprotect_state == GC_MP_DISCARDING) { + if (r == MACH_RCV_TIMED_OUT) { GC_mprotect_state = GC_MP_STOPPED; GC_mprotect_thread_reply(); continue; } - if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME)) + if (r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME)) ABORT("Out of order mprotect thread request"); } # endif /* THREADS */ @@ -4632,15 +4687,15 @@ STATIC void *GC_mprotect_thread(void *arg) ": errcode= %d (%s)", (int)r, mach_error_string(r)); } - switch(id) { + switch (id) { # if defined(THREADS) case ID_STOP: - if(GC_mprotect_state != GC_MP_NORMAL) + if (GC_mprotect_state != GC_MP_NORMAL) ABORT("Called mprotect_stop when state wasn't normal"); GC_mprotect_state = GC_MP_DISCARDING; break; case ID_RESUME: - if(GC_mprotect_state != GC_MP_STOPPED) + if (GC_mprotect_state != GC_MP_STOPPED) ABORT("Called mprotect_resume when state wasn't stopped"); GC_mprotect_state = GC_MP_NORMAL; GC_mprotect_thread_reply(); @@ -4648,13 +4703,13 @@ STATIC void *GC_mprotect_thread(void *arg) # endif /* THREADS */ default: /* Handle the message (calls catch_exception_raise) */ - if(!exc_server(&msg.head, &reply.head)) + if (!exc_server(&msg.head, &reply.head)) ABORT("exc_server failed"); /* Send the reply */ r = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - if(r != MACH_MSG_SUCCESS) { + if (r != MACH_MSG_SUCCESS) { /* This will fail if the thread dies, but the thread */ /* shouldn't die... */ # ifdef BROKEN_EXCEPTION_HANDLING @@ -4665,7 +4720,7 @@ STATIC void *GC_mprotect_thread(void *arg) # endif } } /* switch */ - } /* for(;;) */ + } /* for */ } /* All this SIGBUS code shouldn't be necessary. All protection faults should @@ -4703,7 +4758,7 @@ GC_INNER GC_bool GC_dirty_init(void) pthread_attr_t attr; exception_mask_t mask; -# ifdef CAN_HANDLE_FORK +# if defined(CAN_HANDLE_FORK) && !defined(THREADS) 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 */ @@ -4728,6 +4783,7 @@ GC_INNER GC_bool GC_dirty_init(void) } GC_task_self = me = mach_task_self(); + GC_ASSERT(me != 0); r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.exception); /* TODO: WARN and return FALSE in case of a failure. */ @@ -4745,9 +4801,8 @@ GC_INNER GC_bool GC_dirty_init(void) ABORT("mach_port_allocate failed (reply port)"); # endif - /* The exceptions we want to catch */ + /* The exceptions we want to catch. */ mask = EXC_MASK_BAD_ACCESS; - r = task_get_exception_ports(me, mask, GC_old_exc_ports.masks, &GC_old_exc_ports.count, GC_old_exc_ports.ports, GC_old_exc_ports.behaviors, @@ -4759,14 +4814,13 @@ GC_INNER GC_bool GC_dirty_init(void) GC_MACH_THREAD_STATE); if (r != KERN_SUCCESS) ABORT("task_set_exception_ports failed"); + if (pthread_attr_init(&attr) != 0) ABORT("pthread_attr_init failed"); if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) ABORT("pthread_attr_setdetachedstate failed"); - -# undef pthread_create - /* This will call the real pthread function, not our wrapper */ - if (pthread_create(&thread, &attr, GC_mprotect_thread, NULL) != 0) + /* This will call the real pthread function, not our wrapper. */ + if (GC_inner_pthread_create(&thread, &attr, GC_mprotect_thread, NULL) != 0) ABORT("pthread_create failed"); (void)pthread_attr_destroy(&attr); @@ -4808,9 +4862,10 @@ STATIC kern_return_t GC_forward_exception(mach_port_t thread, mach_port_t task, thread_state_data_t thread_state; mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; - for (i=0; i < GC_old_exc_ports.count; i++) - if (GC_old_exc_ports.masks[i] & (1 << exception)) + for (i = 0; i < GC_old_exc_ports.count; i++) { + if ((GC_old_exc_ports.masks[i] & ((exception_mask_t)1 << exception)) != 0) break; + } if (i == GC_old_exc_ports.count) ABORT("No handler for exception!"); @@ -4820,15 +4875,16 @@ STATIC kern_return_t GC_forward_exception(mach_port_t thread, mach_port_t task, if (behavior == EXCEPTION_STATE || behavior == EXCEPTION_STATE_IDENTITY) { r = thread_get_state(thread, flavor, thread_state, &thread_state_count); - if(r != KERN_SUCCESS) + if (r != KERN_SUCCESS) ABORT("thread_get_state failed in forward_exception"); - } + } - switch(behavior) { + switch (behavior) { case EXCEPTION_STATE: - r = exception_raise_state(port, thread, task, exception, data, data_count, - &flavor, thread_state, thread_state_count, - thread_state, &thread_state_count); + r = exception_raise_state(port, thread, task, exception, data, + data_count, &flavor, thread_state, + thread_state_count, thread_state, + &thread_state_count); break; case EXCEPTION_STATE_IDENTITY: r = exception_raise_state_identity(port, thread, task, exception, data, @@ -4923,7 +4979,7 @@ catch_exception_raise(mach_port_t exception_port GC_ATTR_UNUSED, r = thread_get_state(thread, flavor, (natural_t*)&exc_state, &exc_state_count); - if(r != KERN_SUCCESS) { + if (r != KERN_SUCCESS) { /* The thread is supposed to be suspended while the exception */ /* handler is called. This shouldn't fail. */ # ifdef BROKEN_EXCEPTION_HANDLING @@ -4946,12 +5002,12 @@ catch_exception_raise(mach_port_t exception_port GC_ATTR_UNUSED, static char *last_fault; static int last_fault_count; - if(addr != last_fault) { + if (addr != last_fault) { last_fault = addr; last_fault_count = 0; } - if(++last_fault_count < 32) { - if(last_fault_count == 1) + if (++last_fault_count < 32) { + if (last_fault_count == 1) WARN("Ignoring KERN_PROTECTION_FAILURE at %p\n", addr); return KERN_SUCCESS; } @@ -4976,9 +5032,12 @@ catch_exception_raise(mach_port_t exception_port GC_ATTR_UNUSED, GC_ASSERT(GC_page_size != 0); if (GC_mprotect_state == GC_MP_NORMAL) { /* common case */ - struct hblk * h = (struct hblk*)((word)addr & ~(GC_page_size-1)); + struct hblk * h = (struct hblk *)((word)addr & ~(word)(GC_page_size-1)); size_t i; +# ifdef CHECKSUMS + GC_record_fault(h); +# endif UNPROTECT(h, GC_page_size); for (i = 0; i < divHBLKSZ(GC_page_size); i++) { word index = PHT_HASH(h+i); @@ -5121,16 +5180,32 @@ GC_API int GC_CALL GC_get_pages_executable(void) /* you could use something like pthread_getspecific. */ # endif GC_bool GC_in_save_callers = FALSE; -#endif + +# if defined(THREADS) && defined(DBG_HDRS_ALL) +# include "private/dbg_mlc.h" + + /* A dummy version of GC_save_callers() which does not call */ + /* backtrace(). */ + GC_INNER void GC_save_callers_no_unlock(struct callinfo info[NFRAMES]) + { + GC_ASSERT(I_HOLD_LOCK()); + info[0].ci_pc = (word)(&GC_save_callers_no_unlock); + BZERO(&info[1], sizeof(void *) * (NFRAMES - 1)); + } +# endif +#endif /* REDIRECT_MALLOC */ GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) { void * tmp_info[NFRAMES + 1]; int npcs, i; -# define IGNORE_FRAMES 1 - /* We retrieve NFRAMES+1 pc values, but discard the first, since it */ - /* points to our own frame. */ + GC_ASSERT(I_HOLD_LOCK()); + /* backtrace may call dl_iterate_phdr which is also */ + /* used by GC_register_dynamic_libraries, and */ + /* dl_iterate_phdr is not guaranteed to be reentrant. */ + + GC_STATIC_ASSERT(sizeof(struct callinfo) == sizeof(void *)); # ifdef REDIRECT_MALLOC if (GC_in_save_callers) { info[0].ci_pc = (word)(&GC_save_callers); @@ -5138,19 +5213,21 @@ GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) return; } GC_in_save_callers = TRUE; + /* backtrace() might call a redirected malloc. */ + UNLOCK(); + npcs = backtrace((void **)tmp_info, NFRAMES + 1); + LOCK(); +# else + npcs = backtrace((void **)tmp_info, NFRAMES + 1); # endif - - GC_ASSERT(I_HOLD_LOCK()); - /* backtrace may call dl_iterate_phdr which is also */ - /* used by GC_register_dynamic_libraries, and */ - /* dl_iterate_phdr is not guaranteed to be reentrant. */ - - GC_STATIC_ASSERT(sizeof(struct callinfo) == sizeof(void *)); - npcs = backtrace((void **)tmp_info, NFRAMES + IGNORE_FRAMES); - if (npcs > IGNORE_FRAMES) - BCOPY(&tmp_info[IGNORE_FRAMES], info, - (npcs - IGNORE_FRAMES) * sizeof(void *)); - for (i = npcs - IGNORE_FRAMES; i < NFRAMES; ++i) info[i].ci_pc = 0; + /* We retrieve NFRAMES+1 pc values, but discard the first one, since */ + /* it points to our own frame. */ + i = 0; + if (npcs > 1) { + i = npcs - 1; + BCOPY(&tmp_info[1], info, (unsigned)i * sizeof(void *)); + } + for (; i < NFRAMES; ++i) info[i].ci_pc = 0; # ifdef REDIRECT_MALLOC GC_in_save_callers = FALSE; # endif diff --git a/gc/pthread_stop_world.c b/gc/pthread_stop_world.c index 2b4548905..f903f6fe2 100644 --- a/gc/pthread_stop_world.c +++ b/gc/pthread_stop_world.c @@ -1262,6 +1262,10 @@ GC_INNER void GC_stop_world(void) #else /* !NACL */ +# ifndef GC_OPENBSD_UTHREADS + static GC_bool in_resend_restart_signals; +# endif + /* Restart all threads that were suspended by the collector. */ /* Return the number of restart signals that were sent. */ STATIC int GC_restart_all(void) @@ -1285,8 +1289,24 @@ GC_INNER void GC_stop_world(void) # endif if (GC_retry_signals && AO_load(&p->stop_info.last_stop_count) - == (AO_t)((word)GC_stop_count | THREAD_RESTARTED)) - continue; /* The thread has been restarted. */ + == (AO_t)((word)GC_stop_count | THREAD_RESTARTED)) { + /* The thread has been restarted. */ + if (!in_resend_restart_signals) { + /* Some user signal (which we do not block, e.g. SIGQUIT) */ + /* has already restarted the thread, but nonetheless we */ + /* need to count the thread in n_live_threads, so that */ + /* to decrement the semaphore's value proper amount of */ + /* times. (We are also sending the restart signal to the */ + /* thread, it is not needed actually but does not hurt.) */ + } else { + continue; + /* FIXME: Still, an extremely low chance exists that the */ + /* user signal restarts the thread after the restart */ + /* signal has been lost (causing sem_timedwait() to fail) */ + /* while retrying, causing finally a mismatch between */ + /* GC_suspend_ack_sem and n_live_threads. */ + } + } n_live_threads++; # endif # ifdef DEBUG_THREADS @@ -1337,13 +1357,16 @@ GC_INNER void GC_start_world(void) /* The updated value should now be visible to the */ /* signal handler (note that pthread_kill is not on */ /* the list of functions which synchronize memory). */ + GC_ASSERT(!in_resend_restart_signals); # endif n_live_threads = GC_restart_all(); # ifdef GC_OPENBSD_UTHREADS (void)n_live_threads; # else if (GC_retry_signals) { + in_resend_restart_signals = TRUE; resend_lost_signals_retry(n_live_threads, GC_restart_all); + in_resend_restart_signals = FALSE; } /* else */ # ifdef GC_NETBSD_THREADS_WORKAROUND else { diff --git a/gc/pthread_support.c b/gc/pthread_support.c index 48cd28044..6075d50c2 100644 --- a/gc/pthread_support.c +++ b/gc/pthread_support.c @@ -260,6 +260,16 @@ static GC_bool parallel_initialized = FALSE; +#if defined(MPROTECT_VDB) && defined(DARWIN) + GC_INNER int GC_inner_pthread_create(pthread_t *t, + GC_PTHREAD_CREATE_CONST pthread_attr_t *a, + void *(*fn)(void *), void *arg) + { + INIT_REAL_SYMS(); + return REAL_FUNC(pthread_create)(t, a, fn, arg); + } +#endif + #ifndef GC_ALWAYS_MULTITHREADED GC_INNER GC_bool GC_need_to_lock = FALSE; #endif @@ -552,7 +562,8 @@ static struct GC_Thread_Rep first_thread; void GC_push_thread_structures(void) { GC_ASSERT(I_HOLD_LOCK()); - GC_PUSH_ALL_SYM(GC_threads); + GC_push_all((/* no volatile */ void *)&GC_threads, + (ptr_t)(&GC_threads) + sizeof(GC_threads)); # ifdef E2K GC_PUSH_ALL_SYM(first_thread.backing_store_end); # endif @@ -723,8 +734,15 @@ GC_INNER GC_thread GC_lookup_thread(pthread_t id) GC_INNER unsigned char *GC_check_finalizer_nested(void) { GC_thread me = GC_lookup_thread(pthread_self()); - unsigned nesting_level = me->finalizer_nested; + unsigned nesting_level; +# if defined(INCLUDE_LINUX_THREAD_DESCR) && defined(REDIRECT_MALLOC) + /* As noted in GC_start_routine, an allocation may happen in */ + /* GC_get_stack_base, causing GC_notify_or_invoke_finalizers */ + /* to be called before the thread gets registered. */ + if (EXPECT(NULL == me, FALSE)) return NULL; +# endif + nesting_level = me->finalizer_nested; if (nesting_level) { /* We are inside another GC_invoke_finalizers(). */ /* Skip some implicitly-called GC_invoke_finalizers() */ @@ -1218,6 +1236,9 @@ static void fork_parent_proc(void) static void fork_child_proc(void) { GC_release_dirty_lock(); +# ifndef GC_DISABLE_INCREMENTAL + GC_dirty_update_child(); +# endif # ifdef PARALLEL_MARK if (GC_parallel) { # if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \ @@ -1237,9 +1258,6 @@ static void fork_child_proc(void) # endif /* Clean up the thread table, so that just our thread is left. */ GC_remove_all_threads_but_me(); -# ifndef GC_DISABLE_INCREMENTAL - GC_dirty_update_child(); -# endif RESTORE_CANCEL(fork_cancel_state); UNLOCK(); /* Even though after a fork the child only inherits the single */ @@ -1268,12 +1286,6 @@ static void fork_child_proc(void) GC_API void GC_CALL GC_atfork_prepare(void) { if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); -# if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB) - if (GC_auto_incremental) { - GC_ASSERT(0 == GC_handle_fork); - ABORT("Unable to fork while mprotect_thread is running"); - } -# endif if (GC_handle_fork <= 0) fork_prepare_proc(); } @@ -2068,6 +2080,7 @@ GC_API void GC_CALL GC_allow_register_threads(void) UNLOCK(); # endif INIT_REAL_SYMS(); /* to initialize symbols while single-threaded */ + GC_init_lib_bounds(); GC_start_mark_threads(); set_need_to_lock(); } @@ -2218,6 +2231,7 @@ GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread( INIT_REAL_SYMS(); if (!EXPECT(parallel_initialized, TRUE)) GC_init_parallel(); + GC_init_lib_bounds(); if (sem_init(&si.registered, GC_SEM_INIT_PSHARED, 0) != 0) ABORT("sem_init failed"); diff --git a/gc/ptr_chck.c b/gc/ptr_chck.c index 84b5743aa..2d8e9b46c 100644 --- a/gc/ptr_chck.c +++ b/gc/ptr_chck.c @@ -227,7 +227,7 @@ GC_API void * GC_CALL GC_is_visible(void *p) retry: switch(descr & GC_DS_TAGS) { case GC_DS_LENGTH: - if ((word)p - (word)base > descr) goto fail; + if ((word)p - (word)base >= descr) goto fail; break; case GC_DS_BITMAP: if ((word)p - (word)base >= WORDS_TO_BYTES(BITMAP_BITS) @@ -241,9 +241,13 @@ GC_API void * GC_CALL GC_is_visible(void *p) break; case GC_DS_PER_OBJECT: if ((signed_word)descr >= 0) { - descr = *(word *)((ptr_t)base + (descr & ~GC_DS_TAGS)); + descr = *(word *)((ptr_t)base + + (descr & ~(word)GC_DS_TAGS)); } else { ptr_t type_descr = *(ptr_t *)base; + + if (EXPECT(NULL == type_descr, FALSE)) + goto fail; /* see comment in GC_mark_from */ descr = *(word *)(type_descr - (descr - (word)(GC_DS_PER_OBJECT - GC_INDIR_PER_OBJ_BIAS))); diff --git a/gc/reclaim.c b/gc/reclaim.c index 18e2b2353..926d4b641 100644 --- a/gc/reclaim.c +++ b/gc/reclaim.c @@ -574,7 +574,8 @@ STATIC void GC_print_block_descr(struct hblk *h, } ps = (struct Print_stats *)raw_ps; - ps->total_bytes += (bytes + (HBLKSIZE-1)) & ~(HBLKSIZE-1); /* round up */ + ps->total_bytes += + (bytes + HBLKSIZE-1) & ~(word)(HBLKSIZE-1); /* round up */ ps->number_of_blocks++; } @@ -832,7 +833,8 @@ STATIC void GC_do_enumerate_reachable_objects(struct hblk *hbp, word ped) 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)) { ((struct enumerate_reachable_s *)ped)->proc(p, sz, ((struct enumerate_reachable_s *)ped)->client_data); diff --git a/gc/specific.c b/gc/specific.c index 88988b397..6dae5d463 100644 --- a/gc/specific.c +++ b/gc/specific.c @@ -146,14 +146,12 @@ GC_INNER void * GC_slow_getspecific(tsd * key, word qtid, } if (entry == NULL) return NULL; /* Set cache_entry. */ - entry -> qtid = (AO_t)qtid; + AO_store(&(entry -> qtid), qtid); /* It's safe to do this asynchronously. Either value */ /* is safe, though may produce spurious misses. */ /* We're replacing one qtid with another one for the */ /* same thread. */ - *cache_ptr = entry; - /* Again this is safe since pointer assignments are */ - /* presumed atomic, and either pointer is valid. */ + AO_store((volatile AO_t *)cache_ptr, (AO_t)entry); return TS_REVEAL_PTR(entry -> value); } diff --git a/gc/tests/disclaim_bench.c b/gc/tests/disclaim_bench.c index 2b11d560f..3dcf32160 100644 --- a/gc/tests/disclaim_bench.c +++ b/gc/tests/disclaim_bench.c @@ -23,12 +23,11 @@ #include "gc_disclaim.h" #define NOT_GCBUILD -#include "private/gc_priv.h" /* for CLOCK_TYPE, COVERT_DATAFLOW, GC_random */ +#include "private/gc_priv.h" -#ifdef LINT2 -# undef rand -# define rand() (int)GC_random() -#endif +#undef rand +static GC_RAND_STATE_T seed; +#define rand() GC_RAND_NEXT(&seed) #define my_assert(e) \ if (!(e)) { \ diff --git a/gc/tests/disclaim_test.c b/gc/tests/disclaim_test.c index 4f4faf0e0..22a969c7a 100644 --- a/gc/tests/disclaim_test.c +++ b/gc/tests/disclaim_test.c @@ -28,26 +28,17 @@ #undef GC_NO_THREAD_REDIRECTS #include "gc_disclaim.h" -#if defined(GC_PTHREADS) || defined(LINT2) -# define NOT_GCBUILD -# include "private/gc_priv.h" - - GC_ATTR_NO_SANITIZE_THREAD - static int GC_rand(void) /* nearly identical to GC_random */ - { - static unsigned seed; /* concurrent update does not hurt the test */ - - seed = (seed * 1103515245U + 12345) & (~0U >> 1); - return (int)seed; - } - +#define NOT_GCBUILD +#include "private/gc_priv.h" /* Redefine the standard rand() with a trivial (yet sufficient for */ /* the test purpose) implementation to avoid crashes inside rand() */ - /* on some targets (e.g. FreeBSD 13.0) when used concurrently. */ + /* on some hosts (e.g. FreeBSD 13.0) when used concurrently. */ /* The standard specifies rand() as not a thread-safe API function. */ -# undef rand -# define rand() GC_rand() -#endif /* GC_PTHREADS || LINT2 */ + /* On other hosts (e.g. OpenBSD 7.3), use of the standard rand() */ + /* causes "rand() may return deterministic values" warning. */ +#undef rand +static GC_RAND_STATE_T seed; /* concurrent update does not hurt the test */ +#define rand() GC_RAND_NEXT(&seed) #define my_assert(e) \ if (!(e)) { \ @@ -211,6 +202,8 @@ void *test(void *data) memset(pop, 0, sizeof(pop)); for (i = 0; i < MUTATE_CNT; ++i) { int t = rand() % POP_SIZE; + int j; + switch (rand() % (i > GROW_LIMIT? 5 : 3)) { case 0: case 3: if (pop[t]) @@ -221,8 +214,8 @@ void *test(void *data) pop[t] = pop[t]->cdr; break; case 2: - pop[t] = pair_new(pop[rand() % POP_SIZE], - pop[rand() % POP_SIZE]); + j = rand() % POP_SIZE; + pop[t] = pair_new(pop[j], pop[rand() % POP_SIZE]); break; } if (rand() % 8 == 1) diff --git a/gc/tests/disclaim_weakmap_test.c b/gc/tests/disclaim_weakmap_test.c index 849c4095f..84201e2ab 100644 --- a/gc/tests/disclaim_weakmap_test.c +++ b/gc/tests/disclaim_weakmap_test.c @@ -25,22 +25,11 @@ #include "gc_disclaim.h" /* includes gc.h */ -#if defined(GC_PTHREADS) || defined(LINT2) -# define NOT_GCBUILD -# include "private/gc_priv.h" - - GC_ATTR_NO_SANITIZE_THREAD - static int GC_rand(void) /* same as in disclaim_test.c */ - { - static unsigned seed; /* concurrent update does not hurt the test */ - - seed = (seed * 1103515245U + 12345) & (~0U >> 1); - return (int)seed; - } - -# undef rand -# define rand() GC_rand() -#endif /* GC_PTHREADS || LINT2 */ +#define NOT_GCBUILD +#include "private/gc_priv.h" +#undef rand +static GC_RAND_STATE_T seed; /* concurrent update does not hurt the test */ +#define rand() GC_RAND_NEXT(&seed) #include "gc_mark.h" /* should not precede include gc_priv.h */ @@ -54,7 +43,9 @@ #else # undef NTHREADS # define NTHREADS 1 -# define AO_t GC_word +# ifndef AO_HAVE_compiler_barrier +# define AO_t GC_word +# endif #endif #define POP_SIZE 200 diff --git a/gc/tests/huge_test.c b/gc/tests/huge_test.c index b9493f7a3..8bb8bd328 100644 --- a/gc/tests/huge_test.c +++ b/gc/tests/huge_test.c @@ -11,8 +11,8 @@ #endif #ifndef GC_MAXIMUM_HEAP_SIZE -# define GC_MAXIMUM_HEAP_SIZE 100 * 1024 * 1024 -# define GC_INITIAL_HEAP_SIZE GC_MAXIMUM_HEAP_SIZE / 20 +# define GC_MAXIMUM_HEAP_SIZE (100 * 1024 * 1024) +# define GC_INITIAL_HEAP_SIZE (GC_MAXIMUM_HEAP_SIZE / 20) /* Otherwise heap expansion aborts when deallocating large block. */ /* That's OK. We test this corner case mostly to make sure that */ /* it fails predictably. */ diff --git a/gc/tests/initsecondarythread.c b/gc/tests/initsecondarythread.c index 1ec9c58ea..a2438475a 100644 --- a/gc/tests/initsecondarythread.c +++ b/gc/tests/initsecondarythread.c @@ -41,6 +41,14 @@ #include #include +#define CHECK_OUT_OF_MEMORY(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) + #ifdef GC_PTHREADS static void *thread(void *arg) #else @@ -48,8 +56,8 @@ #endif { GC_INIT(); - (void)GC_MALLOC(123); - (void)GC_MALLOC(12345); + CHECK_OUT_OF_MEMORY(GC_MALLOC(123)); + CHECK_OUT_OF_MEMORY(GC_MALLOC(12345)); # ifdef GC_PTHREADS return arg; # else diff --git a/gc/tests/leak_test.c b/gc/tests/leak_test.c index b20e3a93b..f13d08138 100644 --- a/gc/tests/leak_test.c +++ b/gc/tests/leak_test.c @@ -1,5 +1,17 @@ + +#include +#include + #include "leak_detector.h" +#define CHECK_OUT_OF_MEMORY(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) + int main(void) { char *p[10]; int i; @@ -10,6 +22,8 @@ int main(void) { /* FIXME: This is not ideal. */ for (i = 0; i < 10; ++i) { p[i] = (char*)malloc(sizeof(int)+i); + CHECK_OUT_OF_MEMORY(p[i]); + (void)malloc_usable_size(p[i]); } CHECK_LEAKS(); for (i = 1; i < 10; ++i) { @@ -17,6 +31,7 @@ int main(void) { } for (i = 0; i < 9; ++i) { p[i] = (char*)malloc(sizeof(int)+i); + CHECK_OUT_OF_MEMORY(p[i]); } CHECK_LEAKS(); CHECK_LEAKS(); diff --git a/gc/tests/middle.c b/gc/tests/middle.c index c9f24c4e4..18f90199a 100644 --- a/gc/tests/middle.c +++ b/gc/tests/middle.c @@ -2,8 +2,19 @@ * Test at the boundary between small and large objects. * Inspired by a test case from Zoltan Varga. */ -#include "gc.h" + #include +#include + +#include "gc.h" + +#define CHECK_OUT_OF_MEMORY(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) int main (void) { @@ -15,17 +26,18 @@ int main (void) printf("This test program is not designed for leak detection mode\n"); for (i = 0; i < 20000; ++i) { - (void)GC_malloc_atomic(4096); - (void)GC_malloc(4096); + CHECK_OUT_OF_MEMORY(GC_malloc_atomic(4096)); + CHECK_OUT_OF_MEMORY(GC_malloc(4096)); } /* Test delayed start of marker threads, if they are enabled. */ GC_start_mark_threads(); for (i = 0; i < 20000; ++i) { - (void)GC_malloc_atomic(2048); - (void)GC_malloc(2048); + CHECK_OUT_OF_MEMORY(GC_malloc_atomic(2048)); + CHECK_OUT_OF_MEMORY(GC_malloc(2048)); } + printf("Final heap size is %lu\n", (unsigned long)GC_get_heap_size()); return 0; } diff --git a/gc/tests/realloc_test.c b/gc/tests/realloc_test.c index 8870971b4..c19d7d53d 100644 --- a/gc/tests/realloc_test.c +++ b/gc/tests/realloc_test.c @@ -1,10 +1,19 @@ #include #include + #include "gc.h" #define COUNT 10000000 +#define CHECK_OUT_OF_MEMORY(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) + int main(void) { int i; unsigned long last_heap_size = 0; @@ -15,17 +24,22 @@ int main(void) { for (i = 0; i < COUNT; i++) { int **p = GC_NEW(int *); - int *q = (int*)GC_MALLOC_ATOMIC(sizeof(int)); + int *q; - if (p == 0 || *p != 0) { + CHECK_OUT_OF_MEMORY(p); + q = (int *)GC_MALLOC_ATOMIC(sizeof(int)); + CHECK_OUT_OF_MEMORY(q); + if (*p != NULL) { fprintf(stderr, "GC_malloc returned garbage (or NULL)\n"); exit(1); } - *p = (int*)GC_REALLOC(q, 2 * sizeof(int)); + *p = (int *)GC_REALLOC(q, 2 * sizeof(int)); + CHECK_OUT_OF_MEMORY(*p); if (i % 10 == 0) { unsigned long heap_size = (unsigned long)GC_get_heap_size(); + if (heap_size != last_heap_size) { printf("Heap size: %lu\n", heap_size); last_heap_size = heap_size; diff --git a/gc/tests/smash_test.c b/gc/tests/smash_test.c index 9aab02de6..ed484265c 100644 --- a/gc/tests/smash_test.c +++ b/gc/tests/smash_test.c @@ -7,10 +7,19 @@ #include "gc.h" #include +#include #define COUNT 7000 #define SIZE 40 +#define CHECK_OUT_OF_MEMORY(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) + char * A[COUNT]; char * volatile q; @@ -21,21 +30,20 @@ int main(void) char *p; GC_INIT(); - for (i = 0; i < COUNT; ++i) { - A[i] = p = (char*)GC_MALLOC(SIZE); - - if (i%3000 == 0) { - q = NULL; - GC_gcollect(); - } else if (i%5678 == 0 && p != 0) { - /* Write a byte past the end of the allocated object */ - /* but not beyond the last word of the object's memory. */ - /* A volatile intermediate pointer variable is used to */ - /* avoid a compiler complain of out-of-bounds access. */ - q = &p[(SIZE + i/2000) /* 42 */]; - *q = 42; - } + A[i] = p = (char *)GC_MALLOC(SIZE); + CHECK_OUT_OF_MEMORY(p); + if (i % 3000 == 0) { + q = NULL; + GC_gcollect(); + } else if (i % 5678 == 0) { + /* Write a byte past the end of the allocated object */ + /* but not beyond the last word of the object's memory. */ + /* A volatile intermediate pointer variable is used to */ + /* avoid a compiler complain of out-of-bounds access. */ + q = &p[(SIZE + i / 2000) /* 42 */]; + *q = 42; + } } return 0; } diff --git a/gc/tests/staticrootslib.c b/gc/tests/staticrootslib.c index 5275ec8b4..ad323a665 100644 --- a/gc/tests/staticrootslib.c +++ b/gc/tests/staticrootslib.c @@ -1,6 +1,9 @@ /* This test file is intended to be compiled into a DLL. */ +#include +#include + #ifndef GC_DEBUG # define GC_DEBUG #endif @@ -17,6 +20,14 @@ # endif #endif +#define CHECK_OUT_OF_MEMORY(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) + struct treenode { struct treenode *x; struct treenode *y; @@ -31,21 +42,24 @@ static struct treenode *root_nz[10] = { (struct treenode *)(GC_word)2 }; GC_TEST_EXPORT_API struct treenode * libsrl_mktree(int i) { - struct treenode * r = GC_NEW(struct treenode); + struct treenode *r = GC_NEW(struct treenode); + struct treenode *x, *y; + + CHECK_OUT_OF_MEMORY(r); if (0 == i) return 0; - if (1 == i) + if (1 == i) { r = (struct treenode *)GC_MALLOC_ATOMIC(sizeof(struct treenode)); - if (r) { - struct treenode *x = libsrl_mktree(i - 1); - struct treenode *y = libsrl_mktree(i - 1); - r -> x = x; - r -> y = y; - if (i != 1) { - GC_END_STUBBORN_CHANGE(r); - GC_reachable_here(x); - GC_reachable_here(y); - } + CHECK_OUT_OF_MEMORY(r); + } + x = libsrl_mktree(i - 1); + y = libsrl_mktree(i - 1); + r -> x = x; + r -> y = y; + if (i != 1) { + GC_END_STUBBORN_CHANGE(r); + GC_reachable_here(x); + GC_reachable_here(y); } return r; } diff --git a/gc/tests/subthread_create.c b/gc/tests/subthread_create.c index d1f095358..8fc28c933 100644 --- a/gc/tests/subthread_create.c +++ b/gc/tests/subthread_create.c @@ -127,8 +127,11 @@ int main(void) exit(1); } # else - DWORD thread_id; - th[i] = CreateThread(NULL, 0, entry, 0, 0, &thread_id); + { + DWORD thread_id; + + th[i] = CreateThread(NULL, 0, entry, 0, 0, &thread_id); + } if (th[i] == NULL) { fprintf(stderr, "Thread creation failed, errcode= %d\n", (int)GetLastError()); @@ -140,6 +143,7 @@ int main(void) for (i = 0; i < n; ++i) { # ifdef GC_PTHREADS void *res; + err = pthread_join(th[i], &res); if (err) { fprintf(stderr, "Failed to join thread, error: %s\n", diff --git a/gc/tests/test.c b/gc/tests/test.c index 8afc0fe7a..529ca33d8 100644 --- a/gc/tests/test.c +++ b/gc/tests/test.c @@ -103,7 +103,7 @@ # include # endif -# if ((defined(DARWIN) && defined(MPROTECT_VDB) \ +# if ((defined(DARWIN) && defined(MPROTECT_VDB) && !defined(THREADS) \ && !defined(MAKE_BACK_GRAPH) && !defined(TEST_HANDLE_FORK)) \ || (defined(THREADS) && !defined(CAN_HANDLE_FORK)) \ || defined(HAVE_NO_FORK) || defined(USE_WINALLOC)) \ @@ -200,6 +200,12 @@ exit(1); \ } +static void *checkOOM(void *p) +{ + CHECK_OUT_OF_MEMORY(p); + return p; +} + /* Define AO primitives for a single-threaded mode. */ #ifndef AO_HAVE_compiler_barrier /* AO_t not defined. */ @@ -458,7 +464,7 @@ sexpr reverse1(sexpr x, sexpr y) sexpr reverse(sexpr x) { # ifdef TEST_WITH_SYSTEM_MALLOC - GC_noop1(GC_HIDE_POINTER(malloc(100000))); + GC_noop1(GC_HIDE_POINTER(checkOOM(malloc(100000)))); # endif return( reverse1(x, nil) ); } @@ -764,7 +770,7 @@ void *GC_CALLBACK reverse_test_inner(void *data) e = uncollectable_ints(1, 1); /* Check that realloc updates object descriptors correctly */ AO_fetch_and_add1(&collectable_count); - f = (sexpr *)GC_MALLOC(4 * sizeof(sexpr)); + f = (sexpr *)checkOOM(GC_MALLOC(4 * sizeof(sexpr))); f = (sexpr *)GC_REALLOC((void *)f, 6 * sizeof(sexpr)); CHECK_OUT_OF_MEMORY(f); AO_fetch_and_add1(&realloc_count); @@ -777,7 +783,7 @@ void *GC_CALLBACK reverse_test_inner(void *data) AO_fetch_and_add1(&realloc_count); GC_PTR_STORE_AND_DIRTY(g + 799, ints(1, 18)); AO_fetch_and_add1(&collectable_count); - h = (sexpr *)GC_MALLOC(1025 * sizeof(sexpr)); + h = (sexpr *)checkOOM(GC_MALLOC(1025 * sizeof(sexpr))); h = (sexpr *)GC_REALLOC((void *)h, 2000 * sizeof(sexpr)); CHECK_OUT_OF_MEMORY(h); AO_fetch_and_add1(&realloc_count); @@ -827,7 +833,7 @@ void *GC_CALLBACK reverse_test_inner(void *data) a_set(reverse(reverse(a_get()))); # if !defined(AT_END) && !defined(THREADS) /* This is not thread safe, since realloc explicitly deallocates */ - a_set(GC_REALLOC(a_get(), (i & 1) != 0 ? 500 : 8200)); + a_set(checkOOM(GC_REALLOC(a_get(), (i & 1) != 0 ? 500 : 8200))); AO_fetch_and_add1(&realloc_count); # endif } @@ -915,6 +921,7 @@ tn * mktree(int n) tn * result = GC_NEW(tn); tn * left, * right; + CHECK_OUT_OF_MEMORY(result); AO_fetch_and_add1(&collectable_count); # if defined(MACOS) /* get around static data limitations. */ @@ -925,7 +932,6 @@ tn * mktree(int n) } # endif if (n == 0) return(0); - CHECK_OUT_OF_MEMORY(result); result -> level = n; result -> lchild = left = mktree(n - 1); result -> rchild = right = mktree(n - 1); @@ -1053,56 +1059,71 @@ void chktree(tn *t, int n) FAIL; } if (AO_fetch_and_add1(&extra_count) % 373 == 0) { - (void)GC_MALLOC((unsigned)AO_fetch_and_add1(&extra_count) % 5001); + (void)checkOOM(GC_MALLOC( + (unsigned)AO_fetch_and_add1(&extra_count) % 5001)); AO_fetch_and_add1(&collectable_count); } chktree(t -> lchild, n-1); if (AO_fetch_and_add1(&extra_count) % 73 == 0) { - (void)GC_MALLOC((unsigned)AO_fetch_and_add1(&extra_count) % 373); + (void)checkOOM(GC_MALLOC( + (unsigned)AO_fetch_and_add1(&extra_count) % 373)); AO_fetch_and_add1(&collectable_count); } chktree(t -> rchild, n-1); } -#if defined(GC_PTHREADS) - pthread_key_t fl_key; -#endif +#ifndef VERY_SMALL_CONFIG +# if defined(GC_PTHREADS) + pthread_key_t fl_key; +# endif -void * alloc8bytes(void) -{ -# ifndef GC_PTHREADS - AO_fetch_and_add1(&atomic_count); - return GC_MALLOC_ATOMIC(8); -# elif defined(SMALL_CONFIG) || defined(GC_DEBUG) - AO_fetch_and_add1(&collectable_count); - return(GC_MALLOC(8)); -# else - void ** my_free_list_ptr; - void * my_free_list; - void * next; + void * alloc8bytes(void) + { +# ifndef GC_PTHREADS + AO_fetch_and_add1(&atomic_count); + return GC_MALLOC_ATOMIC(8); +# elif defined(SMALL_CONFIG) || defined(GC_DEBUG) + AO_fetch_and_add1(&collectable_count); + return GC_MALLOC(8); +# else + void ** my_free_list_ptr; + void * my_free_list; + void * next; - my_free_list_ptr = (void **)pthread_getspecific(fl_key); - if (my_free_list_ptr == 0) { + my_free_list_ptr = (void **)pthread_getspecific(fl_key); + if (NULL == my_free_list_ptr) { my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); if (NULL == my_free_list_ptr) return NULL; AO_fetch_and_add1(&uncollectable_count); if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) { - GC_printf("pthread_setspecific failed\n"); - FAIL; + GC_printf("pthread_setspecific failed\n"); + FAIL; } - } - my_free_list = *my_free_list_ptr; - if (my_free_list == 0) { + } + my_free_list = *my_free_list_ptr; + if (NULL == my_free_list) { my_free_list = GC_malloc_many(8); if (NULL == my_free_list) return NULL; + } + next = GC_NEXT(my_free_list); + GC_PTR_STORE_AND_DIRTY(my_free_list_ptr, next); + GC_NEXT(my_free_list) = NULL; + AO_fetch_and_add1(&collectable_count); + return my_free_list; +# endif + } + + void alloc_small(int n) + { + int i; + + for (i = 0; i < n; i += 8) { + void *p = alloc8bytes(); + + CHECK_OUT_OF_MEMORY(p); } - next = GC_NEXT(my_free_list); - GC_PTR_STORE_AND_DIRTY(my_free_list_ptr, next); - GC_NEXT(my_free_list) = 0; - AO_fetch_and_add1(&collectable_count); - return(my_free_list); -# endif -} + } +#endif /* !VERY_SMALL_CONFIG */ #include "gc_inline.h" @@ -1117,19 +1138,11 @@ void test_tinyfl(void) BZERO(tfls, sizeof(tfls)); /* TODO: Improve testing of FAST_MALLOC functionality. */ GC_MALLOC_WORDS(results[0], 11, tfls[0]); + CHECK_OUT_OF_MEMORY(results[0]); GC_MALLOC_ATOMIC_WORDS(results[1], 20, tfls[1]); + CHECK_OUT_OF_MEMORY(results[1]); GC_CONS(results[2], results[0], results[1], tfls[2]); -} - -void alloc_small(int n) -{ - int i; - - for (i = 0; i < n; i += 8) { - void *p = alloc8bytes(); - - CHECK_OUT_OF_MEMORY(p); - } + CHECK_OUT_OF_MEMORY(results[2]); } # if defined(THREADS) && defined(GC_DEBUG) @@ -1204,8 +1217,8 @@ void typed_test(void) GC_descr d2; GC_descr d3 = GC_make_descriptor(bm_large, 32); GC_descr d4 = GC_make_descriptor(bm_huge, 320); - GC_word * x = (GC_word *)GC_MALLOC_EXPLICITLY_TYPED( - 320 * sizeof(GC_word) + 123, d4); + GC_word * x = (GC_word *)checkOOM(GC_MALLOC_EXPLICITLY_TYPED( + 320 * sizeof(GC_word) + 123, d4)); int i; AO_fetch_and_add1(&collectable_count); @@ -1517,16 +1530,16 @@ void run_one_test(void) { size_t i; for (i = 0; i < 10000; ++i) { - (void)GC_MALLOC(0); + (void)checkOOM(GC_MALLOC(0)); AO_fetch_and_add1(&collectable_count); - GC_FREE(GC_MALLOC(0)); - (void)GC_MALLOC_ATOMIC(0); + GC_FREE(checkOOM(GC_MALLOC(0))); + (void)checkOOM(GC_MALLOC_ATOMIC(0)); AO_fetch_and_add1(&atomic_count); - GC_FREE(GC_MALLOC_ATOMIC(0)); + GC_FREE(checkOOM(GC_MALLOC_ATOMIC(0))); test_generic_malloc_or_special(GC_malloc_atomic(1)); AO_fetch_and_add1(&atomic_count); - GC_FREE(GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(1)); - GC_FREE(GC_MALLOC_IGNORE_OFF_PAGE(2)); + GC_FREE(checkOOM(GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(1))); + GC_FREE(checkOOM(GC_MALLOC_IGNORE_OFF_PAGE(2))); } } thr_hndl_sb.gc_thread_handle = GC_get_my_stackbottom(&thr_hndl_sb.sb); @@ -1548,10 +1561,10 @@ void run_one_test(void) (GC_gcollect(),GC_malloc(12)), (void *)0); /* GC_malloc(0) must return NULL or something we can deallocate. */ - GC_free(GC_malloc(0)); - GC_free(GC_malloc_atomic(0)); - GC_free(GC_malloc(0)); - GC_free(GC_malloc_atomic(0)); + GC_free(checkOOM(GC_malloc(0))); + GC_free(checkOOM(GC_malloc_atomic(0))); + GC_free(checkOOM(GC_malloc(0))); + GC_free(checkOOM(GC_malloc_atomic(0))); # ifndef NO_TEST_HANDLE_FORK GC_atfork_prepare(); pid = fork(); @@ -1638,8 +1651,8 @@ void run_one_test(void) # endif /* DBG_HDRS_ALL */ tree_test(); # ifdef TEST_WITH_SYSTEM_MALLOC - free(calloc(1,1)); - free(realloc(NULL, 64)); + free(checkOOM(calloc(1,1))); + free(checkOOM(realloc(NULL, 64))); # endif # ifndef NO_CLOCK if (print_stats) { @@ -1673,7 +1686,7 @@ void run_one_test(void) /* Execute some tests after termination of other test threads (if any). */ void run_single_threaded_test(void) { GC_disable(); - GC_FREE(GC_MALLOC(100)); + GC_FREE(checkOOM(GC_MALLOC(100))); GC_enable(); } @@ -1714,23 +1727,19 @@ void check_heap_stats(void) GC_printf("GC should be initialized!\n"); FAIL; } -# ifdef VERY_SMALL_CONFIG + /* The upper bounds are a guess, which has been empirically */ - /* adjusted. On low end uniprocessors with incremental GC */ + /* adjusted. On low-end uniprocessors with incremental GC */ /* these may be particularly dubious, since empirically the */ /* heap tends to grow largely as a result of the GC not */ /* getting enough cycles. */ -# if CPP_WORDSZ == 64 - max_heap_sz = 4500000; -# else - max_heap_sz = 2800000; -# endif +# if CPP_WORDSZ == 64 + max_heap_sz = 26000000; # else -# if CPP_WORDSZ == 64 - max_heap_sz = 26000000; -# else - max_heap_sz = 16000000; -# endif + max_heap_sz = 16000000; +# endif +# ifdef VERY_SMALL_CONFIG + max_heap_sz /= 4; # endif # ifdef GC_DEBUG max_heap_sz *= 2; @@ -1887,6 +1896,10 @@ void check_heap_stats(void) (void)GC_get_size_map_at(-1); (void)GC_get_size_map_at(1); # endif + if (GC_size(NULL) != 0) { + GC_printf("GC_size(NULL) failed\n"); + FAIL; + } # ifdef NO_CLOCK GC_printf("Completed %u collections\n", (unsigned)GC_get_gc_no()); @@ -2416,10 +2429,12 @@ int main(void) if (GC_get_rate() != 10 || GC_get_max_prior_attempts() != 1) FAIL; GC_set_warn_proc(warn_proc); - if ((code = pthread_key_create(&fl_key, 0)) != 0) { +# ifndef VERY_SMALL_CONFIG + if ((code = pthread_key_create(&fl_key, 0)) != 0) { GC_printf("Key creation failed, errno= %d\n", code); FAIL; - } + } +# endif set_print_procs(); # if NTHREADS > 0 for (i = 0; i < NTHREADS; ++i) { diff --git a/gc/tests/thread_leak_test.c b/gc/tests/thread_leak_test.c index 6902dcd7d..84fd79cd0 100644 --- a/gc/tests/thread_leak_test.c +++ b/gc/tests/thread_leak_test.c @@ -22,6 +22,15 @@ #endif /* !GC_PTHREADS */ #include +#include + +#define CHECK_OUT_OF_MEMORY(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) #ifdef GC_PTHREADS void * test(void * arg) @@ -33,6 +42,7 @@ int i; for (i = 0; i < 10; ++i) { p[i] = (int *)malloc(sizeof(int) + i); + CHECK_OUT_OF_MEMORY(p[i]); } CHECK_LEAKS(); for (i = 1; i < 10; ++i) { diff --git a/gc/tests/threadkey_test.c b/gc/tests/threadkey_test.c index 44df1278a..ac01ccf7e 100644 --- a/gc/tests/threadkey_test.c +++ b/gc/tests/threadkey_test.c @@ -94,7 +94,7 @@ int main(void) if (GC_get_find_leak()) printf("This test program is not designed for leak detection mode\n"); # ifdef GC_SOLARIS_THREADS - pthread_key_create (&key, on_thread_exit); + make_key(); # else pthread_once (&key_once, make_key); # endif diff --git a/gc/tests/trace_test.c b/gc/tests/trace_test.c index d97bbed15..5db68e32a 100644 --- a/gc/tests/trace_test.c +++ b/gc/tests/trace_test.c @@ -8,21 +8,29 @@ #include "gc.h" #include "gc_backptr.h" +#define CHECK_OUT_OF_MEMORY(p) \ + do { \ + if (NULL == (p)) { \ + fprintf(stderr, "Out of memory\n"); \ + exit(69); \ + } \ + } while (0) + struct treenode { struct treenode *x; struct treenode *y; -} * root[10]; +} *root[10]; struct treenode * mktree(int i) { struct treenode * r = GC_NEW(struct treenode); struct treenode *x, *y; + + CHECK_OUT_OF_MEMORY(r); if (0 == i) - return 0; - if (1 == i) + return NULL; + if (1 == i) { r = (struct treenode *)GC_MALLOC_ATOMIC(sizeof(struct treenode)); - if (r == NULL) { - fprintf(stderr, "Out of memory\n"); - exit(1); + CHECK_OUT_OF_MEMORY(r); } x = mktree(i - 1); y = mktree(i - 1); diff --git a/gc/tools/callprocs.sh b/gc/tools/callprocs.sh old mode 100644 new mode 100755 diff --git a/gc/tools/setjmp_t.c b/gc/tools/setjmp_t.c index 3bcf3ff12..b2ae60d5c 100644 --- a/gc/tools/setjmp_t.c +++ b/gc/tools/setjmp_t.c @@ -87,7 +87,8 @@ int main(void) volatile word sp; unsigned ps = GETPAGESIZE(); JMP_BUF b; -# if !defined(__cplusplus) || __cplusplus < 201703L /* before c++17 */ +# if (!defined(__cplusplus) || __cplusplus < 201703L /* before c++17 */) \ + && (!defined(__GNUC__) || defined(__OPTIMIZE__)) register # endif int x = (int)strlen(a_str); /* 1, slightly disguised */ @@ -101,12 +102,12 @@ int main(void) if (nested_sp_fn() < 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)sp + ps) & ~(ps-1)); + ((unsigned long)sp + ps) & ~(unsigned long)(ps-1)); } else { printf("Stack appears to grow up.\n"); printf("Define STACK_GROWS_UP in gc_priv.h\n"); printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", - ((unsigned long)sp + ps) & ~(ps-1)); + (unsigned long)sp & ~(unsigned long)(ps-1)); /* round down */ } printf("Note that this may vary between machines of ostensibly\n"); printf("the same architecture (e.g. Sun 3/50s and 3/80s).\n"); diff --git a/gc/typd_mlc.c b/gc/typd_mlc.c index 741598a0d..c5b8ae0e8 100644 --- a/gc/typd_mlc.c +++ b/gc/typd_mlc.c @@ -169,7 +169,7 @@ STATIC GC_descr GC_double_descr(GC_descr descriptor, word nwords) if ((descriptor & GC_DS_TAGS) == GC_DS_LENGTH) { descriptor = GC_bm_table[BYTES_TO_WORDS((word)descriptor)]; } - descriptor |= (descriptor & ~GC_DS_TAGS) >> nwords; + descriptor |= (descriptor & ~(GC_descr)GC_DS_TAGS) >> nwords; return(descriptor); } @@ -577,7 +577,7 @@ GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * bm, size_t len) if (index == -1) return(WORDS_TO_BYTES(last_set_bit+1) | GC_DS_LENGTH); /* Out of memory: use conservative */ /* approximation. */ - result = GC_MAKE_PROC(GC_typed_mark_proc_index, (word)index); + result = GC_MAKE_PROC(GC_typed_mark_proc_index, index); } return result; } @@ -598,8 +598,6 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_explicitly_typed(size_t lb, /* the former might be updated asynchronously. */ lg = BYTES_TO_GRANULES(GC_size(op)); set_obj_descr(op, GRANULES_TO_WORDS(lg), d); - GC_dirty(op + GRANULES_TO_WORDS(lg) - 1); - REACHABLE_AFTER_DIRTY(d); return op; } @@ -644,8 +642,6 @@ GC_API GC_ATTR_MALLOC void * GC_CALL lg = BYTES_TO_GRANULES(GC_size(op)); } set_obj_descr(op, GRANULES_TO_WORDS(lg), d); - GC_dirty((word *)op + GRANULES_TO_WORDS(lg) - 1); - REACHABLE_AFTER_DIRTY(d); return op; } diff --git a/gc/win32_threads.c b/gc/win32_threads.c index f649f03a9..99d3613c8 100644 --- a/gc/win32_threads.c +++ b/gc/win32_threads.c @@ -83,7 +83,16 @@ static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext); # else # define PUSHED_REGS_COUNT 7 # endif -#elif defined(X86_64) || defined(SHx) +#elif defined(X86_64) +# ifdef XMM_CANT_STORE_PTRS + /* If pointers can't be located in Xmm registers. */ +# define PUSHED_REGS_COUNT 15 +# else + /* gcc-13 may store pointers into SIMD registers when */ + /* certain compiler optimizations are enabled. */ +# define PUSHED_REGS_COUNT (15+32) +# endif +#elif defined(SHx) # define PUSHED_REGS_COUNT 15 #elif defined(ARM32) # define PUSHED_REGS_COUNT 13 @@ -1266,7 +1275,7 @@ void GC_push_thread_structures(void) } else # endif /* else */ { - GC_PUSH_ALL_SYM(GC_threads); + GC_push_all(&GC_threads, (ptr_t)(&GC_threads) + sizeof(GC_threads)); } # if defined(THREAD_LOCAL_ALLOC) && defined(USE_CUSTOM_SPECIFIC) GC_PUSH_ALL_SYM(GC_thread_key); @@ -1285,9 +1294,12 @@ void GC_push_thread_structures(void) ? CONTEXT_INTEGER | CONTEXT_CONTROL \ | CONTEXT_EXCEPTION_REQUEST | CONTEXT_SEGMENTS \ : CONTEXT_INTEGER | CONTEXT_CONTROL) -#else +#elif defined(I386) || defined(XMM_CANT_STORE_PTRS) # define GET_THREAD_CONTEXT_FLAGS (CONTEXT_INTEGER | CONTEXT_CONTROL) -#endif /* !WOW64_THREAD_CONTEXT_WORKAROUND */ +#else +# define GET_THREAD_CONTEXT_FLAGS (CONTEXT_INTEGER | CONTEXT_CONTROL \ + | CONTEXT_FLOATING_POINT) +#endif /* !WOW64_THREAD_CONTEXT_WORKAROUND && !I386 */ /* Suspend the given thread, if it's still active. */ STATIC void GC_suspend(GC_thread t) @@ -1581,6 +1593,8 @@ static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext) { # define PUSH1(reg) (regs[cnt++] = (word)pcontext->reg) # define PUSH2(r1,r2) (PUSH1(r1), PUSH1(r2)) # define PUSH4(r1,r2,r3,r4) (PUSH2(r1,r2), PUSH2(r3,r4)) +# define PUSH8_LH(r1,r2,r3,r4) (PUSH4(r1.Low,r1.High,r2.Low,r2.High), \ + PUSH4(r3.Low,r3.High,r4.Low,r4.High)) # if defined(I386) # ifdef WOW64_THREAD_CONTEXT_WORKAROUND PUSH2(ContextFlags, SegFs); /* cannot contain pointers */ @@ -1590,6 +1604,12 @@ static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext) { # elif defined(X86_64) PUSH4(Rax,Rcx,Rdx,Rbx); PUSH2(Rbp, Rsi); PUSH1(Rdi); PUSH4(R8, R9, R10, R11); PUSH4(R12, R13, R14, R15); +# ifndef XMM_CANT_STORE_PTRS + PUSH8_LH(Xmm0, Xmm1, Xmm2, Xmm3); + PUSH8_LH(Xmm4, Xmm5, Xmm6, Xmm7); + PUSH8_LH(Xmm8, Xmm9, Xmm10, Xmm11); + PUSH8_LH(Xmm12, Xmm13, Xmm14, Xmm15); +# endif sp = (ptr_t)context.Rsp; # elif defined(ARM32) PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11); @@ -2015,16 +2035,6 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, #ifdef PARALLEL_MARK -# if defined(GC_PTHREADS) && !defined(GC_PTHREADS_PARAMARK) - /* Use pthread-based parallel mark implementation. */ - - /* Workaround a deadlock in winpthreads-3.0b internals (observed */ - /* with MinGW 32/64). */ -# if !defined(__MINGW32__) -# define GC_PTHREADS_PARAMARK -# endif -# endif - # if !defined(GC_PTHREADS_PARAMARK) STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1] = {0}; /* Events with manual reset (one for each */ From 8405b6616b07cbe856bd409e2d8389581b590b71 Mon Sep 17 00:00:00 2001 From: John Bowman Date: Tue, 24 Dec 2024 09:39:09 -0800 Subject: [PATCH 15/24] GC: Update GC to latest stable release. --- Makefile.in | 1 - configure.ac | 5 ----- 2 files changed, 6 deletions(-) diff --git a/Makefile.in b/Makefile.in index 7b221647a..4a81a2d30 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,7 +4,6 @@ ARCH = unix POLL = poll ASYGLVERSION = @ASYGLVERSION@ -GCVERSION = @GCVERSION@ GC = gc LIBATOMIC = libatomic_ops GCOPTIONS = @GCOPTIONS@ diff --git a/configure.ac b/configure.ac index 4bfdb569b..41efd9681 100644 --- a/configure.ac +++ b/configure.ac @@ -271,9 +271,6 @@ AC_ARG_WITH(vcpkg-host-triplet, # --------------- GC ------------------------------- -ATOMICVERSION=7.6.12 -GCVERSION=8.2.4 -GCFILE=gc-$GCVERSION ac_cv_use_gc="yes" AC_ARG_ENABLE(gc, @@ -743,9 +740,7 @@ CPPFLAGS=$CPPFLAGS" $INCL" CXX_STANDARD=$cxxstd AC_SUBST(getopt) -AC_SUBST(GCVERSION) AC_SUBST(ASYGLVERSION) -AC_SUBST(ATOMICVERSION) AC_SUBST(GCOPTIONS) AC_SUBST(GCLIB) AC_SUBST(GCPPLIB) From 0916cfcb4fb4c3dc8a6a902898b4fb8be2e9b269 Mon Sep 17 00:00:00 2001 From: John Bowman Date: Wed, 25 Dec 2024 15:35:18 -0800 Subject: [PATCH 16/24] Remove obsolete configuration code. --- configure.ac | 132 ++++++++++++++++++++++----------------------------- settings.cc | 2 +- 2 files changed, 57 insertions(+), 77 deletions(-) diff --git a/configure.ac b/configure.ac index 41efd9681..0f75a44b6 100644 --- a/configure.ac +++ b/configure.ac @@ -270,62 +270,6 @@ AC_ARG_WITH(vcpkg-host-triplet, ) -# --------------- GC ------------------------------- -ac_cv_use_gc="yes" - -AC_ARG_ENABLE(gc, - [AS_HELP_STRING(--enable-gc[[[=yes]]], enable system Boehm garbage collector)], - [ # if true - if test "x$enableval" != "xyes" ; then # system, no or else - ac_cv_use_gc=$enableval - fi - ] -) - -AC_ARG_ENABLE(gc-debug, -[AS_HELP_STRING(--enable-gc-debug,enable (slow) garbage collector debugging)], -[ if test "x$ac_cv_use_gc" != "xno" ; then - if test "x$enableval" = "xyes" ; then - AC_DEFINE(GC_DEBUG,1,[GC Debug is enabled]) - AC_MSG_NOTICE([*** Enabling GC debugging: remember to make clean ***]) - AC_MSG_NOTICE([*** Set the environment variable GC_FIND_LEAK at runtime ***]) - fi - fi -]) - -AC_ARG_ENABLE(gc-full-debug, -[AS_HELP_STRING(--enable-gc-full-debug,enable (very slow) garbage collector backtrace)], -[ if test "x$ac_cv_use_gc" != "xno" ; then - if test "x$enableval" = "xyes" ; then - AC_DEFINE(GC_DEBUG,1,[GC Debug is enabled]) - AC_DEFINE(GC_BACKTRACE,1,[GC backtrace is enabled]) - GCOPTIONS=$GCOPTIONS"--enable-gc-debug " - AC_MSG_NOTICE([*** Enabling GC backtrace debugging; remember to make gc-clean ***]) - fi - fi -]) - - -GCNAME="Boehm Garbage Collector" -if test "x$ac_cv_use_gc" != "xno" ; then - AC_DEFINE(USEGC,1,[GC Enabled]) - case _$ac_cv_use_gc in - _|_system|_*[[\\/]]*) - AC_MSG_ERROR([*** System gc is not supported ***]) - # for some reason, if this line is removed, configure produces an error - AC_CHECK_HEADER(_.h,AC_CHECK_LIB([_],[__],[],[]),) - ;; - *) - AC_MSG_NOTICE([enabling local gc]) - GCLIB="\$(GC)/.libs/libgc.a" - INCL=$INCL" -I\$(GC)/include " - ;; - esac -else - AC_MSG_NOTICE([disabling the $GCNAME]) -fi - -# -------------------------- end gc ------------------ # Checks for libraries. #AC_SEARCH_LIBS([lgamma],[m c],, @@ -419,29 +363,12 @@ if test "x$enable_lsp" != "xno" -a "x$enable_threads" != "xno"; then AC_MSG_NOTICE([*** Could not find libboost_filesystem: will compile without optional Language Server Protocol. ***])) fi -# ------------- arguments for readline & static linking --- - AC_ARG_ENABLE(readline, [AS_HELP_STRING(--enable-readline[[[=yes]]],enable GNU Readline Library)]) AC_ARG_ENABLE(static, [AS_HELP_STRING(--enable-static[[[=no]]],link against static libraries)]) -STATIC="" -DYNAMIC="" -if test "x$enable_static" = "xyes"; then - STATIC="-Wl,-Bstatic " - DYNAMIC="-Wl,-Bdynamic " -fi - -# ------------- readline & defines -------------------- - -AC_DEFUN([READLINE],[ -AC_MSG_NOTICE([*** Could not find GNU readline 4.3 or later: will compile without readline support ***]) -AC_CHECK_LIB([edit],[readline]) -AC_CHECK_HEADERS(editline/readline.h) -]) - AC_DEFUN([PKG_CONFIG],[ ifdef( [PKG_CHECK_MODULES], @@ -450,6 +377,13 @@ ifdef( ) ]) +STATIC="" +DYNAMIC="" +if test "x$enable_static" = "xyes"; then + STATIC="-Wl,-Bstatic " + DYNAMIC="-Wl,-Bdynamic " +fi + AC_DEFUN([CHECK_LIB_STATIC],[ PKG_CONFIG(PKG_FLAGS,,--libs,$1) if test "x$PKG_FLAGS" == "x "; then @@ -487,7 +421,6 @@ AC_DEFUN([CHECK_LIB_STATIC],[ $PKG_FLAGS ) ]) -# --------------- end readline ---------------- if test "x$enable_readline" != "xno"; then if test "x$with_vcpkg" != "xno"; then # vcpkg @@ -510,8 +443,14 @@ else # managed by the system abort #endif ])], - CHECK_LIB_STATIC(readline,readline,history_list,HAVE_LIBREADLINE,READLINE), - READLINE) + CHECK_LIB_STATIC(readline,readline,history_list,HAVE_LIBREADLINE, + readline="no"),readline="no") + + if test "x$readline" == "xno"; then + AC_MSG_NOTICE([*** Could not find GNU readline 4.3 or later: will compile without readline support ***]) + AC_CHECK_LIB([edit],[readline]) + AC_CHECK_HEADERS(editline/readline.h) + fi PKG_CONFIG(CPPFLAGS,$CPPFLAGS,--cflags,tinfo) CHECK_LIB_STATIC(tinfo,tinfo,tgetent,HAVE_LIBTINFO,AC_MSG_NOTICE([perhaps tgetent is in -lncurses])) @@ -523,6 +462,47 @@ else # managed by the system fi fi +# --------------- gc ------------------------------- + +GCNAME="Boehm Garbage Collector" + +AC_ARG_ENABLE(gc, +[AS_HELP_STRING(--enable-gc[[[=yes]]],enable local $GCNAME)]) + +if test "x$enable_gc" != "xno" ; then + if test "x$with_vcpkg" == "xno"; then + AC_DEFINE(USEGC,1,[GC Enabled]) + GCLIB="\$(GC)/.libs/libgc.a" + INCL=$INCL" -I\$(GC)/include " + AC_MSG_NOTICE([$GCNAME is enabled]) + fi +else + AC_MSG_NOTICE([*** $GCNAME disabled by configure flag: will compile without garbage collection. ***]) +fi + +AC_ARG_ENABLE(gc-debug, +[AS_HELP_STRING(--enable-gc-debug,enable (slow) garbage collector debugging)], +[ if test "x$enable_gc" != "xno" ; then + if test "x$enableval" = "xyes" ; then + AC_DEFINE(GC_DEBUG,1,[GC Debug is enabled]) + AC_MSG_NOTICE([*** Enabling GC debugging: remember to make clean ***]) + AC_MSG_NOTICE([*** Set the environment variable GC_FIND_LEAK at runtime ***]) + fi + fi +]) + +AC_ARG_ENABLE(gc-full-debug, +[AS_HELP_STRING(--enable-gc-full-debug,enable (very slow) garbage collector backtrace)], +[ if test "x$enable_gc" != "xno" ; then + if test "x$enableval" = "xyes" ; then + AC_DEFINE(GC_DEBUG,1,[GC Debug is enabled]) + AC_DEFINE(GC_BACKTRACE,1,[GC backtrace is enabled]) + GCOPTIONS=$GCOPTIONS"--enable-gc-debug " + AC_MSG_NOTICE([*** Enabling GC backtrace debugging; remember to make gc-clean ***]) + fi + fi +]) + # ---------------- curl ------------------------ AC_ARG_ENABLE(curl, diff --git a/settings.cc b/settings.cc index e227b220f..19173c809 100644 --- a/settings.cc +++ b/settings.cc @@ -1159,7 +1159,7 @@ struct versionOption : public option { feature("LSP Language Server Protocol",lsp); feature("Readline Interactive history and editing",readline); if(!readline) - feature("Editline interactive editing (if Readline is unavailable)",editline); + feature("Editline interactive editing (Readline is unavailable)",editline); feature("Sigsegv Distinguish stack overflows from segmentation faults", sigsegv); feature("GC Boehm garbage collector",usegc); From 91d4d8d76f919d9729ff7cb9ef80be42b58c7f2d Mon Sep 17 00:00:00 2001 From: John Bowman Date: Wed, 25 Dec 2024 16:00:34 -0800 Subject: [PATCH 17/24] Update autoconf version. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 0f75a44b6..83f61e631 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Run autoheader and autoconf to produce a header and configure script from # this file. -AC_PREREQ(2) +AC_PREREQ([2.71]) AC_INIT([Asymptote],[2.96git],[https://github.com/vectorgraphics/asymptote/issues]) VERSION=$PACKAGE_VERSION AC_SUBST(VERSION) From 468f63ac40e191951d23523b040a5f5b9f3b3cf0 Mon Sep 17 00:00:00 2001 From: John Bowman Date: Wed, 25 Dec 2024 16:17:47 -0800 Subject: [PATCH 18/24] Remove obsolete code. --- Makefile.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 4a81a2d30..a6b7ddfb1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -5,7 +5,6 @@ POLL = poll ASYGLVERSION = @ASYGLVERSION@ GC = gc -LIBATOMIC = libatomic_ops GCOPTIONS = @GCOPTIONS@ GCLIB = @GCLIB@ GCPPLIB = @GCPPLIB@ @@ -175,7 +174,7 @@ $(LSP_BUILD_ROOT)/liblspcpp.a: all: asy sty man faq asy-keywords.el -$(GC)/libatomic_ops: $(LIBATOMIC) +$(GC)/libatomic_ops: ln -sf ../$< $@ $(GC)/Makefile: $(GC)/libatomic_ops From 3bcf5fdd5788000ff1c86cdb671afce5037fb376 Mon Sep 17 00:00:00 2001 From: John Bowman Date: Wed, 25 Dec 2024 19:36:48 -0800 Subject: [PATCH 19/24] GC: Update configure.ac. --- gc/configure.ac | 79 +++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 48 deletions(-) diff --git a/gc/configure.ac b/gc/configure.ac index c8deb0840..3d30c9c13 100644 --- a/gc/configure.ac +++ b/gc/configure.ac @@ -14,13 +14,13 @@ dnl Process this file with autoconf to produce configure. dnl Initialization. -AC_INIT(gc,8.2.8,https://github.com/ivmai/bdwgc/issues) +AC_INIT([gc],[8.2.8],[https://github.com/ivmai/bdwgc/issues]) dnl Version must conform to: [0-9]+[.][0-9]+[.][0-9]+ AC_CONFIG_SRCDIR(gcj_mlc.c) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_TARGET -AC_PREREQ(2.61) +AC_PREREQ([2.71]) GC_SET_VERSION AM_INIT_AUTOMAKE([foreign nostdinc subdir-objects]) AC_CONFIG_HEADERS([include/config.h]) @@ -37,7 +37,7 @@ LT_INIT([disable-static]) # Only the shared libraries are produced by default, use "--enable-static" # option to override it. dnl Note: If Autoconf reports that LIBTOOL (or AC_ENABLE_SHARED, or -dnl AC_PROG_LIBTOOL) is undefined, Libtool installation should be checked. +dnl LT_INIT) is undefined, Libtool installation should be checked. # Special CFLAGS to use when building gc_cflags="" @@ -392,15 +392,15 @@ if test "$GCC" = yes; then AC_MSG_CHECKING([whether compiler supports -Wextra]) old_CFLAGS="$CFLAGS" CFLAGS="-Wextra $CFLAGS" - AC_TRY_COMPILE([],[], [ac_cv_cc_wextra=yes], [ac_cv_cc_wextra=no]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[ac_cv_cc_wextra=yes],[ac_cv_cc_wextra=no]) CFLAGS="$old_CFLAGS" AC_MSG_RESULT($ac_cv_cc_wextra) AS_IF([test "$ac_cv_cc_wextra" = yes], [WEXTRA="-Wextra"], [WEXTRA="-W"]) AC_MSG_CHECKING([whether compiler supports -Wpedantic]) CFLAGS="-Wpedantic -Wno-long-long $CFLAGS" - AC_TRY_COMPILE([],[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ extern int quiet; - ], [ac_cv_cc_pedantic=yes], [ac_cv_cc_pedantic=no]) + ]])],[ac_cv_cc_pedantic=yes],[ac_cv_cc_pedantic=no]) CFLAGS="$old_CFLAGS" AC_MSG_RESULT($ac_cv_cc_pedantic) WPEDANTIC= @@ -411,11 +411,11 @@ if test "$GCC" = yes; then fi AC_MSG_CHECKING(for xlc) -AC_TRY_COMPILE([],[ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ #ifndef __xlC__ # error #endif -], [compiler_xlc=yes], [compiler_xlc=no]) +]])],[compiler_xlc=yes],[compiler_xlc=no]) AC_MSG_RESULT($compiler_xlc) if test $compiler_xlc = yes -a "$powerpc_darwin" = true; then # the darwin stack-frame-walking code is completely broken on xlc @@ -443,7 +443,7 @@ if test "$GCC" = yes; then if test "$ac_cv_fno_strict_aliasing" != skipped; then old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fno-strict-aliasing" - AC_TRY_COMPILE([],[], [ac_cv_fno_strict_aliasing=yes], []) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[ac_cv_fno_strict_aliasing=yes],[]) CFLAGS="$old_CFLAGS" AS_IF([test "$ac_cv_fno_strict_aliasing" = yes], [CFLAGS="$CFLAGS -fno-strict-aliasing"], []) @@ -575,8 +575,7 @@ if test "${enable_shared}" != no -a "${enable_static}" != yes; then AC_MSG_CHECKING([whether compiler supports -fvisibility]) old_CFLAGS="$CFLAGS" CFLAGS="-Werror -fvisibility=hidden $CFLAGS" - AC_TRY_COMPILE([],[], [ac_cv_fvisibility_hidden=yes], - [ac_cv_fvisibility_hidden=no]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[ac_cv_fvisibility_hidden=yes],[ac_cv_fvisibility_hidden=no]) CFLAGS="$old_CFLAGS" AS_IF([test "$ac_cv_fvisibility_hidden" = yes], [CFLAGS="-DGC_VISIBILITY_HIDDEN_SET -fvisibility=hidden $CFLAGS"], @@ -641,7 +640,7 @@ addobjs="$addobjs $machdep" AC_SUBST(addobjs) AC_SUBST(addlibs) -AC_PROG_LIBTOOL +LT_INIT # Suppress "extension used" clang warning (when compiling .S files). if test x$compile_asm = xtrue -a "$GCC" = yes; then @@ -797,9 +796,9 @@ AC_MSG_CHECKING([whether -Wno-frame-address works]) use_wno_error_frame_address=no old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Werror -Wno-frame-address $CFLAGS_EXTRA" -AC_TRY_COMPILE([], [{ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[{ if (!__builtin_return_address(1)) return 1; -}], [ use_wno_error_frame_address=yes ]) +}]])],[ use_wno_error_frame_address=yes ],[]) CFLAGS="$old_CFLAGS" AC_MSG_RESULT($use_wno_error_frame_address) if test x"$use_wno_error_frame_address" = xyes; then @@ -811,12 +810,12 @@ AC_MSG_CHECKING(for dladdr) have_dladdr=no old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $CFLAGS_EXTRA" -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #define _GNU_SOURCE 1 -#include ], [{ +#include ]], [[{ Dl_info info; (void)dladdr("", &info); -}], [ have_dladdr=yes ]) +}]])],[ have_dladdr=yes ],[]) CFLAGS="$old_CFLAGS" AC_MSG_RESULT($have_dladdr) if test x"$have_dladdr" = xyes; then @@ -827,10 +826,7 @@ fi AC_MSG_CHECKING(for sigsetjmp) old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $CFLAGS_EXTRA" -AC_TRY_LINK([#include ], - [sigjmp_buf t; sigsetjmp(t, 0)], - [AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[sigjmp_buf t; sigsetjmp(t, 0)]])],[AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) AC_DEFINE([GC_NO_SIGSETJMP], [1], [Missing sigsetjmp function.])]) CFLAGS="$old_CFLAGS" @@ -839,25 +835,24 @@ AS_IF([test "$THREADS" = posix], [AC_MSG_CHECKING(for pthread_setname_np) old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $CFLAGS_EXTRA -Werror" - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ # define _GNU_SOURCE 1 # include - ], [pthread_setname_np("thread-name")], + ], [pthread_setname_np("thread-name")])], [AC_MSG_RESULT([yes (w/o tid)]) AC_DEFINE([HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID], [1], - [Define to use 'pthread_setname_np(const char*)' function.])], - [AC_TRY_COMPILE([ + [Define to use 'pthread_setname_np(const char*)' function.])], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ # define _GNU_SOURCE 1 # include - ], [pthread_setname_np(pthread_self(), "thread-name-%u", 0)], + ], [pthread_setname_np(pthread_self(), "thread-name-%u", 0)])], [AC_MSG_RESULT([yes (with tid and arg)]) AC_DEFINE([HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG], [1], [Define to use 'pthread_setname_np(pthread_t, const char*, void *)' function.])], - [AC_TRY_COMPILE([ + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ # define _GNU_SOURCE 1 # include - ], [pthread_setname_np(pthread_self(), "thread-name")], + ], [pthread_setname_np(pthread_self(), "thread-name")])], [AC_MSG_RESULT([yes (with tid)]) AC_DEFINE([HAVE_PTHREAD_SETNAME_NP_WITH_TID], [1], [Define to use 'pthread_setname_np(pthread_t, const char*)' @@ -1086,10 +1081,8 @@ if test x"$with_libatomic_ops" = xcheck; then old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $CFLAGS_EXTRA -DGC_BUILTIN_ATOMIC" CFLAGS="$CFLAGS -I${srcdir}/include -I${srcdir}/tests" - AC_TRY_RUN([#include "test_atomic_ops.c"], - [AC_MSG_RESULT(yes) - with_libatomic_ops=none], - [AC_MSG_RESULT(no)], [AC_MSG_RESULT(skipped because cross-compiling)]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include "test_atomic_ops.c"]])],[AC_MSG_RESULT(yes) + with_libatomic_ops=none],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(skipped because cross-compiling)]) CFLAGS="$old_CFLAGS" fi @@ -1156,20 +1149,14 @@ AS_IF([test x$with_libatomic_ops != xnone -a x$need_atomic_ops_asm != xtrue], [ old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $AO_TRYLINK_CFLAGS $CFLAGS_EXTRA" AC_MSG_CHECKING([for lock-free AO_or primitive]) - AC_TRY_LINK([#include "atomic_ops.h"], - [AO_t x=0;AO_or(&x,1)], - [ AC_MSG_RESULT(yes) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "atomic_ops.h"]], [[AO_t x=0;AO_or(&x,1)]])],[ AC_MSG_RESULT(yes) AC_DEFINE([HAVE_LOCKFREE_AO_OR], [1], - [libatomic_ops AO_or primitive implementation is lock-free.]) ], - [ AC_MSG_RESULT(no) ]) + [libatomic_ops AO_or primitive implementation is lock-free.]) ],[ AC_MSG_RESULT(no) ]) AC_MSG_CHECKING([for lock-free AO load/store, test-and-set primitives]) - AC_TRY_LINK([#include "atomic_ops.h"], - [AO_t x=0;unsigned char c=0;AO_TS_t z=AO_TS_INITIALIZER; + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "atomic_ops.h"]], [[AO_t x=0;unsigned char c=0;AO_TS_t z=AO_TS_INITIALIZER; (void)AO_test_and_set_acquire(&z);AO_CLEAR(&z);AO_compiler_barrier(); AO_store(&x,AO_load(&x)+1);AO_char_store(&c,AO_char_load(&c)+1); - AO_store_release(&x,AO_load_acquire(&x)+1)], - [ AC_MSG_RESULT(yes) ], - [ AC_MSG_RESULT(no) + AO_store_release(&x,AO_load_acquire(&x)+1)]])],[ AC_MSG_RESULT(yes) ],[ AC_MSG_RESULT(no) use_thread_local_alloc=no AC_DEFINE([BASE_ATOMIC_OPS_EMULATED], [1], [AO load, store and/or test-and-set primitives are @@ -1177,12 +1164,8 @@ AS_IF([test x$with_libatomic_ops != xnone -a x$need_atomic_ops_asm != xtrue], AS_IF([test x$use_parallel_mark != xno], [ AC_MSG_CHECKING( [for lock-free compare-and-swap and fetch-and-add primitives]) - AC_TRY_LINK( - [#define AO_REQUIRE_CAS - #include "atomic_ops.h"], - [AO_t x=0;(void)AO_fetch_and_add(&x,1);(void)AO_compare_and_swap(&x,1,2)], - [ AC_MSG_RESULT(yes) ], - [ AC_MSG_RESULT(no) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define AO_REQUIRE_CAS + #include "atomic_ops.h"]], [[AO_t x=0;(void)AO_fetch_and_add(&x,1);(void)AO_compare_and_swap(&x,1,2)]])],[ AC_MSG_RESULT(yes) ],[ AC_MSG_RESULT(no) use_parallel_mark=no ]) ]) CFLAGS="$old_CFLAGS" ]) From 637ba8f6cf939e1367e577108409c6cd3a85ae5e Mon Sep 17 00:00:00 2001 From: John Bowman Date: Wed, 25 Dec 2024 22:22:45 -0800 Subject: [PATCH 20/24] Quote ENABLE_LSP_MACRO. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 83f61e631..416b7ae9a 100644 --- a/configure.ac +++ b/configure.ac @@ -346,7 +346,7 @@ if test "x$enable_lsp" != "xno" -a "x$enable_threads" != "xno"; then fi fi - AC_DEFUN(ENABLE_LSP_MACRO, + AC_DEFUN([ENABLE_LSP_MACRO], AC_SUBST(LSP_CXX_BUILD_FLAGS) AC_SUBST(LSP_CMAKE_OPTIONS) AC_SUBST(LSP_BUILD_ROOT) From 7fbc53c5807e6836260e6cc0a73a73f5227e7cc6 Mon Sep 17 00:00:00 2001 From: John Bowman Date: Thu, 26 Dec 2024 20:54:22 -0800 Subject: [PATCH 21/24] Check for libtoolize. --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index 416b7ae9a..d7bb57d1a 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,9 @@ AC_SUBST(VERSION) m4_include([ax_pthread.m4]) m4_include([pkg.m4]) +AC_CHECK_PROG([LIBTOOLIZE], [libtoolize], [yes], [no]) +AS_IF([test "x$LIBTOOLIZE" = "xno"], [AC_MSG_ERROR([libtoolize not found])]) + AH_TOP([#pragma once]) AC_C_BIGENDIAN( From 9a22de4ac344b69fd8e8ccfe1462db662d3b2f52 Mon Sep 17 00:00:00 2001 From: John Bowman Date: Fri, 27 Dec 2024 20:30:18 -0800 Subject: [PATCH 22/24] Remove extra spaces; fix gc compilation; require libtool instead of libtoolize. --- Makefile.in | 11 +++-------- configure.ac | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Makefile.in b/Makefile.in index a6b7ddfb1..d57ed2106 100644 --- a/Makefile.in +++ b/Makefile.in @@ -90,7 +90,7 @@ DEFS = @DEFS@ @OPTIONS@ @PTHREAD_CFLAGS@ -DFFTWPP_SINGLE_THREAD -Wall CFLAGS = @CFLAGS@ OPTS = $(DEFS) @CPPFLAGS@ @CXXFLAGS@ $(CFLAGS) \ -Ibackports/optional/include \ - -Iprc/include -I$(LSP_ROOT)/include -I. + -Iprc/include -I$(LSP_ROOT)/include GLEWOPTS = $(DEFS) @CPPFLAGS@ $(CFLAGS) -DGLEW_NO_GLU -DGLEW_BUILD -O1 -fPIC # Options for compiling the object files for the shared library. @@ -174,14 +174,9 @@ $(LSP_BUILD_ROOT)/liblspcpp.a: all: asy sty man faq asy-keywords.el -$(GC)/libatomic_ops: - ln -sf ../$< $@ - -$(GC)/Makefile: $(GC)/libatomic_ops - cd $(GC) && ./autogen.sh +$(GCLIB): + ln -sf ../libatomic_ops libatomic_ops cd $(GC) && ./configure CC="$(CC)" CXX="$(CXX)" $(GCOPTIONS) - -$(GCLIB): $(GC)/Makefile $(GC)/libatomic_ops $(MAKE) -C $(GC) check sty: doc/version.texi diff --git a/configure.ac b/configure.ac index d7bb57d1a..e97cab205 100644 --- a/configure.ac +++ b/configure.ac @@ -9,8 +9,8 @@ AC_SUBST(VERSION) m4_include([ax_pthread.m4]) m4_include([pkg.m4]) -AC_CHECK_PROG([LIBTOOLIZE], [libtoolize], [yes], [no]) -AS_IF([test "x$LIBTOOLIZE" = "xno"], [AC_MSG_ERROR([libtoolize not found])]) +AC_CHECK_PROG([LIBTOOL], [libtool], [yes], [no]) +AS_IF([test "x$LIBTOOL" = "xno"], [AC_MSG_ERROR([libtool not found])]) AH_TOP([#pragma once]) @@ -250,8 +250,8 @@ AC_ARG_WITH(vcpkg-target-triplet, if test "x$with_vcpkg" != "xno"; then VCPKG_INSTALL_ARGUMENTS="--triplet=$vcpkg_triplet " VCPKG_PKG_CONFIG_LOC="vcpkg_installed/$vcpkg_triplet/lib/pkgconfig" - INCL=$INCL" -Ivcpkg_installed/$vcpkg_triplet/include " - LIBS=$LIBS" -Lvcpkg_installed/$vcpkg_triplet/lib " + INCL=$INCL" -Ivcpkg_installed/$vcpkg_triplet/include" + LIBS=$LIBS"-Lvcpkg_installed/$vcpkg_triplet/lib " VCPKG_LIBS_TO_USE_PKG_CONFIG="" AC_DEFUN([ADD_VCPKG_LIB_FOR_PKGCONFIG],[VCPKG_LIBS_TO_USE_PKG_CONFIG=$VCPKG_LIBS_TO_USE_PKG_CONFIG"$1 "]) @@ -476,7 +476,7 @@ if test "x$enable_gc" != "xno" ; then if test "x$with_vcpkg" == "xno"; then AC_DEFINE(USEGC,1,[GC Enabled]) GCLIB="\$(GC)/.libs/libgc.a" - INCL=$INCL" -I\$(GC)/include " + INCL=$INCL" -I\$(GC)/include" AC_MSG_NOTICE([$GCNAME is enabled]) fi else @@ -636,7 +636,7 @@ else fi TINYEXR_ROOT=tinyexr -INCL=$INCL"-I$TINYEXR_ROOT " +INCL=$INCL" -I$TINYEXR_ROOT" AC_SUBST(TINYEXR_ROOT) if test "x$enable_gl" != "xno"; then @@ -645,7 +645,7 @@ if test "x$with_vcpkg" != "xno"; then # managed by vcpkg AC_DEFINE(HAVE_LIBGL,1,[libgl is enabled]) AC_DEFINE(FREEGLUT,1,[Freeglut is enabled]) AC_DEFINE(HAVE_LIBGLUT,1,[Freeglut library is available]) - INCL=$INCL" -Ibackports/glew/include " + INCL=$INCL" -Ibackports/glew/include" GLEW="glew.o " ADD_VCPKG_LIB_FOR_PKGCONFIG(glut) else # managed by the system @@ -660,7 +660,7 @@ case "$OSTYPE" in AC_CHECK_HEADER(GLUT/glut.h, [AC_DEFINE(HAVE_LIBGLUT,1, DEFINE_LIB[GLUT]) LIBS=$LIBS"-framework GLUT -framework OpenGL -framework Cocoa " - INCL=$INCL" -Ibackports/glew/include " + INCL=$INCL" -Ibackports/glew/include" GLEW="glew.o "], AC_MSG_NOTICE([*** Could not find GLUT: will compile without OpenGLLUT support ***])) ;; @@ -672,7 +672,7 @@ case "$OSTYPE" in DEFINE_LIB([GL])) LIBS=$LIBS"-lGL " GLEW="glew.o " - INCL=$INCL" -Ibackports/glew/include " + INCL=$INCL" -Ibackports/glew/include" AC_CHECK_LIB([GLX],[glXGetProcAddressARB], GLEW=$GLEW"-lGLX ")], AC_MSG_NOTICE([*** Could not find libGL: will compile without OpenGL support ***])) @@ -692,7 +692,7 @@ $vcpkg_exec install $VCPKG_INSTALL_ARGUMENTS LIBS=$LIBS"$(PKG_CONFIG_PATH=$VCPKG_PKG_CONFIG_LOC pkg-config --libs $VCPKG_LIBS_TO_USE_PKG_CONFIG) " CFLAGS=$CFLAGS" $(PKG_CONFIG_PATH=$VCPKG_PKG_CONFIG_LOC pkg-config --cflags-only-other $VCPKG_LIBS_TO_USE_PKG_CONFIG)" -INCL=$INCL"$(PKG_CONFIG_PATH=$VCPKG_PKG_CONFIG_LOC pkg-config --cflags-only-I $VCPKG_LIBS_TO_USE_PKG_CONFIG) " +INCL=$INCL" $(PKG_CONFIG_PATH=$VCPKG_PKG_CONFIG_LOC pkg-config --cflags-only-I $VCPKG_LIBS_TO_USE_PKG_CONFIG)" fi # ----------------- xdr ------------------------ From 492d88321e4c693f57f716c1b481c9316cc4ea6b Mon Sep 17 00:00:00 2001 From: John Bowman Date: Fri, 27 Dec 2024 22:25:57 -0800 Subject: [PATCH 23/24] Update build-asymptote. --- build-scripts/build-asymptote | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/build-scripts/build-asymptote b/build-scripts/build-asymptote index aab19e143..1ceb1e90f 100755 --- a/build-scripts/build-asymptote +++ b/build-scripts/build-asymptote @@ -5,10 +5,6 @@ SHARED=$HOME/shared/asy ASYMPTOTE=$HOME/asymptote BUILD=/usr/local/src -GCVERSION=8.2.4 -ATOMICVERSION=7.6.12 -GC=gc-$GCVERSION - MAKEFLAGS=-j8 export MAKEFLAGS @@ -31,10 +27,6 @@ cd $BUILD rm -rf asymptote-$VERSION rm -rf $BUILD/$BINDIR mv asymptote asymptote-$VERSION -wget https://github.com/ivmai/bdwgc/releases/download/v$GCVERSION/gc-$GCVERSION.tar.gz -wget https://github.com/ivmai/libatomic_ops/releases/download/v$ATOMICVERSION/libatomic_ops-$ATOMICVERSION.tar.gz -cp /usr/local/src/$GC.tar.gz asymptote-$VERSION -cp /usr/local/src/libatomic_ops-$ATOMICVERSION.tar.gz asymptote-$VERSION chown -R root:root asymptote-$VERSION cd asymptote-$VERSION find . -name ".[^.]*" -exec rm -rf {} \; @@ -54,11 +46,8 @@ make $MAKEFLAGS asy rm base/webgl/asygl-*.js make -j1 all make -j1 install -#rm asy -#make LFLAGS=-static strip asy make DESTDIR="$BUILD/" latexdir=$LATEXDIR install -#rm $GC.tar.gz rm $BUILD/$BINDIR/local/info/dir cp -a $BUILD/asymptote-$VERSION/ChangeLog . cp -a $ASYMPTOTE/ReleaseNotes . From 94ccb43dfd7618d047eeb0d6bc9261a9f4ad25ca Mon Sep 17 00:00:00 2001 From: John Bowman Date: Fri, 27 Dec 2024 22:38:21 -0800 Subject: [PATCH 24/24] Run gc/autogen.sh. --- autogen.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autogen.sh b/autogen.sh index 5ec2b3585..56c285fa5 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,3 +1,5 @@ #!/bin/sh autoheader && autoconf + +cd gc && ./autogen.sh