30267 lines
772 KiB
C
30267 lines
772 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef __cplusplus
|
|
#define GC_INNER STATIC
|
|
#define GC_EXTERN GC_INNER
|
|
#endif
|
|
#ifndef GC_DBG_MLC_H
|
|
#define GC_DBG_MLC_H
|
|
#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(GC_THREADS)||!defined(USE_MMAP))))&&!defined(_GNU_SOURCE)
|
|
#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)
|
|
#define __MINGW_EXCPT_DEFINE_PSDK 1
|
|
#endif
|
|
#if defined(NO_DEBUGGING)&&!defined(GC_ASSERTIONS)&&!defined(NDEBUG)
|
|
#define NDEBUG 1
|
|
#endif
|
|
#ifndef GC_H
|
|
#ifndef GC_H
|
|
#define GC_H
|
|
#if (defined(WIN64)&&!defined(_WIN64))&&defined(_MSC_VER)
|
|
#pragma message("Warning:Expecting _WIN64 for x64 targets!Notice the leading underscore!")
|
|
#endif
|
|
#if defined(GC_H)
|
|
#define GC_TMP_VERSION_MAJOR 8
|
|
#define GC_TMP_VERSION_MINOR 1
|
|
#define GC_TMP_VERSION_MICRO 0
|
|
#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
|
|
#endif
|
|
#if defined(GC_H)
|
|
#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
|
|
#endif
|
|
#if defined(SOLARIS_THREADS)||defined(_SOLARIS_THREADS)||defined(_SOLARIS_PTHREADS)||defined(GC_SOLARIS_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)
|
|
#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(SN_TARGET_ORBIS))
|
|
#define GC_FREEBSD_THREADS
|
|
#elif defined(__NetBSD__)
|
|
#define GC_NETBSD_THREADS
|
|
#elif defined(__alpha)||defined(__alpha__)
|
|
#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)||((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)
|
|
#define GC_WIN32_THREADS
|
|
#endif
|
|
#if defined(__rtems__)&&(defined(i386)||defined(__i386__))
|
|
#define GC_RTEMS_PTHREADS
|
|
#endif
|
|
#endif
|
|
#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)
|
|
#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)
|
|
#define _REENTRANT 1
|
|
#endif
|
|
#define __GC
|
|
#if!defined(_WIN32_WCE)||defined(__GNUC__)
|
|
#include <stddef.h>
|
|
#if defined(__MINGW32__)&&!defined(_WIN32_WCE)
|
|
#include <stdint.h>
|
|
#endif
|
|
#else
|
|
#include <stdlib.h>
|
|
#ifndef _PTRDIFF_T_DEFINED
|
|
#define _PTRDIFF_T_DEFINED
|
|
typedef long ptrdiff_t;
|
|
#endif
|
|
#endif
|
|
#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__)
|
|
#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
|
|
#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
|
|
#ifdef GC_OOM_FUNC_RETURNS_ALIAS
|
|
#define GC_ATTR_MALLOC
|
|
#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
|
|
#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)
|
|
#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)
|
|
#endif
|
|
#endif
|
|
#ifndef GC_ATTR_CALLOC_SIZE
|
|
#define GC_ATTR_CALLOC_SIZE(n,s)
|
|
#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)
|
|
#endif
|
|
#endif
|
|
#ifndef GC_ATTR_CONST
|
|
#if GC_GNUC_PREREQ(4,0)
|
|
#define GC_ATTR_CONST __attribute__((__const__))
|
|
#else
|
|
#define GC_ATTR_CONST
|
|
#endif
|
|
#endif
|
|
#ifndef GC_ATTR_DEPRECATED
|
|
#ifdef GC_BUILD
|
|
#undef GC_ATTR_DEPRECATED
|
|
#define GC_ATTR_DEPRECATED
|
|
#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
|
|
#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 <features.h>
|
|
#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
|
|
#if defined(_MSC_VER)&&_MSC_VER>=1200&&!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 (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)
|
|
#define GC_RETURN_ADDR (GC_word)__builtin_return_address(0)
|
|
#if GC_GNUC_PREREQ(4,0)&&(defined(__i386__)||defined(__amd64__)||defined(__x86_64__))
|
|
#define GC_HAVE_RETURN_ADDR_PARENT
|
|
#define GC_RETURN_ADDR_PARENT (GC_word)__builtin_extract_return_addr(__builtin_return_address(1))
|
|
#endif
|
|
#else
|
|
#define GC_RETURN_ADDR 0
|
|
#endif
|
|
#endif
|
|
#ifdef GC_PTHREADS
|
|
#if (defined(GC_DARWIN_THREADS)||defined(GC_WIN32_PTHREADS)||defined(__native_client__)||defined(GC_RTEMS_PTHREADS))&&!defined(GC_NO_DLOPEN)
|
|
#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)
|
|
#define GC_NO_PTHREAD_SIGMASK
|
|
#endif
|
|
#if defined(__native_client__)
|
|
#ifndef GC_PTHREAD_CREATE_CONST
|
|
#define GC_PTHREAD_CREATE_CONST
|
|
#endif
|
|
#ifndef GC_HAVE_PTHREAD_EXIT
|
|
#define GC_HAVE_PTHREAD_EXIT
|
|
#define GC_PTHREAD_EXIT_ATTRIBUTE
|
|
#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
|
|
#if GC_GNUC_PREREQ(2,7)
|
|
#define GC_PTHREAD_EXIT_ATTRIBUTE __attribute__((__noreturn__))
|
|
#elif defined(__NORETURN)
|
|
#define GC_PTHREAD_EXIT_ATTRIBUTE __NORETURN
|
|
#else
|
|
#define GC_PTHREAD_EXIT_ATTRIBUTE
|
|
#endif
|
|
#endif
|
|
#if (!defined(GC_HAVE_PTHREAD_EXIT)||defined(__native_client__))&&!defined(GC_NO_PTHREAD_CANCEL)
|
|
#define GC_NO_PTHREAD_CANCEL
|
|
#endif
|
|
#endif
|
|
#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
|
|
#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
|
|
#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
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
typedef void*GC_PTR;
|
|
#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
|
|
GC_API unsigned GC_CALL GC_get_version(void);
|
|
GC_API GC_ATTR_DEPRECATED GC_word GC_gc_no;
|
|
GC_API GC_word GC_CALL GC_get_gc_no(void);
|
|
#ifdef GC_THREADS
|
|
GC_API GC_ATTR_DEPRECATED int GC_parallel;
|
|
GC_API int GC_CALL GC_get_parallel(void);
|
|
GC_API void GC_CALL GC_set_markers_count(unsigned);
|
|
#endif
|
|
typedef void*(GC_CALLBACK*GC_oom_func)(size_t);
|
|
GC_API GC_ATTR_DEPRECATED GC_oom_func GC_oom_fn;
|
|
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);
|
|
GC_API GC_ATTR_DEPRECATED GC_on_heap_resize_proc GC_on_heap_resize;
|
|
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);
|
|
typedef enum {
|
|
GC_EVENT_START,
|
|
GC_EVENT_MARK_START,
|
|
GC_EVENT_MARK_END,
|
|
GC_EVENT_RECLAIM_START,
|
|
GC_EVENT_RECLAIM_END,
|
|
GC_EVENT_END,
|
|
GC_EVENT_PRE_STOP_WORLD,
|
|
GC_EVENT_POST_STOP_WORLD,
|
|
GC_EVENT_PRE_START_WORLD,
|
|
GC_EVENT_POST_START_WORLD,
|
|
GC_EVENT_THREAD_SUSPENDED,
|
|
GC_EVENT_THREAD_UNSUSPENDED
|
|
} GC_EventType;
|
|
typedef void (GC_CALLBACK*GC_on_collection_event_proc)(GC_EventType);
|
|
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);
|
|
#if defined(GC_THREADS)||(defined(GC_BUILD)&&defined(NN_PLATFORM_CTR))
|
|
typedef void (GC_CALLBACK*GC_on_thread_event_proc)(GC_EventType,
|
|
void*);
|
|
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);
|
|
#endif
|
|
GC_API GC_ATTR_DEPRECATED int GC_find_leak;
|
|
GC_API void GC_CALL GC_set_find_leak(int);
|
|
GC_API int GC_CALL GC_get_find_leak(void);
|
|
GC_API GC_ATTR_DEPRECATED int GC_all_interior_pointers;
|
|
GC_API void GC_CALL GC_set_all_interior_pointers(int);
|
|
GC_API int GC_CALL GC_get_all_interior_pointers(void);
|
|
GC_API GC_ATTR_DEPRECATED int GC_finalize_on_demand;
|
|
GC_API void GC_CALL GC_set_finalize_on_demand(int);
|
|
GC_API int GC_CALL GC_get_finalize_on_demand(void);
|
|
GC_API GC_ATTR_DEPRECATED int GC_java_finalization;
|
|
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;
|
|
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;
|
|
GC_API GC_ATTR_DEPRECATED int GC_dont_expand;
|
|
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;
|
|
GC_API GC_ATTR_DEPRECATED int GC_full_freq;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
GC_API GC_ATTR_DEPRECATED int GC_dont_precollect;
|
|
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;
|
|
#define GC_TIME_UNLIMITED 999999
|
|
GC_API void GC_CALL GC_set_time_limit(unsigned long);
|
|
GC_API unsigned long GC_CALL GC_get_time_limit(void);
|
|
struct GC_timeval_s {
|
|
unsigned long tv_ms;
|
|
unsigned long tv_nsec;
|
|
};
|
|
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);
|
|
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);
|
|
GC_API void GC_CALL GC_start_performance_measurement(void);
|
|
GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void);
|
|
GC_API void GC_CALL GC_set_pages_executable(int);
|
|
GC_API int GC_CALL GC_get_pages_executable(void);
|
|
GC_API void GC_CALL GC_set_min_bytes_allocd(size_t);
|
|
GC_API size_t GC_CALL GC_get_min_bytes_allocd(void);
|
|
GC_API void GC_CALL GC_set_rate(int);
|
|
GC_API int GC_CALL GC_get_rate(void);
|
|
GC_API void GC_CALL GC_set_max_prior_attempts(int);
|
|
GC_API int GC_CALL GC_get_max_prior_attempts(void);
|
|
GC_API void GC_CALL GC_set_handle_fork(int);
|
|
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);
|
|
GC_API void GC_CALL GC_init(void);
|
|
GC_API int GC_CALL GC_is_init_called(void);
|
|
GC_API void GC_CALL GC_deinit(void);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc(size_t);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc_atomic(size_t);
|
|
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);
|
|
GC_API GC_ATTR_DEPRECATED void*GC_CALL GC_malloc_stubborn(size_t);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2)void*GC_CALL
|
|
GC_memalign(size_t,size_t);
|
|
GC_API int GC_CALL GC_posix_memalign(void**,size_t,
|
|
size_t)GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_free(void*);
|
|
#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*);
|
|
GC_API void GC_CALL GC_end_stubborn_change(const void*)GC_ATTR_NONNULL(1);
|
|
GC_API void*GC_CALL GC_base(void*);
|
|
GC_API int GC_CALL GC_is_heap_ptr(const void*);
|
|
GC_API size_t GC_CALL GC_size(const void*)GC_ATTR_NONNULL(1);
|
|
GC_API void*GC_CALL GC_realloc(void*,
|
|
size_t)
|
|
GC_ATTR_ALLOC_SIZE(2);
|
|
GC_API int GC_CALL GC_expand_hp(size_t);
|
|
GC_API void GC_CALL GC_set_max_heap_size(GC_word);
|
|
GC_API void GC_CALL GC_exclude_static_roots(void*,
|
|
void*);
|
|
GC_API void GC_CALL GC_clear_exclusion_table(void);
|
|
GC_API void GC_CALL GC_clear_roots(void);
|
|
GC_API void GC_CALL GC_add_roots(void*,
|
|
void*);
|
|
GC_API void GC_CALL GC_remove_roots(void*,
|
|
void*);
|
|
GC_API void GC_CALL GC_register_displacement(size_t);
|
|
GC_API void GC_CALL GC_debug_register_displacement(size_t);
|
|
GC_API void GC_CALL GC_gcollect(void);
|
|
GC_API void GC_CALL GC_gcollect_and_unmap(void);
|
|
typedef int (GC_CALLBACK*GC_stop_func)(void);
|
|
GC_API int GC_CALL GC_try_to_collect(GC_stop_func)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_set_stop_func(GC_stop_func)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API GC_stop_func GC_CALL GC_get_stop_func(void);
|
|
GC_API size_t GC_CALL GC_get_heap_size(void);
|
|
GC_API size_t GC_CALL GC_get_free_bytes(void);
|
|
GC_API size_t GC_CALL GC_get_unmapped_bytes(void);
|
|
GC_API size_t GC_CALL GC_get_bytes_since_gc(void);
|
|
GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void);
|
|
GC_API size_t GC_CALL GC_get_total_bytes(void);
|
|
GC_API void GC_CALL GC_get_heap_usage_safe(GC_word*,
|
|
GC_word*,
|
|
GC_word*,
|
|
GC_word*,
|
|
GC_word*);
|
|
struct GC_prof_stats_s {
|
|
GC_word heapsize_full;
|
|
GC_word free_bytes_full;
|
|
GC_word unmapped_bytes;
|
|
GC_word bytes_allocd_since_gc;
|
|
GC_word allocd_bytes_before_gc;
|
|
GC_word non_gc_bytes;
|
|
GC_word gc_no;
|
|
GC_word markers_m1;
|
|
GC_word bytes_reclaimed_since_gc;
|
|
GC_word reclaimed_bytes_before_gc;
|
|
GC_word expl_freed_bytes_since_gc;
|
|
};
|
|
GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s*,
|
|
size_t);
|
|
#ifdef GC_THREADS
|
|
GC_API size_t GC_CALL GC_get_prof_stats_unsafe(struct GC_prof_stats_s*,
|
|
size_t);
|
|
#endif
|
|
GC_API size_t GC_CALL GC_get_size_map_at(int i);
|
|
GC_API size_t GC_CALL GC_get_memory_use(void);
|
|
GC_API void GC_CALL GC_disable(void);
|
|
GC_API int GC_CALL GC_is_disabled(void);
|
|
GC_API void GC_CALL GC_enable(void);
|
|
GC_API void GC_CALL GC_set_manual_vdb_allowed(int);
|
|
GC_API int GC_CALL GC_get_manual_vdb_allowed(void);
|
|
GC_API void GC_CALL GC_enable_incremental(void);
|
|
GC_API int GC_CALL GC_is_incremental_mode(void);
|
|
#define GC_PROTECTS_POINTER_HEAP 1
|
|
#define GC_PROTECTS_PTRFREE_HEAP 2
|
|
#define GC_PROTECTS_STATIC_DATA 4
|
|
#define GC_PROTECTS_STACK 8
|
|
#define GC_PROTECTS_NONE 0
|
|
GC_API int GC_CALL GC_incremental_protection_needs(void);
|
|
GC_API int GC_CALL GC_collect_a_little(void);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc_ignore_off_page(size_t);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc_atomic_ignore_off_page(size_t);
|
|
#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
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc_atomic_uncollectable(size_t);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_debug_malloc_atomic_uncollectable(size_t,GC_EXTRA_PARAMS);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_debug_malloc(size_t,GC_EXTRA_PARAMS);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_debug_malloc_atomic(size_t,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,
|
|
GC_EXTRA_PARAMS);
|
|
GC_API GC_ATTR_DEPRECATED void*GC_CALL
|
|
GC_debug_malloc_stubborn(size_t,GC_EXTRA_PARAMS);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_debug_malloc_ignore_off_page(size_t,
|
|
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,
|
|
GC_EXTRA_PARAMS);
|
|
GC_API void GC_CALL GC_debug_free(void*);
|
|
GC_API void*GC_CALL GC_debug_realloc(void*,
|
|
size_t,GC_EXTRA_PARAMS)
|
|
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);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_debug_malloc_replacement(size_t);
|
|
GC_API GC_ATTR_ALLOC_SIZE(2)void*GC_CALL
|
|
GC_debug_realloc_replacement(void*,
|
|
size_t);
|
|
#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
|
|
#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(( void*)(obj)))
|
|
#define GC_REGISTER_LONG_LINK(link,obj)GC_register_long_link(link,GC_base(( 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
|
|
#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
|
|
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
|
|
typedef void (GC_CALLBACK*GC_finalization_proc)(void*,
|
|
void*);
|
|
GC_API void GC_CALL GC_register_finalizer(void*,
|
|
GC_finalization_proc,void*,
|
|
GC_finalization_proc*,void**)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_debug_register_finalizer(void*,
|
|
GC_finalization_proc,void*,
|
|
GC_finalization_proc*,void**)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_register_finalizer_ignore_self(void*,
|
|
GC_finalization_proc,void*,
|
|
GC_finalization_proc*,void**)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_debug_register_finalizer_ignore_self(void*,
|
|
GC_finalization_proc,void*,
|
|
GC_finalization_proc*,void**)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_register_finalizer_no_order(void*,
|
|
GC_finalization_proc,void*,
|
|
GC_finalization_proc*,void**)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_debug_register_finalizer_no_order(void*,
|
|
GC_finalization_proc,void*,
|
|
GC_finalization_proc*,void**)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_register_finalizer_unreachable(void*,
|
|
GC_finalization_proc,void*,
|
|
GC_finalization_proc*,void**)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_debug_register_finalizer_unreachable(void*,
|
|
GC_finalization_proc,void*,
|
|
GC_finalization_proc*,void**)
|
|
GC_ATTR_NONNULL(1);
|
|
#define GC_NO_MEMORY 2
|
|
GC_API int GC_CALL GC_register_disappearing_link(void**)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API int GC_CALL GC_general_register_disappearing_link(void**,
|
|
const void*)
|
|
GC_ATTR_NONNULL(1)GC_ATTR_NONNULL(2);
|
|
GC_API int GC_CALL GC_move_disappearing_link(void**,
|
|
void**)
|
|
GC_ATTR_NONNULL(2);
|
|
GC_API int GC_CALL GC_unregister_disappearing_link(void**);
|
|
GC_API int GC_CALL GC_register_long_link(void**,
|
|
const void*)
|
|
GC_ATTR_NONNULL(1)GC_ATTR_NONNULL(2);
|
|
GC_API int GC_CALL GC_move_long_link(void**,
|
|
void**)
|
|
GC_ATTR_NONNULL(2);
|
|
GC_API int GC_CALL GC_unregister_long_link(void**);
|
|
typedef enum {
|
|
GC_TOGGLE_REF_DROP,
|
|
GC_TOGGLE_REF_STRONG,
|
|
GC_TOGGLE_REF_WEAK
|
|
} GC_ToggleRefStatus;
|
|
typedef GC_ToggleRefStatus (GC_CALLBACK*GC_toggleref_func)(void*);
|
|
GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func);
|
|
GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void);
|
|
GC_API int GC_CALL GC_toggleref_add(void*,int)
|
|
GC_ATTR_NONNULL(1);
|
|
typedef void (GC_CALLBACK*GC_await_finalize_proc)(void*);
|
|
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);
|
|
GC_API int GC_CALL GC_should_invoke_finalizers(void);
|
|
GC_API int GC_CALL GC_invoke_finalizers(void);
|
|
#if defined(__GNUC__)&&!defined(__INTEL_COMPILER)
|
|
#define GC_reachable_here(ptr)__asm__ __volatile__(" "::"X"(ptr):"memory")
|
|
#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))
|
|
#else
|
|
#define GC_reachable_here(ptr)GC_noop1((GC_word)(ptr))
|
|
#endif
|
|
#endif
|
|
typedef void (GC_CALLBACK*GC_warn_proc)(char*,
|
|
GC_word);
|
|
GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc)GC_ATTR_NONNULL(1);
|
|
GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void);
|
|
GC_API void GC_CALLBACK GC_ignore_warn_proc(char*,GC_word);
|
|
GC_API void GC_CALL GC_set_log_fd(int);
|
|
typedef void (GC_CALLBACK*GC_abort_func)(const char*);
|
|
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);
|
|
GC_API void GC_CALL GC_abort_on_oom(void);
|
|
typedef GC_word GC_hidden_pointer;
|
|
#define GC_HIDE_POINTER(p)(~(GC_hidden_pointer)(p))
|
|
#define GC_REVEAL_POINTER(p)((void*)GC_HIDE_POINTER(p))
|
|
#if defined(I_HIDE_POINTERS)||defined(GC_I_HIDE_POINTERS)
|
|
#define HIDE_POINTER(p)GC_HIDE_POINTER(p)
|
|
#define REVEAL_POINTER(p)GC_REVEAL_POINTER(p)
|
|
#endif
|
|
#ifdef GC_THREADS
|
|
GC_API void GC_CALL GC_alloc_lock(void);
|
|
GC_API void GC_CALL GC_alloc_unlock(void);
|
|
#else
|
|
#define GC_alloc_lock()(void)0
|
|
#define GC_alloc_unlock()(void)0
|
|
#endif
|
|
typedef void*(GC_CALLBACK*GC_fn_type)(void*);
|
|
GC_API void*GC_CALL GC_call_with_alloc_lock(GC_fn_type,
|
|
void*)GC_ATTR_NONNULL(1);
|
|
struct GC_stack_base {
|
|
void*mem_base;
|
|
#if defined(__ia64)||defined(__ia64__)||defined(_M_IA64)
|
|
void*reg_base;
|
|
#endif
|
|
};
|
|
typedef void*(GC_CALLBACK*GC_stack_base_func)(
|
|
struct GC_stack_base*,void*);
|
|
GC_API void*GC_CALL GC_call_with_stack_base(GC_stack_base_func,
|
|
void*)GC_ATTR_NONNULL(1);
|
|
#define GC_SUCCESS 0
|
|
#define GC_DUPLICATE 1
|
|
#define GC_NO_THREADS 2
|
|
#define GC_UNIMPLEMENTED 3
|
|
#define GC_NOT_FOUND 4
|
|
#if defined(GC_DARWIN_THREADS)||defined(GC_WIN32_THREADS)
|
|
GC_API void GC_CALL GC_use_threads_discovery(void);
|
|
#endif
|
|
#ifdef GC_THREADS
|
|
GC_API void GC_CALL GC_set_suspend_signal(int);
|
|
GC_API void GC_CALL GC_set_thr_restart_signal(int);
|
|
GC_API int GC_CALL GC_get_suspend_signal(void);
|
|
GC_API int GC_CALL GC_get_thr_restart_signal(void);
|
|
GC_API void GC_CALL GC_start_mark_threads(void);
|
|
GC_API void GC_CALL GC_allow_register_threads(void);
|
|
GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base*)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API int GC_CALL GC_thread_is_registered(void);
|
|
GC_API void GC_CALL GC_register_altstack(void*,
|
|
GC_word,
|
|
void*,
|
|
GC_word);
|
|
GC_API int GC_CALL GC_unregister_my_thread(void);
|
|
GC_API void GC_CALL GC_stop_world_external(void);
|
|
GC_API void GC_CALL GC_start_world_external(void);
|
|
#endif
|
|
GC_API void*GC_CALL GC_do_blocking(GC_fn_type,
|
|
void*)GC_ATTR_NONNULL(1);
|
|
GC_API void*GC_CALL GC_call_with_gc_active(GC_fn_type,
|
|
void*)GC_ATTR_NONNULL(1);
|
|
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void*GC_CALL GC_get_my_stackbottom(struct GC_stack_base*)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void GC_CALL GC_set_stackbottom(void*,
|
|
const struct GC_stack_base*)
|
|
GC_ATTR_NONNULL(2);
|
|
GC_API void*GC_CALL GC_same_obj(void*,void*);
|
|
GC_API void*GC_CALL GC_pre_incr(void**,ptrdiff_t)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void*GC_CALL GC_post_incr(void**,ptrdiff_t)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void*GC_CALL GC_is_visible(void*);
|
|
GC_API void*GC_CALL GC_is_valid_displacement(void*);
|
|
GC_API void GC_CALL GC_dump(void);
|
|
GC_API void GC_CALL GC_dump_named(const char*);
|
|
GC_API void GC_CALL GC_dump_regions(void);
|
|
GC_API void GC_CALL GC_dump_finalization(void);
|
|
#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
|
|
#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
|
|
#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_API void GC_CALL GC_ptr_store_and_dirty(void*,
|
|
const void*);
|
|
GC_API void GC_CALL GC_debug_ptr_store_and_dirty(void*,
|
|
const void*);
|
|
GC_API void (GC_CALLBACK*GC_same_obj_print_proc)(void*,
|
|
void*);
|
|
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
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#ifndef GC_PTHREAD_REDIRECTS_H
|
|
#define GC_PTHREAD_REDIRECTS_H
|
|
#if defined(GC_H)&&defined(GC_PTHREADS)
|
|
#ifndef GC_PTHREAD_REDIRECTS_ONLY
|
|
#include <pthread.h>
|
|
#ifndef GC_NO_DLOPEN
|
|
#include <dlfcn.h>
|
|
#endif
|
|
#ifndef GC_NO_PTHREAD_SIGMASK
|
|
#include <signal.h>
|
|
#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*,int);
|
|
#endif
|
|
#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,const sigset_t*,
|
|
sigset_t*);
|
|
#endif
|
|
#endif
|
|
#ifndef GC_PTHREAD_CREATE_CONST
|
|
#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*);
|
|
GC_API int GC_pthread_join(pthread_t,void**);
|
|
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
|
|
}
|
|
#endif
|
|
#endif
|
|
#if!defined(GC_NO_THREAD_REDIRECTS)&&!defined(GC_USE_LD_WRAP)
|
|
#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
|
|
#endif
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#endif
|
|
GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_many(size_t);
|
|
#define GC_NEXT(p)(*(void**)(p))
|
|
typedef int (GC_CALLBACK*GC_has_static_roots_func)(
|
|
const char*,
|
|
void*,
|
|
size_t);
|
|
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)
|
|
#define GC_WINDOWS_H_INCLUDED
|
|
#endif
|
|
#if defined(GC_WIN32_THREADS)&&(!defined(GC_PTHREADS)||defined(GC_BUILD)||defined(GC_WINDOWS_H_INCLUDED))
|
|
#if (!defined(GC_NO_THREAD_DECLS)||defined(GC_BUILD))&&!defined(GC_DONT_INCL_WINDOWS_H)
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#if!defined(_WIN32_WCE)&&!defined(__CEGCC__)
|
|
#include <process.h>
|
|
#endif
|
|
#if defined(GC_BUILD)||!defined(GC_DONT_INCLUDE_WINDOWS_H)
|
|
#include <windows.h>
|
|
#define GC_WINDOWS_H_INCLUDED
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#ifdef GC_UNDERSCORE_STDCALL
|
|
#define GC_CreateThread _GC_CreateThread
|
|
#define GC_ExitThread _GC_ExitThread
|
|
#endif
|
|
#ifndef DECLSPEC_NORETURN
|
|
#ifdef GC_WINDOWS_H_INCLUDED
|
|
#define DECLSPEC_NORETURN
|
|
#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
|
|
#ifdef GC_UNDERSCORE_STDCALL
|
|
#define GC_DllMain _GC_DllMain
|
|
#endif
|
|
#ifdef GC_WINDOWS_H_INCLUDED
|
|
GC_API BOOL WINAPI GC_DllMain(HINSTANCE,
|
|
ULONG,
|
|
LPVOID);
|
|
#else
|
|
GC_API int __stdcall GC_DllMain(void*,unsigned long,void*);
|
|
#endif
|
|
#endif
|
|
#ifdef GC_WINDOWS_H_INCLUDED
|
|
GC_API HANDLE WINAPI GC_CreateThread(
|
|
LPSECURITY_ATTRIBUTES,
|
|
GC_WIN32_SIZE_T,
|
|
LPTHREAD_START_ROUTINE,
|
|
LPVOID,DWORD,
|
|
LPDWORD);
|
|
GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread(
|
|
DWORD);
|
|
#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*,unsigned,
|
|
unsigned (__stdcall*)(void*),
|
|
void*,unsigned,
|
|
unsigned*);
|
|
GC_API void GC_CALL GC_endthreadex(unsigned);
|
|
#endif
|
|
#endif
|
|
#ifdef GC_WINMAIN_REDIRECT
|
|
#define WinMain GC_WinMain
|
|
#endif
|
|
#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
|
|
#endif
|
|
#endif
|
|
GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int);
|
|
GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
|
|
#if defined(__CYGWIN32__)||defined(__CYGWIN__)
|
|
#ifdef __x86_64__
|
|
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
|
|
#define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART,GC_DATAEND);GC_gcollect()
|
|
#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)
|
|
#pragma weak __dso_handle
|
|
extern int __dso_handle[];
|
|
GC_API void*GC_CALL GC_find_limit(void*,int);
|
|
#define GC_INIT_CONF_ROOTS (void)(__dso_handle!=0?(GC_add_roots(__dso_handle,GC_find_limit(__dso_handle,1)),0):0)
|
|
#else
|
|
#define GC_INIT_CONF_ROOTS
|
|
#endif
|
|
#ifdef GC_DONT_EXPAND
|
|
#define GC_INIT_CONF_DONT_EXPAND GC_set_dont_expand(1)
|
|
#else
|
|
#define GC_INIT_CONF_DONT_EXPAND
|
|
#endif
|
|
#ifdef GC_FORCE_UNMAP_ON_GCOLLECT
|
|
#define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT GC_set_force_unmap_on_gcollect(1)
|
|
#else
|
|
#define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT
|
|
#endif
|
|
#ifdef GC_DONT_GC
|
|
#define GC_INIT_CONF_MAX_RETRIES (void)(GC_dont_gc=1)
|
|
#elif defined(GC_MAX_RETRIES)&&!defined(CPPCHECK)
|
|
#define GC_INIT_CONF_MAX_RETRIES GC_set_max_retries(GC_MAX_RETRIES)
|
|
#else
|
|
#define GC_INIT_CONF_MAX_RETRIES
|
|
#endif
|
|
#if defined(GC_ALLOCD_BYTES_PER_FINALIZER)&&!defined(CPPCHECK)
|
|
#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
|
|
#endif
|
|
#if defined(GC_FREE_SPACE_DIVISOR)&&!defined(CPPCHECK)
|
|
#define GC_INIT_CONF_FREE_SPACE_DIVISOR GC_set_free_space_divisor(GC_FREE_SPACE_DIVISOR)
|
|
#else
|
|
#define GC_INIT_CONF_FREE_SPACE_DIVISOR
|
|
#endif
|
|
#if defined(GC_FULL_FREQ)&&!defined(CPPCHECK)
|
|
#define GC_INIT_CONF_FULL_FREQ GC_set_full_freq(GC_FULL_FREQ)
|
|
#else
|
|
#define GC_INIT_CONF_FULL_FREQ
|
|
#endif
|
|
#if defined(GC_TIME_LIMIT)&&!defined(CPPCHECK)
|
|
#define GC_INIT_CONF_TIME_LIMIT GC_set_time_limit(GC_TIME_LIMIT)
|
|
#else
|
|
#define GC_INIT_CONF_TIME_LIMIT
|
|
#endif
|
|
#if defined(GC_MARKERS)&&defined(GC_THREADS)&&!defined(CPPCHECK)
|
|
#define GC_INIT_CONF_MARKERS GC_set_markers_count(GC_MARKERS)
|
|
#else
|
|
#define GC_INIT_CONF_MARKERS
|
|
#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
|
|
#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
|
|
#endif
|
|
#if defined(GC_MAXIMUM_HEAP_SIZE)&&!defined(CPPCHECK)
|
|
#define GC_INIT_CONF_MAXIMUM_HEAP_SIZE GC_set_max_heap_size(GC_MAXIMUM_HEAP_SIZE)
|
|
#else
|
|
#define GC_INIT_CONF_MAXIMUM_HEAP_SIZE
|
|
#endif
|
|
#ifdef GC_IGNORE_WARN
|
|
#define GC_INIT_CONF_IGNORE_WARN GC_set_warn_proc(GC_ignore_warn_proc)
|
|
#else
|
|
#define GC_INIT_CONF_IGNORE_WARN
|
|
#endif
|
|
#if defined(GC_INITIAL_HEAP_SIZE)&&!defined(CPPCHECK)
|
|
#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
|
|
#endif
|
|
#define GC_INIT(){ GC_INIT_CONF_DONT_EXPAND;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();GC_INIT_CONF_ROOTS;GC_INIT_CONF_IGNORE_WARN;GC_INIT_CONF_INITIAL_HEAP_SIZE;}
|
|
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)
|
|
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
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#include <stdlib.h>
|
|
#if!defined(sony_news)
|
|
#include <stddef.h>
|
|
#endif
|
|
#ifdef DGUX
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#endif
|
|
#ifdef BSD_TIME
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#endif
|
|
#ifdef PARALLEL_MARK
|
|
#define AO_REQUIRE_CAS
|
|
#if!defined(__GNUC__)&&!defined(AO_ASSUME_WINDOWS98)
|
|
#define AO_ASSUME_WINDOWS98
|
|
#endif
|
|
#endif
|
|
#ifndef GC_TINY_FL_H
|
|
#define GC_TINY_FL_H
|
|
#ifndef GC_GRANULE_BYTES
|
|
#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
|
|
#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
|
|
#ifndef GC_TINY_FREELISTS
|
|
#if GC_GRANULE_BYTES==16
|
|
#define GC_TINY_FREELISTS 25
|
|
#else
|
|
#define GC_TINY_FREELISTS 33
|
|
#endif
|
|
#endif
|
|
#define GC_RAW_BYTES_FROM_INDEX(i)((i)*GC_GRANULE_BYTES)
|
|
#endif
|
|
#ifndef GC_MARK_H
|
|
#define GC_MARK_H
|
|
#ifndef 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
|
|
typedef struct GC_ms_entry*(*GC_mark_proc)(GC_word*,
|
|
struct GC_ms_entry*,
|
|
struct GC_ms_entry*,
|
|
GC_word);
|
|
#define GC_LOG_MAX_MARK_PROCS 6
|
|
#define GC_MAX_MARK_PROCS (1<<GC_LOG_MAX_MARK_PROCS)
|
|
#define GC_RESERVED_MARK_PROCS 8
|
|
#define GC_GCJ_RESERVED_MARK_PROC_INDEX 0
|
|
#define GC_DS_TAG_BITS 2
|
|
#define GC_DS_TAGS ((1<<GC_DS_TAG_BITS)- 1)
|
|
#define GC_DS_LENGTH 0
|
|
#define GC_DS_BITMAP 1
|
|
#define GC_DS_PROC 2
|
|
#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
|
|
#define GC_INDIR_PER_OBJ_BIAS 0x10
|
|
GC_API void*GC_least_plausible_heap_addr;
|
|
GC_API void*GC_greatest_plausible_heap_addr;
|
|
GC_API struct GC_ms_entry*GC_CALL GC_mark_and_push(void*,
|
|
struct GC_ms_entry*,
|
|
struct GC_ms_entry*,
|
|
void**);
|
|
#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))
|
|
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()))
|
|
GC_API GC_ATTR_DEPRECATED
|
|
#ifdef GC_BUILD
|
|
const
|
|
#endif
|
|
size_t GC_debug_header_size;
|
|
GC_API void**GC_CALL GC_new_free_list(void);
|
|
GC_API void**GC_CALL GC_new_free_list_inner(void);
|
|
GC_API unsigned GC_CALL GC_new_kind(void**,
|
|
GC_word,
|
|
int,
|
|
int)GC_ATTR_NONNULL(1);
|
|
GC_API unsigned GC_CALL GC_new_kind_inner(void**,
|
|
GC_word,
|
|
int,
|
|
int)GC_ATTR_NONNULL(1);
|
|
GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc);
|
|
GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL GC_generic_malloc(
|
|
size_t,
|
|
int);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_generic_malloc_ignore_off_page(
|
|
size_t,int);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_generic_malloc_uncollectable(
|
|
size_t,int);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_generic_or_special_malloc(
|
|
size_t,int);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_debug_generic_or_special_malloc(
|
|
size_t,int,
|
|
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_API int GC_CALL GC_get_kind_and_size(const void*,size_t*)
|
|
GC_ATTR_NONNULL(1);
|
|
typedef void (GC_CALLBACK*GC_describe_type_fn)(void*,
|
|
char*);
|
|
#define GC_TYPE_DESCR_LEN 40
|
|
GC_API void GC_CALL GC_register_describe_type_fn(int,
|
|
GC_describe_type_fn);
|
|
GC_API void*GC_CALL GC_clear_stack(void*);
|
|
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);
|
|
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);
|
|
GC_API void GC_CALL GC_push_all(void*,void*);
|
|
GC_API void GC_CALL GC_push_all_eager(void*,void*);
|
|
GC_API void GC_CALL GC_push_conditional(void*,void*,
|
|
int);
|
|
GC_API void GC_CALL GC_push_finalizer_structures(void);
|
|
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);
|
|
typedef void (GC_CALLBACK*GC_reachable_object_proc)(void*,
|
|
size_t,
|
|
void*);
|
|
GC_API void GC_CALL GC_enumerate_reachable_objects_inner(
|
|
GC_reachable_object_proc,
|
|
void*)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_API void GC_CALL GC_print_trace_inner(GC_word);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
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;
|
|
#define PTR_T_DEFINED
|
|
#endif
|
|
#ifndef SIZE_MAX
|
|
#include <limits.h>
|
|
#endif
|
|
#if defined(SIZE_MAX)&&!defined(CPPCHECK)
|
|
#define GC_SIZE_MAX ((size_t)SIZE_MAX)
|
|
#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)
|
|
#else
|
|
#define EXPECT(expr,outcome)(expr)
|
|
#endif
|
|
#define SIZET_SAT_ADD(a,b)(EXPECT((a)< GC_SIZE_MAX - (b),TRUE)?(a)+(b):GC_SIZE_MAX)
|
|
#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 <stddef.h>
|
|
#endif
|
|
#ifdef __cplusplus
|
|
#define EXTERN_C_BEGIN extern "C" {
|
|
#define EXTERN_C_END }
|
|
#else
|
|
#define EXTERN_C_BEGIN
|
|
#define EXTERN_C_END
|
|
#endif
|
|
EXTERN_C_BEGIN
|
|
#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
|
|
#define GC_CLANG_PREREQ_FULL(major,minor,patchlevel)0
|
|
#endif
|
|
#ifdef LINT2
|
|
#define COVERT_DATAFLOW(w)(~(GC_word)(w)^(~(GC_word)0))
|
|
#else
|
|
#define COVERT_DATAFLOW(w)((GC_word)(w))
|
|
#endif
|
|
#if defined(__ANDROID__)&&!defined(HOST_ANDROID)
|
|
#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
|
|
#if (defined(linux)||defined(__linux__)||defined(HOST_ANDROID))&&!defined(LINUX)&&!defined(__native_client__)
|
|
#define LINUX
|
|
#endif
|
|
#if defined(__NetBSD__)
|
|
#define NETBSD
|
|
#endif
|
|
#if defined(__OpenBSD__)
|
|
#define OPENBSD
|
|
#endif
|
|
#if (defined(__FreeBSD__)||defined(__DragonFly__)||defined(__FreeBSD_kernel__))&&!defined(FREEBSD)&&!defined(SN_TARGET_ORBIS)
|
|
#define FREEBSD
|
|
#endif
|
|
#if defined(macosx)||(defined(__APPLE__)&&defined(__MACH__))
|
|
#define DARWIN
|
|
EXTERN_C_END
|
|
#include <TargetConditionals.h>
|
|
EXTERN_C_BEGIN
|
|
#endif
|
|
#if defined(__native_client__)
|
|
#define NACL
|
|
#if!defined(__portable_native_client__)&&!defined(__arm__)
|
|
#define I386
|
|
#define mach_type_known
|
|
#else
|
|
#endif
|
|
#endif
|
|
#if defined(__aarch64__)
|
|
#define AARCH64
|
|
#if!defined(LINUX)&&!defined(DARWIN)&&!defined(FREEBSD)&&!defined(NETBSD)&&!defined(NN_BUILD_TARGET_PLATFORM_NX)&&!defined(OPENBSD)
|
|
#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(SN_TARGET_ORBIS)&&!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)
|
|
#define MIPS
|
|
#if defined(nec_ews)||defined(_nec_ews)
|
|
#define EWS4800
|
|
#endif
|
|
#if!defined(LINUX)&&!defined(EWS4800)&&!defined(NETBSD)&&!defined(OPENBSD)
|
|
#if defined(ultrix)||defined(__ultrix)
|
|
#define ULTRIX
|
|
#else
|
|
#define IRIX5
|
|
#endif
|
|
#endif
|
|
#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
|
|
#define mach_type_known
|
|
#endif
|
|
#if defined(__or1k__)
|
|
#define 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))
|
|
EXTERN_C_END
|
|
#include <errno.h>
|
|
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)
|
|
#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(__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(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
|
|
#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(SN_TARGET_ORBIS))&&(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
|
|
#define mach_type_known
|
|
#endif
|
|
#if defined(_WIN32_WCE)||defined(__CEGCC__)||defined(__MINGW32CE__)
|
|
#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
|
|
#define I386
|
|
#endif
|
|
#ifdef _XBOX_ONE
|
|
#define MSWIN_XBOX1
|
|
#else
|
|
#ifndef MSWIN32
|
|
#define MSWIN32
|
|
#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
|
|
#endif
|
|
#endif
|
|
#if defined(__DJGPP__)
|
|
#define I386
|
|
#ifndef DJGPP
|
|
#define DJGPP
|
|
#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
|
|
#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__)
|
|
#define HURD
|
|
#define I386
|
|
#define mach_type_known
|
|
#endif
|
|
#endif
|
|
#if defined(__TANDEM)
|
|
#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))
|
|
#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__)
|
|
#define I386
|
|
#define mach_type_known
|
|
#endif
|
|
#if!defined(mach_type_known)&&!defined(CPPCHECK)
|
|
#error The collector has not been ported to this machine/OS combination
|
|
#endif
|
|
#if GC_GNUC_PREREQ(2,8)&&!defined(__INTEL_COMPILER)&&!defined(__PATHCC__)&&!defined(__FUJITSU)&&!(defined(POWERPC)&&defined(DARWIN))&&!defined(RTEMS)&&!defined(__ARMCC_VERSION)&&!defined(__clang__)
|
|
#define HAVE_BUILTIN_UNWIND_INIT
|
|
#endif
|
|
#ifdef SYMBIAN
|
|
#define MACH_TYPE "SYMBIAN"
|
|
#define OS_TYPE "SYMBIAN"
|
|
#define CPP_WORDSZ 32
|
|
#define ALIGNMENT 4
|
|
#define DATASTART (ptr_t)ALIGNMENT
|
|
#define DATAEND (ptr_t)ALIGNMENT
|
|
#endif
|
|
#define STACK_GRAN 0x1000000
|
|
#ifdef M68K
|
|
#define MACH_TYPE "M68K"
|
|
#define ALIGNMENT 2
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "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
|
|
#define OS_TYPE "NETBSD"
|
|
#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 LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#if!defined(REDIRECT_MALLOC)
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#ifdef __ELF__
|
|
#define DYNAMIC_LOADING
|
|
EXTERN_C_END
|
|
#include <features.h>
|
|
EXTERN_C_BEGIN
|
|
#if defined(__GLIBC__)&&__GLIBC__>=2
|
|
#define SEARCH_FOR_DATA_START
|
|
#else
|
|
extern char**__environ;
|
|
#define DATASTART ((ptr_t)(&__environ))
|
|
#endif
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#else
|
|
extern int etext[];
|
|
#define DATASTART ((ptr_t)((((word)(etext))+0xfff)&~0xfff))
|
|
#endif
|
|
#endif
|
|
#ifdef AMIGA
|
|
#define OS_TYPE "AMIGA"
|
|
#define DATAEND
|
|
#define GETPAGESIZE()4096
|
|
#endif
|
|
#ifdef MACOS
|
|
#ifndef __LOWMEM__
|
|
EXTERN_C_END
|
|
#include <LowMem.h>
|
|
EXTERN_C_BEGIN
|
|
#endif
|
|
#define OS_TYPE "MACOS"
|
|
#define STACKBOTTOM ((ptr_t)LMGetCurStackBase())
|
|
#define DATAEND
|
|
#define GETPAGESIZE()4096
|
|
#endif
|
|
#ifdef NEXT
|
|
#define OS_TYPE "NEXT"
|
|
#define DATASTART ((ptr_t)get_etext())
|
|
#define DATASTART_IS_FUNC
|
|
#define STACKBOTTOM ((ptr_t)0x4000000)
|
|
#define DATAEND
|
|
#endif
|
|
#endif
|
|
#ifdef POWERPC
|
|
#define MACH_TYPE "POWERPC"
|
|
#ifdef MACOS
|
|
#define ALIGNMENT 2
|
|
#ifndef __LOWMEM__
|
|
EXTERN_C_END
|
|
#include <LowMem.h>
|
|
EXTERN_C_BEGIN
|
|
#endif
|
|
#define OS_TYPE "MACOS"
|
|
#define STACKBOTTOM ((ptr_t)LMGetCurStackBase())
|
|
#define DATAEND
|
|
#endif
|
|
#ifdef LINUX
|
|
#if defined(__powerpc64__)
|
|
#define ALIGNMENT 8
|
|
#define CPP_WORDSZ 64
|
|
#ifndef HBLKSIZE
|
|
#define HBLKSIZE 4096
|
|
#endif
|
|
#else
|
|
#define ALIGNMENT 4
|
|
#endif
|
|
#define OS_TYPE "LINUX"
|
|
#if defined(__bg__)
|
|
#define HEURISTIC2
|
|
#define NO_PTHREAD_GETATTR_NP
|
|
#else
|
|
#define LINUX_STACKBOTTOM
|
|
#endif
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
#define SEARCH_FOR_DATA_START
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#endif
|
|
#ifdef DARWIN
|
|
#define OS_TYPE "DARWIN"
|
|
#define DYNAMIC_LOADING
|
|
#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 DATASTART ((ptr_t)get_etext())
|
|
#define DATAEND ((ptr_t)get_end())
|
|
#define USE_MMAP_ANON
|
|
#define MPROTECT_VDB
|
|
EXTERN_C_END
|
|
#include <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)getpagesize()
|
|
#if defined(USE_PPC_PREFETCH)&&defined(__GNUC__)
|
|
#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
|
|
#define NO_PTHREAD_TRYLOCK
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#if defined(__powerpc64__)
|
|
#define ALIGNMENT 8
|
|
#define CPP_WORDSZ 64
|
|
#else
|
|
#define ALIGNMENT 4
|
|
#endif
|
|
#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
|
|
#ifdef FREEBSD
|
|
#if defined(__powerpc64__)
|
|
#define ALIGNMENT 8
|
|
#define CPP_WORDSZ 64
|
|
#ifndef HBLKSIZE
|
|
#define HBLKSIZE 4096
|
|
#endif
|
|
#else
|
|
#define ALIGNMENT 4
|
|
#endif
|
|
#define OS_TYPE "FREEBSD"
|
|
#ifndef GC_FREEBSD_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#define FREEBSD_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
extern char etext[];
|
|
#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext)
|
|
#define DATASTART_USES_BSDGETDATASTART
|
|
#endif
|
|
#ifdef NETBSD
|
|
#define ALIGNMENT 4
|
|
#define OS_TYPE "NETBSD"
|
|
#define HEURISTIC2
|
|
extern ptr_t GC_data_start;
|
|
#define DATASTART GC_data_start
|
|
#define DYNAMIC_LOADING
|
|
#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
|
|
#endif
|
|
#ifdef AIX
|
|
#define OS_TYPE "AIX"
|
|
#undef ALIGNMENT
|
|
#undef IA64
|
|
#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
|
|
extern int _data[],_end[];
|
|
#define DATASTART ((ptr_t)((ulong)_data))
|
|
#define DATAEND ((ptr_t)((ulong)_end))
|
|
extern int errno;
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#ifdef NOSYS
|
|
#define ALIGNMENT 4
|
|
#define OS_TYPE "NOSYS"
|
|
extern void __end[],__dso_handle[];
|
|
#define DATASTART ((ptr_t)__dso_handle)
|
|
#define DATAEND ((ptr_t)(__end))
|
|
#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
|
|
extern char etext[];
|
|
#define DATASTART ((ptr_t)(etext))
|
|
#ifdef BSD
|
|
#define OS_TYPE "BSD"
|
|
#define HEURISTIC1
|
|
#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
|
|
#define CPP_WORDSZ 32
|
|
#endif
|
|
#ifdef SOLARIS
|
|
#define OS_TYPE "SOLARIS"
|
|
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))
|
|
#if!defined(USE_MMAP)&&defined(REDIRECT_MALLOC)
|
|
#define USE_MMAP 1
|
|
#endif
|
|
#ifdef USE_MMAP
|
|
#define HEAP_START (ptr_t)0x40000000
|
|
#else
|
|
#define HEAP_START DATAEND
|
|
#endif
|
|
#define PROC_VDB
|
|
EXTERN_C_END
|
|
#include <sys/vmparam.h>
|
|
#include <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#ifdef USERLIMIT
|
|
#define STACKBOTTOM ((ptr_t)USRSTACK)
|
|
#else
|
|
#define HEURISTIC2
|
|
#endif
|
|
#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGESIZE)
|
|
#define DYNAMIC_LOADING
|
|
#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
|
|
#define OS_TYPE "LINUX"
|
|
#ifdef __ELF__
|
|
#define DYNAMIC_LOADING
|
|
#elif!defined(CPPCHECK)
|
|
#error Linux SPARC a.out not supported
|
|
#endif
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
extern int _end[];
|
|
extern int _etext[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#define SVR4
|
|
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
|
|
#define LINUX_STACKBOTTOM
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#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
|
|
#ifdef NETBSD
|
|
#define OS_TYPE "NETBSD"
|
|
#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 FREEBSD
|
|
#define OS_TYPE "FREEBSD"
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#define FREEBSD_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
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
|
|
#endif
|
|
#ifdef SEQUENT
|
|
#define OS_TYPE "SEQUENT"
|
|
extern int etext[];
|
|
#define DATASTART ((ptr_t)((((word)(etext))+0xfff)&~0xfff))
|
|
#define STACKBOTTOM ((ptr_t)0x3ffff000)
|
|
#endif
|
|
#if defined(__EMSCRIPTEN__)
|
|
#define OS_TYPE "EMSCRIPTEN"
|
|
#define DATASTART (ptr_t)ALIGNMENT
|
|
#define DATAEND (ptr_t)ALIGNMENT
|
|
#define USE_MMAP_ANON
|
|
#define STACK_GROWS_DOWN
|
|
#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
|
|
#define OS_TYPE "HAIKU"
|
|
EXTERN_C_END
|
|
#include <OS.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)B_PAGE_SIZE
|
|
extern int etext[];
|
|
#define DATASTART ((ptr_t)((((word)(etext))+0xfff)&~0xfff))
|
|
#define DYNAMIC_LOADING
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#ifdef SOLARIS
|
|
#define OS_TYPE "SOLARIS"
|
|
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))
|
|
EXTERN_C_END
|
|
#include <sys/vmparam.h>
|
|
EXTERN_C_BEGIN
|
|
#ifdef USERLIMIT
|
|
#define STACKBOTTOM ((ptr_t)USRSTACK)
|
|
#else
|
|
#define HEURISTIC2
|
|
#endif
|
|
#ifdef SOLARIS25_PROC_VDB_BUG_FIXED
|
|
#define PROC_VDB
|
|
#endif
|
|
#ifndef GC_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define DYNAMIC_LOADING
|
|
#if!defined(USE_MMAP)&&defined(REDIRECT_MALLOC)
|
|
#define USE_MMAP 1
|
|
#endif
|
|
#ifdef USE_MMAP
|
|
#define HEAP_START (ptr_t)0x40000000
|
|
#else
|
|
#define HEAP_START DATAEND
|
|
#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 <unistd.h>
|
|
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
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#if!defined(REDIRECT_MALLOC)
|
|
#define MPROTECT_VDB
|
|
#else
|
|
#endif
|
|
#define HEAP_START (ptr_t)0x1000
|
|
#ifdef __ELF__
|
|
#define DYNAMIC_LOADING
|
|
EXTERN_C_END
|
|
#include <features.h>
|
|
EXTERN_C_BEGIN
|
|
#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))
|
|
#endif
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#if!defined(GC_NO_SIGSETJMP)&&(defined(HOST_TIZEN)||(defined(HOST_ANDROID)&&!(GC_GNUC_PREREQ(4,8)||GC_CLANG_PREREQ(3,2)||__ANDROID_API__>=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)))
|
|
#ifdef FORCE_WRITE_PREFETCH
|
|
#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__)
|
|
#define GLIBC_2_19_TSX_BUG
|
|
EXTERN_C_END
|
|
#include <gnu/libc-version.h>
|
|
EXTERN_C_BEGIN
|
|
#endif
|
|
#endif
|
|
#ifdef CYGWIN32
|
|
#define OS_TYPE "CYGWIN32"
|
|
#define WOW64_THREAD_CONTEXT_WORKAROUND
|
|
#define RETRY_GET_THREAD_CONTEXT
|
|
#define DATASTART ((ptr_t)GC_DATASTART)
|
|
#define DATAEND ((ptr_t)GC_DATAEND)
|
|
#ifdef USE_WINALLOC
|
|
#define GWW_VDB
|
|
#else
|
|
#
|
|
#ifdef USE_MMAP
|
|
#define NEED_FIND_LIMIT
|
|
#define USE_MMAP_ANON
|
|
#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"
|
|
#define DATAEND
|
|
#endif
|
|
#ifdef MSWIN32
|
|
#define OS_TYPE "MSWIN32"
|
|
#define WOW64_THREAD_CONTEXT_WORKAROUND
|
|
#define RETRY_GET_THREAD_CONTEXT
|
|
#define MPROTECT_VDB
|
|
#define GWW_VDB
|
|
#define DATAEND
|
|
#endif
|
|
#ifdef MSWINCE
|
|
#define OS_TYPE "MSWINCE"
|
|
#define DATAEND
|
|
#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)__djgpp_stack_limit+_stklen))
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#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
|
|
#ifdef FREEBSD
|
|
#define OS_TYPE "FREEBSD"
|
|
#ifndef GC_FREEBSD_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#ifdef __GLIBC__
|
|
#define SIG_SUSPEND (32+6)
|
|
#define SIG_THR_RESTART (32+5)
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#else
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#endif
|
|
#define FREEBSD_STACKBOTTOM
|
|
#ifdef __ELF__
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
extern char etext[];
|
|
#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext)
|
|
#define DATASTART_USES_BSDGETDATASTART
|
|
#endif
|
|
#ifdef NETBSD
|
|
#define OS_TYPE "NETBSD"
|
|
#ifdef __ELF__
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#endif
|
|
#ifdef THREE86BSD
|
|
#define OS_TYPE "THREE86BSD"
|
|
#endif
|
|
#ifdef BSDI
|
|
#define OS_TYPE "BSDI"
|
|
#endif
|
|
#if defined(NETBSD)||defined(THREE86BSD)||defined(BSDI)
|
|
#define HEURISTIC2
|
|
extern char etext[];
|
|
#define DATASTART ((ptr_t)(etext))
|
|
#endif
|
|
#ifdef NEXT
|
|
#define OS_TYPE "NEXT"
|
|
#define DATASTART ((ptr_t)get_etext())
|
|
#define DATASTART_IS_FUNC
|
|
#define STACKBOTTOM ((ptr_t)0xc0000000)
|
|
#define DATAEND
|
|
#endif
|
|
#ifdef RTEMS
|
|
#define OS_TYPE "RTEMS"
|
|
EXTERN_C_END
|
|
#include <sys/unistd.h>
|
|
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)
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#endif
|
|
#ifdef DOS4GW
|
|
#define OS_TYPE "DOS4GW"
|
|
extern long __nullarea;
|
|
extern char _end;
|
|
extern char*_STACKTOP;
|
|
#pragma aux __nullarea "*";
|
|
#pragma aux _end "*";
|
|
#define STACKBOTTOM ((ptr_t)_STACKTOP)
|
|
#define DATASTART ((ptr_t)(&__nullarea))
|
|
#define DATAEND ((ptr_t)(&_end))
|
|
#endif
|
|
#ifdef HURD
|
|
#define OS_TYPE "HURD"
|
|
#define STACK_GROWS_DOWN
|
|
#define HEURISTIC2
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#define SEARCH_FOR_DATA_START
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#define DYNAMIC_LOADING
|
|
#define USE_MMAP_ANON
|
|
#endif
|
|
#ifdef DARWIN
|
|
#define OS_TYPE "DARWIN"
|
|
#define DARWIN_DONT_PARSE_STACK 1
|
|
#define DYNAMIC_LOADING
|
|
#define DATASTART ((ptr_t)get_etext())
|
|
#define DATAEND ((ptr_t)get_end())
|
|
#define STACKBOTTOM ((ptr_t)0xc0000000)
|
|
#define USE_MMAP_ANON
|
|
#define MPROTECT_VDB
|
|
EXTERN_C_END
|
|
#include <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)getpagesize()
|
|
#define NO_PTHREAD_TRYLOCK
|
|
#if TARGET_OS_IPHONE&&!defined(NO_DYLD_BIND_FULLY_IMAGE)
|
|
#define NO_DYLD_BIND_FULLY_IMAGE
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#ifdef NS32K
|
|
#define MACH_TYPE "NS32K"
|
|
#define ALIGNMENT 4
|
|
extern char**environ;
|
|
#define DATASTART ((ptr_t)(&environ))
|
|
#define STACKBOTTOM ((ptr_t)0xfffff000)
|
|
#endif
|
|
#ifdef MIPS
|
|
#define MACH_TYPE "MIPS"
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define DYNAMIC_LOADING
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
extern int _end[];
|
|
#pragma weak __data_start
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)(__data_start))
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#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
|
|
#ifdef 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
|
|
#define OS_TYPE "EWS4800"
|
|
#endif
|
|
#ifdef ULTRIX
|
|
#define HEURISTIC2
|
|
#define DATASTART ((ptr_t)0x10000000)
|
|
#define OS_TYPE "ULTRIX"
|
|
#define ALIGNMENT 4
|
|
#endif
|
|
#ifdef 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
|
|
#define OS_TYPE "IRIX5"
|
|
#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 OS_TYPE "MSWINCE"
|
|
#define ALIGNMENT 4
|
|
#define DATAEND
|
|
#endif
|
|
#ifdef NETBSD
|
|
#define OS_TYPE "NETBSD"
|
|
#define ALIGNMENT 4
|
|
#define HEURISTIC2
|
|
#ifdef __ELF__
|
|
extern ptr_t GC_data_start;
|
|
#define DATASTART GC_data_start
|
|
#define NEED_FIND_LIMIT
|
|
#define DYNAMIC_LOADING
|
|
#else
|
|
#define DATASTART ((ptr_t)0x10000000)
|
|
#define STACKBOTTOM ((ptr_t)0x7ffff000)
|
|
#endif
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#define CPP_WORDSZ 64
|
|
#define ALIGNMENT 8
|
|
#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
|
|
#ifdef FREEBSD
|
|
#define OS_TYPE "FREEBSD"
|
|
#define ALIGNMENT 4
|
|
#ifndef GC_FREEBSD_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#define FREEBSD_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
extern char etext[];
|
|
#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext)
|
|
#define DATASTART_USES_BSDGETDATASTART
|
|
#endif
|
|
#if defined(NONSTOP)
|
|
#define CPP_WORDSZ 32
|
|
#define OS_TYPE "NONSTOP"
|
|
#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
|
|
#define OS_TYPE "LINUX"
|
|
#define DYNAMIC_LOADING
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
extern int _end[];
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)(__data_start))
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#define ALIGNMENT 4
|
|
#ifndef HBLKSIZE
|
|
#define HBLKSIZE 4096
|
|
#endif
|
|
#define LINUX_STACKBOTTOM
|
|
#endif
|
|
#endif
|
|
#ifdef OR1K
|
|
#define CPP_WORDSZ 32
|
|
#define MACH_TYPE "OR1K"
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define DYNAMIC_LOADING
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
extern int _end[];
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)(__data_start))
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#define ALIGNMENT 4
|
|
#ifndef HBLKSIZE
|
|
#define HBLKSIZE 4096
|
|
#endif
|
|
#define LINUX_STACKBOTTOM
|
|
#endif
|
|
#endif
|
|
#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
|
|
#if!defined(GC_HPUX_THREADS)&&!defined(GC_LINUX_THREADS)&&!defined(OPENBSD)&&!defined(LINUX)
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define STACK_GROWS_UP
|
|
#ifdef HPUX
|
|
#define OS_TYPE "HPUX"
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)(__data_start))
|
|
#ifdef USE_MMAP
|
|
#define USE_MMAP_ANON
|
|
#endif
|
|
#ifdef USE_HPUX_FIXED_STACKBOTTOM
|
|
#define STACKBOTTOM ((ptr_t)0x7b033000)
|
|
#elif defined(USE_ENVIRON_POINTER)
|
|
extern char**environ;
|
|
#define STACKBOTTOM ((ptr_t)environ)
|
|
#elif!defined(HEURISTIC2)
|
|
#define HPUX_MAIN_STACKBOTTOM
|
|
#define NEED_FIND_LIMIT
|
|
#endif
|
|
#define DYNAMIC_LOADING
|
|
EXTERN_C_END
|
|
#include <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGE_SIZE)
|
|
#ifndef __GNUC__
|
|
#define PREFETCH(x)do { register long addr=(long)(x);(void)_asm ("LDW",0,0,addr,0);} while (0)
|
|
#endif
|
|
#endif
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
#define SEARCH_FOR_DATA_START
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(&_end))
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#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
|
|
#ifdef ALPHA
|
|
#define MACH_TYPE "ALPHA"
|
|
#define ALIGNMENT 8
|
|
#define CPP_WORDSZ 64
|
|
#ifdef NETBSD
|
|
#define OS_TYPE "NETBSD"
|
|
#define HEURISTIC2
|
|
extern ptr_t GC_data_start;
|
|
#define DATASTART GC_data_start
|
|
#define ELFCLASS32 32
|
|
#define ELFCLASS64 64
|
|
#define ELF_CLASS ELFCLASS64
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#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
|
|
#ifdef FREEBSD
|
|
#define OS_TYPE "FREEBSD"
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#define FREEBSD_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
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;
|
|
#define STACKBOTTOM ((ptr_t)(((word)(environ)|(getpagesize()-1))+1))
|
|
extern int __start[];
|
|
#define HEURISTIC2_LIMIT ((ptr_t)((word)(__start)&~(getpagesize()-1)))
|
|
#ifndef GC_OSF1_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#ifdef __ELF__
|
|
#define SEARCH_FOR_DATA_START
|
|
#define DYNAMIC_LOADING
|
|
#else
|
|
#define DATASTART ((ptr_t)0x140000000)
|
|
#endif
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#if!defined(REDIRECT_MALLOC)
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#ifdef IA64
|
|
#define MACH_TYPE "IA64"
|
|
#ifdef HPUX
|
|
#ifdef _ILP32
|
|
#define CPP_WORDSZ 32
|
|
#define ALIGNMENT 4
|
|
#else
|
|
#if!defined(_LP64)&&!defined(CPPCHECK)
|
|
#error Unknown ABI
|
|
#endif
|
|
#define CPP_WORDSZ 64
|
|
#define ALIGNMENT 8
|
|
#endif
|
|
#define OS_TYPE "HPUX"
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)(__data_start))
|
|
#ifdef USE_MMAP
|
|
#define USE_MMAP_ANON
|
|
#endif
|
|
extern char**environ;
|
|
#define STACKBOTTOM ((ptr_t)environ)
|
|
#define HPUX_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
EXTERN_C_END
|
|
#include <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGE_SIZE)
|
|
#define BACKING_STORE_DISPLACEMENT 0x1000000
|
|
#define BACKING_STORE_ALIGNMENT 0x1000
|
|
extern ptr_t GC_register_stackbottom;
|
|
#define BACKING_STORE_BASE GC_register_stackbottom
|
|
#endif
|
|
#ifdef LINUX
|
|
#define CPP_WORDSZ 64
|
|
#define ALIGNMENT 8
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
extern ptr_t GC_register_stackbottom;
|
|
#define BACKING_STORE_BASE GC_register_stackbottom
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define SEARCH_FOR_DATA_START
|
|
#ifdef __GNUC__
|
|
#define DYNAMIC_LOADING
|
|
#else
|
|
#endif
|
|
#if!defined(REDIRECT_MALLOC)
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#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 <ia64intrin.h>
|
|
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
|
|
#endif
|
|
#endif
|
|
#ifdef MSWIN32
|
|
#define OS_TYPE "MSWIN32"
|
|
#define DATAEND
|
|
#if defined(_WIN64)
|
|
#define CPP_WORDSZ 64
|
|
#else
|
|
#define CPP_WORDSZ 32
|
|
#endif
|
|
#define ALIGNMENT 8
|
|
#endif
|
|
#endif
|
|
#ifdef M88K
|
|
#define MACH_TYPE "M88K"
|
|
#define ALIGNMENT 4
|
|
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
|
|
#define STACKBOTTOM ((char*)0xf0000000)
|
|
#endif
|
|
#ifdef S370
|
|
#define MACH_TYPE "S370"
|
|
#define ALIGNMENT 4
|
|
#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
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
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
|
|
#endif
|
|
#endif
|
|
#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
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#if!defined(REDIRECT_MALLOC)
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define DYNAMIC_LOADING
|
|
#if defined(HOST_ANDROID)
|
|
#define SEARCH_FOR_DATA_START
|
|
#else
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)__data_start)
|
|
#endif
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(&_end))
|
|
#endif
|
|
#ifdef DARWIN
|
|
#define OS_TYPE "DARWIN"
|
|
#define DARWIN_DONT_PARSE_STACK 1
|
|
#define DYNAMIC_LOADING
|
|
#define DATASTART ((ptr_t)get_etext())
|
|
#define DATAEND ((ptr_t)get_end())
|
|
#define STACKBOTTOM ((ptr_t)0x16fdfffff)
|
|
#define USE_MMAP_ANON
|
|
EXTERN_C_END
|
|
#include <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)getpagesize()
|
|
#define NO_PTHREAD_TRYLOCK
|
|
#if TARGET_OS_IPHONE&&!defined(NO_DYLD_BIND_FULLY_IMAGE)
|
|
#define NO_DYLD_BIND_FULLY_IMAGE
|
|
#endif
|
|
#endif
|
|
#ifdef FREEBSD
|
|
#define OS_TYPE "FREEBSD"
|
|
#ifndef GC_FREEBSD_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define FREEBSD_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
extern char etext[];
|
|
#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext)
|
|
#define DATASTART_USES_BSDGETDATASTART
|
|
#endif
|
|
#ifdef NETBSD
|
|
#define OS_TYPE "NETBSD"
|
|
#define HEURISTIC2
|
|
extern ptr_t GC_data_start;
|
|
#define DATASTART GC_data_start
|
|
#define ELF_CLASS ELFCLASS64
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#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
|
|
#ifdef NINTENDO_SWITCH
|
|
#define OS_TYPE "NINTENDO_SWITCH"
|
|
extern int __bss_end[];
|
|
#define NO_HANDLE_FORK 1
|
|
#define DATASTART (ptr_t)ALIGNMENT
|
|
#define DATAEND (ptr_t)(&__bss_end)
|
|
void*switch_get_stack_bottom(void);
|
|
#define STACKBOTTOM ((ptr_t)switch_get_stack_bottom())
|
|
#endif
|
|
#ifdef MSWIN32
|
|
#define OS_TYPE "MSWIN32"
|
|
#ifndef DATAEND
|
|
#define DATAEND
|
|
#endif
|
|
#endif
|
|
#ifdef NOSYS
|
|
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
|
|
#define OS_TYPE "NETBSD"
|
|
#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 LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#if!defined(REDIRECT_MALLOC)
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define DYNAMIC_LOADING
|
|
EXTERN_C_END
|
|
#include <features.h>
|
|
EXTERN_C_BEGIN
|
|
#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))
|
|
#endif
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#endif
|
|
#ifdef MSWINCE
|
|
#define OS_TYPE "MSWINCE"
|
|
#define DATAEND
|
|
#endif
|
|
#ifdef FREEBSD
|
|
#define OS_TYPE "FREEBSD"
|
|
#ifndef GC_FREEBSD_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#define FREEBSD_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
extern char etext[];
|
|
#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext)
|
|
#define DATASTART_USES_BSDGETDATASTART
|
|
#endif
|
|
#ifdef DARWIN
|
|
#define OS_TYPE "DARWIN"
|
|
#define DARWIN_DONT_PARSE_STACK 1
|
|
#define DYNAMIC_LOADING
|
|
#define DATASTART ((ptr_t)get_etext())
|
|
#define DATAEND ((ptr_t)get_end())
|
|
#define STACKBOTTOM ((ptr_t)0x30000000)
|
|
#define USE_MMAP_ANON
|
|
EXTERN_C_END
|
|
#include <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)getpagesize()
|
|
#define NO_PTHREAD_TRYLOCK
|
|
#if TARGET_OS_IPHONE&&!defined(NO_DYLD_BIND_FULLY_IMAGE)
|
|
#define NO_DYLD_BIND_FULLY_IMAGE
|
|
#endif
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#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
|
|
#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
|
|
#define OS_TYPE "MSWIN32"
|
|
#ifndef DATAEND
|
|
#define DATAEND
|
|
#endif
|
|
#endif
|
|
#ifdef NOSYS
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)(__data_start))
|
|
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 OS_TYPE "LINUX"
|
|
#define DYNAMIC_LOADING
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define SEARCH_FOR_DATA_START
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#endif
|
|
#endif
|
|
#if defined(SH)&&!defined(SH4)
|
|
#define MACH_TYPE "SH"
|
|
#define ALIGNMENT 4
|
|
#ifdef MSWINCE
|
|
#define OS_TYPE "MSWINCE"
|
|
#define DATAEND
|
|
#endif
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
#define SEARCH_FOR_DATA_START
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#endif
|
|
#ifdef NETBSD
|
|
#define OS_TYPE "NETBSD"
|
|
#define HEURISTIC2
|
|
extern ptr_t GC_data_start;
|
|
#define DATASTART GC_data_start
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#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
|
|
#ifdef SH4
|
|
#define MACH_TYPE "SH4"
|
|
#define ALIGNMENT 4
|
|
#ifdef MSWINCE
|
|
#define OS_TYPE "MSWINCE"
|
|
#define DATAEND
|
|
#endif
|
|
#endif
|
|
#ifdef AVR32
|
|
#define MACH_TYPE "AVR32"
|
|
#define CPP_WORDSZ 32
|
|
#define ALIGNMENT 4
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define DYNAMIC_LOADING
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define SEARCH_FOR_DATA_START
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#endif
|
|
#endif
|
|
#ifdef M32R
|
|
#define CPP_WORDSZ 32
|
|
#define MACH_TYPE "M32R"
|
|
#define ALIGNMENT 4
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
#define SEARCH_FOR_DATA_START
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#endif
|
|
#endif
|
|
#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 SN_TARGET_ORBIS
|
|
#define OS_TYPE "SN_TARGET_ORBIS"
|
|
#define DATASTART (ptr_t)ALIGNMENT
|
|
#define DATAEND (ptr_t)ALIGNMENT
|
|
void*ps4_get_stack_bottom(void);
|
|
#define STACKBOTTOM ((ptr_t)ps4_get_stack_bottom())
|
|
#endif
|
|
#ifdef OPENBSD
|
|
#define OS_TYPE "OPENBSD"
|
|
#ifndef GC_OPENBSD_THREADS
|
|
#define HEURISTIC2
|
|
#endif
|
|
extern int __data_start[];
|
|
extern int _end[];
|
|
#define DATASTART ((ptr_t)__data_start)
|
|
#define DATAEND ((ptr_t)(&_end))
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#if!defined(REDIRECT_MALLOC)
|
|
#define MPROTECT_VDB
|
|
#else
|
|
#endif
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
EXTERN_C_END
|
|
#include <features.h>
|
|
EXTERN_C_BEGIN
|
|
#define SEARCH_FOR_DATA_START
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#if defined(__GLIBC__)&&!defined(__UCLIBC__)
|
|
#define USE_MMAP_ANON
|
|
#define GETCONTEXT_FPU_EXCMASK_BUG
|
|
#define GLIBC_2_19_TSX_BUG
|
|
EXTERN_C_END
|
|
#include <gnu/libc-version.h>
|
|
EXTERN_C_BEGIN
|
|
#endif
|
|
#endif
|
|
#ifdef DARWIN
|
|
#define OS_TYPE "DARWIN"
|
|
#define DARWIN_DONT_PARSE_STACK 1
|
|
#define DYNAMIC_LOADING
|
|
#define DATASTART ((ptr_t)get_etext())
|
|
#define DATAEND ((ptr_t)get_end())
|
|
#define STACKBOTTOM ((ptr_t)0x7fff5fc00000)
|
|
#define USE_MMAP_ANON
|
|
#define MPROTECT_VDB
|
|
EXTERN_C_END
|
|
#include <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)getpagesize()
|
|
#define NO_PTHREAD_TRYLOCK
|
|
#if TARGET_OS_IPHONE&&!defined(NO_DYLD_BIND_FULLY_IMAGE)
|
|
#define NO_DYLD_BIND_FULLY_IMAGE
|
|
#endif
|
|
#endif
|
|
#ifdef FREEBSD
|
|
#define OS_TYPE "FREEBSD"
|
|
#ifndef GC_FREEBSD_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#ifdef __GLIBC__
|
|
#define SIG_SUSPEND (32+6)
|
|
#define SIG_THR_RESTART (32+5)
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#else
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#endif
|
|
#define FREEBSD_STACKBOTTOM
|
|
#if defined(__DragonFly__)
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#endif
|
|
#define DYNAMIC_LOADING
|
|
extern char etext[];
|
|
#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext)
|
|
#define DATASTART_USES_BSDGETDATASTART
|
|
#endif
|
|
#ifdef NETBSD
|
|
#define OS_TYPE "NETBSD"
|
|
#define HEURISTIC2
|
|
extern ptr_t GC_data_start;
|
|
#define DATASTART GC_data_start
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#ifdef HAIKU
|
|
#define OS_TYPE "HAIKU"
|
|
EXTERN_C_END
|
|
#include <OS.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)B_PAGE_SIZE
|
|
#define HEURISTIC2
|
|
#define SEARCH_FOR_DATA_START
|
|
#define DYNAMIC_LOADING
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#ifdef SOLARIS
|
|
#define OS_TYPE "SOLARIS"
|
|
#define ELF_CLASS ELFCLASS64
|
|
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))
|
|
EXTERN_C_END
|
|
#include <sys/vmparam.h>
|
|
EXTERN_C_BEGIN
|
|
#ifdef USERLIMIT
|
|
#define STACKBOTTOM ((ptr_t)USRSTACK)
|
|
#else
|
|
#define HEURISTIC2
|
|
#endif
|
|
#ifdef SOLARIS25_PROC_VDB_BUG_FIXED
|
|
#define PROC_VDB
|
|
#endif
|
|
#ifndef GC_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define DYNAMIC_LOADING
|
|
#if!defined(USE_MMAP)&&defined(REDIRECT_MALLOC)
|
|
#define USE_MMAP 1
|
|
#endif
|
|
#ifdef USE_MMAP
|
|
#define HEAP_START (ptr_t)0x40000000
|
|
#else
|
|
#define HEAP_START DATAEND
|
|
#endif
|
|
#endif
|
|
#ifdef CYGWIN32
|
|
#define OS_TYPE "CYGWIN32"
|
|
#define RETRY_GET_THREAD_CONTEXT
|
|
#ifdef USE_WINALLOC
|
|
#define GWW_VDB
|
|
#else
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
#else
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#ifdef USE_MMAP
|
|
#define USE_MMAP_ANON
|
|
#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
|
|
#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 OS_TYPE "MSWIN32"
|
|
#define RETRY_GET_THREAD_CONTEXT
|
|
#if!defined(__GNUC__)||defined(__INTEL_COMPILER)||GC_GNUC_PREREQ(4,7)
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define GWW_VDB
|
|
#ifndef DATAEND
|
|
#define DATAEND
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#ifdef ARC
|
|
#define CPP_WORDSZ 32
|
|
#define MACH_TYPE "ARC"
|
|
#define ALIGNMENT 4
|
|
#define CACHE_LINE_SIZE 64
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
extern int __data_start[] __attribute__((__weak__));
|
|
#define DATASTART ((ptr_t)__data_start)
|
|
#endif
|
|
#endif
|
|
#ifdef HEXAGON
|
|
#define CPP_WORDSZ 32
|
|
#define MACH_TYPE "HEXAGON"
|
|
#define ALIGNMENT 4
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
#define LINUX_STACKBOTTOM
|
|
#if!defined(REDIRECT_MALLOC)
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
EXTERN_C_END
|
|
#include <features.h>
|
|
EXTERN_C_BEGIN
|
|
#if defined(__GLIBC__)
|
|
#define SEARCH_FOR_DATA_START
|
|
#elif!defined(CPPCHECK)
|
|
#error Unknown Hexagon libc configuration
|
|
#endif
|
|
extern int _end[];
|
|
#define DATAEND ((ptr_t)(_end))
|
|
#endif
|
|
#endif
|
|
#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
|
|
#define OS_TYPE "LINUX"
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)__data_start)
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#endif
|
|
#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
|
|
#define OS_TYPE "LINUX"
|
|
extern int __data_start[];
|
|
#define DATASTART ((ptr_t)__data_start)
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#endif
|
|
#ifdef RISCV
|
|
#define MACH_TYPE "RISC-V"
|
|
#define CPP_WORDSZ __riscv_xlen
|
|
#define ALIGNMENT (CPP_WORDSZ/8)
|
|
#ifdef FREEBSD
|
|
#define OS_TYPE "FREEBSD"
|
|
#ifndef GC_FREEBSD_THREADS
|
|
#define MPROTECT_VDB
|
|
#endif
|
|
#define SIG_SUSPEND SIGUSR1
|
|
#define SIG_THR_RESTART SIGUSR2
|
|
#define FREEBSD_STACKBOTTOM
|
|
#define DYNAMIC_LOADING
|
|
extern char etext[];
|
|
#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext)
|
|
#define DATASTART_USES_BSDGETDATASTART
|
|
#endif
|
|
#ifdef LINUX
|
|
#define OS_TYPE "LINUX"
|
|
extern int __data_start[] __attribute__((__weak__));
|
|
#define DATASTART ((ptr_t)__data_start)
|
|
#define LINUX_STACKBOTTOM
|
|
#define COUNT_UNMAPPED_REGIONS
|
|
#define DYNAMIC_LOADING
|
|
#endif
|
|
#endif
|
|
#if defined(__GLIBC__)&&!defined(DONT_USE_LIBC_PRIVATES)
|
|
#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)
|
|
#undef LINUX_STACKBOTTOM
|
|
#define HEURISTIC2
|
|
#endif
|
|
#if defined(USE_MMAP_ANON)&&!defined(USE_MMAP)
|
|
#define USE_MMAP 1
|
|
#elif (defined(LINUX)||defined(OPENBSD))&&defined(USE_MMAP)
|
|
#define USE_MMAP_ANON
|
|
#endif
|
|
#if defined(GC_LINUX_THREADS)&&defined(REDIRECT_MALLOC)&&!defined(USE_PROC_FOR_LIBRARIES)
|
|
#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
|
|
#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 <unistd.h>
|
|
EXTERN_C_BEGIN
|
|
#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGESIZE)
|
|
#endif
|
|
#ifndef GETPAGESIZE
|
|
#if defined(SOLARIS)||defined(IRIX5)||defined(LINUX)||defined(NETBSD)||defined(FREEBSD)||defined(HPUX)
|
|
EXTERN_C_END
|
|
#include <unistd.h>
|
|
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))
|
|
#define USE_TKILL_ON_ANDROID
|
|
#endif
|
|
#if defined(SOLARIS)||defined(DRSNX)||defined(UTS4)
|
|
#define SVR4
|
|
#endif
|
|
#if defined(SOLARIS)||defined(DRSNX)
|
|
#define SOLARISDL
|
|
#define SUNOS5SIGS
|
|
#endif
|
|
#if defined(HPUX)
|
|
#define SUNOS5SIGS
|
|
#endif
|
|
#if defined(FREEBSD)&&(defined(__DragonFly__)||__FreeBSD__>=4||(__FreeBSD_kernel__>=4))
|
|
#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))
|
|
#define NO_MARKER_SPECIAL_SIGMASK
|
|
#endif
|
|
#ifdef GC_NETBSD_THREADS
|
|
#define SIGRTMIN 33
|
|
#define SIGRTMAX 63
|
|
#define GC_NETBSD_THREADS_WORKAROUND
|
|
#endif
|
|
#ifdef GC_OPENBSD_THREADS
|
|
EXTERN_C_END
|
|
#include <sys/param.h>
|
|
EXTERN_C_BEGIN
|
|
#if OpenBSD < 201211
|
|
#define GC_OPENBSD_UTHREADS 1
|
|
#endif
|
|
#endif
|
|
#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
|
|
#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)
|
|
#define GC_DISABLE_INCREMENTAL
|
|
#endif
|
|
#if (defined(MSWIN32)||defined(MSWINCE))&&!defined(USE_WINALLOC)
|
|
#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))
|
|
#define MMAP_SUPPORTED
|
|
#endif
|
|
#if defined(USE_MUNMAP)&&!defined(MUNMAP_THRESHOLD)&&(defined(SN_TARGET_ORBIS)||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)
|
|
#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
|
|
#endif
|
|
#ifdef GC_DISABLE_INCREMENTAL
|
|
#undef CHECKSUMS
|
|
#endif
|
|
#ifdef USE_GLOBAL_ALLOC
|
|
#undef GWW_VDB
|
|
#endif
|
|
#if defined(BASE_ATOMIC_OPS_EMULATED)
|
|
#undef MPROTECT_VDB
|
|
#endif
|
|
#if defined(USE_PROC_FOR_LIBRARIES)&&defined(GC_LINUX_THREADS)
|
|
#undef MPROTECT_VDB
|
|
#endif
|
|
#if defined(MPROTECT_VDB)&&defined(GC_PREFER_MPROTECT_VDB)
|
|
#undef PCR_VDB
|
|
#undef PROC_VDB
|
|
#endif
|
|
#ifdef PROC_VDB
|
|
#undef MPROTECT_VDB
|
|
#endif
|
|
#if defined(MPROTECT_VDB)&&!defined(MSWIN32)&&!defined(MSWINCE)
|
|
#include <signal.h>
|
|
#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(DEFAULT_VDB)&&!defined(GC_DISABLE_INCREMENTAL)
|
|
#define DEFAULT_VDB
|
|
#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
|
|
#endif
|
|
#ifndef STATIC
|
|
#ifdef GC_ASSERTIONS
|
|
#define STATIC
|
|
#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
|
|
#endif
|
|
#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)
|
|
#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
|
|
#if defined(PCR)||defined(GC_WIN32_THREADS)||defined(GC_PTHREADS)||((defined(NN_PLATFORM_CTR)||defined(NINTENDO_SWITCH)||defined(SN_TARGET_ORBIS)||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(__GNUC__))||(defined(MSWIN32)&&defined(I386))||(defined(USE_PROC_FOR_LIBRARIES)&&defined(THREADS)))&&!defined(NO_CRT)&&!defined(NO_WRAP_MARK_SOME)
|
|
#define WRAP_MARK_SOME
|
|
#endif
|
|
#if defined(PARALLEL_MARK)&&!defined(DEFAULT_STACK_MAYBE_SMALL)&&(defined(HPUX)||defined(GC_DGUX386_THREADS)||defined(NO_GETCONTEXT))
|
|
#define DEFAULT_STACK_MAYBE_SMALL
|
|
#endif
|
|
#ifdef PARALLEL_MARK
|
|
#define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word))
|
|
#endif
|
|
#if defined(HOST_ANDROID)&&!defined(THREADS)&&!defined(USE_GET_STACKBASE_FOR_MAIN)
|
|
#define USE_GET_STACKBASE_FOR_MAIN
|
|
#endif
|
|
#if ((defined(FREEBSD)&&defined(__GLIBC__))||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
|
|
#define HAVE_PTHREAD_ATTR_GET_NP 1
|
|
#endif
|
|
#if defined(UNIX_LIKE)&&defined(THREADS)&&!defined(NO_CANCEL_SAFE)&&!defined(HOST_ANDROID)
|
|
#define CANCEL_SAFE
|
|
#endif
|
|
#ifdef CANCEL_SAFE
|
|
#define IF_CANCEL(x)x
|
|
#else
|
|
#define IF_CANCEL(x)
|
|
#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))
|
|
#define CAN_HANDLE_FORK
|
|
#endif
|
|
#if defined(CAN_HANDLE_FORK)&&!defined(CAN_CALL_ATFORK)&&!defined(HURD)&&!defined(SN_TARGET_ORBIS)&&!defined(HOST_TIZEN)&&(!defined(HOST_ANDROID)||__ANDROID_API__>=21)
|
|
#define CAN_CALL_ATFORK
|
|
#endif
|
|
#if!defined(CAN_HANDLE_FORK)&&!defined(HAVE_NO_FORK)&&(defined(MSWIN32)||defined(MSWINCE)||defined(DOS4GW)||defined(OS2)||defined(SYMBIAN))
|
|
#define HAVE_NO_FORK
|
|
#endif
|
|
#if!defined(USE_MARK_BITS)&&!defined(USE_MARK_BYTES)&&defined(PARALLEL_MARK)
|
|
#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)
|
|
#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
|
|
#define STRTOULL strtoul
|
|
#endif
|
|
#endif
|
|
#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
|
|
#if defined(__has_feature)
|
|
#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__
|
|
#define ADDRESS_SANITIZER
|
|
#endif
|
|
#endif
|
|
#if defined(SPARC)
|
|
#define ASM_CLEAR_CODE
|
|
#endif
|
|
#if defined(SPARC)
|
|
#define CAN_SAVE_CALL_ARGS
|
|
#endif
|
|
#if (defined(I386)||defined(X86_64))&&(defined(LINUX)||defined(__GLIBC__))
|
|
#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
|
|
#endif
|
|
#endif
|
|
#ifdef SAVE_CALL_CHAIN
|
|
#if!defined(SAVE_CALL_COUNT)||defined(CPPCHECK)
|
|
#define NFRAMES 6
|
|
#else
|
|
#define NFRAMES ((SAVE_CALL_COUNT+1)&~1)
|
|
#endif
|
|
#define NEED_CALLINFO
|
|
#endif
|
|
#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)(-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
|
|
#endif
|
|
#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)
|
|
#error REDIRECT_MALLOC with THREADS works at most on Linux
|
|
#endif
|
|
#endif
|
|
#ifdef GC_PRIVATE_H
|
|
struct hblk;
|
|
#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(SN_TARGET_ORBIS)
|
|
void*ps4_get_mem(size_t bytes);
|
|
#define GET_MEM(bytes)(struct hblk*)ps4_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)
|
|
#else
|
|
ptr_t GC_unix_get_mem(size_t bytes);
|
|
#define GET_MEM(bytes)(struct hblk*)GC_unix_get_mem(bytes)
|
|
#endif
|
|
#endif
|
|
EXTERN_C_END
|
|
#endif
|
|
#if!defined(GC_ATOMIC_UNCOLLECTABLE)&&defined(ATOMIC_UNCOLLECTABLE)
|
|
#define GC_ATOMIC_UNCOLLECTABLE
|
|
#endif
|
|
#ifndef GC_INNER
|
|
#if defined(GC_DLL)&&defined(__GNUC__)&&!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32)
|
|
#if GC_GNUC_PREREQ(4,0)&&!defined(GC_NO_VISIBILITY)
|
|
#define GC_INNER __attribute__((__visibility__("hidden")))
|
|
#else
|
|
#define GC_INNER
|
|
#endif
|
|
#else
|
|
#define GC_INNER
|
|
#endif
|
|
#define GC_EXTERN extern GC_INNER
|
|
#endif
|
|
#ifdef __cplusplus
|
|
#define REGISTER
|
|
#else
|
|
#define REGISTER register
|
|
#endif
|
|
#if defined(CPPCHECK)
|
|
#define MACRO_BLKSTMT_BEGIN {
|
|
#define MACRO_BLKSTMT_END }
|
|
#else
|
|
#define MACRO_BLKSTMT_BEGIN do {
|
|
#define MACRO_BLKSTMT_END } while (0)
|
|
#endif
|
|
#if defined(M68K)&&defined(__GNUC__)
|
|
#define GC_ATTR_WORD_ALIGNED __attribute__((__aligned__(sizeof(word))))
|
|
#else
|
|
#define GC_ATTR_WORD_ALIGNED
|
|
#endif
|
|
#ifndef HEADERS_H
|
|
#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;
|
|
#if CPP_WORDSZ > 32
|
|
#define HASH_TL
|
|
#endif
|
|
#if defined(LARGE_CONFIG)||!defined(SMALL_CONFIG)
|
|
#define LOG_BOTTOM_SZ 10
|
|
#else
|
|
#define LOG_BOTTOM_SZ 11
|
|
#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)
|
|
#ifdef COUNT_HDR_CACHE_HITS
|
|
extern word GC_hdr_cache_hits;
|
|
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()
|
|
#define HC_MISS()
|
|
#endif
|
|
typedef struct hce {
|
|
word block_addr;
|
|
hdr*hce_hdr;
|
|
} hdr_cache_entry;
|
|
#define HDR_CACHE_SIZE 8
|
|
#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
|
|
#define HC_GET_HDR(p,hhdr,source){ 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;} }
|
|
typedef struct bi {
|
|
hdr*index[BOTTOM_SZ];
|
|
struct bi*asc_link;
|
|
struct bi*desc_link;
|
|
word key;
|
|
#ifdef HASH_TL
|
|
struct bi*hash_link;
|
|
#endif
|
|
} bottom_index;
|
|
#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
|
|
#define TL_HASH(hi)((hi)&(TOP_SZ - 1))
|
|
#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
|
|
#define IS_FORWARDING_ADDR_OR_NIL(hhdr)((size_t)(hhdr)<=MAX_JUMP)
|
|
#define FORWARDED_ADDR(h,hhdr)((struct hblk*)(h)- (size_t)(hhdr))
|
|
EXTERN_C_END
|
|
#endif
|
|
#endif
|
|
#ifndef GC_ATTR_NO_SANITIZE_ADDR
|
|
#ifndef ADDRESS_SANITIZER
|
|
#define GC_ATTR_NO_SANITIZE_ADDR
|
|
#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
|
|
#ifndef GC_ATTR_NO_SANITIZE_MEMORY
|
|
#ifndef MEMORY_SANITIZER
|
|
#define GC_ATTR_NO_SANITIZE_MEMORY
|
|
#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
|
|
#ifndef GC_ATTR_NO_SANITIZE_THREAD
|
|
#ifndef THREAD_SANITIZER
|
|
#define GC_ATTR_NO_SANITIZE_THREAD
|
|
#elif GC_CLANG_PREREQ(3,8)
|
|
#define GC_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
|
|
#else
|
|
#define GC_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
|
|
#endif
|
|
#endif
|
|
#ifndef GC_ATTR_UNUSED
|
|
#if GC_GNUC_PREREQ(3,4)
|
|
#define GC_ATTR_UNUSED __attribute__((__unused__))
|
|
#else
|
|
#define GC_ATTR_UNUSED
|
|
#endif
|
|
#endif
|
|
#ifdef HAVE_CONFIG_H
|
|
#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
|
|
#endif
|
|
#endif
|
|
#ifndef GC_API_OSCALL
|
|
#if defined(__GNUC__)
|
|
#if GC_GNUC_PREREQ(4,0)&&!defined(GC_NO_VISIBILITY)
|
|
#define GC_API_OSCALL extern __attribute__((__visibility__("default")))
|
|
#else
|
|
#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)
|
|
#ifndef GC_ATOMIC_OPS_H
|
|
#define GC_ATOMIC_OPS_H
|
|
#ifdef GC_BUILTIN_ATOMIC
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
typedef GC_word AO_t;
|
|
#ifdef GC_PRIVATE_H
|
|
#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
|
|
#endif
|
|
#define AO_CLEAR(p)__atomic_clear(p,__ATOMIC_RELEASE)
|
|
#define AO_test_and_set_acquire(p)__atomic_test_and_set(p,__ATOMIC_ACQUIRE)
|
|
#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
|
|
}
|
|
#endif
|
|
#ifndef NO_LOCKFREE_AO_OR
|
|
#define HAVE_LOCKFREE_AO_OR 1
|
|
#endif
|
|
#else
|
|
#include "atomic_ops.h"
|
|
#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
|
|
#endif
|
|
#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 <windows.h>
|
|
#include <winbase.h>
|
|
#endif
|
|
#ifndef GC_LOCKS_H
|
|
#define GC_LOCKS_H
|
|
#ifdef THREADS
|
|
#ifdef PCR
|
|
#include <base/PCR_Base.h>
|
|
#include <th/PCR_Th.h>
|
|
#endif
|
|
EXTERN_C_BEGIN
|
|
#ifdef PCR
|
|
GC_EXTERN PCR_Th_ML GC_allocate_ml;
|
|
#if defined(CPPCHECK)
|
|
#define DCL_LOCK_STATE
|
|
#else
|
|
#define DCL_LOCK_STATE PCR_ERes GC_fastLockRes;PCR_sigset_t GC_old_sig_mask
|
|
#endif
|
|
#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_ORBIS)||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
|
|
#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
|
|
#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
|
|
#elif defined(GC_PTHREADS)
|
|
EXTERN_C_END
|
|
#include <pthread.h>
|
|
EXTERN_C_BEGIN
|
|
#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)
|
|
#define NUMERIC_THREAD_ID(id)((unsigned long)(id))
|
|
#define THREAD_EQUAL(id1,id2)((id1)==(id2))
|
|
#ifndef _WIN64
|
|
#define NUMERIC_THREAD_ID_UNIQUE
|
|
#endif
|
|
#else
|
|
#define NUMERIC_THREAD_ID(id)((unsigned long)(word)(id.p))
|
|
#define THREAD_EQUAL(id1,id2)((id1.p==id2.p)&&(id1.x==id2.x))
|
|
#undef NUMERIC_THREAD_ID_UNIQUE
|
|
#endif
|
|
#define NO_THREAD ((unsigned long)(-1l))
|
|
#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)
|
|
#undef USE_SPIN_LOCK
|
|
#define USE_SPIN_LOCK
|
|
GC_EXTERN volatile AO_TS_t GC_allocate_lock;
|
|
GC_INNER void GC_lock(void);
|
|
#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
|
|
#else
|
|
#ifndef USE_PTHREAD_LOCKS
|
|
#define USE_PTHREAD_LOCKS
|
|
#endif
|
|
#endif
|
|
#ifdef USE_PTHREAD_LOCKS
|
|
EXTERN_C_END
|
|
#include <pthread.h>
|
|
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
|
|
#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
|
|
#endif
|
|
#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
|
|
#else
|
|
#define I_DONT_HOLD_LOCK()(!GC_need_to_lock||GC_lock_holder!=NUMERIC_THREAD_ID(pthread_self()))
|
|
#endif
|
|
#endif
|
|
#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
|
|
#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
|
|
#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)
|
|
#endif
|
|
#endif
|
|
EXTERN_C_END
|
|
#else
|
|
#define LOCK()(void)0
|
|
#define UNLOCK()(void)0
|
|
#ifdef GC_ASSERTIONS
|
|
#define I_HOLD_LOCK()TRUE
|
|
#define I_DONT_HOLD_LOCK()TRUE
|
|
#endif
|
|
#endif
|
|
#if defined(UNCOND_LOCK)&&!defined(LOCK)
|
|
#if (defined(LINT2)&&defined(USE_PTHREAD_LOCKS))||defined(GC_ALWAYS_MULTITHREADED)
|
|
#define LOCK()UNCOND_LOCK()
|
|
#define UNLOCK()UNCOND_UNLOCK()
|
|
#else
|
|
#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
|
|
#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
|
|
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);
|
|
GC_INNER void GC_finalize(void);
|
|
#ifndef GC_TOGGLE_REFS_NOT_NEEDED
|
|
GC_INNER void GC_process_togglerefs(void);
|
|
#endif
|
|
#ifndef SMALL_CONFIG
|
|
GC_INNER void GC_print_finalization_stats(void);
|
|
#endif
|
|
#else
|
|
#define GC_INVOKE_FINALIZERS()(void)0
|
|
#endif
|
|
#if!defined(DONT_ADD_BYTE_AT_END)
|
|
#ifdef LINT2
|
|
#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
|
|
#define MAXHINCR 2048
|
|
#else
|
|
#define MINHINCR 64
|
|
#define MAXHINCR 4096
|
|
#endif
|
|
#define BL_LIMIT GC_black_list_spacing
|
|
#ifdef NEED_CALLINFO
|
|
struct callinfo {
|
|
word ci_pc;
|
|
#if NARGS > 0
|
|
word ci_arg[NARGS];
|
|
#endif
|
|
#if (NFRAMES*(NARGS+1))% 2==1
|
|
word ci_dummy;
|
|
#endif
|
|
};
|
|
#endif
|
|
#ifdef SAVE_CALL_CHAIN
|
|
GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]);
|
|
GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]);
|
|
#endif
|
|
EXTERN_C_END
|
|
#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)))
|
|
#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)
|
|
#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)||!QueryPerformanceCounter(&tc))ABORT("QueryPerformanceCounter requires WinXP+");x=(CLOCK_TYPE)((double)tc.QuadPart/freq.QuadPart*1e9);} while (0)
|
|
#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
|
|
#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
|
|
#elif defined(NINTENDO_SWITCH)||(((defined(LINUX)&&defined(__USE_POSIX199309))||defined(CYGWIN32))&&defined(_POSIX_TIMERS))
|
|
#include <time.h>
|
|
#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)((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
|
|
#include <time.h>
|
|
#if defined(FREEBSD)&&!defined(CLOCKS_PER_SEC)
|
|
#include <machine/limits.h>
|
|
#define CLOCKS_PER_SEC CLK_TCK
|
|
#endif
|
|
#if!defined(CLOCKS_PER_SEC)
|
|
#define CLOCKS_PER_SEC 1000000
|
|
#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)
|
|
#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
|
|
#ifndef CLOCK_TYPE_INITIALIZER
|
|
#define CLOCK_TYPE_INITIALIZER 0
|
|
#endif
|
|
#endif
|
|
#if defined(SPARC)&&defined(SUNOS4)||(defined(M68K)&&defined(NEXT))||defined(VAX)
|
|
#define BCOPY_EXISTS
|
|
#elif defined(AMIGA)||defined(DARWIN)
|
|
#include <string.h>
|
|
#define BCOPY_EXISTS
|
|
#elif defined(MACOS)&&defined(POWERPC)
|
|
#include <MacMemory.h>
|
|
#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 <string.h>
|
|
#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
|
|
#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
|
|
#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
|
|
#if defined(SMALL_CONFIG)||defined(PCR)
|
|
#define GC_on_abort(msg)(void)0
|
|
#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)))
|
|
#define DebugBreak()_exit(-1)
|
|
#endif
|
|
#if defined(MSWIN32)&&(defined(NO_DEBUGGING)||defined(LINT2))
|
|
#define ABORT(msg)(GC_on_abort(msg),_exit(-1))
|
|
#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();}
|
|
#else
|
|
#define ABORT(msg){ GC_on_abort(msg);DebugBreak();}
|
|
#endif
|
|
#else
|
|
#define ABORT(msg)(GC_on_abort(msg),abort())
|
|
#endif
|
|
#endif
|
|
#define ABORT_ARG1(C_msg,C_fmt,arg1)MACRO_BLKSTMT_BEGIN GC_INFOLOG_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_INFOLOG_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_INFOLOG_PRINTF(C_msg C_fmt "\n",arg1,arg2,arg3);ABORT(C_msg);MACRO_BLKSTMT_END
|
|
#define ABORT_RET(msg)if ((signed_word)GC_current_warn_proc==-1){} else ABORT(msg)
|
|
#ifdef PCR
|
|
#define EXIT()PCR_Base_Exit(1,PCR_waitForever)
|
|
#else
|
|
#define EXIT()(GC_on_abort(NULL),exit(1))
|
|
#endif
|
|
#define WARN(msg,arg)(*GC_current_warn_proc)(( char*)("GC Warning:" msg),(word)(arg))
|
|
GC_EXTERN GC_warn_proc GC_current_warn_proc;
|
|
#ifndef WARN_PRIdPTR
|
|
#define WARN_PRIdPTR "ld"
|
|
#endif
|
|
#define TRUSTED_STRING(s)(char*)COVERT_DATAFLOW(s)
|
|
#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)
|
|
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 <mach/thread_status.h>
|
|
#ifndef MAC_OS_X_VERSION_MAX_ALLOWED
|
|
#include <AvailabilityMacros.h>
|
|
#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)
|
|
#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
|
|
#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
|
|
#include <setjmp.h>
|
|
#if __STDC_VERSION__>=201112L
|
|
#include <assert.h>
|
|
#endif
|
|
EXTERN_C_BEGIN
|
|
#if CPP_WORDSZ==32
|
|
#define WORDS_TO_BYTES(x)((x)<<2)
|
|
#define BYTES_TO_WORDS(x)((x)>>2)
|
|
#define LOGWL ((word)5)
|
|
#define modWORDSZ(n)((n)&0x1f)
|
|
#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)
|
|
#define modWORDSZ(n)((n)&0x3f)
|
|
#if ALIGNMENT!=8
|
|
#define UNALIGNED_PTRS
|
|
#endif
|
|
#endif
|
|
#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)
|
|
#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
|
|
#ifndef HBLKSIZE
|
|
#if defined(LARGE_CONFIG)||!defined(SMALL_CONFIG)
|
|
#ifdef ALPHA
|
|
#define CPP_LOG_HBLKSIZE 13
|
|
#elif defined(SN_TARGET_ORBIS)||defined(SN_TARGET_PSP2)
|
|
#define CPP_LOG_HBLKSIZE 16
|
|
#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)
|
|
#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)
|
|
#define modHBLKSZ(n)((n)&(HBLKSIZE-1))
|
|
#define HBLKPTR(objptr)((struct hblk*)(((word)(objptr))&~(word)(HBLKSIZE-1)))
|
|
#define HBLKDISPL(objptr)(((size_t)(objptr))&(HBLKSIZE-1))
|
|
#define ROUNDUP_GRANULE_SIZE(lb)(SIZET_SAT_ADD(lb,GRANULE_BYTES - 1)&~(GRANULE_BYTES - 1))
|
|
#define ROUNDED_UP_GRANULES(lb)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)
|
|
#endif
|
|
#define ADD_SLOP(lb)SIZET_SAT_ADD(lb,EXTRA_BYTES)
|
|
#ifdef LARGE_CONFIG
|
|
#if CPP_WORDSZ==32
|
|
#define LOG_PHT_ENTRIES 20
|
|
#else
|
|
#define LOG_PHT_ENTRIES 21
|
|
#endif
|
|
#elif!defined(SMALL_CONFIG)
|
|
#define LOG_PHT_ENTRIES 18
|
|
#else
|
|
#define LOG_PHT_ENTRIES 15
|
|
#endif
|
|
#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)
|
|
#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
|
|
#define HBLKMASK (HBLKSIZE-1)
|
|
#define MARK_BITS_PER_HBLK (HBLKSIZE/GRANULE_BYTES)
|
|
union word_ptr_ao_u {
|
|
word w;
|
|
signed_word sw;
|
|
void*vp;
|
|
#ifdef PARALLEL_MARK
|
|
volatile AO_t ao;
|
|
#endif
|
|
};
|
|
struct hblkhdr {
|
|
struct hblk*hb_next;
|
|
struct hblk*hb_prev;
|
|
struct hblk*hb_block;
|
|
unsigned char hb_obj_kind;
|
|
unsigned char hb_flags;
|
|
#define IGNORE_OFF_PAGE 1
|
|
#define WAS_UNMAPPED 2
|
|
#define FREE_BLK 4
|
|
#ifdef ENABLE_DISCLAIM
|
|
#define HAS_DISCLAIM 8
|
|
#define MARK_UNCONDITIONALLY 0x10
|
|
#endif
|
|
#ifdef MARK_BIT_PER_GRANULE
|
|
#define LARGE_BLOCK 0x20
|
|
#endif
|
|
unsigned short hb_last_reclaimed;
|
|
#ifdef MARK_BIT_PER_OBJ
|
|
unsigned32 hb_inv_sz;
|
|
#define LARGE_INV_SZ (1<<16)
|
|
#endif
|
|
word hb_sz;
|
|
word hb_descr;
|
|
#ifdef MARK_BIT_PER_GRANULE
|
|
unsigned short*hb_map;
|
|
#endif
|
|
#ifdef PARALLEL_MARK
|
|
volatile AO_t hb_n_marks;
|
|
#else
|
|
size_t hb_n_marks;
|
|
#endif
|
|
#ifdef USE_MARK_BYTES
|
|
#define MARK_BITS_SZ (MARK_BITS_PER_HBLK+1)
|
|
union {
|
|
char _hb_marks[MARK_BITS_SZ];
|
|
word dummy;
|
|
} _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
|
|
};
|
|
#define ANY_INDEX 23
|
|
#define HBLK_WORDS (HBLKSIZE/sizeof(word))
|
|
#define HBLK_GRANULES (HBLKSIZE/GRANULE_BYTES)
|
|
#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)divHBLKSZ(SIZET_SAT_ADD(lb,HBLKSIZE - 1))
|
|
#define obj_link(p)(*(void**)(p))
|
|
#define LOG_MAX_MARK_PROCS 6
|
|
#define MAX_MARK_PROCS (1<<LOG_MAX_MARK_PROCS)
|
|
#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)
|
|
struct exclusion {
|
|
ptr_t e_start;
|
|
ptr_t e_end;
|
|
};
|
|
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;
|
|
};
|
|
#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32)
|
|
#define LOG_RT_SIZE 6
|
|
#define RT_SIZE (1<<LOG_RT_SIZE)
|
|
#endif
|
|
#ifndef MAX_HEAP_SECTS
|
|
#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
|
|
#endif
|
|
#elif CPP_WORDSZ > 32
|
|
#define MAX_HEAP_SECTS 1024
|
|
#else
|
|
#define MAX_HEAP_SECTS 512
|
|
#endif
|
|
#endif
|
|
typedef struct GC_ms_entry {
|
|
ptr_t mse_start;
|
|
union word_ptr_ao_u mse_descr;
|
|
} mse;
|
|
typedef int mark_state_t;
|
|
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;
|
|
struct finalizable_object*finalize_now;
|
|
};
|
|
union toggle_ref_u {
|
|
void*strong_ref;
|
|
GC_hidden_pointer weak_ref;
|
|
};
|
|
typedef struct {
|
|
word ed_bitmap;
|
|
GC_bool ed_continued;
|
|
} typed_ext_descr_t;
|
|
struct _GC_arrays {
|
|
word _heapsize;
|
|
word _requested_heapsize;
|
|
ptr_t _last_heap_addr;
|
|
ptr_t _prev_heap_addr;
|
|
word _large_free_bytes;
|
|
word _large_allocd_bytes;
|
|
word _max_large_allocd_bytes;
|
|
word _bytes_allocd_before_gc;
|
|
#ifndef SEPARATE_GLOBALS
|
|
#define GC_bytes_allocd GC_arrays._bytes_allocd
|
|
word _bytes_allocd;
|
|
#endif
|
|
word _bytes_dropped;
|
|
word _bytes_finalized;
|
|
word _bytes_freed;
|
|
word _finalizer_bytes_freed;
|
|
bottom_index*_all_bottom_indices;
|
|
bottom_index*_all_bottom_indices_end;
|
|
ptr_t _scratch_free_ptr;
|
|
hdr*_hdr_free_list;
|
|
ptr_t _scratch_end_ptr;
|
|
ptr_t _scratch_last_end_ptr;
|
|
mse*_mark_stack;
|
|
mse*_mark_stack_limit;
|
|
#ifdef PARALLEL_MARK
|
|
mse*volatile _mark_stack_top;
|
|
#else
|
|
mse*_mark_stack_top;
|
|
#endif
|
|
word _composite_in_use;
|
|
word _atomic_in_use;
|
|
#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;
|
|
#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;
|
|
#define GC_mark_stack_too_small GC_arrays._mark_stack_too_small
|
|
GC_bool _mark_stack_too_small;
|
|
#define GC_objects_are_marked GC_arrays._objects_are_marked
|
|
GC_bool _objects_are_marked;
|
|
#ifdef ENABLE_TRACE
|
|
#define GC_trace_addr GC_arrays._trace_addr
|
|
ptr_t _trace_addr;
|
|
#endif
|
|
#define GC_n_heap_sects GC_arrays._n_heap_sects
|
|
word _n_heap_sects;
|
|
#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)
|
|
#define GC_n_heap_bases GC_arrays._n_heap_bases
|
|
word _n_heap_bases;
|
|
#endif
|
|
#ifdef USE_PROC_FOR_LIBRARIES
|
|
#define GC_n_memory GC_arrays._n_memory
|
|
word _n_memory;
|
|
#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;
|
|
size_t _excl_table_entries;
|
|
#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;
|
|
size_t _avail_descr;
|
|
typed_ext_descr_t*_ext_descriptors;
|
|
GC_mark_proc _mark_procs[MAX_MARK_PROCS];
|
|
char _modws_valid_offsets[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];
|
|
#endif
|
|
#ifndef SEPARATE_GLOBALS
|
|
#define GC_objfreelist GC_arrays._objfreelist
|
|
void*_objfreelist[MAXOBJGRANULES+1];
|
|
#define GC_aobjfreelist GC_arrays._aobjfreelist
|
|
void*_aobjfreelist[MAXOBJGRANULES+1];
|
|
#endif
|
|
void*_uobjfreelist[MAXOBJGRANULES+1];
|
|
#ifdef GC_ATOMIC_UNCOLLECTABLE
|
|
#define GC_auobjfreelist GC_arrays._auobjfreelist
|
|
void*_auobjfreelist[MAXOBJGRANULES+1];
|
|
#endif
|
|
size_t _size_map[MAXOBJBYTES+1];
|
|
#ifdef MARK_BIT_PER_GRANULE
|
|
#define GC_obj_map GC_arrays._obj_map
|
|
unsigned short*_obj_map[MAXOBJGRANULES+1];
|
|
#define MAP_LEN BYTES_TO_GRANULES(HBLKSIZE)
|
|
#endif
|
|
#define VALID_OFFSET_SZ HBLKSIZE
|
|
char _valid_offsets[VALID_OFFSET_SZ];
|
|
#ifndef GC_DISABLE_INCREMENTAL
|
|
#define GC_grungy_pages GC_arrays._grungy_pages
|
|
page_hash_table _grungy_pages;
|
|
#define GC_dirty_pages GC_arrays._dirty_pages
|
|
volatile page_hash_table _dirty_pages;
|
|
#endif
|
|
#if (defined(CHECKSUMS)&&defined(GWW_VDB))||defined(PROC_VDB)
|
|
#define GC_written_pages GC_arrays._written_pages
|
|
page_hash_table _written_pages;
|
|
#endif
|
|
#define GC_heap_sects GC_arrays._heap_sects
|
|
struct HeapSect {
|
|
ptr_t hs_start;
|
|
size_t hs_bytes;
|
|
} _heap_sects[MAX_HEAP_SECTS];
|
|
#if defined(USE_PROC_FOR_LIBRARIES)
|
|
#define GC_our_memory GC_arrays._our_memory
|
|
struct HeapSect _our_memory[MAX_HEAP_SECTS];
|
|
#endif
|
|
#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)
|
|
#define GC_heap_bases GC_arrays._heap_bases
|
|
ptr_t _heap_bases[MAX_HEAP_SECTS];
|
|
#endif
|
|
#ifdef MSWINCE
|
|
#define GC_heap_lengths GC_arrays._heap_lengths
|
|
word _heap_lengths[MAX_HEAP_SECTS];
|
|
#endif
|
|
struct roots _static_roots[MAX_ROOT_SETS];
|
|
struct exclusion _excl_table[MAX_EXCLUSIONS];
|
|
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_prev_heap_addr GC_arrays._prev_heap_addr
|
|
#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_scratch_last_end_ptr GC_arrays._scratch_last_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)
|
|
#ifndef MAXOBJKINDS
|
|
#define MAXOBJKINDS 16
|
|
#endif
|
|
GC_EXTERN struct obj_kind {
|
|
void**ok_freelist;
|
|
struct hblk**ok_reclaim_list;
|
|
word ok_descriptor;
|
|
GC_bool ok_relocate_descr;
|
|
GC_bool ok_init;
|
|
#ifdef ENABLE_DISCLAIM
|
|
GC_bool ok_mark_unconditionally;
|
|
int (GC_CALLBACK*ok_disclaim_proc)(void*);
|
|
#define OK_DISCLAIM_INITZ,FALSE,0
|
|
#else
|
|
#define OK_DISCLAIM_INITZ
|
|
#endif
|
|
} GC_obj_kinds[MAXOBJKINDS];
|
|
#define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds))
|
|
#define endGC_obj_kinds (beginGC_obj_kinds+(sizeof GC_obj_kinds))
|
|
#ifdef SEPARATE_GLOBALS
|
|
extern word GC_bytes_allocd;
|
|
extern ptr_t GC_objfreelist[MAXOBJGRANULES+1];
|
|
#define beginGC_objfreelist ((ptr_t)(&GC_objfreelist))
|
|
#define endGC_objfreelist (beginGC_objfreelist+sizeof(GC_objfreelist))
|
|
extern ptr_t GC_aobjfreelist[MAXOBJGRANULES+1];
|
|
#define beginGC_aobjfreelist ((ptr_t)(&GC_aobjfreelist))
|
|
#define endGC_aobjfreelist (beginGC_aobjfreelist+sizeof(GC_aobjfreelist))
|
|
#endif
|
|
#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;
|
|
#define ROUNDUP_PAGESIZE(lb)(SIZET_SAT_ADD(lb,GC_page_size - 1)&~(GC_page_size - 1))
|
|
#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;
|
|
#ifdef GC_GCJ_SUPPORT
|
|
extern struct hblk*GC_hblkfreelist[];
|
|
extern word GC_free_bytes[];
|
|
#endif
|
|
GC_EXTERN word GC_root_size;
|
|
GC_EXTERN GC_bool GC_debugging_started;
|
|
struct blocking_data {
|
|
GC_fn_type fn;
|
|
void*client_data;
|
|
};
|
|
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
|
|
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;
|
|
#else
|
|
GC_EXTERN ptr_t GC_blocked_sp;
|
|
GC_EXTERN struct GC_traced_stack_sect_s*GC_traced_stack_sect;
|
|
#endif
|
|
#ifdef IA64
|
|
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
|
|
#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
|
|
#if defined(PARALLEL_MARK)||(defined(THREAD_SANITIZER)&&defined(THREADS))
|
|
#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
|
|
#ifdef MARK_BIT_PER_OBJ
|
|
#define MARK_BIT_NO(offset,sz)(((word)(offset))/(sz))
|
|
#define MARK_BIT_OFFSET(sz)1
|
|
#define IF_PER_OBJ(x)x
|
|
#define FINAL_MARK_BIT(sz)((sz)> MAXOBJBYTES?1:HBLK_OBJS(sz))
|
|
#else
|
|
#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
|
|
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);
|
|
GC_INNER struct hblk*GC_next_block(struct hblk*h,GC_bool allow_free);
|
|
GC_INNER struct hblk*GC_prev_block(struct hblk*h);
|
|
GC_INNER void GC_mark_init(void);
|
|
GC_INNER void GC_clear_marks(void);
|
|
GC_INNER void GC_invalidate_mark_state(void);
|
|
GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame);
|
|
GC_INNER void GC_initiate_gc(void);
|
|
GC_INNER GC_bool GC_collection_in_progress(void);
|
|
#define GC_PUSH_ALL_SYM(sym)GC_push_all(( void*)&(sym),( void*)(&(sym)+1))
|
|
GC_INNER void GC_push_all_stack(ptr_t b,ptr_t t);
|
|
#if defined(WRAP_MARK_SOME)&&defined(PARALLEL_MARK)
|
|
GC_INNER void GC_push_conditional_eager(void*bottom,void*top,
|
|
GC_bool all);
|
|
#endif
|
|
GC_INNER void GC_push_roots(GC_bool all,ptr_t cold_gc_frame);
|
|
GC_API_PRIV GC_push_other_roots_proc GC_push_other_roots;
|
|
#ifdef THREADS
|
|
void GC_push_thread_structures(void);
|
|
#endif
|
|
GC_EXTERN void (*GC_push_typed_structures)(void);
|
|
GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t,void*),
|
|
volatile ptr_t arg);
|
|
#if defined(SPARC)||defined(IA64)
|
|
ptr_t GC_save_regs_in_stack(void);
|
|
#endif
|
|
#if defined(AMIGA)||defined(MACOS)||defined(GC_DARWIN_THREADS)
|
|
void GC_push_one(word p);
|
|
#endif
|
|
#ifdef GC_WIN32_THREADS
|
|
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);
|
|
#else
|
|
GC_INNER void GC_mark_and_push_stack(ptr_t p);
|
|
#endif
|
|
GC_INNER void GC_clear_hdr_marks(hdr*hhdr);
|
|
GC_INNER void GC_set_hdr_marks(hdr*hhdr);
|
|
GC_INNER void GC_set_fl_marks(ptr_t p);
|
|
#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC)
|
|
void GC_check_fl_marks(void**);
|
|
#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);
|
|
#endif
|
|
GC_INNER void GC_cond_register_dynamic_libraries(void);
|
|
ptr_t GC_get_main_stack_base(void);
|
|
#ifdef IA64
|
|
GC_INNER ptr_t GC_get_register_stack_base(void);
|
|
#endif
|
|
void GC_register_data_segments(void);
|
|
#ifdef THREADS
|
|
GC_INNER void GC_thr_init(void);
|
|
GC_INNER void GC_init_parallel(void);
|
|
#else
|
|
GC_INNER GC_bool GC_is_static_root(void*p);
|
|
#ifdef TRACE_BUF
|
|
void GC_add_trace_entry(char*kind,word arg1,word arg2);
|
|
#endif
|
|
#endif
|
|
#ifdef PRINT_BLACK_LIST
|
|
GC_INNER void GC_add_to_black_list_normal(word p,ptr_t source);
|
|
#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
|
|
struct hblk*GC_is_black_listed(struct hblk*h,word len);
|
|
GC_INNER void GC_promote_black_lists(void);
|
|
GC_INNER void GC_unpromote_black_lists(void);
|
|
GC_INNER ptr_t GC_scratch_alloc(size_t bytes);
|
|
#ifdef GWW_VDB
|
|
#else
|
|
#define GC_scratch_recycle_no_gww GC_scratch_recycle_inner
|
|
#endif
|
|
GC_INNER void GC_scratch_recycle_inner(void*ptr,size_t bytes);
|
|
#ifdef MARK_BIT_PER_GRANULE
|
|
GC_INNER GC_bool GC_add_map_entry(size_t sz);
|
|
#endif
|
|
GC_INNER void GC_register_displacement_inner(size_t offset);
|
|
GC_INNER void GC_new_hblk(size_t size_in_granules,int kind);
|
|
GC_INNER ptr_t GC_build_fl(struct hblk*h,size_t words,GC_bool clear,
|
|
ptr_t list);
|
|
GC_INNER struct hblk*GC_allochblk(size_t size_in_bytes,int kind,
|
|
unsigned flags);
|
|
GC_INNER ptr_t GC_alloc_large(size_t lb,int k,unsigned flags);
|
|
GC_INNER void GC_freehblk(struct hblk*p);
|
|
GC_INNER GC_bool GC_expand_hp_inner(word n);
|
|
GC_INNER void GC_start_reclaim(GC_bool abort_if_found);
|
|
GC_INNER void GC_continue_reclaim(word sz,int kind);
|
|
GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func,GC_bool ignore_old);
|
|
GC_INNER ptr_t GC_reclaim_generic(struct hblk*hbp,hdr*hhdr,size_t sz,
|
|
GC_bool init,ptr_t list,
|
|
signed_word*count);
|
|
GC_INNER GC_bool GC_block_empty(hdr*hhdr);
|
|
GC_INNER int GC_CALLBACK GC_never_stop_func(void);
|
|
GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func f);
|
|
#define GC_gcollect_inner()(void)GC_try_to_collect_inner(GC_never_stop_func)
|
|
#ifdef THREADS
|
|
GC_EXTERN GC_bool GC_in_thread_creation;
|
|
#endif
|
|
GC_EXTERN GC_bool GC_is_initialized;
|
|
GC_INNER void GC_collect_a_little_inner(int n);
|
|
GC_INNER void*GC_generic_malloc_inner(size_t lb,int k);
|
|
#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);
|
|
#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);
|
|
#ifdef GC_ADD_CALLER
|
|
#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;
|
|
#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
|
|
#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);
|
|
GC_INNER GC_bool GC_install_counts(struct hblk*h,size_t sz);
|
|
GC_INNER void GC_remove_header(struct hblk*h);
|
|
GC_INNER void GC_remove_counts(struct hblk*h,size_t sz);
|
|
GC_INNER hdr*GC_find_header(ptr_t h);
|
|
GC_INNER void GC_add_to_heap(struct hblk*p,size_t bytes);
|
|
#ifdef USE_PROC_FOR_LIBRARIES
|
|
GC_INNER void GC_add_to_our_memory(ptr_t p,size_t bytes);
|
|
#else
|
|
#define GC_add_to_our_memory(p,bytes)
|
|
#endif
|
|
GC_INNER void GC_print_all_errors(void);
|
|
GC_EXTERN void (*GC_check_heap)(void);
|
|
GC_EXTERN void (*GC_print_all_smashed)(void);
|
|
GC_EXTERN void (*GC_print_heap_obj)(ptr_t p);
|
|
#if defined(LINUX)&&defined(__ELF__)&&!defined(SMALL_CONFIG)
|
|
void GC_print_address_map(void);
|
|
#endif
|
|
#ifndef SHORT_DBG_HDRS
|
|
GC_EXTERN GC_bool GC_findleak_delay_free;
|
|
GC_INNER GC_bool GC_check_leaked(ptr_t base);
|
|
#endif
|
|
GC_EXTERN GC_bool GC_have_errors;
|
|
#define VERBOSE 2
|
|
#if!defined(NO_CLOCK)||!defined(SMALL_CONFIG)
|
|
extern int GC_print_stats;
|
|
#else
|
|
#define GC_print_stats 0
|
|
#endif
|
|
#ifdef KEEP_BACK_PTRS
|
|
GC_EXTERN long GC_backtraces;
|
|
GC_INNER void GC_generate_random_backtrace_no_gc(void);
|
|
#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
|
|
#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
|
|
#ifdef USE_MUNMAP
|
|
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);
|
|
GC_INLINE ptr_t GC_unmap_end(ptr_t start,size_t bytes)
|
|
{
|
|
return (ptr_t)((word)(start+bytes)&~(GC_page_size - 1));
|
|
}
|
|
#endif
|
|
#ifdef CAN_HANDLE_FORK
|
|
GC_EXTERN int GC_handle_fork;
|
|
#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_EXTERN GC_bool GC_incremental;
|
|
GC_INNER void GC_read_dirty(GC_bool output_unneeded);
|
|
GC_INNER GC_bool GC_page_was_dirty(struct hblk*h);
|
|
GC_INNER void GC_remove_protection(struct hblk*h,word nblocks,
|
|
GC_bool pointerfree);
|
|
GC_INNER GC_bool GC_dirty_init(void);
|
|
GC_EXTERN GC_bool GC_manual_vdb;
|
|
#define GC_auto_incremental (GC_incremental&&!GC_manual_vdb)
|
|
GC_INNER void GC_dirty_inner(const void*p);
|
|
#define GC_dirty(p)(GC_manual_vdb?GC_dirty_inner(p):(void)0)
|
|
#define REACHABLE_AFTER_DIRTY(p)GC_reachable_here(p)
|
|
#endif
|
|
#define GC_base_C(p)((const void*)GC_base(( void*)(p)))
|
|
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
|
|
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
|
|
GC_API_PRIV void GC_printf(const char*format,...)
|
|
GC_ATTR_FORMAT_PRINTF(1,2);
|
|
GC_API_PRIV void GC_err_printf(const char*format,...)
|
|
GC_ATTR_FORMAT_PRINTF(1,2);
|
|
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
|
|
#define GC_verbose_log_printf GC_log_printf
|
|
#else
|
|
extern GC_bool GC_quiet;
|
|
#define GC_PRINT_STATS_FLAG (!GC_quiet)
|
|
#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
|
|
#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);
|
|
#define TO_KiB_UL(v)((unsigned long)(((v)+((1<<9)- 1))>>10))
|
|
GC_EXTERN unsigned GC_fail_count;
|
|
GC_EXTERN long GC_large_alloc_warn_interval;
|
|
GC_EXTERN signed_word GC_bytes_found;
|
|
#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
|
|
GC_EXTERN word GC_reclaimed_bytes_before_gc;
|
|
#endif
|
|
#ifdef USE_MUNMAP
|
|
GC_EXTERN int GC_unmap_threshold;
|
|
GC_EXTERN GC_bool GC_force_unmap_on_gcollect;
|
|
#endif
|
|
#ifdef MSWIN32
|
|
GC_EXTERN GC_bool GC_no_win32_dlls;
|
|
GC_EXTERN GC_bool GC_wnt;
|
|
#endif
|
|
#ifdef THREADS
|
|
#if (defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE)
|
|
GC_EXTERN CRITICAL_SECTION GC_write_cs;
|
|
#ifdef GC_ASSERTIONS
|
|
GC_EXTERN GC_bool GC_write_disabled;
|
|
#endif
|
|
#endif
|
|
#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
|
|
#define GC_acquire_dirty_lock()do { } 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;
|
|
#endif
|
|
#ifdef MSWINCE
|
|
GC_EXTERN GC_bool GC_dont_query_stack_min;
|
|
#endif
|
|
#elif defined(IA64)
|
|
GC_EXTERN ptr_t GC_save_regs_ret_val;
|
|
#endif
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
GC_EXTERN GC_bool GC_world_stopped;
|
|
GC_INNER void GC_mark_thread_local_free_lists(void);
|
|
#endif
|
|
#if defined(MPROTECT_VDB)&&defined(GWW_VDB)
|
|
GC_INNER GC_bool GC_gww_dirty_init(void);
|
|
#endif
|
|
#if defined(CHECKSUMS)||defined(PROC_VDB)
|
|
GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk*h);
|
|
#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);
|
|
GC_INNER void GC_bl_init(void);
|
|
GC_INNER void GC_bl_init_no_interiors(void);
|
|
GC_INNER void GC_start_debugging_inner(void);
|
|
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);
|
|
#endif
|
|
#elif defined(USE_WINALLOC)
|
|
GC_INNER void GC_add_current_malloc_heap(void);
|
|
#endif
|
|
#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);
|
|
#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);
|
|
#endif
|
|
#endif
|
|
#ifdef THREADS
|
|
GC_INNER void GC_reset_finalizer_nested(void);
|
|
GC_INNER unsigned char*GC_check_finalizer_nested(void);
|
|
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
|
|
#ifdef IA64
|
|
GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound);
|
|
#endif
|
|
#endif
|
|
#ifdef DYNAMIC_LOADING
|
|
GC_INNER GC_bool GC_register_main_static_data(void);
|
|
#ifdef DARWIN
|
|
GC_INNER void GC_init_dyld(void);
|
|
#endif
|
|
#endif
|
|
#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 char*GC_parse_map_entry(char*buf_ptr,ptr_t*start,ptr_t*end,
|
|
char**prot,unsigned int*maj_dev,
|
|
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 char*GC_get_maps(void);
|
|
#endif
|
|
#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
|
|
#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__)
|
|
#define GC_STATIC_ASSERT(expr)do { if (0){ char j[(expr)?1:-1];j[0]='\0';j[0]=j[0];} } while(0)
|
|
#else
|
|
#define GC_STATIC_ASSERT(expr)(void)sizeof(char[(expr)?1:-1])
|
|
#endif
|
|
#if GC_GNUC_PREREQ(4,0)
|
|
#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;
|
|
#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)
|
|
#define GC_markers_m1 GC_parallel
|
|
GC_EXTERN GC_bool GC_parallel_mark_disabled;
|
|
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;
|
|
GC_INNER void GC_notify_all_marker(void);
|
|
GC_INNER void GC_wait_marker(void);
|
|
GC_EXTERN word GC_mark_no;
|
|
GC_INNER void GC_help_marker(word my_mark_no);
|
|
GC_INNER void GC_start_mark_threads_inner(void);
|
|
#endif
|
|
#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS)&&!defined(NACL)&&!defined(GC_DARWIN_THREADS)&&!defined(SIG_SUSPEND)
|
|
#if (defined(GC_LINUX_THREADS)||defined(GC_DGUX386_THREADS))&&!defined(GC_USESIGRT_SIGNALS)
|
|
#if defined(SPARC)&&!defined(SIGPWR)
|
|
#define SIG_SUSPEND SIGLOST
|
|
#else
|
|
#define SIG_SUSPEND SIGPWR
|
|
#endif
|
|
#elif defined(GC_OPENBSD_THREADS)
|
|
#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
|
|
#if defined(GC_PTHREADS)&&!defined(GC_SEM_INIT_PSHARED)
|
|
#define GC_SEM_INIT_PSHARED 0
|
|
#endif
|
|
#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 <sys/siginfo.h>
|
|
EXTERN_C_BEGIN
|
|
#endif
|
|
#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
|
|
#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 <machine/trap.h>
|
|
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
|
|
#if (defined(NETBSD)||defined(OPENBSD))&&defined(__ELF__)&&!defined(NEED_FIND_LIMIT)
|
|
#define NEED_FIND_LIMIT
|
|
#endif
|
|
#if defined(IA64)&&!defined(NEED_FIND_LIMIT)
|
|
#define NEED_FIND_LIMIT
|
|
#endif
|
|
#if defined(NEED_FIND_LIMIT)||(defined(USE_PROC_FOR_LIBRARIES)&&defined(THREADS))
|
|
GC_EXTERN JMP_BUF GC_jmp_buf;
|
|
GC_INNER void GC_setup_temporary_fault_handler(void);
|
|
GC_INNER void GC_reset_fault_handler(void);
|
|
#endif
|
|
#if defined(CANCEL_SAFE)
|
|
#if defined(GC_ASSERTIONS)&&(defined(USE_COMPILER_TLS)||(defined(LINUX)&&!defined(ARM32)&&GC_GNUC_PREREQ(3,3)||defined(HPUX)))
|
|
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
|
|
#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
|
|
EXTERN_C_END
|
|
#endif
|
|
#ifdef KEEP_BACK_PTRS
|
|
#ifndef GC_BACKPTR_H
|
|
#define GC_BACKPTR_H
|
|
#ifndef GC_H
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
typedef enum {
|
|
GC_UNREFERENCED,
|
|
GC_NO_SPACE,
|
|
GC_REFD_FROM_ROOT,
|
|
GC_REFD_FROM_REG,
|
|
GC_REFD_FROM_HEAP,
|
|
GC_FINALIZER_REFD
|
|
} GC_ref_kind;
|
|
GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void*,
|
|
void**,size_t*)
|
|
GC_ATTR_NONNULL(1);
|
|
GC_API void*GC_CALL GC_generate_random_heap_address(void);
|
|
GC_API void*GC_CALL GC_generate_random_valid_address(void);
|
|
GC_API void GC_CALL GC_generate_random_backtrace(void);
|
|
GC_API void GC_CALL GC_print_backtrace(void*)GC_ATTR_NONNULL(1);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
#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
|
|
#if defined(KEEP_BACK_PTRS)||defined(PRINT_BLACK_LIST)||defined(MAKE_BACK_GRAPH)
|
|
#define NOT_MARKED (ptr_t)0
|
|
#define MARKED_FOR_FINALIZATION ((ptr_t)(word)2)
|
|
#define MARKED_FROM_REGISTER ((ptr_t)(word)4)
|
|
#endif
|
|
typedef struct {
|
|
#if defined(KEEP_BACK_PTRS)||defined(MAKE_BACK_GRAPH)
|
|
#if ALIGNMENT==1
|
|
#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)
|
|
word oh_dummy;
|
|
#endif
|
|
#endif
|
|
const char*oh_string;
|
|
signed_word oh_int;
|
|
#ifdef NEED_CALLINFO
|
|
struct callinfo oh_ci[NFRAMES];
|
|
#endif
|
|
#ifndef SHORT_DBG_HDRS
|
|
word oh_sz;
|
|
word oh_sf;
|
|
#endif
|
|
} oh;
|
|
#ifdef SHORT_DBG_HDRS
|
|
#define DEBUG_BYTES (sizeof (oh))
|
|
#define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES
|
|
#else
|
|
#define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh)+sizeof (word))
|
|
#define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES)
|
|
#endif
|
|
#define SIMPLE_ROUNDED_UP_WORDS(n)BYTES_TO_WORDS((n)+WORDS_TO_BYTES(1)- 1)
|
|
#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
|
|
#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
|
|
#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)
|
|
#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
|
|
EXTERN_C_END
|
|
#endif
|
|
#ifdef MAKE_BACK_GRAPH
|
|
#define MAX_IN 10
|
|
#if (!defined(DBG_HDRS_ALL)||(ALIGNMENT!=CPP_WORDSZ/8))&&!defined(CPPCHECK)
|
|
#error The configuration does not support MAKE_BACK_GRAPH
|
|
#endif
|
|
#define FLAG_MANY 2
|
|
typedef struct back_edges_struct {
|
|
word n_edges;
|
|
unsigned short flags;
|
|
#define RETAIN 1
|
|
unsigned short height_gc_no;
|
|
signed_word height;
|
|
#define HEIGHT_UNKNOWN (-2)
|
|
#define HEIGHT_IN_PROGRESS (-1)
|
|
ptr_t edges[MAX_IN];
|
|
struct back_edges_struct*cont;
|
|
} back_edges;
|
|
#define MAX_BACK_EDGE_STRUCTS 100000
|
|
static back_edges*back_edge_space=0;
|
|
STATIC int GC_n_back_edge_structs=0;
|
|
static back_edges*avail_back_edges=0;
|
|
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++);
|
|
}
|
|
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;
|
|
}
|
|
#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));
|
|
}
|
|
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)
|
|
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))
|
|
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);
|
|
}
|
|
}
|
|
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)){
|
|
return;
|
|
}
|
|
if (NULL==pred){
|
|
static unsigned random_number=13;
|
|
#define GOT_LUCKY_NUMBER (((++random_number)&0x7f)==0)
|
|
SET_OH_BG_PTR(q,p);
|
|
if (GOT_LUCKY_NUMBER)ensure_struct(q);
|
|
return;
|
|
}
|
|
{
|
|
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){
|
|
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)
|
|
{
|
|
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));
|
|
be->n_edges=0;
|
|
if (0!=be->cont){
|
|
deallocate_back_edges(be->cont);
|
|
be->cont=0;
|
|
}
|
|
GC_ASSERT(GC_is_marked(p));
|
|
be->flags&=~RETAIN;
|
|
}
|
|
} else {
|
|
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));
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
GC_INNER void GC_build_back_graph(void)
|
|
{
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
GC_apply_to_each_object(add_back_edges);
|
|
}
|
|
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;
|
|
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;
|
|
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){
|
|
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++];
|
|
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);
|
|
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;
|
|
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;
|
|
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){
|
|
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 (!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){
|
|
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);
|
|
}
|
|
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
|
|
STATIC word*GC_old_normal_bl=NULL;
|
|
STATIC word*GC_incomplete_normal_bl=NULL;
|
|
STATIC word*GC_old_stack_bl=NULL;
|
|
STATIC word*GC_incomplete_stack_bl=NULL;
|
|
STATIC word GC_total_stack_black_listed=0;
|
|
GC_INNER word GC_black_list_spacing=MINHINCR*HBLKSIZE;
|
|
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 {
|
|
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
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
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
|
|
#define backlist_set_pht_entry_from_index(bl,index)set_pht_entry_from_index(bl,index)
|
|
#endif
|
|
#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);
|
|
}
|
|
}
|
|
}
|
|
#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);
|
|
}
|
|
}
|
|
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){
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
#ifdef CHECKSUMS
|
|
#define NSUMS 10000
|
|
#define OFFSET 0x10000
|
|
typedef struct {
|
|
GC_bool new_valid;
|
|
word old_sum;
|
|
word new_sum;
|
|
struct hblk*block;
|
|
} page_entry;
|
|
page_entry GC_sums[NSUMS];
|
|
STATIC word GC_faulted[NSUMS]={ 0 };
|
|
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);
|
|
}
|
|
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
|
|
&&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);
|
|
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");
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
GC_n_faulted=0;
|
|
}
|
|
#endif
|
|
#ifndef GC_PMARK_H
|
|
#define GC_PMARK_H
|
|
#if defined(HAVE_CONFIG_H)&&!defined(GC_PRIVATE_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)
|
|
#endif
|
|
EXTERN_C_BEGIN
|
|
#ifndef MARK_DESCR_OFFSET
|
|
#define MARK_DESCR_OFFSET sizeof(word)
|
|
#endif
|
|
#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;
|
|
#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8)
|
|
#ifdef PARALLEL_MARK
|
|
#endif
|
|
GC_INNER mse*GC_signal_mark_stack_overflow(mse*msp);
|
|
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;
|
|
}
|
|
#define PUSH_CONTENTS(current,mark_stack_top,mark_stack_limit,source)do { hdr*my_hhdr;HC_GET_HDR(current,my_hhdr,source);mark_stack_top=GC_push_contents_hdr(current,mark_stack_top,mark_stack_limit,source,my_hhdr,TRUE);} while (0)
|
|
#ifdef USE_MARK_BYTES
|
|
#if defined(PARALLEL_MARK)&&defined(AO_HAVE_char_store)&&!defined(BASE_ATOMIC_OPS_EMULATED)
|
|
#define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no){ volatile unsigned char*mark_byte_addr=(unsigned char*)(hhdr)->hb_marks+(bit_no);if (AO_char_load(mark_byte_addr)!=0)break;AO_char_store(mark_byte_addr,1);}
|
|
#else
|
|
#define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no){ char*mark_byte_addr=(char*)(hhdr)->hb_marks+(bit_no);if (*mark_byte_addr!=0)break;*mark_byte_addr=1;}
|
|
#endif
|
|
#else
|
|
#ifdef PARALLEL_MARK
|
|
#ifdef THREAD_SANITIZER
|
|
#define OR_WORD_EXIT_IF_SET(addr,bits){ if (!((word)AO_load((volatile AO_t*)(addr))&(bits))){ AO_or((volatile AO_t*)(addr),(AO_t)(bits));} else { break;} }
|
|
#else
|
|
#define OR_WORD_EXIT_IF_SET(addr,bits){ if (!(*(addr)&(bits))){ AO_or((volatile AO_t*)(addr),(AO_t)(bits));} else { break;} }
|
|
#endif
|
|
#else
|
|
#define OR_WORD_EXIT_IF_SET(addr,bits){ word old=*(addr);word my_bits=(bits);if ((old&my_bits)!=0)break;*(addr)=old|my_bits;}
|
|
#endif
|
|
#define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no){ word*mark_word_addr=(hhdr)->hb_marks+divWORDSZ(bit_no);OR_WORD_EXIT_IF_SET(mark_word_addr,(word)1<<modWORDSZ(bit_no));}
|
|
#endif
|
|
#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!=0&&(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
|
|
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);
|
|
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);
|
|
if (EXPECT((gran_offset|byte_offset)!=0,FALSE))
|
|
#else
|
|
unsigned32 gran_displ;
|
|
unsigned32 inv_sz=hhdr->hb_inv_sz;
|
|
#endif
|
|
{
|
|
#ifdef MARK_BIT_PER_GRANULE
|
|
if ((hhdr->hb_flags&LARGE_BLOCK)!=0)
|
|
#else
|
|
if (EXPECT(inv_sz==LARGE_INV_SZ,FALSE))
|
|
#endif
|
|
{
|
|
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);
|
|
} 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
|
|
{
|
|
#if defined(MARK_BIT_PER_OBJ)&&!defined(MARK_BIT_PER_GRANULE)
|
|
size_t obj_displ;
|
|
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
|
|
GC_ASSERT(gran_displ<=HBLK_OBJS(hhdr->hb_sz));
|
|
#endif
|
|
TRACE(source,GC_log_printf("GC #%u:passed validity tests\n",
|
|
(unsigned)GC_gc_no));
|
|
SET_MARK_BIT_EXIT_IF_SET(hhdr,gran_displ);
|
|
TRACE(source,GC_log_printf("GC #%u:previously unmarked\n",
|
|
(unsigned)GC_gc_no));
|
|
TRACE_TARGET(base,GC_log_printf("GC #%u:marking %p from %p instead\n",
|
|
(unsigned)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
|
|
#ifdef 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);} 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
|
|
#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
|
|
#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)
|
|
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)
|
|
#define GC_MARK_FO(real_ptr,mark_proc)do { (*(mark_proc))(real_ptr);while (!GC_mark_stack_empty())MARK_FROM_MARK_STACK();if (GC_mark_state!=MS_NONE){ GC_set_mark_bit(real_ptr);while (!GC_mark_some((ptr_t)0)){ } } } while (0)
|
|
#define MS_NONE 0
|
|
#define MS_PUSH_RESCUERS 1
|
|
#define MS_PUSH_UNCOLLECTABLE 2
|
|
#define MS_ROOTS_PUSHED 3
|
|
#define MS_PARTIALLY_INVALID 4
|
|
#define MS_INVALID 5
|
|
EXTERN_C_END
|
|
#endif
|
|
#ifdef GC_GCJ_SUPPORT
|
|
#ifndef GC_GCJ_H
|
|
#define GC_GCJ_H
|
|
#ifndef GC_H
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
GC_API void GC_CALL GC_init_gcj_malloc(int,
|
|
void*);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_gcj_malloc(size_t,
|
|
void*);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_debug_gcj_malloc(size_t,
|
|
void*,
|
|
GC_EXTRA_PARAMS);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_gcj_malloc_ignore_off_page(size_t,
|
|
void*);
|
|
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
|
|
}
|
|
#endif
|
|
#endif
|
|
int GC_gcj_kind=0;
|
|
int GC_gcj_debug_kind=0;
|
|
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;
|
|
}
|
|
GC_API void GC_CALL GC_init_gcj_malloc(int mp_index,
|
|
void*mp)
|
|
{
|
|
#ifndef GC_IGNORE_GCJ_INFO
|
|
GC_bool ignore_gcj_info;
|
|
#endif
|
|
DCL_LOCK_STATE;
|
|
if (mp==0)
|
|
mp=(void*)(word)GC_gcj_fake_mark_proc;
|
|
GC_init();
|
|
LOCK();
|
|
if (GC_gcjobjfreelist!=NULL){
|
|
UNLOCK();
|
|
return;
|
|
}
|
|
#ifdef GC_IGNORE_GCJ_INFO
|
|
#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);
|
|
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");
|
|
GC_gcjobjfreelist=(ptr_t*)GC_new_free_list_inner();
|
|
if (ignore_gcj_info){
|
|
GC_gcj_kind=GC_new_kind_inner((void**)GC_gcjobjfreelist,
|
|
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);
|
|
GC_gcj_debug_kind=GC_new_kind_inner(GC_new_free_list_inner(),
|
|
GC_MAKE_PROC(mp_index,
|
|
1),
|
|
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))
|
|
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;
|
|
}
|
|
#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;
|
|
}
|
|
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;
|
|
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;
|
|
}
|
|
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_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
|
|
}
|
|
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));
|
|
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);
|
|
return 0;
|
|
}
|
|
} else {
|
|
GC_ADD_TO_BLACK_LIST_NORMAL(p,source);
|
|
}
|
|
GC_ASSERT(hhdr==0||!HBLK_IS_FREE(hhdr));
|
|
return hhdr;
|
|
} 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;
|
|
}
|
|
}
|
|
}
|
|
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_scratch_free_ptr+=bytes;
|
|
if ((word)GC_scratch_free_ptr<=(word)GC_scratch_end_ptr){
|
|
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);
|
|
GC_add_to_our_memory(result,bytes_to_get);
|
|
GC_scratch_free_ptr-=bytes;
|
|
if (result!=NULL){
|
|
GC_scratch_last_end_ptr=result+bytes;
|
|
}
|
|
return result;
|
|
}
|
|
bytes_to_get=ROUNDUP_PAGESIZE_IF_MMAP(MINHINCR*HBLKSIZE);
|
|
result=(ptr_t)GET_MEM(bytes_to_get);
|
|
GC_add_to_our_memory(result,bytes_to_get);
|
|
if (NULL==result){
|
|
WARN("Out of memory - trying to allocate requested amount"
|
|
" (%" WARN_PRIdPTR " bytes)...\n",(word)bytes);
|
|
GC_scratch_free_ptr-=bytes;
|
|
bytes_to_get=ROUNDUP_PAGESIZE_IF_MMAP(bytes);
|
|
result=(ptr_t)GET_MEM(bytes_to_get);
|
|
GC_add_to_our_memory(result,bytes_to_get);
|
|
return result;
|
|
}
|
|
GC_scratch_free_ptr=result;
|
|
GC_scratch_end_ptr=GC_scratch_free_ptr+bytes_to_get;
|
|
GC_scratch_last_end_ptr=GC_scratch_end_ptr;
|
|
}
|
|
}
|
|
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
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
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
|
|
prev=&GC_all_bottom_indices;
|
|
pi=0;
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
GC_INNER GC_bool GC_install_counts(struct hblk*h,size_t sz)
|
|
{
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
GC_INNER void GC_remove_header(struct hblk*h)
|
|
{
|
|
hdr**ha;
|
|
GET_HDR_ADDR(h,ha);
|
|
free_hdr(*ha);
|
|
*ha=0;
|
|
}
|
|
GC_INNER void GC_remove_counts(struct hblk*h,size_t sz)
|
|
{
|
|
struct hblk*hbp;
|
|
for (hbp=h+1;(word)hbp < (word)h+sz;hbp+=1){
|
|
SET_HDR(hbp,0);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
#include <stdio.h>
|
|
#ifndef SMALL_CONFIG
|
|
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));
|
|
}
|
|
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));
|
|
}
|
|
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));
|
|
}
|
|
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
|
|
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;
|
|
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
|
|
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
|
|
if (clear)BZERO(h,HBLKSIZE);
|
|
p=(word*)(h->hb_body)+sz;
|
|
prev=(word*)(h->hb_body);
|
|
last_object=(word*)((char*)h+HBLKSIZE);
|
|
last_object-=sz;
|
|
while ((word)p<=(word)last_object){
|
|
obj_link(p)=(ptr_t)prev;
|
|
prev=p;
|
|
p+=sz;
|
|
}
|
|
p-=sz;
|
|
*(ptr_t*)h=list;
|
|
return ((ptr_t)p);
|
|
}
|
|
GC_INNER void GC_new_hblk(size_t gran,int kind)
|
|
{
|
|
struct hblk*h;
|
|
GC_bool clear=GC_obj_kinds[kind].ok_init;
|
|
GC_STATIC_ASSERT((sizeof (struct hblk))==HBLKSIZE);
|
|
if (GC_debugging_started)clear=TRUE;
|
|
h=GC_allochblk(GRANULES_TO_BYTES(gran),kind,0);
|
|
if (h==0)return;
|
|
if (IS_UNCOLLECTABLE(kind))GC_set_hdr_marks(HDR(h));
|
|
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]);
|
|
}
|
|
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
|
|
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;
|
|
}
|
|
} 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
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
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 (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;
|
|
base=(ptr_t)p - offset;
|
|
limit=base+sz;
|
|
}
|
|
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;
|
|
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();
|
|
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
|
|
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
|
|
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 {
|
|
return(p);
|
|
}
|
|
#else
|
|
if (GC_on_stack(p))return(p);
|
|
hhdr=HDR((word)p);
|
|
if (hhdr==0){
|
|
if (GC_is_static_root(p))return(p);
|
|
#if defined(DYNAMIC_LOADING)||defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)||defined(PCR)
|
|
GC_register_dynamic_libraries();
|
|
if (GC_is_static_root(p))
|
|
return(p);
|
|
#endif
|
|
goto fail;
|
|
} else {
|
|
word descr;
|
|
ptr_t base=(ptr_t)GC_base(p);
|
|
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:
|
|
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);
|
|
}
|
|
#ifndef GC_INLINE_H
|
|
#define GC_INLINE_H
|
|
#if GC_GNUC_PREREQ(3,0)
|
|
#define GC_EXPECT(expr,outcome)__builtin_expect(expr,outcome)
|
|
#else
|
|
#define GC_EXPECT(expr,outcome)(expr)
|
|
#endif
|
|
#ifndef GC_ASSERT
|
|
#ifdef NDEBUG
|
|
#define GC_ASSERT(expr)
|
|
#else
|
|
#include <assert.h>
|
|
#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
|
|
#define GC_I_PTRFREE 0
|
|
#define GC_I_NORMAL 1
|
|
GC_API void GC_CALL GC_generic_malloc_many(size_t,int,
|
|
void**);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc_kind(size_t,int);
|
|
#ifdef GC_THREADS
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc_kind_global(size_t,int);
|
|
#else
|
|
#define GC_malloc_kind_global GC_malloc_kind
|
|
#endif
|
|
#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
|
|
#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;} if ((GC_signed_word)my_entry - (GC_signed_word)(num_direct)<=0&&my_entry!=0){ GC_FAST_M_AO_STORE(my_fl,(char*)my_entry+(granules)+1);result=(default_expr);break;} else { 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)
|
|
#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)
|
|
#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){*(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,
|
|
size_t);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
#include <stdio.h>
|
|
#ifdef GC_USE_ENTIRE_HEAP
|
|
int GC_use_entire_heap=TRUE;
|
|
#else
|
|
int GC_use_entire_heap=FALSE;
|
|
#endif
|
|
#define MAX_BLACK_LIST_ALLOC (2*HBLKSIZE)
|
|
#define UNIQUE_THRESHOLD 32
|
|
#define HUGE_THRESHOLD 256
|
|
#define FL_COMPRESSION 8
|
|
#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 };
|
|
#ifndef GC_GCJ_SUPPORT
|
|
STATIC
|
|
#endif
|
|
word GC_free_bytes[N_HBLK_FLS+1]={ 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;
|
|
}
|
|
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
|
|
#if!defined(NO_DEBUGGING)||defined(GC_ASSERTIONS)
|
|
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
|
|
#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){
|
|
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);
|
|
}
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)){
|
|
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
|
|
GC_clear_hdr_marks(hhdr);
|
|
hhdr->hb_last_reclaimed=(unsigned short)GC_gc_no;
|
|
return(TRUE);
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
GC_INLINE void GC_remove_from_fl(hdr*hhdr)
|
|
{
|
|
GC_remove_from_fl_at(hhdr,GC_hblk_fl_from_blocks(divHBLKSZ(hhdr->hb_sz)));
|
|
}
|
|
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;
|
|
}
|
|
STATIC struct hblk*GC_free_block_ending_at(struct hblk*h)
|
|
{
|
|
struct hblk*p=get_block_ending_at(h);
|
|
if (p){
|
|
hdr*phdr=HDR(p);
|
|
if (HBLK_IS_FREE(phdr)){
|
|
return p;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
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);
|
|
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){
|
|
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
|
|
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);
|
|
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){
|
|
return IS_MAPPED(hhdr)?-1:1;
|
|
}
|
|
if (!prev_unmapped&&!next_unmapped){
|
|
return IS_MAPPED(hhdr)?1:-1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
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
|
|
}
|
|
GC_INNER void GC_unmap_old(void)
|
|
{
|
|
int i;
|
|
if (GC_unmap_threshold==0)
|
|
return;
|
|
#ifdef COUNT_UNMAPPED_REGIONS
|
|
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;
|
|
if ((unsigned short)(GC_gc_no - hhdr->hb_last_reclaimed)>
|
|
(unsigned short)GC_unmap_threshold){
|
|
#ifdef COUNT_UNMAPPED_REGIONS
|
|
int delta=calc_num_unmapped_regions_delta(h,hhdr);
|
|
signed_word regions=GC_num_unmapped_regions+delta;
|
|
if (delta>=0&®ions>=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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
if (0!=nexthdr&&HBLK_IS_FREE(nexthdr)
|
|
&&(signed_word)(size+(nextsize=nexthdr->hb_sz))> 0
|
|
){
|
|
if (IS_MAPPED(hhdr)&&!IS_MAPPED(nexthdr)){
|
|
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)){
|
|
GC_unmap_gap((ptr_t)h,size,(ptr_t)next,nextsize);
|
|
}
|
|
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);
|
|
h=GC_hblkfreelist[i];
|
|
} else {
|
|
h=hhdr->hb_next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
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){
|
|
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
|
|
hhdr->hb_flags&=~FREE_BLK;
|
|
#endif
|
|
GC_add_to_fl(rest,rest_hdr);
|
|
return h;
|
|
}
|
|
STATIC void GC_split_block(struct hblk*h,hdr*hhdr,struct hblk*n,
|
|
hdr*nhdr,int index)
|
|
{
|
|
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;
|
|
nhdr->hb_prev=prev;
|
|
nhdr->hb_next=next;
|
|
nhdr->hb_sz=total_size - h_size;
|
|
nhdr->hb_flags=0;
|
|
if (prev){
|
|
HDR(prev)->hb_next=n;
|
|
} else {
|
|
GC_hblkfreelist[index]=n;
|
|
}
|
|
if (next){
|
|
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,int kind,unsigned flags,int n,
|
|
int may_split);
|
|
#define AVOID_SPLIT_REMAPPED 2
|
|
GC_INNER struct hblk*
|
|
GC_allochblk(size_t sz,int kind,unsigned flags)
|
|
{
|
|
word blocks;
|
|
int start_list;
|
|
struct hblk*result;
|
|
int may_split;
|
|
int split_limit;
|
|
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);
|
|
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()){
|
|
split_limit=N_HBLK_FLS;
|
|
} else if (GC_finalizer_bytes_freed > (GC_heapsize>>4)){
|
|
split_limit=0;
|
|
} else {
|
|
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){
|
|
++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;
|
|
STATIC struct hblk*
|
|
GC_allochblk_nth(size_t sz,int kind,unsigned flags,int n,int may_split)
|
|
{
|
|
struct hblk*hbp;
|
|
hdr*hhdr;
|
|
struct hblk*thishbp;
|
|
hdr*thishdr;
|
|
signed_word size_needed=HBLKSIZE*OBJ_SZ_TO_BLOCKS_CHECKED(sz);
|
|
for (hbp=GC_hblkfreelist[n];;hbp=hhdr->hb_next){
|
|
signed_word size_avail;
|
|
if (hbp){
|
|
} else {
|
|
return NULL;
|
|
}
|
|
GET_HDR(hbp,hhdr);
|
|
size_avail=(signed_word)hhdr->hb_sz;
|
|
if (size_avail < size_needed)continue;
|
|
if (size_avail!=size_needed){
|
|
if (!may_split)continue;
|
|
thishbp=hhdr->hb_next;
|
|
if (thishbp){
|
|
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
|
|
if (may_split==AVOID_SPLIT_REMAPPED&&!IS_MAPPED(hhdr))
|
|
continue;
|
|
#endif
|
|
thishdr=GC_install_header(thishbp);
|
|
if (0!=thishdr){
|
|
#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
|
|
GC_split_block(hbp,hhdr,thishbp,thishdr,n);
|
|
hbp=thishbp;
|
|
hhdr=thishdr;
|
|
}
|
|
}
|
|
} else if (size_needed > (signed_word)BL_LIMIT
|
|
&&orig_avail - size_needed
|
|
> (signed_word)BL_LIMIT){
|
|
if (++GC_large_alloc_warn_suppressed
|
|
>=GC_large_alloc_warn_interval){
|
|
WARN("Repeated allocation of very large block "
|
|
"(appr. size %" WARN_PRIdPTR "):\n"
|
|
"\tMay lead to memory leak and poor performance\n",
|
|
size_needed);
|
|
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;
|
|
if ((++count&3)==0){
|
|
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);
|
|
if (GC_debugging_started){
|
|
BZERO(h,HBLKSIZE);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
#endif
|
|
hbp=GC_get_first_part(hbp,hhdr,size_needed,n);
|
|
break;
|
|
}
|
|
}
|
|
if (0==hbp)return 0;
|
|
if (!GC_install_counts(hbp,(word)size_needed))return(0);
|
|
if (!setup_header(hhdr,hbp,sz,kind,flags)){
|
|
GC_remove_counts(hbp,(word)size_needed);
|
|
return(0);
|
|
}
|
|
#ifndef GC_DISABLE_INCREMENTAL
|
|
GC_ASSERT((size_needed&(HBLKSIZE-1))==0);
|
|
GC_remove_protection(hbp,divHBLKSZ(size_needed),
|
|
(hhdr->hb_descr==0));
|
|
#endif
|
|
GC_fail_count=0;
|
|
GC_large_free_bytes-=size_needed;
|
|
GC_ASSERT(IS_MAPPED(hhdr));
|
|
return( hbp);
|
|
}
|
|
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?");
|
|
GC_remove_counts(hbp,size);
|
|
hhdr->hb_sz=size;
|
|
#ifdef USE_MUNMAP
|
|
hhdr->hb_last_reclaimed=(unsigned short)GC_gc_no;
|
|
#endif
|
|
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);
|
|
if(0!=nexthdr&&HBLK_IS_FREE(nexthdr)&&IS_MAPPED(nexthdr)
|
|
&&(signed_word)(hhdr->hb_sz+nexthdr->hb_sz)> 0
|
|
){
|
|
GC_remove_from_fl(nexthdr);
|
|
hhdr->hb_sz+=nexthdr->hb_sz;
|
|
GC_remove_header(next);
|
|
}
|
|
if (prev){
|
|
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;
|
|
}
|
|
}
|
|
GC_large_free_bytes+=size;
|
|
GC_add_to_fl(hbp,hhdr);
|
|
}
|
|
#include <stdio.h>
|
|
#if!defined(MACOS)&&!defined(MSWINCE)
|
|
#include <signal.h>
|
|
#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)&&!defined(__CC_ARM)
|
|
#include <sys/types.h>
|
|
#endif
|
|
#endif
|
|
word GC_non_gc_bytes=0;
|
|
word GC_gc_no=0;
|
|
#ifndef NO_CLOCK
|
|
static unsigned long full_gc_total_time=0;
|
|
static unsigned full_gc_total_ns_frac=0;
|
|
static GC_bool measure_performance=FALSE;
|
|
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
|
|
#ifndef GC_DISABLE_INCREMENTAL
|
|
GC_INNER GC_bool GC_incremental=FALSE;
|
|
#endif
|
|
GC_API int GC_CALL GC_is_incremental_mode(void)
|
|
{
|
|
return (int)GC_incremental;
|
|
}
|
|
#ifdef THREADS
|
|
int GC_parallel=FALSE;
|
|
#endif
|
|
#if defined(GC_FULL_FREQ)&&!defined(CPPCHECK)
|
|
int GC_full_freq=GC_FULL_FREQ;
|
|
#else
|
|
int GC_full_freq=19;
|
|
#endif
|
|
STATIC GC_bool GC_need_full_gc=FALSE;
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
GC_INNER GC_bool GC_world_stopped=FALSE;
|
|
#endif
|
|
STATIC word GC_used_heap_size_after_full=0;
|
|
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-2020 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." };
|
|
#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;
|
|
}
|
|
#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;
|
|
#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;
|
|
#elif defined(PARALLEL_MARK)
|
|
unsigned long GC_time_limit=GC_TIME_UNLIMITED;
|
|
#else
|
|
unsigned long GC_time_limit=50;
|
|
#endif
|
|
#ifndef NO_CLOCK
|
|
STATIC unsigned long GC_time_lim_nsec=0;
|
|
#define TV_NSEC_LIMIT (1000UL*1000)
|
|
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;
|
|
#endif
|
|
STATIC int GC_n_attempts=0;
|
|
STATIC GC_stop_func GC_default_stop_func=GC_never_stop_func;
|
|
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
|
|
#ifdef THREADS
|
|
GC_INNER word GC_total_stacksize=0;
|
|
#endif
|
|
static size_t min_bytes_allocd_minimum=1;
|
|
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;
|
|
}
|
|
static word min_bytes_allocd(void)
|
|
{
|
|
word result;
|
|
word stack_size;
|
|
word total_root_size;
|
|
word scan_size;
|
|
#ifdef THREADS
|
|
if (GC_need_to_lock){
|
|
stack_size=GC_total_stacksize;
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Total stacks size:%lu\n",
|
|
(unsigned long)stack_size);
|
|
#endif
|
|
} else
|
|
#endif
|
|
{
|
|
#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;
|
|
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;
|
|
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;
|
|
}
|
|
result+=GC_bytes_finalized;
|
|
if (result < (signed_word)(GC_bytes_allocd>>3)){
|
|
return(GC_bytes_allocd>>3);
|
|
} else {
|
|
return(result);
|
|
}
|
|
}
|
|
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));
|
|
}
|
|
STATIC word GC_collect_at_heapsize=GC_WORD_MAX;
|
|
GC_INNER GC_bool GC_should_collect(void)
|
|
{
|
|
static word last_min_bytes_allocd;
|
|
static word last_gc_no;
|
|
if (last_gc_no!=GC_gc_no){
|
|
last_min_bytes_allocd=min_bytes_allocd();
|
|
last_gc_no=GC_gc_no;
|
|
}
|
|
return(GC_adj_bytes_allocd()>=last_min_bytes_allocd
|
|
||GC_heapsize>=GC_collect_at_heapsize);
|
|
}
|
|
GC_start_callback_proc GC_start_call_back=0;
|
|
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);
|
|
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){
|
|
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++;
|
|
}
|
|
}
|
|
#ifndef NO_CLOCK
|
|
if (GC_time_limit!=GC_TIME_UNLIMITED){ GET_TIME(GC_start_time);}
|
|
#endif
|
|
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){
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
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");
|
|
while(GC_collection_in_progress()){
|
|
if ((*stop_func)()){
|
|
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();
|
|
#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)){
|
|
return(FALSE);
|
|
}
|
|
GC_invalidate_mark_state();
|
|
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){
|
|
GC_invalidate_mark_state();
|
|
GC_unpromote_black_lists();
|
|
}
|
|
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;
|
|
full_gc_total_ns_frac+=(unsigned)ns_frac_diff;
|
|
if (full_gc_total_ns_frac>=1000000U){
|
|
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);
|
|
}
|
|
#ifndef GC_RATE
|
|
#define GC_RATE 10
|
|
#endif
|
|
#ifndef MAX_PRIOR_ATTEMPTS
|
|
#define MAX_PRIOR_ATTEMPTS 1
|
|
#endif
|
|
STATIC int GC_deficit=0;
|
|
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;
|
|
}
|
|
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());
|
|
if (GC_dont_gc)return;
|
|
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){
|
|
#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 {
|
|
(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 {
|
|
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();
|
|
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
|
|
static unsigned world_stopped_total_time=0;
|
|
static unsigned world_stopped_total_divisor=0;
|
|
#ifndef MAX_TOTAL_TIME_DIVISOR
|
|
#define MAX_TOTAL_TIME_DIVISOR 1000
|
|
#endif
|
|
#endif
|
|
#ifdef USE_MUNMAP
|
|
#define IF_USE_MUNMAP(x)x
|
|
#define COMMA_IF_USE_MUNMAP(x),x
|
|
#else
|
|
#define IF_USE_MUNMAP(x)
|
|
#define COMMA_IF_USE_MUNMAP(x)
|
|
#endif
|
|
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
|
|
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
|
|
if (GC_on_collection_event)
|
|
GC_on_collection_event(GC_EVENT_MARK_START);
|
|
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;
|
|
#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
|
|
return FALSE;
|
|
}
|
|
GC_gc_no++;
|
|
GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes,heap %lu KiB"
|
|
IF_USE_MUNMAP(" (+%lu KiB unmapped)")"\n",
|
|
(unsigned long)GC_gc_no,(long)GC_bytes_found,
|
|
TO_KiB_UL(GC_heapsize - GC_unmapped_bytes)
|
|
COMMA_IF_USE_MUNMAP(TO_KiB_UL(GC_unmapped_bytes)));
|
|
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);
|
|
total_time=world_stopped_total_time;
|
|
divisor=world_stopped_total_divisor;
|
|
if ((int)total_time < 0||divisor>=MAX_TOTAL_TIME_DIVISOR){
|
|
total_time>>=1;
|
|
divisor>>=1;
|
|
}
|
|
total_time+=time_diff < (((unsigned)-1)>>1)?
|
|
(unsigned)time_diff:((unsigned)-1)>>1;
|
|
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);
|
|
}
|
|
GC_INNER void GC_set_fl_marks(ptr_t q)
|
|
{
|
|
if (q){
|
|
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)
|
|
void GC_check_fl_marks(void**pfreelist)
|
|
{
|
|
#if defined(AO_HAVE_load_acquire_read)&&!defined(THREAD_SANITIZER)
|
|
AO_t*list=(AO_t*)AO_load_acquire_read((AO_t*)pfreelist);
|
|
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);
|
|
}
|
|
next=(AO_t*)AO_load_acquire_read(p);
|
|
if (AO_load(prev)!=(AO_t)p)
|
|
break;
|
|
prev=p;
|
|
p=next;
|
|
}
|
|
#else
|
|
(void)pfreelist;
|
|
#endif
|
|
}
|
|
#endif
|
|
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;
|
|
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
|
|
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;
|
|
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;
|
|
#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));
|
|
}
|
|
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)
|
|
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){
|
|
word 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);
|
|
}
|
|
#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
|
|
}
|
|
{
|
|
word size;
|
|
ptr_t q;
|
|
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);
|
|
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));
|
|
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)
|
|
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 GC_bool GC_try_to_collect_general(GC_stop_func stop_func,
|
|
GC_bool force_unmap GC_ATTR_UNUSED)
|
|
{
|
|
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();
|
|
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;
|
|
#endif
|
|
ENTER_GC();
|
|
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_CANCEL(cancel_state);
|
|
UNLOCK();
|
|
if (result){
|
|
if (GC_debugging_started)GC_print_all_smashed();
|
|
GC_INVOKE_FINALIZERS();
|
|
}
|
|
return(result);
|
|
}
|
|
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)
|
|
{
|
|
(void)GC_try_to_collect_general(0,FALSE);
|
|
if (GC_have_errors)GC_print_all_errors();
|
|
}
|
|
STATIC word GC_heapsize_at_forced_unmap=0;
|
|
GC_API void GC_CALL GC_gcollect_and_unmap(void)
|
|
{
|
|
GC_heapsize_at_forced_unmap=GC_heapsize;
|
|
(void)GC_try_to_collect_general(GC_never_stop_func,TRUE);
|
|
}
|
|
#ifdef USE_PROC_FOR_LIBRARIES
|
|
GC_INNER void GC_add_to_our_memory(ptr_t p,size_t bytes)
|
|
{
|
|
if (0==p)return;
|
|
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++;
|
|
}
|
|
#endif
|
|
GC_INNER void GC_add_to_heap(struct hblk*p,size_t bytes)
|
|
{
|
|
hdr*phdr;
|
|
word endp;
|
|
if (GC_n_heap_sects>=MAX_HEAP_SECTS){
|
|
ABORT("Too many heap sections:Increase MAXHINCR or MAX_HEAP_SECTS");
|
|
}
|
|
while ((word)p<=HBLKSIZE){
|
|
++p;
|
|
bytes-=HBLKSIZE;
|
|
if (0==bytes)return;
|
|
}
|
|
endp=(word)p+bytes;
|
|
if (endp<=(word)p){
|
|
bytes-=HBLKSIZE;
|
|
if (0==bytes)return;
|
|
endp-=HBLKSIZE;
|
|
}
|
|
phdr=GC_install_header(p);
|
|
if (0==phdr){
|
|
return;
|
|
}
|
|
GC_ASSERT(endp > (word)p&&endp==(word)p+bytes);
|
|
GC_heap_sects[GC_n_heap_sects].hs_start=(ptr_t)p;
|
|
GC_heap_sects[GC_n_heap_sects].hs_bytes=bytes;
|
|
GC_n_heap_sects++;
|
|
phdr->hb_sz=bytes;
|
|
phdr->hb_flags=0;
|
|
GC_freehblk(p);
|
|
GC_heapsize+=bytes;
|
|
GC_collect_at_heapsize+=bytes;
|
|
if (GC_collect_at_heapsize < GC_heapsize)
|
|
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));
|
|
}
|
|
if ((word)p+bytes>=(word)GC_greatest_plausible_heap_addr){
|
|
GC_greatest_plausible_heap_addr=(void*)endp;
|
|
}
|
|
}
|
|
#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 GC_bool GC_expand_hp_inner(word n)
|
|
{
|
|
size_t bytes;
|
|
struct hblk*space;
|
|
word expansion_slop;
|
|
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)){
|
|
return(FALSE);
|
|
}
|
|
space=GET_MEM(bytes);
|
|
GC_add_to_our_memory((ptr_t)space,bytes);
|
|
if (space==0){
|
|
WARN("Failed to expand heap by %" WARN_PRIdPTR " bytes\n",
|
|
(word)bytes);
|
|
return(FALSE);
|
|
}
|
|
GC_INFOLOG_PRINTF("Grow heap to %lu KiB after %lu bytes allocated\n",
|
|
TO_KiB_UL(GC_heapsize+(word)bytes),
|
|
(unsigned long)GC_bytes_allocd);
|
|
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)){
|
|
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 {
|
|
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_prev_heap_addr=GC_last_heap_addr;
|
|
GC_last_heap_addr=(ptr_t)space;
|
|
GC_add_to_heap(space,bytes);
|
|
GC_collect_at_heapsize=
|
|
GC_heapsize+expansion_slop - 2*MAXHINCR*HBLKSIZE;
|
|
if (GC_collect_at_heapsize < GC_heapsize)
|
|
GC_collect_at_heapsize=GC_WORD_MAX;
|
|
if (GC_on_heap_resize)
|
|
(*GC_on_heap_resize)(GC_heapsize);
|
|
return(TRUE);
|
|
}
|
|
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;
|
|
#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;
|
|
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())){
|
|
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){
|
|
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;
|
|
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){
|
|
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)
|
|
WARN("Out of Memory!Heap size:%" WARN_PRIdPTR " 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);
|
|
}
|
|
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_collect_a_little_inner(1);
|
|
}
|
|
#endif
|
|
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_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();
|
|
}
|
|
}
|
|
}
|
|
GC_fail_count=0;
|
|
return (ptr_t)(*flh);
|
|
}
|
|
#ifndef MSWINCE
|
|
#include <errno.h>
|
|
#endif
|
|
#include <string.h>
|
|
#ifndef SHORT_DBG_HDRS
|
|
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){
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
#ifdef LINT2
|
|
long GC_random(void)
|
|
{
|
|
static unsigned seed=1;
|
|
seed=(seed*1103515245U+12345)&GC_RAND_MAX;
|
|
return (long)seed;
|
|
}
|
|
#endif
|
|
#ifdef KEEP_BACK_PTRS
|
|
#ifdef LINT2
|
|
#define RANDOM()GC_random()
|
|
#else
|
|
#include <stdlib.h>
|
|
#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
|
|
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);
|
|
}
|
|
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
|
|
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
|
|
{
|
|
ptr_t alternate_ptr=bp+1;
|
|
ptr_t target=*(ptr_t*)bp;
|
|
ptr_t alternate_target=*(ptr_t*)alternate_ptr;
|
|
if ((word)alternate_target>=(word)GC_least_plausible_heap_addr
|
|
&&(word)alternate_target<=(word)GC_greatest_plausible_heap_addr
|
|
&&((word)target < (word)GC_least_plausible_heap_addr
|
|
||(word)target > (word)GC_greatest_plausible_heap_addr)){
|
|
bp=alternate_ptr;
|
|
}
|
|
}
|
|
#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;
|
|
}
|
|
}
|
|
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;
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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);
|
|
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_INNER void GC_generate_random_backtrace_no_gc(void)
|
|
{
|
|
void*current;
|
|
current=GC_generate_random_valid_address();
|
|
GC_printf("\n****Chosen address %p in object\n",current);
|
|
GC_print_backtrace(current);
|
|
}
|
|
GC_API void GC_CALL GC_generate_random_backtrace(void)
|
|
{
|
|
if (GC_try_to_collect(GC_never_stop_func)==0){
|
|
GC_err_printf("Cannot generate a backtrace:"
|
|
"garbage collection is disabled!\n");
|
|
return;
|
|
}
|
|
GC_generate_random_backtrace_no_gc();
|
|
}
|
|
#endif
|
|
#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;
|
|
}
|
|
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
|
|
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
|
|
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),x
|
|
#else
|
|
#define IF_NOT_SHORTDBG_HDRS(x)
|
|
#define COMMA_IFNOT_SHORTDBG_HDRS(x)
|
|
#endif
|
|
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);
|
|
hhdr=GC_find_header(q);
|
|
kind=hhdr->hb_obj_kind;
|
|
if (0!=GC_describe_type_fns[kind]&&GC_is_marked(ohdr)){
|
|
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;
|
|
}
|
|
}
|
|
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
|
|
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(<smashed>,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 <dlfcn.h>
|
|
STATIC void GC_caller_func_offset(word ad,const char**symp,int*offp)
|
|
{
|
|
Dl_info caller;
|
|
if (ad&&dladdr((void*)ad,&caller)&&caller.dli_sname!=NULL){
|
|
*symp=caller.dli_sname;
|
|
*offp=(int)((char*)ad - (char*)caller.dli_saddr);
|
|
}
|
|
if (NULL==*symp){
|
|
*symp="unknown";
|
|
}
|
|
}
|
|
#else
|
|
#define GC_caller_func_offset(ad,symp,offp)(void)(*(symp)="unknown")
|
|
#endif
|
|
#endif
|
|
GC_API GC_ATTR_MALLOC void*GC_CALL GC_debug_malloc(size_t lb,
|
|
GC_EXTRA_PARAMS)
|
|
{
|
|
void*result;
|
|
result=GC_malloc(SIZET_SAT_ADD(lb,DEBUG_BYTES));
|
|
#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
|
|
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
|
|
#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
|
|
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);
|
|
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 <wchar.h>
|
|
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_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
|
|
#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))
|
|
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)
|
|
#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_have_errors=TRUE;
|
|
if (((oh*)base)->oh_sz==sz){
|
|
GC_print_smashed_obj(
|
|
"GC_debug_free:found previously deallocated (?)object at",
|
|
p,clobbered);
|
|
return;
|
|
} else {
|
|
GC_print_smashed_obj("GC_debug_free:found smashed location at",
|
|
p,clobbered);
|
|
}
|
|
}
|
|
((oh*)base)->oh_sz=sz;
|
|
#endif
|
|
}
|
|
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));
|
|
LOCK();
|
|
GC_bytes_freed+=sz;
|
|
UNLOCK();
|
|
}
|
|
}
|
|
}
|
|
#if defined(THREADS)&&defined(DBG_HDRS_ALL)
|
|
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
|
|
((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){
|
|
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;
|
|
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
|
|
#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)));
|
|
GC_smashed[GC_n_smashed]=smashed;
|
|
if (GC_n_smashed < MAX_SMASHED - 1)++GC_n_smashed;
|
|
GC_have_errors=TRUE;
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
STATIC void GC_check_heap_proc(void)
|
|
{
|
|
GC_STATIC_ASSERT((sizeof(oh)&(GRANULE_BYTES - 1))==0);
|
|
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;
|
|
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);
|
|
GC_add_smashed((ptr_t)(&p[i]));
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
#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);
|
|
}
|
|
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);
|
|
}
|
|
#define OFN_UNSET ((GC_finalization_proc)~(signed_word)0)
|
|
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){
|
|
return;
|
|
}
|
|
if (my_old_fn!=GC_debug_invoke_finalizer){
|
|
GC_err_printf("Debuggable object at %p had a non-debug finalizer\n",
|
|
obj);
|
|
} 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;
|
|
ptr_t base=(ptr_t)GC_base(obj);
|
|
if (NULL==base){
|
|
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;
|
|
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;
|
|
ptr_t base=(ptr_t)GC_base(obj);
|
|
if (NULL==base){
|
|
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;
|
|
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;
|
|
ptr_t base=(ptr_t)GC_base(obj);
|
|
if (NULL==base){
|
|
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;
|
|
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;
|
|
ptr_t base=(ptr_t)GC_base(obj);
|
|
if (NULL==base){
|
|
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;
|
|
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_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);
|
|
}
|
|
#ifndef GC_NO_FINALIZATION
|
|
#ifndef GC_JAVAXFC_H
|
|
#define GC_JAVAXFC_H
|
|
#ifndef GC_H
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
GC_API void GC_CALL GC_finalize_all(void);
|
|
#ifdef GC_THREADS
|
|
#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
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
typedef void (*finalization_mark_proc)(ptr_t);
|
|
#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
|
|
#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;
|
|
};
|
|
struct finalizable_object {
|
|
struct hash_chain_entry prolog;
|
|
#define fo_hidden_base prolog.hidden_key
|
|
#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;
|
|
ptr_t fo_client_data;
|
|
word fo_object_size;
|
|
finalization_mark_proc fo_mark_proc;
|
|
};
|
|
#ifdef AO_HAVE_store
|
|
#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
|
|
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);
|
|
}
|
|
#ifndef GC_ON_GROW_LOG_SIZE_MIN
|
|
#define GC_ON_GROW_LOG_SIZE_MIN CPP_LOG_HBLKSIZE
|
|
#endif
|
|
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;
|
|
struct hash_chain_entry**new_table;
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
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);
|
|
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);
|
|
}
|
|
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;
|
|
}
|
|
LOCK();
|
|
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();
|
|
#ifndef DBG_HDRS_ALL
|
|
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
|
|
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)){
|
|
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);
|
|
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;
|
|
}
|
|
#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);
|
|
}
|
|
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)){
|
|
}
|
|
}
|
|
}
|
|
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 {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
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;
|
|
}
|
|
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
|
|
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);
|
|
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
|
|
#ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED
|
|
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;
|
|
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;
|
|
}
|
|
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){
|
|
return GC_DUPLICATE;
|
|
}
|
|
}
|
|
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);
|
|
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;
|
|
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;
|
|
LOCK();
|
|
result=GC_move_disappearing_link_inner(&GC_ll_hashtbl,link,new_link);
|
|
UNLOCK();
|
|
return result;
|
|
}
|
|
#endif
|
|
#endif
|
|
STATIC void GC_normal_finalize_mark_proc(ptr_t p)
|
|
{
|
|
#if defined(_MSC_VER)&&defined(I386)
|
|
hdr*hhdr=HDR(p);
|
|
#define mark_stack_top GC_mark_stack_top
|
|
mse*mark_stack_limit=GC_mark_stack+GC_mark_stack_size;
|
|
word descr=hhdr->hb_descr;
|
|
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=p;
|
|
mark_stack_top->mse_descr.w=descr;
|
|
}
|
|
#undef mark_stack_top
|
|
#else
|
|
GC_mark_stack_top=GC_push_obj(p,HDR(p),GC_mark_stack_top,
|
|
GC_mark_stack+GC_mark_stack_size);
|
|
#endif
|
|
}
|
|
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){}
|
|
STATIC void GC_unreachable_finalize_mark_proc(ptr_t p)
|
|
{
|
|
GC_normal_finalize_mark_proc(p);
|
|
}
|
|
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;
|
|
DCL_LOCK_STATE;
|
|
if (EXPECT(GC_find_leak,FALSE))return;
|
|
LOCK();
|
|
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);
|
|
}
|
|
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)){
|
|
if (ocd)*ocd=(void*)(curr_fo->fo_client_data);
|
|
if (ofn)*ofn=curr_fo->fo_fn;
|
|
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--;
|
|
#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);
|
|
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
|
|
GC_free((void*)new_fo);
|
|
#endif
|
|
return;
|
|
}
|
|
prev_fo=curr_fo;
|
|
curr_fo=fo_next(curr_fo);
|
|
}
|
|
if (EXPECT(new_fo!=0,FALSE)){
|
|
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)){
|
|
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){
|
|
return;
|
|
}
|
|
LOCK();
|
|
}
|
|
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);
|
|
}
|
|
static GC_bool need_unreachable_finalization=FALSE;
|
|
GC_API void GC_CALL GC_register_finalizer_unreachable(void*obj,
|
|
GC_finalization_proc fn,void*cd,
|
|
GC_finalization_proc*ofn,void**ocd)
|
|
{
|
|
need_unreachable_finalization=TRUE;
|
|
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;
|
|
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
|
|
#ifndef SMALL_CONFIG
|
|
STATIC word GC_old_dl_entries=0;
|
|
#ifndef GC_LONG_REFS_NOT_NEEDED
|
|
STATIC word GC_old_ll_entries=0;
|
|
#endif
|
|
#endif
|
|
#ifndef THREADS
|
|
STATIC int GC_finalizer_nested=0;
|
|
STATIC unsigned GC_finalizer_skipped=0;
|
|
STATIC unsigned char*GC_check_finalizer_nested(void)
|
|
{
|
|
unsigned nesting_level=*(unsigned char*)&GC_finalizer_nested;
|
|
if (nesting_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
|
|
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;
|
|
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;
|
|
}
|
|
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);
|
|
}
|
|
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
|
|
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);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
fo_set_next(curr_fo,GC_fnlz_roots.finalize_now);
|
|
GC_dirty(curr_fo);
|
|
SET_FINALIZE_NOW(curr_fo);
|
|
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){
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
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){
|
|
#ifdef THREADS
|
|
GC_reset_finalizer_nested();
|
|
#else
|
|
GC_finalizer_nested=0;
|
|
#endif
|
|
}
|
|
}
|
|
#ifndef JAVA_FINALIZATION_NOT_NEEDED
|
|
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);
|
|
fo_set_next(curr_fo,GC_fnlz_roots.finalize_now);
|
|
GC_dirty(curr_fo);
|
|
SET_FINALIZE_NOW(curr_fo);
|
|
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;
|
|
}
|
|
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();
|
|
LOCK();
|
|
}
|
|
UNLOCK();
|
|
}
|
|
#endif
|
|
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
|
|
}
|
|
GC_API int GC_CALL GC_invoke_finalizers(void)
|
|
{
|
|
int count=0;
|
|
word bytes_freed_before=0;
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
if (count!=0
|
|
#if defined(THREADS)&&!defined(THREAD_SANITIZER)
|
|
&&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;
|
|
#endif
|
|
DCL_LOCK_STATE;
|
|
#if defined(THREADS)&&!defined(KEEP_BACK_PTRS)&&!defined(MAKE_BACK_GRAPH)
|
|
if (!GC_should_invoke_finalizers())
|
|
return;
|
|
#endif
|
|
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;
|
|
last_back_trace_gc_no=GC_WORD_MAX;
|
|
for (i=0;i < GC_backtraces;++i){
|
|
UNLOCK();
|
|
GC_generate_random_backtrace_no_gc();
|
|
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();
|
|
if (pnested!=NULL){
|
|
(void)GC_invoke_finalizers();
|
|
*pnested=0;
|
|
#ifndef THREADS
|
|
GC_ASSERT(NULL==GC_fnlz_roots.finalize_now);
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
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)();
|
|
}
|
|
#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
|
|
#endif
|
|
#ifdef ENABLE_DISCLAIM
|
|
#ifndef GC_DISCLAIM_H
|
|
#define GC_DISCLAIM_H
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
GC_API void GC_CALL GC_init_finalized_malloc(void);
|
|
typedef int (GC_CALLBACK*GC_disclaim_proc)(void*);
|
|
GC_API void GC_CALL GC_register_disclaim_proc(int,
|
|
GC_disclaim_proc,
|
|
int)GC_ATTR_NONNULL(2);
|
|
struct GC_finalizer_closure {
|
|
GC_finalization_proc proc;
|
|
void*cd;
|
|
};
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_finalized_malloc(size_t,
|
|
const struct GC_finalizer_closure*)GC_ATTR_NONNULL(2);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
#if defined(KEEP_BACK_PTRS)||defined(MAKE_BACK_GRAPH)
|
|
#define FINALIZER_CLOSURE_FLAG 0x2
|
|
#else
|
|
#define FINALIZER_CLOSURE_FLAG 0x1
|
|
#endif
|
|
STATIC int GC_CALLBACK GC_finalized_disclaim(void*obj)
|
|
{
|
|
word fc_word=*(word*)obj;
|
|
if ((fc_word&FINALIZER_CLOSURE_FLAG)!=0){
|
|
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();
|
|
LOCK();
|
|
if (GC_finalized_kind!=0){
|
|
UNLOCK();
|
|
return;
|
|
}
|
|
GC_register_displacement_inner(sizeof(word));
|
|
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);
|
|
GC_ASSERT(NONNULL_ARG_NOT_NULL(proc));
|
|
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)
|
|
{
|
|
word*op;
|
|
GC_ASSERT(GC_finalized_kind!=0);
|
|
GC_ASSERT(NONNULL_ARG_NOT_NULL(fclos));
|
|
GC_ASSERT(((word)fclos&FINALIZER_CLOSURE_FLAG)==0);
|
|
op=(word*)GC_malloc_kind(SIZET_SAT_ADD(lb,sizeof(word)),
|
|
GC_finalized_kind);
|
|
if (EXPECT(NULL==op,FALSE))
|
|
return NULL;
|
|
*op=(word)fclos|FINALIZER_CLOSURE_FLAG;
|
|
GC_dirty(op);
|
|
REACHABLE_AFTER_DIRTY(fclos);
|
|
return op+1;
|
|
}
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
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);
|
|
}
|
|
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();
|
|
GC_init();
|
|
LOCK();
|
|
}
|
|
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;
|
|
}
|
|
result=h->hb_body;
|
|
}
|
|
return result;
|
|
}
|
|
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);
|
|
BZERO(result,n_blocks*HBLKSIZE);
|
|
}
|
|
return result;
|
|
}
|
|
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);
|
|
size_t smaller_than_i=byte_sz - (byte_sz>>3);
|
|
size_t low_limit;
|
|
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);
|
|
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;
|
|
}
|
|
granule_sz=(granule_sz+1)&~1;
|
|
if (granule_sz > MAXOBJGRANULES)
|
|
granule_sz=MAXOBJGRANULES;
|
|
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;
|
|
for (;low_limit<=byte_sz;low_limit++)
|
|
GC_size_map[low_limit]=granule_sz;
|
|
}
|
|
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();
|
|
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);
|
|
}
|
|
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 {
|
|
op=(ptr_t)GC_alloc_large_and_clear(ADD_SLOP(lb),k,0);
|
|
if (op!=NULL)
|
|
GC_bytes_allocd+=lb;
|
|
}
|
|
return op;
|
|
}
|
|
#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)
|
|
{
|
|
word 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
|
|
#if defined(CPPCHECK)
|
|
size_t GC_dbg_collect_at_malloc_min_lb=16*1024;
|
|
#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(GC_have_errors,FALSE))
|
|
GC_print_all_errors();
|
|
GC_INVOKE_FINALIZERS();
|
|
GC_DBG_COLLECT_AT_MALLOC(lb);
|
|
if (SMALL_OBJ(lb)){
|
|
LOCK();
|
|
result=GC_generic_malloc_inner(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){
|
|
BZERO(result,n_blocks*HBLKSIZE);
|
|
} else {
|
|
#ifdef THREADS
|
|
((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&&0!=result){
|
|
BZERO(result,n_blocks*HBLKSIZE);
|
|
}
|
|
}
|
|
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();
|
|
}
|
|
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
|
|
GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_atomic(size_t lb)
|
|
{
|
|
return GC_malloc_kind(lb,PTRFREE);
|
|
}
|
|
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--;
|
|
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);
|
|
GC_non_gc_bytes+=GRANULES_TO_BYTES((word)lg);
|
|
UNLOCK();
|
|
} else {
|
|
UNLOCK();
|
|
op=GC_generic_malloc(lb,k);
|
|
}
|
|
GC_ASSERT(0==op||GC_is_marked(op));
|
|
} else {
|
|
op=GC_generic_malloc(lb,k);
|
|
if (op){
|
|
hdr*hhdr=HDR(op);
|
|
GC_ASSERT(((word)op&(HBLKSIZE - 1))==0);
|
|
LOCK();
|
|
set_mark_bit_from_hdr(hhdr,0);
|
|
#ifndef THREADS
|
|
GC_ASSERT(hhdr->hb_n_marks==0);
|
|
#endif
|
|
hhdr->hb_n_marks=1;
|
|
UNLOCK();
|
|
}
|
|
}
|
|
return op;
|
|
}
|
|
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
|
|
GC_API GC_ATTR_MALLOC void*GC_CALL
|
|
GC_malloc_atomic_uncollectable(size_t lb)
|
|
{
|
|
return GC_generic_malloc_uncollectable(lb,AUNCOLLECTABLE);
|
|
}
|
|
#endif
|
|
#if defined(REDIRECT_MALLOC)&&!defined(REDIRECT_MALLOC_IN_HEADER)
|
|
#ifndef MSWINCE
|
|
#include <errno.h>
|
|
#endif
|
|
#define GC_debug_malloc_replacement(lb)GC_debug_malloc(lb,GC_DBG_EXTRAS)
|
|
#if defined(CPPCHECK)
|
|
#define REDIRECT_MALLOC_F GC_malloc
|
|
#else
|
|
#define REDIRECT_MALLOC_F REDIRECT_MALLOC
|
|
#endif
|
|
void*malloc(size_t lb)
|
|
{
|
|
#if defined(I386)&&defined(GC_SOLARIS_THREADS)
|
|
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 (!GC_text_mapping("libpthread-",
|
|
&GC_libpthread_start,&GC_libpthread_end)){
|
|
WARN("Failed to find libpthread.so text mapping:Expect crash\n",0);
|
|
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
|
|
void*calloc(size_t n,size_t lb)
|
|
{
|
|
if ((lb|n)> GC_SQRT_SIZE_MAX
|
|
&&lb&&n > GC_SIZE_MAX/lb)
|
|
return (*GC_get_oom_fn())(GC_SIZE_MAX);
|
|
#if defined(GC_LINUX_THREADS)
|
|
{
|
|
static GC_bool lib_bounds_set=FALSE;
|
|
ptr_t caller=(ptr_t)__builtin_return_address(0);
|
|
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);
|
|
}
|
|
#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
|
|
#ifndef strndup
|
|
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
|
|
#undef GC_debug_malloc_replacement
|
|
#endif
|
|
GC_API void GC_CALL GC_free(void*p)
|
|
{
|
|
struct hblk*h;
|
|
hdr*hhdr;
|
|
size_t sz;
|
|
size_t ngranules;
|
|
int knd;
|
|
struct obj_kind*ok;
|
|
DCL_LOCK_STATE;
|
|
if (p){
|
|
} else {
|
|
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))
|
|
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;
|
|
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();
|
|
}
|
|
}
|
|
#ifdef THREADS
|
|
GC_INNER void GC_free_inner(void*p)
|
|
{
|
|
struct hblk*h;
|
|
hdr*hhdr;
|
|
size_t sz;
|
|
size_t ngranules;
|
|
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
|
|
#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
|
|
#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)
|
|
ptr_t caller=(ptr_t)__builtin_return_address(0);
|
|
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
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#ifndef MSWINCE
|
|
#include <errno.h>
|
|
#endif
|
|
#ifndef GC_ALLOC_PTRS_H
|
|
#define GC_ALLOC_PTRS_H
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
GC_API void**const GC_objfreelist_ptr;
|
|
GC_API void**const GC_aobjfreelist_ptr;
|
|
GC_API void**const GC_uobjfreelist_ptr;
|
|
#ifdef GC_ATOMIC_UNCOLLECTABLE
|
|
GC_API void**const GC_auobjfreelist_ptr;
|
|
#endif
|
|
GC_API void GC_CALL GC_incr_bytes_allocd(size_t bytes);
|
|
GC_API void GC_CALL GC_incr_bytes_freed(size_t bytes);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
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);
|
|
}
|
|
}
|
|
GC_API void*GC_CALL GC_realloc(void*p,size_t lb)
|
|
{
|
|
struct hblk*h;
|
|
hdr*hhdr;
|
|
void*result;
|
|
size_t sz;
|
|
size_t orig_sz;
|
|
int obj_kind;
|
|
if (p==0)return(GC_malloc(lb));
|
|
if (0==lb){
|
|
#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){
|
|
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;
|
|
#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);
|
|
}
|
|
if (ADD_SLOP(lb)<=sz){
|
|
if (lb>=(sz>>1)){
|
|
if (orig_sz > lb){
|
|
BZERO(((ptr_t)p)+lb,orig_sz - lb);
|
|
}
|
|
return(p);
|
|
}
|
|
sz=lb;
|
|
}
|
|
result=GC_generic_or_special_malloc((word)lb,obj_kind);
|
|
if (result!=NULL){
|
|
BCOPY(p,result,sz);
|
|
#ifndef IGNORE_FREE
|
|
GC_free(p);
|
|
#endif
|
|
}
|
|
return result;
|
|
}
|
|
#if defined(REDIRECT_MALLOC)&&!defined(REDIRECT_REALLOC)
|
|
#define REDIRECT_REALLOC GC_realloc
|
|
#endif
|
|
#ifdef REDIRECT_REALLOC
|
|
#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
|
|
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(GC_have_errors,FALSE))
|
|
GC_print_all_errors();
|
|
GC_INVOKE_FINALIZERS();
|
|
GC_DBG_COLLECT_AT_MALLOC(lb);
|
|
LOCK();
|
|
result=(ptr_t)GC_alloc_large(ADD_SLOP(lb),k,IGNORE_OFF_PAGE);
|
|
if (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
|
|
((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);
|
|
}
|
|
GC_API void GC_CALL GC_incr_bytes_allocd(size_t n)
|
|
{
|
|
GC_bytes_allocd+=n;
|
|
}
|
|
GC_API 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;
|
|
#endif
|
|
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;
|
|
size_t lg;
|
|
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);
|
|
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(GC_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();
|
|
if (GC_incremental&&!GC_dont_gc){
|
|
ENTER_GC();
|
|
GC_collect_a_little_inner(1);
|
|
EXIT_GC();
|
|
}
|
|
rlh=ok->ok_reclaim_list;
|
|
if (rlh!=NULL){
|
|
struct hblk*hbp;
|
|
hdr*hhdr;
|
|
for (rlh+=lg;(hbp=*rlh)!=NULL;){
|
|
hhdr=HDR(hbp);
|
|
*rlh=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);
|
|
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;
|
|
GC_release_mark_lock();
|
|
#endif
|
|
(void)GC_clear_stack(0);
|
|
return;
|
|
}
|
|
#endif
|
|
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();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
{
|
|
struct hblk*h=GC_allochblk(lb,k,0);
|
|
if (h){
|
|
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;
|
|
}
|
|
}
|
|
op=GC_generic_malloc_inner(lb,k);
|
|
if (0!=op)obj_link(op)=0;
|
|
out:
|
|
*result=op;
|
|
UNLOCK();
|
|
(void)GC_clear_stack(0);
|
|
}
|
|
GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_many(size_t lb)
|
|
{
|
|
void*result;
|
|
lb=SIZET_SAT_ADD(lb,EXTRA_BYTES+GRANULE_BYTES - 1)
|
|
&~(GRANULE_BYTES - 1);
|
|
GC_generic_malloc_many(lb,NORMAL,&result);
|
|
return result;
|
|
}
|
|
#include <limits.h>
|
|
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);
|
|
}
|
|
return GC_malloc(lb<=HBLKSIZE?HBLKSIZE:lb);
|
|
}
|
|
new_lb=SIZET_SAT_ADD(lb,align - 1);
|
|
result=(ptr_t)GC_malloc(new_lb);
|
|
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;
|
|
}
|
|
GC_API int GC_CALL GC_posix_memalign(void**memptr,size_t align,size_t lb)
|
|
{
|
|
size_t align_minus_one=align - 1;
|
|
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;
|
|
}
|
|
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);
|
|
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 <wchar.h>
|
|
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
|
|
#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)
|
|
{
|
|
}
|
|
#endif
|
|
GC_API void GC_CALL GC_end_stubborn_change(const void*p)
|
|
{
|
|
GC_dirty(p);
|
|
}
|
|
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);
|
|
}
|
|
#if defined(__MINGW32__)&&!defined(__MINGW_EXCPT_DEFINE_PSDK)&&defined(__i386__)
|
|
#define __MINGW_EXCPT_DEFINE_PSDK 1
|
|
#endif
|
|
#include <stdio.h>
|
|
#if defined(MSWIN32)&&defined(__GNUC__)
|
|
#include <excpt.h>
|
|
#endif
|
|
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)
|
|
{
|
|
#if defined(AO_HAVE_compiler_barrier)&&!defined(BASE_ATOMIC_OPS_EMULATED)
|
|
AO_compiler_barrier();
|
|
#else
|
|
GC_noop1(0);
|
|
#endif
|
|
}
|
|
volatile word GC_noop_sink;
|
|
GC_ATTR_NO_SANITIZE_THREAD
|
|
GC_API void GC_CALL GC_noop1(word x)
|
|
{
|
|
GC_noop_sink=x;
|
|
}
|
|
GC_INNER struct obj_kind GC_obj_kinds[MAXOBJKINDS]={
|
|
{&GC_aobjfreelist[0],0,
|
|
GC_DS_LENGTH,FALSE,FALSE
|
|
OK_DISCLAIM_INITZ },
|
|
{&GC_objfreelist[0],0,
|
|
GC_DS_LENGTH,
|
|
TRUE,TRUE
|
|
OK_DISCLAIM_INITZ },
|
|
{&GC_uobjfreelist[0],0,
|
|
GC_DS_LENGTH,TRUE,TRUE
|
|
OK_DISCLAIM_INITZ },
|
|
#ifdef GC_ATOMIC_UNCOLLECTABLE
|
|
{&GC_auobjfreelist[0],0,
|
|
GC_DS_LENGTH,FALSE,FALSE
|
|
OK_DISCLAIM_INITZ },
|
|
#endif
|
|
};
|
|
#ifndef INITIAL_MARK_STACK_SIZE
|
|
#define INITIAL_MARK_STACK_SIZE (1*HBLKSIZE)
|
|
#endif
|
|
#if!defined(GC_DISABLE_INCREMENTAL)
|
|
STATIC word GC_n_rescuing_pages=0;
|
|
#endif
|
|
#ifdef PARALLEL_MARK
|
|
GC_INNER GC_bool GC_parallel_mark_disabled=FALSE;
|
|
#endif
|
|
GC_INNER GC_bool GC_collection_in_progress(void)
|
|
{
|
|
return(GC_mark_state!=MS_NONE);
|
|
}
|
|
GC_INNER void GC_clear_hdr_marks(hdr*hhdr)
|
|
{
|
|
size_t last_bit;
|
|
#ifdef AO_HAVE_load
|
|
last_bit=FINAL_MARK_BIT((size_t)AO_load((volatile AO_t*)&hhdr->hb_sz));
|
|
#else
|
|
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;
|
|
}
|
|
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
|
|
}
|
|
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;
|
|
GC_clear_hdr_marks(hhdr);
|
|
}
|
|
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;
|
|
#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);
|
|
}
|
|
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;
|
|
}
|
|
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");
|
|
}
|
|
GC_scan_ptr=NULL;
|
|
}
|
|
#ifdef PARALLEL_MARK
|
|
STATIC void GC_do_parallel_mark(void);
|
|
#endif
|
|
#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);
|
|
#endif
|
|
STATIC struct hblk*GC_push_next_marked(struct hblk*h);
|
|
STATIC struct hblk*GC_push_next_marked_uncollectable(struct hblk*h);
|
|
static void alloc_mark_stack(size_t);
|
|
#ifdef WRAP_MARK_SOME
|
|
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)){
|
|
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
|
|
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
|
|
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){
|
|
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
|
|
#if (defined(MSWIN32)||defined(MSWINCE))&&defined(__GNUC__)
|
|
typedef struct {
|
|
EXCEPTION_REGISTRATION ex_reg;
|
|
void*alt_path;
|
|
} ext_ex_regn;
|
|
static EXCEPTION_DISPOSITION mark_ex_handler(
|
|
struct _EXCEPTION_RECORD*ex_rec,
|
|
void*est_frame,
|
|
struct _CONTEXT*context,
|
|
void*disp_ctxt GC_ATTR_UNUSED)
|
|
{
|
|
if (ex_rec->ExceptionCode==STATUS_ACCESS_VIOLATION){
|
|
ext_ex_regn*xer=(ext_ex_regn*)est_frame;
|
|
context->Esp=context->Ebp;
|
|
context->Ebp=*((DWORD*)context->Esp);
|
|
context->Esp=context->Esp - 8;
|
|
context->Eip=(DWORD)(xer->alt_path);
|
|
return ExceptionContinueExecution;
|
|
} else {
|
|
return ExceptionContinueSearch;
|
|
}
|
|
}
|
|
#endif
|
|
GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame)
|
|
{
|
|
GC_bool ret_val;
|
|
#if defined(MSWIN32)||defined(MSWINCE)
|
|
#ifndef __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;
|
|
}
|
|
#if defined(GC_WIN32_THREADS)&&!defined(GC_PTHREADS)
|
|
if (GC_started_thread_while_stopped())
|
|
goto handle_thr_start;
|
|
#endif
|
|
rm_handler:
|
|
return ret_val;
|
|
#else
|
|
ext_ex_regn er;
|
|
#if GC_GNUC_PREREQ(4,7)||GC_CLANG_PREREQ(3,3)
|
|
#pragma GCC diagnostic push
|
|
#if defined(__clang__)||GC_GNUC_PREREQ(6,4)
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
#else
|
|
#pragma GCC diagnostic ignored "-pedantic"
|
|
#endif
|
|
er.alt_path=&&handle_ex;
|
|
#pragma GCC diagnostic pop
|
|
#elif!defined(CPPCHECK)
|
|
er.alt_path=&&handle_ex;
|
|
#endif
|
|
er.ex_reg.handler=mark_ex_handler;
|
|
__asm__ __volatile__ ("movl %%fs:0,%0":"=r" (er.ex_reg.prev));
|
|
__asm__ __volatile__ ("movl %0,%%fs:0"::"r" (&er));
|
|
ret_val=GC_mark_some_inner(cold_gc_frame);
|
|
if (er.alt_path==0)
|
|
goto handle_ex;
|
|
#if defined(GC_WIN32_THREADS)&&!defined(GC_PTHREADS)
|
|
if (GC_started_thread_while_stopped())
|
|
goto handle_thr_start;
|
|
#endif
|
|
rm_handler:
|
|
__asm__ __volatile__ ("mov %0,%%fs:0"::"r" (er.ex_reg.prev));
|
|
return ret_val;
|
|
#endif
|
|
#else
|
|
#ifndef DEFAULT_VDB
|
|
if (GC_auto_incremental){
|
|
WARN("Incremental GC incompatible with/proc roots\n",0);
|
|
}
|
|
#endif
|
|
GC_setup_temporary_fault_handler();
|
|
if(SETJMP(GC_jmp_buf)!=0)goto handle_ex;
|
|
ret_val=GC_mark_some_inner(cold_gc_frame);
|
|
rm_handler:
|
|
GC_reset_fault_handler();
|
|
return ret_val;
|
|
#endif
|
|
handle_ex:
|
|
{
|
|
static word warned_gc_no;
|
|
if (warned_gc_no!=GC_gc_no){
|
|
WARN("Caught ACCESS_VIOLATION in marker;"
|
|
" memory mapping disappeared\n",0);
|
|
warned_gc_no=GC_gc_no;
|
|
}
|
|
}
|
|
#if (defined(MSWIN32)||defined(MSWINCE))&&defined(GC_WIN32_THREADS)&&!defined(GC_PTHREADS)
|
|
handle_thr_start:
|
|
#endif
|
|
#ifdef REGISTER_LIBRARIES_EARLY
|
|
START_WORLD();
|
|
GC_cond_register_dynamic_libraries();
|
|
STOP_WORLD();
|
|
#endif
|
|
GC_invalidate_mark_state();
|
|
GC_scan_ptr=NULL;
|
|
ret_val=FALSE;
|
|
goto rm_handler;
|
|
}
|
|
#endif
|
|
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
|
|
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);
|
|
}
|
|
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;
|
|
ptr_t current_p;
|
|
word current;
|
|
ptr_t limit=0;
|
|
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
|
|
GC_objects_are_marked=TRUE;
|
|
INIT_HDR_CACHE;
|
|
#ifdef OS2
|
|
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:
|
|
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:
|
|
GC_ASSERT(descr < (word)GC_greatest_plausible_heap_addr
|
|
- (word)GC_least_plausible_heap_addr
|
|
||(word)(current_p+descr)
|
|
<=(word)GC_least_plausible_heap_addr
|
|
||(word)current_p>=(word)GC_greatest_plausible_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);
|
|
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 #%u:large section;start %p,len %lu,"
|
|
" splitting (parallel)at %p\n",
|
|
(unsigned)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
|
|
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 #%u:large section;start %p,len %lu,"
|
|
" splitting at %p\n",
|
|
(unsigned)GC_gc_no,(void*)current_p,
|
|
(unsigned long)descr,(void*)limit);
|
|
}
|
|
#endif
|
|
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 #%u:tracing from %p bitmap descr %lu\n",
|
|
(unsigned)GC_gc_no,(void*)current_p,
|
|
(unsigned long)descr);
|
|
}
|
|
#endif
|
|
descr&=~GC_DS_TAGS;
|
|
credit-=WORDS_TO_BYTES(WORDSZ/2);
|
|
while (descr!=0){
|
|
if ((descr&SIGNB)!=0){
|
|
current=*(word*)current_p;
|
|
FIXUP_POINTER(current);
|
|
if (current>=(word)least_ha&¤t < (word)greatest_ha){
|
|
PREFETCH((ptr_t)current);
|
|
#ifdef ENABLE_TRACE
|
|
if (GC_trace_addr==current_p){
|
|
GC_log_printf("GC #%u:considering(3)%p->%p\n",
|
|
(unsigned)GC_gc_no,(void*)current_p,
|
|
(void*)current);
|
|
}
|
|
#endif
|
|
PUSH_CONTENTS((ptr_t)current,mark_stack_top,
|
|
mark_stack_limit,current_p);
|
|
}
|
|
}
|
|
descr<<=1;
|
|
current_p+=sizeof(word);
|
|
}
|
|
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 #%u:tracing from %p,proc descr %lu\n",
|
|
(unsigned)GC_gc_no,(void*)current_p,
|
|
(unsigned long)descr);
|
|
}
|
|
#endif
|
|
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){
|
|
descr=*(word*)(current_p+descr - GC_DS_PER_OBJECT);
|
|
} else {
|
|
ptr_t type_descr=*(ptr_t*)current_p;
|
|
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){
|
|
mark_stack_top--;
|
|
continue;
|
|
}
|
|
goto retry;
|
|
}
|
|
} else {
|
|
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 #%u:small object;start %p,len %lu\n",
|
|
(unsigned)GC_gc_no,(void*)current_p,
|
|
(unsigned long)descr);
|
|
}
|
|
#endif
|
|
limit=current_p+(word)descr;
|
|
}
|
|
GC_ASSERT(!((word)current_p&(ALIGNMENT-1)));
|
|
credit-=limit - current_p;
|
|
limit-=sizeof(word);
|
|
{
|
|
#define PREF_DIST 4
|
|
#ifndef SMALL_CONFIG
|
|
word deferred;
|
|
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;
|
|
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
|
|
while ((word)current_p<=(word)limit){
|
|
current=*(word*)current_p;
|
|
FIXUP_POINTER(current);
|
|
PREFETCH(current_p+PREF_DIST*CACHE_LINE_SIZE);
|
|
if (current>=(word)least_ha&¤t < (word)greatest_ha){
|
|
PREFETCH((ptr_t)current);
|
|
#ifdef ENABLE_TRACE
|
|
if (GC_trace_addr==current_p){
|
|
GC_log_printf("GC #%u:considering(1)%p->%p\n",
|
|
(unsigned)GC_gc_no,(void*)current_p,
|
|
(void*)current);
|
|
}
|
|
#endif
|
|
PUSH_CONTENTS((ptr_t)current,mark_stack_top,
|
|
mark_stack_limit,current_p);
|
|
}
|
|
current_p+=ALIGNMENT;
|
|
}
|
|
#ifndef SMALL_CONFIG
|
|
#ifdef ENABLE_TRACE
|
|
if (GC_trace_addr==current_p){
|
|
GC_log_printf("GC #%u:considering(2)%p->%p\n",
|
|
(unsigned)GC_gc_no,(void*)current_p,
|
|
(void*)deferred);
|
|
}
|
|
#endif
|
|
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;
|
|
STATIC unsigned GC_helper_count=0;
|
|
STATIC unsigned GC_active_count=0;
|
|
GC_INNER word GC_mark_no=0;
|
|
#ifdef LINT2
|
|
#define LOCAL_MARK_STACK_SIZE (HBLKSIZE/8)
|
|
#else
|
|
#define LOCAL_MARK_STACK_SIZE HBLKSIZE
|
|
#endif
|
|
GC_INNER void GC_wait_for_markers_init(void)
|
|
{
|
|
signed_word count;
|
|
if (GC_markers_m1==0)
|
|
return;
|
|
#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);
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
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){
|
|
AO_store_release_write(&p->mse_descr.ao,0);
|
|
++top;
|
|
top->mse_descr.w=descr;
|
|
top->mse_start=p->mse_start;
|
|
GC_ASSERT((descr&GC_DS_TAGS)!=GC_DS_LENGTH
|
|
||descr < (word)GC_greatest_plausible_heap_addr
|
|
- (word)GC_least_plausible_heap_addr
|
|
||(word)(p->mse_start+descr)
|
|
<=(word)GC_least_plausible_heap_addr
|
|
||(word)p->mse_start
|
|
>=(word)GC_greatest_plausible_heap_addr);
|
|
++i;
|
|
if ((descr&GC_DS_TAGS)==GC_DS_LENGTH)i+=(int)(descr>>8);
|
|
}
|
|
}
|
|
*next=p;
|
|
return top;
|
|
}
|
|
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;
|
|
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;
|
|
} 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));
|
|
}
|
|
GC_release_mark_lock();
|
|
GC_notify_all_marker();
|
|
}
|
|
#ifndef N_LOCAL_ITERS
|
|
#define N_LOCAL_ITERS 1
|
|
#endif
|
|
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;
|
|
}
|
|
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()){
|
|
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
|
|
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);
|
|
}
|
|
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;
|
|
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);
|
|
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){
|
|
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;
|
|
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;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
STATIC void GC_do_parallel_mark(void)
|
|
{
|
|
GC_acquire_mark_lock();
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
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();
|
|
GC_mark_local(GC_main_local_mark_stack,0);
|
|
GC_help_wanted=FALSE;
|
|
while (GC_helper_count > 0){
|
|
GC_wait_marker();
|
|
}
|
|
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();
|
|
}
|
|
GC_INNER void GC_help_marker(word my_mark_no)
|
|
{
|
|
#define my_id my_id_mse.mse_descr.w
|
|
mse my_id_mse;
|
|
mse local_mark_stack[LOCAL_MARK_STACK_SIZE];
|
|
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){
|
|
return;
|
|
}
|
|
GC_helper_count=(unsigned)my_id+1;
|
|
GC_mark_local(local_mark_stack,(int)my_id);
|
|
#undef my_id
|
|
}
|
|
#endif
|
|
GC_INNER void GC_scratch_recycle_inner(void*ptr,size_t bytes)
|
|
{
|
|
if (ptr!=NULL){
|
|
size_t page_offset=(word)ptr&(GC_page_size - 1);
|
|
size_t displ=0;
|
|
size_t recycled_bytes;
|
|
GC_ASSERT(bytes!=0);
|
|
GC_ASSERT(GC_page_size!=0);
|
|
if (page_offset!=0)
|
|
displ=GC_page_size - page_offset;
|
|
recycled_bytes=(bytes - displ)&~(GC_page_size - 1);
|
|
GC_COND_LOG_PRINTF("Recycle %lu scratch-allocated bytes at %p\n",
|
|
(unsigned long)recycled_bytes,ptr);
|
|
if (recycled_bytes > 0)
|
|
GC_add_to_heap((struct hblk*)((word)ptr+displ),recycled_bytes);
|
|
}
|
|
}
|
|
static void alloc_mark_stack(size_t n)
|
|
{
|
|
mse*new_stack=(mse*)GC_scratch_alloc(n*sizeof(struct GC_ms_entry));
|
|
#ifdef GWW_VDB
|
|
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){
|
|
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;
|
|
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_PRIdPTR " 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);
|
|
}
|
|
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
|
|
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){
|
|
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){
|
|
GC_push_selected((ptr_t)bottom,(ptr_t)top,GC_page_was_ever_dirty);
|
|
} else
|
|
#endif
|
|
{
|
|
GC_push_all(bottom,top);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
GC_API void GC_CALL GC_push_conditional(void*bottom,void*top,
|
|
int all GC_ATTR_UNUSED)
|
|
{
|
|
GC_push_all(bottom,top);
|
|
}
|
|
#endif
|
|
#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 GC_ATTR_UNUSED)
|
|
{
|
|
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);
|
|
}
|
|
#if defined(PRINT_BLACK_LIST)||defined(KEEP_BACK_PTRS)
|
|
GC_INNER void GC_mark_and_push_stack(ptr_t p,ptr_t source)
|
|
#else
|
|
GC_INNER void 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
|
|
GC_dirty(p);
|
|
#endif
|
|
GC_mark_stack_top=GC_push_contents_hdr(r,GC_mark_stack_top,
|
|
GC_mark_stack_limit,
|
|
source,hhdr,FALSE);
|
|
}
|
|
#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
|
|
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;
|
|
lim=t - 1;
|
|
for (p=b;(word)p<=(word)lim;
|
|
p=(word*)(((ptr_t)p)+ALIGNMENT)){
|
|
REGISTER word 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
|
|
{
|
|
GC_push_all_eager(bottom,top);
|
|
}
|
|
}
|
|
#if defined(WRAP_MARK_SOME)&&defined(PARALLEL_MARK)
|
|
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;
|
|
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
|
|
#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
|
|
#ifdef USE_PUSH_MARKED_ACCELERATORS
|
|
STATIC void GC_push_marked1(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);
|
|
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
|
|
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);
|
|
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
|
|
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);
|
|
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
|
|
#endif
|
|
#endif
|
|
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;
|
|
if (( GC_DS_LENGTH)==descr)return;
|
|
if (GC_block_empty(hhdr))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:
|
|
#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)){
|
|
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
|
|
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 (( 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
|
|
#ifndef GC_DISABLE_INCREMENTAL
|
|
STATIC GC_bool GC_block_was_dirty(struct hblk*h,hdr*hhdr)
|
|
{
|
|
word sz=hhdr->hb_sz;
|
|
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
|
|
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
|
|
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);
|
|
} else
|
|
#endif
|
|
{
|
|
GC_push_marked(h,hhdr);
|
|
}
|
|
return(h+OBJ_SZ_TO_BLOCKS(hhdr->hb_sz));
|
|
}
|
|
#endif
|
|
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));
|
|
}
|
|
#include <stdio.h>
|
|
int GC_no_dls=0;
|
|
#if!defined(NO_DEBUGGING)||defined(GC_ASSERTIONS)
|
|
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
|
|
#if!defined(NO_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
|
|
#ifndef THREADS
|
|
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
|
|
#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32)
|
|
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);
|
|
}
|
|
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;
|
|
}
|
|
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
|
|
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();
|
|
}
|
|
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));
|
|
e=(ptr_t)((word)e&~(word)(sizeof(word)- 1));
|
|
if ((word)b>=(word)e)return;
|
|
#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)
|
|
{
|
|
int i;
|
|
struct roots*old=NULL;
|
|
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){
|
|
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;
|
|
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;
|
|
}
|
|
if (old->r_tmp==tmp||!tmp){
|
|
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();
|
|
}
|
|
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)
|
|
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
|
|
#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32)
|
|
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;
|
|
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();
|
|
}
|
|
STATIC void GC_remove_roots_inner(ptr_t b,ptr_t e)
|
|
{
|
|
int i;
|
|
GC_bool rebuild=FALSE;
|
|
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);
|
|
rebuild=TRUE;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
if (rebuild)
|
|
GC_rebuild_root_index();
|
|
}
|
|
#endif
|
|
#ifdef USE_PROC_FOR_LIBRARIES
|
|
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;
|
|
GC_static_roots[j].r_start=r_start;
|
|
GC_static_roots[j].r_end=r_end;
|
|
GC_static_roots[j].r_tmp=r_tmp;
|
|
}
|
|
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){
|
|
#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;
|
|
if ((word)e < (word)r_end){
|
|
int j;
|
|
if (rebuild){
|
|
GC_rebuild_root_index();
|
|
rebuild=FALSE;
|
|
}
|
|
GC_add_roots_inner(e,r_end,FALSE);
|
|
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){
|
|
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;
|
|
swap_static_roots(i,j - 1);
|
|
}
|
|
i--;
|
|
}
|
|
rebuild=TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (rebuild)
|
|
GC_rebuild_root_index();
|
|
}
|
|
#endif
|
|
#if!defined(NO_DEBUGGING)
|
|
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
|
|
GC_INNER ptr_t GC_approx_sp(void)
|
|
{
|
|
volatile word sp;
|
|
#if defined(S390)&&!defined(CPPCHECK)&&(__clang_major__ < 8)
|
|
sp=(word)&sp;
|
|
#elif defined(CPPCHECK)||(__GNUC__>=4&&!defined(STACK_NOT_SCANNED))
|
|
sp=(word)__builtin_frame_address(0);
|
|
#else
|
|
sp=(word)&sp;
|
|
#endif
|
|
return((ptr_t)sp);
|
|
}
|
|
GC_API void GC_CALL GC_clear_exclusion_table(void)
|
|
{
|
|
GC_excl_table_entries=0;
|
|
}
|
|
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;
|
|
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;
|
|
}
|
|
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 (0!=next){
|
|
size_t i;
|
|
if ((word)(next->e_start)< (word)finish){
|
|
ABORT("Exclusion ranges overlap");
|
|
}
|
|
if ((word)(next->e_start)==(word)finish){
|
|
next->e_start=(ptr_t)start;
|
|
return;
|
|
}
|
|
next_index=next - GC_excl_table;
|
|
for (i=GC_excl_table_entries;i > next_index;--i){
|
|
GC_excl_table[i]=GC_excl_table[i-1];
|
|
}
|
|
} else {
|
|
next_index=GC_excl_table_entries;
|
|
}
|
|
if (GC_excl_table_entries==MAX_EXCLUSIONS)ABORT("Too many exclusions");
|
|
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;
|
|
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));
|
|
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(b,t,all))
|
|
#elif defined(GC_DISABLE_INCREMENTAL)
|
|
#define GC_PUSH_CONDITIONAL(b,t,all)GC_push_all(b,t)
|
|
#else
|
|
#define GC_PUSH_CONDITIONAL(b,t,all)GC_push_conditional(b,t,all)
|
|
#endif
|
|
STATIC void GC_push_conditional_with_exclusions(ptr_t bottom,ptr_t top,
|
|
GC_bool all GC_ATTR_UNUSED)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
#ifdef IA64
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
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
|
|
#ifdef THREADS
|
|
GC_INNER void GC_push_all_stack_sections(ptr_t lo,ptr_t hi,
|
|
struct GC_traced_stack_sect_s*traced_stack_sect)
|
|
{
|
|
while (traced_stack_sect!=NULL){
|
|
GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect);
|
|
#ifdef STACK_GROWS_UP
|
|
GC_push_all_stack((ptr_t)traced_stack_sect,lo);
|
|
#else
|
|
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
|
|
GC_push_all_stack(hi,lo);
|
|
#else
|
|
GC_push_all_stack(lo,hi);
|
|
#endif
|
|
}
|
|
#else
|
|
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){
|
|
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
|
|
GC_push_all(bottom,cold_gc_frame+sizeof(ptr_t));
|
|
GC_push_all_eager(cold_gc_frame,top);
|
|
#endif
|
|
} else
|
|
#endif
|
|
{
|
|
GC_push_all_eager(bottom,top);
|
|
}
|
|
#ifdef TRACE_BUF
|
|
GC_add_trace_entry("GC_push_all_stack",(word)bottom,(word)top);
|
|
#endif
|
|
}
|
|
STATIC void GC_push_all_stack_part_eager_sections(ptr_t lo,ptr_t hi,
|
|
ptr_t cold_gc_frame,struct GC_traced_stack_sect_s*traced_stack_sect)
|
|
{
|
|
GC_ASSERT(traced_stack_sect==NULL||cold_gc_frame==NULL||
|
|
(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
|
|
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;
|
|
}
|
|
GC_ASSERT(!((word)hi HOTTER_THAN (word)lo));
|
|
#ifdef STACK_GROWS_UP
|
|
GC_push_all_stack_partially_eager(hi,lo,cold_gc_frame);
|
|
#else
|
|
GC_push_all_stack_partially_eager(lo,hi,cold_gc_frame);
|
|
#endif
|
|
}
|
|
#endif
|
|
STATIC void GC_push_current_stack(ptr_t cold_gc_frame,
|
|
void*context GC_ATTR_UNUSED)
|
|
{
|
|
#if defined(THREADS)
|
|
#ifdef STACK_GROWS_DOWN
|
|
GC_push_all_eager(GC_approx_sp(),cold_gc_frame);
|
|
#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
|
|
{
|
|
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){
|
|
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,GC_traced_stack_sect);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
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;
|
|
#endif
|
|
GC_with_callee_saves_pushed(GC_push_current_stack,cold_gc_frame);
|
|
}
|
|
GC_INNER void GC_push_roots(GC_bool all,ptr_t cold_gc_frame GC_ATTR_UNUSED)
|
|
{
|
|
int i;
|
|
unsigned kind;
|
|
#if!defined(REGISTER_LIBRARIES_EARLY)
|
|
GC_cond_register_dynamic_libraries();
|
|
#endif
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
#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();
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
if (GC_world_stopped)
|
|
GC_mark_thread_local_free_lists();
|
|
#endif
|
|
#ifndef STACK_NOT_SCANNED
|
|
GC_push_regs_and_stack(cold_gc_frame);
|
|
#endif
|
|
if (GC_push_other_roots!=0){
|
|
(*GC_push_other_roots)();
|
|
}
|
|
}
|
|
#ifdef ENABLE_DISCLAIM
|
|
#endif
|
|
#include <stdio.h>
|
|
GC_INNER signed_word GC_bytes_found=0;
|
|
#if defined(PARALLEL_MARK)
|
|
GC_INNER signed_word GC_fl_builder_count=0;
|
|
#endif
|
|
#ifndef MAX_LEAKED
|
|
#define MAX_LEAKED 40
|
|
#endif
|
|
STATIC ptr_t GC_leaked[MAX_LEAKED]={ NULL };
|
|
STATIC unsigned GC_n_leaked=0;
|
|
GC_INNER GC_bool GC_have_errors=FALSE;
|
|
#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_have_errors=TRUE;
|
|
if (GC_n_leaked < MAX_LEAKED){
|
|
GC_leaked[GC_n_leaked++]=leaked;
|
|
GC_set_mark_bit(leaked);
|
|
}
|
|
}
|
|
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=GC_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();
|
|
}
|
|
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;
|
|
}
|
|
STATIC ptr_t GC_reclaim_clear(struct hblk*hbp,hdr*hhdr,word sz,
|
|
ptr_t list,signed_word*count)
|
|
{
|
|
word bit_no=0;
|
|
word*p,*q,*plim;
|
|
signed_word n_bytes_found=0;
|
|
GC_ASSERT(hhdr==GC_find_header((ptr_t)hbp));
|
|
#ifndef THREADS
|
|
GC_ASSERT(sz==hhdr->hb_sz);
|
|
#else
|
|
#endif
|
|
GC_ASSERT((sz&(BYTES_PER_WORD-1))==0);
|
|
p=(word*)(hbp->hb_body);
|
|
plim=(word*)(hbp->hb_body+HBLKSIZE - sz);
|
|
while ((word)p<=(word)plim){
|
|
if (mark_bit_from_hdr(hhdr,bit_no)){
|
|
p=(word*)((ptr_t)p+sz);
|
|
} else {
|
|
n_bytes_found+=sz;
|
|
obj_link(p)=list;
|
|
list=((ptr_t)p);
|
|
q=(word*)((ptr_t)p+sz);
|
|
#ifdef USE_MARK_BYTES
|
|
GC_ASSERT(!(sz&1)
|
|
&&!((word)p&(2*sizeof(word)- 1)));
|
|
p[1]=0;
|
|
p+=2;
|
|
while ((word)p < (word)q){
|
|
CLEAR_DOUBLE(p);
|
|
p+=2;
|
|
}
|
|
#else
|
|
p++;
|
|
while ((word)p < (word)q){
|
|
*p++=0;
|
|
}
|
|
#endif
|
|
}
|
|
bit_no+=MARK_BIT_OFFSET(sz);
|
|
}
|
|
*count+=n_bytes_found;
|
|
return(list);
|
|
}
|
|
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);
|
|
while ((word)p<=(word)plim){
|
|
if (!mark_bit_from_hdr(hhdr,bit_no)){
|
|
n_bytes_found+=sz;
|
|
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
|
|
STATIC ptr_t GC_disclaim_and_reclaim(struct hblk*hbp,hdr*hhdr,word sz,
|
|
ptr_t list,signed_word*count)
|
|
{
|
|
word bit_no=0;
|
|
word*p,*q,*plim;
|
|
signed_word n_bytes_found=0;
|
|
struct obj_kind*ok=&GC_obj_kinds[hhdr->hb_obj_kind];
|
|
int (GC_CALLBACK*disclaim)(void*)=ok->ok_disclaim_proc;
|
|
#ifndef THREADS
|
|
GC_ASSERT(sz==hhdr->hb_sz);
|
|
#endif
|
|
p=(word*)(hbp->hb_body);
|
|
plim=(word*)((ptr_t)p+HBLKSIZE - sz);
|
|
while ((word)p<=(word)plim){
|
|
int marked=mark_bit_from_hdr(hhdr,bit_no);
|
|
if (!marked&&(*disclaim)(p)){
|
|
set_mark_bit_from_hdr(hhdr,bit_no);
|
|
hhdr->hb_n_marks++;
|
|
marked=1;
|
|
}
|
|
if (marked)
|
|
p=(word*)((ptr_t)p+sz);
|
|
else {
|
|
n_bytes_found+=sz;
|
|
obj_link(p)=list;
|
|
list=((ptr_t)p);
|
|
q=(word*)((ptr_t)p+sz);
|
|
#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++;
|
|
while ((word)p < (word)q){
|
|
*p++=0;
|
|
}
|
|
#endif
|
|
}
|
|
bit_no+=MARK_BIT_OFFSET(sz);
|
|
}
|
|
*count+=n_bytes_found;
|
|
return list;
|
|
}
|
|
#endif
|
|
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
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
#ifdef AO_HAVE_load
|
|
#define IS_PTRFREE_SAFE(hhdr)(AO_load((volatile AO_t*)&(hhdr)->hb_descr)==0)
|
|
#else
|
|
#define IS_PTRFREE_SAFE(hhdr)((hhdr)->hb_descr==0)
|
|
#endif
|
|
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
|
|
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;
|
|
}
|
|
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
|
|
STATIC void GC_reclaim_block(struct hblk*hbp,word report_if_found)
|
|
{
|
|
hdr*hhdr=HDR(hbp);
|
|
word sz;
|
|
struct obj_kind*ok=&GC_obj_kinds[hhdr->hb_obj_kind];
|
|
#ifdef AO_HAVE_load
|
|
sz=(word)AO_load((volatile AO_t*)&hhdr->hb_sz);
|
|
#else
|
|
sz=hhdr->hb_sz;
|
|
#endif
|
|
if( sz > MAXOBJBYTES){
|
|
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)){
|
|
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
|
|
GC_ASSERT(hhdr->hb_n_marks<=2*(HBLKSIZE/sz+1)+16);
|
|
#else
|
|
GC_ASSERT(sz*hhdr->hb_n_marks<=HBLKSIZE);
|
|
#endif
|
|
if (report_if_found){
|
|
GC_reclaim_small_nonempty_block(hbp,sz,
|
|
TRUE);
|
|
} else if (empty){
|
|
#ifdef ENABLE_DISCLAIM
|
|
if ((hhdr->hb_flags&HAS_DISCLAIM)!=0){
|
|
GC_disclaim_and_reclaim_or_free_small_block(hbp);
|
|
} else
|
|
#endif
|
|
{
|
|
GC_bytes_found+=HBLKSIZE;
|
|
GC_freehblk(hbp);
|
|
}
|
|
} else if (GC_find_leak||!GC_block_nearly_full(hhdr,sz)){
|
|
struct hblk**rlh=ok->ok_reclaim_list;
|
|
if (rlh!=NULL){
|
|
rlh+=BYTES_TO_GRANULES(sz);
|
|
hhdr->hb_next=*rlh;
|
|
*rlh=hbp;
|
|
}
|
|
}
|
|
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)
|
|
struct Print_stats
|
|
{
|
|
size_t number_of_blocks;
|
|
size_t total_bytes;
|
|
};
|
|
#ifdef USE_MARK_BYTES
|
|
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
|
|
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
|
|
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;
|
|
}
|
|
#endif
|
|
STATIC void GC_print_block_descr(struct hblk*h,
|
|
word 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);
|
|
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);
|
|
}
|
|
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
|
|
STATIC void GC_clear_fl_links(void**flp)
|
|
{
|
|
void*next=*flp;
|
|
while (0!=next){
|
|
*flp=0;
|
|
flp=&(obj_link(next));
|
|
next=*flp;
|
|
}
|
|
}
|
|
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
|
|
GC_composite_in_use=0;
|
|
GC_atomic_in_use=0;
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
BZERO(rlist,(MAXOBJGRANULES+1)*sizeof(void*));
|
|
}
|
|
GC_apply_to_all_blocks(GC_reclaim_block,(word)report_if_found);
|
|
#ifdef EAGER_SWEEP
|
|
GC_reclaim_all((GC_stop_func)0,FALSE);
|
|
#elif defined(ENABLE_DISCLAIM)
|
|
GC_reclaim_unconditionally_marked();
|
|
#endif
|
|
#if defined(PARALLEL_MARK)
|
|
GC_ASSERT(0==GC_fl_builder_count);
|
|
#endif
|
|
}
|
|
GC_INNER void GC_continue_reclaim(word sz,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;
|
|
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;
|
|
}
|
|
}
|
|
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){
|
|
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)
|
|
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
|
|
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){
|
|
plim=p;
|
|
} else {
|
|
plim=hbp->hb_body+HBLKSIZE - sz;
|
|
}
|
|
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);
|
|
}
|
|
#ifndef GC_TYPED_H
|
|
#define GC_TYPED_H
|
|
#ifndef GC_H
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
typedef GC_word*GC_bitmap;
|
|
#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*,
|
|
size_t);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc_explicitly_typed(size_t,
|
|
GC_descr);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL
|
|
GC_malloc_explicitly_typed_ignore_off_page(size_t,
|
|
GC_descr);
|
|
GC_API GC_ATTR_MALLOC GC_ATTR_CALLOC_SIZE(1,2)void*GC_CALL
|
|
GC_calloc_explicitly_typed(size_t,
|
|
size_t,
|
|
GC_descr);
|
|
#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
|
|
}
|
|
#endif
|
|
#endif
|
|
#define TYPD_EXTRA_BYTES (sizeof(word)- EXTRA_BYTES)
|
|
STATIC int GC_explicit_kind=0;
|
|
STATIC int GC_array_kind=0;
|
|
struct LeafDescriptor {
|
|
word ld_tag;
|
|
#define LEAF_TAG 1
|
|
size_t ld_size;
|
|
size_t ld_nelements;
|
|
GC_descr ld_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;
|
|
STATIC int GC_array_mark_proc_index=0;
|
|
STATIC void GC_push_typed_structures_proc(void)
|
|
{
|
|
GC_PUSH_ALL_SYM(GC_ext_descriptors);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
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];
|
|
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);
|
|
}
|
|
STATIC GC_descr GC_bm_table[WORDSZ/2];
|
|
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);
|
|
#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
|
|
if ((descriptor&GC_DS_TAGS)==GC_DS_LENGTH){
|
|
if (descriptor==(GC_descr)size){
|
|
*simple_d=nelements*descriptor;
|
|
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;
|
|
}
|
|
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));
|
|
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);
|
|
GC_explicit_kind=GC_new_kind_inner(GC_new_free_list_inner(),
|
|
(WORDS_TO_BYTES((word)-1)|GC_DS_PER_OBJECT),
|
|
TRUE,TRUE);
|
|
GC_typed_mark_proc_index=GC_new_proc_inner(GC_typed_mark_proc);
|
|
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&¤t<=(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){
|
|
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);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
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=(complex_descriptor*)(addr[nwords-1]);
|
|
mse*orig_mark_stack_ptr=mark_stack_ptr;
|
|
mse*new_mark_stack_ptr;
|
|
if (descr==0){
|
|
return(orig_mark_stack_ptr);
|
|
}
|
|
new_mark_stack_ptr=GC_push_complex_descriptor(addr,descr,
|
|
mark_stack_ptr,
|
|
mark_stack_limit-1);
|
|
if (new_mark_stack_ptr==0){
|
|
if (NULL==mark_stack_ptr)ABORT("Bad mark_stack_ptr");
|
|
#ifdef PARALLEL_MARK
|
|
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 {
|
|
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=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);
|
|
#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){
|
|
return (WORDS_TO_BYTES(last_set_bit+1)|GC_DS_LENGTH);
|
|
}
|
|
}
|
|
#endif
|
|
if ((word)last_set_bit < BITMAP_BITS){
|
|
signed_word i;
|
|
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);
|
|
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);
|
|
lb=SIZET_SAT_ADD(lb,TYPD_EXTRA_BYTES);
|
|
op=(word*)GC_malloc_kind(lb,GC_explicit_kind);
|
|
if (EXPECT(NULL==op,FALSE))
|
|
return NULL;
|
|
lg=BYTES_TO_GRANULES(GC_size(op));
|
|
op[GRANULES_TO_WORDS(lg)- 1]=d;
|
|
GC_dirty(op+GRANULES_TO_WORDS(lg)- 1);
|
|
REACHABLE_AFTER_DIRTY(d);
|
|
return op;
|
|
}
|
|
#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);
|
|
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;
|
|
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));
|
|
}
|
|
((word*)op)[GRANULES_TO_WORDS(lg)- 1]=d;
|
|
GC_dirty(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;
|
|
GC_ASSERT(GC_explicit_typing_initialized);
|
|
descr_type=GC_make_array_descriptor((word)n,(word)lb,d,&simple_descr,
|
|
&complex_descr,&leaf);
|
|
if ((lb|n)> GC_SQRT_SIZE_MAX
|
|
&&lb > 0&&n > GC_SIZE_MAX/lb)
|
|
return (*GC_get_oom_fn())(GC_SIZE_MAX);
|
|
lb*=n;
|
|
switch(descr_type){
|
|
case NO_MEM:return(0);
|
|
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){
|
|
volatile 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;
|
|
((volatile word*)op)[GRANULES_TO_WORDS(lg)- 1]=(word)lp;
|
|
} else {
|
|
#ifndef GC_NO_FINALIZATION
|
|
size_t lw=GRANULES_TO_WORDS(lg);
|
|
op[lw - 1]=(word)complex_descr;
|
|
GC_dirty(op+lw - 1);
|
|
REACHABLE_AFTER_DIRTY(complex_descr);
|
|
if (EXPECT(GC_general_register_disappearing_link(
|
|
(void**)(op+lw - 1),op)
|
|
==GC_NO_MEMORY,FALSE))
|
|
#endif
|
|
{
|
|
return (*GC_get_oom_fn())(lb);
|
|
}
|
|
}
|
|
return op;
|
|
}
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#ifndef MSWINCE
|
|
#include <signal.h>
|
|
#endif
|
|
#ifdef GC_SOLARIS_THREADS
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
#if defined(UNIX_LIKE)||defined(CYGWIN32)||defined(SYMBIAN)||(defined(CONSOLE_LOG)&&defined(MSWIN32))
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#if defined(CONSOLE_LOG)&&defined(MSWIN32)&&defined(_MSC_VER)
|
|
#include <io.h>
|
|
#endif
|
|
#ifdef NONSTOP
|
|
#include <floss.h>
|
|
#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(SN_TARGET_ORBIS)||defined(SN_TARGET_PS3)
|
|
#include <pthread.h>
|
|
GC_INNER pthread_mutex_t GC_allocate_ml;
|
|
#endif
|
|
#endif
|
|
#ifdef DYNAMIC_LOADING
|
|
#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
|
|
#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;
|
|
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;
|
|
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;
|
|
#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;
|
|
#else
|
|
GC_INNER GC_bool GC_dump_regularly=FALSE;
|
|
#endif
|
|
#ifndef NO_CLOCK
|
|
STATIC CLOCK_TYPE GC_init_time;
|
|
#endif
|
|
#endif
|
|
#ifdef KEEP_BACK_PTRS
|
|
GC_INNER long GC_backtraces=0;
|
|
#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
|
|
#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
|
|
GC_finalizer_notifier_proc GC_finalizer_notifier=
|
|
(GC_finalizer_notifier_proc)0;
|
|
#ifdef GC_FORCE_UNMAP_ON_GCOLLECT
|
|
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;
|
|
STATIC void*GC_CALLBACK GC_default_oom_fn(
|
|
size_t bytes_requested GC_ATTR_UNUSED)
|
|
{
|
|
return(0);
|
|
}
|
|
GC_oom_func GC_oom_fn=GC_default_oom_fn;
|
|
#ifdef CAN_HANDLE_FORK
|
|
#ifdef HANDLE_FORK
|
|
GC_INNER int GC_handle_fork=1;
|
|
#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)
|
|
{
|
|
}
|
|
GC_API void GC_CALL GC_atfork_child(void)
|
|
{
|
|
}
|
|
#endif
|
|
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;
|
|
#elif defined(THREADS)||(defined(DARWIN)&&defined(MPROTECT_VDB))
|
|
if (!GC_is_initialized&&value){
|
|
#ifndef SMALL_CONFIG
|
|
GC_init();
|
|
#ifndef THREADS
|
|
if (GC_manual_vdb)
|
|
return;
|
|
#endif
|
|
#endif
|
|
ABORT("fork()handling unsupported");
|
|
}
|
|
#else
|
|
#endif
|
|
}
|
|
STATIC void GC_init_size_map(void)
|
|
{
|
|
size_t i;
|
|
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);
|
|
#endif
|
|
}
|
|
}
|
|
#ifndef SMALL_CLEAR_SIZE
|
|
#define SMALL_CLEAR_SIZE 256
|
|
#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(( void*)dummy,sizeof(dummy));
|
|
#endif
|
|
return arg;
|
|
}
|
|
#else
|
|
#ifdef THREADS
|
|
#define BIG_CLEAR_SIZE 2048
|
|
#else
|
|
STATIC word GC_stack_last_cleared=0;
|
|
STATIC ptr_t GC_min_sp=NULL;
|
|
STATIC ptr_t GC_high_water=NULL;
|
|
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
|
|
void*GC_clear_stack_inner(void*arg,
|
|
#if defined(__APPLE_CC__)&&!GC_CLANG_PREREQ(6,0)
|
|
volatile
|
|
#endif
|
|
ptr_t limit)
|
|
{
|
|
#define CLEAR_SIZE 213
|
|
volatile word dummy[CLEAR_SIZE];
|
|
BZERO(( void*)dummy,sizeof(dummy));
|
|
if ((word)GC_approx_sp()COOLER_THAN (word)limit){
|
|
(void)GC_clear_stack_inner(arg,limit);
|
|
}
|
|
#if defined(CPPCHECK)
|
|
GC_noop1(dummy[0]);
|
|
#else
|
|
GC_noop1(COVERT_DATAFLOW(dummy));
|
|
#endif
|
|
return(arg);
|
|
}
|
|
#endif
|
|
#ifdef THREADS
|
|
GC_ATTR_NO_SANITIZE_THREAD
|
|
static unsigned next_random_no(void)
|
|
{
|
|
static unsigned random_no=0;
|
|
return++random_no % 13;
|
|
}
|
|
#endif
|
|
GC_API void*GC_CALL GC_clear_stack(void*arg)
|
|
{
|
|
ptr_t sp=GC_approx_sp();
|
|
#ifdef THREADS
|
|
word volatile dummy[SMALL_CLEAR_SIZE];
|
|
#endif
|
|
#define SLOP 400
|
|
#define GC_SLOP 4000
|
|
#define CLEAR_THRESHOLD 100000
|
|
#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);
|
|
return GC_clear_stack_inner(arg,limit);
|
|
}
|
|
BZERO((void*)dummy,SMALL_CLEAR_SIZE*sizeof(word));
|
|
#else
|
|
if (GC_gc_no > GC_stack_last_cleared){
|
|
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;
|
|
}
|
|
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);
|
|
GC_min_sp=sp;
|
|
return GC_clear_stack_inner(arg,limit);
|
|
}
|
|
}
|
|
if (GC_bytes_allocd - GC_bytes_allocd_at_reset > CLEAR_THRESHOLD){
|
|
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
|
|
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);
|
|
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);
|
|
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);
|
|
}
|
|
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;
|
|
}
|
|
GC_API size_t GC_CALL GC_size(const void*p)
|
|
{
|
|
hdr*hhdr=HDR(p);
|
|
return (size_t)hhdr->hb_sz;
|
|
}
|
|
GC_API size_t GC_CALL GC_get_heap_size(void)
|
|
{
|
|
return (size_t)(GC_heapsize - GC_unmapped_bytes);
|
|
}
|
|
GC_API size_t GC_CALL GC_get_free_bytes(void)
|
|
{
|
|
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]);
|
|
}
|
|
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;
|
|
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;
|
|
#ifdef PARALLEL_MARK
|
|
pstats->markers_m1=(word)((signed_word)GC_markers_m1);
|
|
#else
|
|
pstats->markers_m1=0;
|
|
#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;
|
|
}
|
|
#include <string.h>
|
|
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)){
|
|
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
|
|
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
|
|
#endif
|
|
#if defined(GC_DARWIN_THREADS)||defined(GC_OPENBSD_UTHREADS)||defined(GC_WIN32_THREADS)||(defined(NACL)&&defined(THREADS))
|
|
GC_API void GC_CALL GC_set_suspend_signal(int sig GC_ATTR_UNUSED)
|
|
{
|
|
}
|
|
GC_API void GC_CALL GC_set_thr_restart_signal(int sig GC_ATTR_UNUSED)
|
|
{
|
|
}
|
|
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
|
|
#if!defined(_MAX_PATH)&&(defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32))
|
|
#define _MAX_PATH MAX_PATH
|
|
#endif
|
|
#ifdef GC_READ_ENV_FILE
|
|
STATIC char*GC_envfile_content=NULL;
|
|
STATIC unsigned GC_envfile_length=0;
|
|
#ifndef GC_ENVFILE_MAXLEN
|
|
#define GC_ENVFILE_MAXLEN 0x4000
|
|
#endif
|
|
#define GC_ENV_FILE_EXT ".gc.env"
|
|
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];
|
|
len=(unsigned)GetModuleFileName(NULL,path,
|
|
_MAX_PATH+1);
|
|
if (len > 4&&path[len - 4]==(TCHAR)'.'){
|
|
len-=4;
|
|
}
|
|
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,OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,NULL);
|
|
if (hFile==INVALID_HANDLE_VALUE)
|
|
return;
|
|
len=(unsigned)GetFileSize(hFile,NULL);
|
|
if (len<=1||len>=GC_ENVFILE_MAXLEN){
|
|
CloseHandle(hFile);
|
|
return;
|
|
}
|
|
GC_ASSERT(GC_page_size!=0);
|
|
content=(char*)GET_MEM(ROUNDUP_PAGESIZE_IF_MMAP((size_t)len+1));
|
|
if (content==NULL){
|
|
CloseHandle(hFile);
|
|
return;
|
|
}
|
|
ofs=0;
|
|
nBytesRead=(DWORD)-1L;
|
|
while (ReadFile(hFile,content+ofs,len - ofs+1,&nBytesRead,
|
|
NULL)&&nBytesRead!=0){
|
|
if ((ofs+=nBytesRead)> len)
|
|
break;
|
|
}
|
|
CloseHandle(hFile);
|
|
if (ofs!=len||nBytesRead!=0)
|
|
return;
|
|
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
|
|
}
|
|
GC_INNER char*GC_envfile_getenv(const char*name)
|
|
{
|
|
char*p;
|
|
char*end_of_content;
|
|
unsigned namelen;
|
|
#ifndef NO_GETENV
|
|
p=getenv(name);
|
|
if (p!=NULL)
|
|
return*p!='\0'?p:NULL;
|
|
#endif
|
|
p=GC_envfile_content;
|
|
if (p==NULL)
|
|
return NULL;
|
|
namelen=strlen(name);
|
|
if (namelen==0)
|
|
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++;
|
|
return*p!='\0'?p:NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
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)
|
|
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
|
|
GC_in_thread_creation=TRUE;
|
|
GC_gcollect();
|
|
GC_in_thread_creation=FALSE;
|
|
#else
|
|
GC_gcollect();
|
|
#endif
|
|
}
|
|
}
|
|
#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 (;;){
|
|
}
|
|
}
|
|
static GC_bool installed_looping_handler=FALSE;
|
|
static void maybe_install_looping_handler(void)
|
|
{
|
|
if (!installed_looping_handler&&0!=GETENV("GC_LOOP_ON_ABORT")){
|
|
GC_set_and_save_fault_handler(looping_handler);
|
|
installed_looping_handler=TRUE;
|
|
}
|
|
}
|
|
#else
|
|
#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
|
|
(void)MessageBoxA(NULL,msg,caption,flags);
|
|
#else
|
|
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,msg,caption,flags);
|
|
(void)FreeLibrary(hU32);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
#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
|
|
STATIC word GC_parse_mem_size_arg(const char*str)
|
|
{
|
|
word result=0;
|
|
if (*str!='\0'){
|
|
char*endptr;
|
|
char ch;
|
|
result=(word)STRTOULL(str,&endptr,10);
|
|
ch=*endptr;
|
|
if (ch!='\0'){
|
|
if (*(endptr+1)!='\0')
|
|
return 0;
|
|
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)
|
|
{
|
|
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);
|
|
#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
|
|
#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
|
|
InitializeCriticalSection(&GC_allocate_ml);
|
|
}
|
|
#endif
|
|
#endif
|
|
#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
|
|
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
|
|
if (str!=NULL&&*str=='0'&&*(str+1)=='\0')
|
|
#else
|
|
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")){
|
|
GC_dont_gc=1;
|
|
}
|
|
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'){
|
|
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'){
|
|
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'){
|
|
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
|
|
if (EXTRA_BYTES!=0)
|
|
GC_obj_kinds[NORMAL].ok_descriptor=(word)(-ALIGNMENT)|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);
|
|
#endif
|
|
#if defined(SEARCH_FOR_DATA_START)
|
|
GC_init_linux_data_start();
|
|
#endif
|
|
#if defined(NETBSD)&&defined(__ELF__)
|
|
GC_init_netbsd_elf();
|
|
#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);
|
|
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);
|
|
#endif
|
|
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
|
|
#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)
|
|
#else
|
|
if (manual_vdb_allowed){
|
|
GC_manual_vdb=TRUE;
|
|
GC_incremental=TRUE;
|
|
} else
|
|
#endif
|
|
{
|
|
GC_incremental=GC_dirty_init();
|
|
GC_ASSERT(GC_bytes_allocd==0);
|
|
}
|
|
}
|
|
#endif
|
|
if (GC_REGISTER_MAIN_STATIC_DATA())GC_register_data_segments();
|
|
GC_init_headers();
|
|
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();
|
|
#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){
|
|
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)
|
|
GC_thr_init();
|
|
#ifdef PARALLEL_MARK
|
|
#if defined(GC_ASSERTIONS)&&defined(GC_ALWAYS_MULTITHREADED)
|
|
UNLOCK();
|
|
#endif
|
|
GC_start_mark_threads_inner();
|
|
#if defined(GC_ASSERTIONS)&&defined(GC_ALWAYS_MULTITHREADED)
|
|
LOCK();
|
|
#endif
|
|
#endif
|
|
#endif
|
|
COND_DUMP;
|
|
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)
|
|
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){
|
|
atexit(GC_exit_check);
|
|
}
|
|
#endif
|
|
#if defined(PARALLEL_MARK)||defined(THREAD_LOCAL_ALLOC)||(defined(GC_ALWAYS_MULTITHREADED)&&defined(GC_WIN32_THREADS)&&!defined(GC_NO_THREADS_DISCOVERY))
|
|
GC_init_parallel();
|
|
#endif
|
|
#if defined(DYNAMIC_LOADING)&&defined(DARWIN)
|
|
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 (!GC_find_leak&&0==GETENV("GC_DISABLE_INCREMENTAL")){
|
|
LOCK();
|
|
if (!GC_incremental){
|
|
GC_setpagesize();
|
|
maybe_install_looping_handler();
|
|
if (!GC_is_initialized){
|
|
UNLOCK();
|
|
GC_incremental=TRUE;
|
|
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
|
|
{
|
|
GC_incremental=GC_dirty_init();
|
|
}
|
|
}
|
|
if (GC_incremental&&!GC_dont_gc){
|
|
IF_CANCEL(int cancel_state;)
|
|
DISABLE_CANCEL(cancel_state);
|
|
if (GC_bytes_allocd > 0){
|
|
GC_gcollect_inner();
|
|
}
|
|
GC_read_dirty(FALSE);
|
|
RESTORE_CANCEL(cancel_state);
|
|
}
|
|
}
|
|
UNLOCK();
|
|
return;
|
|
}
|
|
#endif
|
|
GC_init();
|
|
}
|
|
#if defined(THREADS)
|
|
GC_API void GC_CALL GC_start_mark_threads(void)
|
|
{
|
|
#if defined(PARALLEL_MARK)&&defined(CAN_HANDLE_FORK)&&!defined(THREAD_SANITIZER)
|
|
IF_CANCEL(int cancel_state;)
|
|
DISABLE_CANCEL(cancel_state);
|
|
GC_start_mark_threads_inner();
|
|
RESTORE_CANCEL(cancel_state);
|
|
#else
|
|
GC_ASSERT(I_DONT_HOLD_LOCK());
|
|
#endif
|
|
}
|
|
#endif
|
|
GC_API void GC_CALL GC_deinit(void)
|
|
{
|
|
if (GC_is_initialized){
|
|
GC_is_initialized=FALSE;
|
|
#if defined(GC_WIN32_THREADS)&&(defined(MSWIN32)||defined(MSWINCE))
|
|
#if!defined(CONSOLE_LOG)||defined(MSWINCE)
|
|
DeleteCriticalSection(&GC_write_cs);
|
|
#endif
|
|
DeleteCriticalSection(&GC_allocate_ml);
|
|
#endif
|
|
}
|
|
}
|
|
#if (defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE)
|
|
#if defined(_MSC_VER)&&defined(_DEBUG)&&!defined(MSWINCE)
|
|
#include <crtdbg.h>
|
|
#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
|
|
#ifdef MSWINRT_FLAVOR
|
|
#include <windows.storage.h>
|
|
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
|
|
STATIC HANDLE GC_CreateLogFile(void)
|
|
{
|
|
HANDLE hFile;
|
|
#ifdef MSWINRT_FLAVOR
|
|
TCHAR pathBuf[_MAX_PATH+0x10];
|
|
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];
|
|
logPath=pathBuf;
|
|
#endif
|
|
#ifndef NO_GETENV_WIN32
|
|
if (GetEnvironmentVariable(TEXT("GC_LOG_FILE"),pathBuf,
|
|
_MAX_PATH+1)- 1U < (DWORD)_MAX_PATH){
|
|
appendToFile=TRUE;
|
|
} else
|
|
#endif
|
|
{
|
|
#ifdef OLD_WIN32_LOG_FILE
|
|
logPath=TEXT(GC_LOG_STD_NAME);
|
|
#else
|
|
int len=(int)GetModuleFileName(NULL,pathBuf,
|
|
_MAX_PATH+1);
|
|
if (len > 4&&pathBuf[len - 4]==(TCHAR)'.'){
|
|
len-=4;
|
|
}
|
|
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,
|
|
appendToFile?OPEN_ALWAYS:CREATE_ALWAYS,
|
|
GC_print_stats==VERBOSE?FILE_ATTRIBUTE_NORMAL:
|
|
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH,
|
|
NULL);
|
|
#ifndef NO_GETENV_WIN32
|
|
if (appendToFile&&hFile!=INVALID_HANDLE_VALUE){
|
|
LONG posHigh=0;
|
|
(void)SetFilePointer(hFile,0,&posHigh,FILE_END);
|
|
}
|
|
#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;
|
|
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
|
|
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
|
|
{
|
|
WCHAR wbuf[1024];
|
|
wbuf[MultiByteToWideChar(CP_ACP,0,
|
|
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;
|
|
}
|
|
#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;
|
|
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 <android/log.h>
|
|
#ifndef GC_ANDROID_LOG_TAG
|
|
#define GC_ANDROID_LOG_TAG "BDWGC"
|
|
#endif
|
|
#define GC_stdout ANDROID_LOG_DEBUG
|
|
#define GC_stderr ANDROID_LOG_ERROR
|
|
#define GC_log GC_stdout
|
|
#define WRITE(level,buf,unused_len)__android_log_write(level,GC_ANDROID_LOG_TAG,buf)
|
|
#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(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)
|
|
#if!defined(AMIGA)&&!defined(MSWIN32)&&!defined(MSWIN_XBOX1)&&!defined(__CC_ARM)
|
|
#include <unistd.h>
|
|
#endif
|
|
#if!defined(ECOS)&&!defined(NOSYS)
|
|
#include <errno.h>
|
|
#endif
|
|
#endif
|
|
STATIC int GC_write(int fd,const char*buf,size_t len)
|
|
{
|
|
#if defined(ECOS)||defined(SN_TARGET_ORBIS)||defined(SN_TARGET_PSP2)||defined(NOSYS)
|
|
#ifdef ECOS
|
|
#else
|
|
#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)
|
|
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
|
|
#define BUFSZ 1024
|
|
#if defined(DJGPP)||defined(__STRICT_ANSI__)
|
|
#define GC_VSNPRINTF(buf,bufsz,format,args)vsprintf(buf,format,args)
|
|
#elif defined(_MSC_VER)
|
|
#ifdef MSWINCE
|
|
#define GC_VSNPRINTF StringCchVPrintfA
|
|
#else
|
|
#define GC_VSNPRINTF _vsnprintf
|
|
#endif
|
|
#else
|
|
#define GC_VSNPRINTF vsnprintf
|
|
#endif
|
|
#define GC_PRINTF_FILLBUF(buf,format)do { va_list args;va_start(args,format);(buf)[sizeof(buf)- 1]=0x15;(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));
|
|
#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);
|
|
}
|
|
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);
|
|
}
|
|
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
|
|
void GC_err_puts(const char*s)
|
|
{
|
|
(void)WRITE(GC_stderr,s,strlen(s));
|
|
}
|
|
STATIC void GC_CALLBACK GC_default_warn_proc(char*msg,GC_word arg)
|
|
{
|
|
GC_warn_printf(msg,arg);
|
|
}
|
|
GC_INNER GC_warn_proc GC_current_warn_proc=GC_default_warn_proc;
|
|
GC_API void GC_CALLBACK GC_ignore_warn_proc(char*msg,GC_word arg)
|
|
{
|
|
if (GC_print_stats){
|
|
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
|
|
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)
|
|
STATIC void GC_CALLBACK GC_default_on_abort(const char*msg)
|
|
{
|
|
#ifndef DONT_USE_ATEXIT
|
|
skip_gc_atexit=TRUE;
|
|
#endif
|
|
if (msg!=NULL){
|
|
#ifdef MSGBOX_ON_ERROR
|
|
GC_win32_MessageBoxA(msg,"Fatal error in GC",MB_ICONERROR|MB_OK);
|
|
#endif
|
|
#ifndef GC_ANDROID_LOG
|
|
#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("*",GC_ANDROID_LOG_TAG,"%s\n",msg);
|
|
#endif
|
|
}
|
|
#if!defined(NO_DEBUGGING)&&!defined(GC_ANDROID_LOG)
|
|
if (GETENV("GC_LOOP_ON_ABORT")!=NULL){
|
|
for(;;){
|
|
}
|
|
}
|
|
#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
|
|
GC_API void GC_CALL GC_enable(void)
|
|
{
|
|
DCL_LOCK_STATE;
|
|
LOCK();
|
|
GC_ASSERT(GC_dont_gc!=0);
|
|
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;
|
|
}
|
|
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);
|
|
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;
|
|
#ifdef THREADS
|
|
LOCK();
|
|
#endif
|
|
result=(*fn)(client_data);
|
|
#ifdef THREADS
|
|
UNLOCK();
|
|
#endif
|
|
return(result);
|
|
}
|
|
GC_API void*GC_CALL GC_call_with_stack_base(GC_stack_base_func fn,void*arg)
|
|
{
|
|
struct GC_stack_base base;
|
|
void*result;
|
|
base.mem_base=(void*)&base;
|
|
#ifdef IA64
|
|
base.reg_base=(void*)GC_save_regs_in_stack();
|
|
#endif
|
|
result=fn(&base,arg);
|
|
GC_noop1(COVERT_DATAFLOW(&base));
|
|
return result;
|
|
}
|
|
#ifndef THREADS
|
|
GC_INNER ptr_t GC_blocked_sp=NULL;
|
|
#ifdef IA64
|
|
STATIC ptr_t GC_blocked_register_sp=NULL;
|
|
#endif
|
|
GC_INNER struct GC_traced_stack_sect_s*GC_traced_stack_sect=NULL;
|
|
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);
|
|
if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect))
|
|
GC_stackbottom=(ptr_t)COVERT_DATAFLOW(&stacksect);
|
|
if (GC_blocked_sp==NULL){
|
|
client_data=fn(client_data);
|
|
GC_noop1(COVERT_DATAFLOW(&stacksect));
|
|
return client_data;
|
|
}
|
|
stacksect.saved_stack_ptr=GC_blocked_sp;
|
|
#ifdef IA64
|
|
stacksect.backing_store_end=GC_save_regs_in_stack();
|
|
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
|
|
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;
|
|
}
|
|
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;
|
|
#endif
|
|
#ifdef IA64
|
|
GC_blocked_register_sp=GC_save_regs_in_stack();
|
|
#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);
|
|
(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;
|
|
#endif
|
|
return&GC_stackbottom;
|
|
}
|
|
#endif
|
|
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;
|
|
}
|
|
#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
|
|
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
|
|
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;
|
|
}
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
#endif
|
|
GC_API void GC_CALL GC_set_oom_fn(GC_oom_func fn)
|
|
{
|
|
GC_ASSERT(NONNULL_ARG_NOT_NULL(fn));
|
|
DCL_LOCK_STATE;
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
GC_API void GC_CALL GC_set_find_leak(int value)
|
|
{
|
|
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){
|
|
LOCK();
|
|
GC_initialize_offsets();
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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
|
|
#if!defined(OS2)&&!defined(PCR)&&!defined(AMIGA)&&!defined(MACOS)&&!defined(MSWINCE)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)&&!defined(__CC_ARM)
|
|
#include <sys/types.h>
|
|
#if!defined(MSWIN32)&&!defined(MSWIN_XBOX1)
|
|
#include <unistd.h>
|
|
#endif
|
|
#endif
|
|
#include <stdio.h>
|
|
#if defined(MSWINCE)||defined(SN_TARGET_PS3)
|
|
#define SIGSEGV 0
|
|
#else
|
|
#include <signal.h>
|
|
#endif
|
|
#if defined(UNIX_LIKE)||defined(CYGWIN32)||defined(NACL)||defined(SYMBIAN)
|
|
#include <fcntl.h>
|
|
#endif
|
|
#if defined(LINUX)||defined(LINUX_STACKBOTTOM)
|
|
#include <ctype.h>
|
|
#endif
|
|
#ifdef AMIGA
|
|
#define GC_AMIGA_DEF
|
|
#if!defined(GC_AMIGA_DEF)&&!defined(GC_AMIGA_SB)&&!defined(GC_AMIGA_DS)&&!defined(GC_AMIGA_AM)
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#define GC_AMIGA_DEF
|
|
#define GC_AMIGA_SB
|
|
#define GC_AMIGA_DS
|
|
#define GC_AMIGA_AM
|
|
#endif
|
|
#ifdef GC_AMIGA_DEF
|
|
#ifndef __GNUC__
|
|
#include <exec/exec.h>
|
|
#endif
|
|
#include <proto/exec.h>
|
|
#include <proto/dos.h>
|
|
#include <dos/dosextens.h>
|
|
#include <workbench/startup.h>
|
|
#endif
|
|
#ifdef GC_AMIGA_SB
|
|
ptr_t GC_get_main_stack_base(void)
|
|
{
|
|
struct Process*proc=(struct Process*)SysBase->ThisTask;
|
|
if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS
|
|
&&proc->pr_CLI!=NULL){
|
|
return (char*)proc->pr_ReturnAddr+sizeof(ULONG);
|
|
} else {
|
|
return (char*)proc->pr_Task.tc_SPUpper;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef GC_AMIGA_DS
|
|
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;
|
|
#endif
|
|
proc=(struct Process*)SysBase->ThisTask;
|
|
myseglist=proc->pr_SegList;
|
|
if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS){
|
|
if (proc->pr_CLI!=NULL){
|
|
cli=BADDR(proc->pr_CLI);
|
|
myseglist=cli->cli_Module;
|
|
}
|
|
} else {
|
|
ABORT("Not a Process.");
|
|
}
|
|
if (myseglist==NULL){
|
|
ABORT("Arrrgh.. can't find segments,aborting");
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
#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;
|
|
struct GC_Amiga_AllocedMemoryHeader{
|
|
ULONG size;
|
|
struct GC_Amiga_AllocedMemoryHeader*next;
|
|
};
|
|
struct GC_Amiga_AllocedMemoryHeader*GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader*)(int)~(NULL);
|
|
ULONG GC_AMIGA_MEMF=MEMF_FAST|MEMF_CLEAR;
|
|
#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
|
|
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
|
|
char*chipmax;
|
|
size_t latestsize;
|
|
#endif
|
|
#ifdef GC_AMIGA_FASTALLOC
|
|
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;
|
|
}
|
|
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_am<chipmax){
|
|
allochip+=size;
|
|
}else{
|
|
allocfast+=size;
|
|
}
|
|
#endif
|
|
return gc_am+1;
|
|
}
|
|
#endif
|
|
#ifndef GC_AMIGA_ONLYFAST
|
|
#ifdef GC_AMIGA_RETRY
|
|
void*GC_amiga_rec_alloc(size_t size,void*(*AllocFunction)(size_t size2),const int rec){
|
|
void*ret;
|
|
ret=(*AllocFunction)(size);
|
|
#ifdef GC_AMIGA_PRINTSTATS
|
|
if((char*)ret>chipmax||ret==NULL){
|
|
if(ret==NULL){
|
|
nsucc++;
|
|
nsucc2+=size;
|
|
if(rec==0)ncur0++;
|
|
if(rec==1)ncur1++;
|
|
if(rec>1&&rec<10)ncur10++;
|
|
if(rec>=10&&rec<50)ncur50++;
|
|
if(rec>=50&&rec<150)ncur150++;
|
|
if(rec>=150)ncur151++;
|
|
}else{
|
|
succ++;
|
|
succ2+=size;
|
|
if(rec==0)cur0++;
|
|
if(rec==1)cur1++;
|
|
if(rec>1&&rec<10)cur10++;
|
|
if(rec>=10&&rec<50)cur50++;
|
|
if(rec>=50&&rec<150)cur150++;
|
|
if(rec>=150)cur151++;
|
|
}
|
|
}
|
|
#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
|
|
void*GC_amiga_allocwrapper_any(size_t size,void*(*AllocFunction)(size_t size2)){
|
|
void*ret;
|
|
GC_amiga_dontalloc=TRUE;
|
|
latestsize=size;
|
|
ret=(*AllocFunction)(size);
|
|
if(((char*)ret)<=chipmax){
|
|
if(ret==NULL){
|
|
#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;
|
|
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*)ret2<chipmax||ret2==NULL){
|
|
nsucc++;
|
|
nsucc2+=size;
|
|
ncur0++;
|
|
}else{
|
|
succ++;
|
|
succ2+=size;
|
|
cur0++;
|
|
}
|
|
#endif
|
|
}
|
|
if(((char*)ret2)>chipmax){
|
|
GC_free(ret);
|
|
ret=ret2;
|
|
}else{
|
|
GC_free(ret2);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#if defined(CPPCHECK)
|
|
if (GC_amiga_dontalloc)
|
|
#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
|
|
void*GC_amiga_allocwrapper_fast(size_t size,void*(*AllocFunction)(size_t size2)){
|
|
void*ret;
|
|
ret=(*AllocFunction)(size);
|
|
if(ret==NULL){
|
|
#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;
|
|
GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_fast;
|
|
return GC_amiga_allocwrapper_fast(size,AllocFunction);
|
|
}
|
|
#endif
|
|
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)){
|
|
#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)<chipmax&&ret!=NULL){
|
|
chipa+=new_size_in_bytes;
|
|
}
|
|
#endif
|
|
return ret;
|
|
#endif
|
|
}
|
|
#endif
|
|
#undef GC_AMIGA_DEF
|
|
#endif
|
|
#ifdef MACOS
|
|
#include <Processes.h>
|
|
#endif
|
|
#ifdef IRIX5
|
|
#include <sys/uio.h>
|
|
#include <malloc.h>
|
|
#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 <sys/types.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#endif
|
|
#ifdef DARWIN
|
|
#include <mach-o/getsect.h>
|
|
#endif
|
|
#ifdef DJGPP
|
|
typedef long unsigned int caddr_t;
|
|
#endif
|
|
#ifdef PCR
|
|
#include "mm/PCR_MM.h"
|
|
#endif
|
|
#if defined(GC_DARWIN_THREADS)&&defined(MPROTECT_VDB)
|
|
#ifndef GC_DARWIN_STOP_WORLD_H
|
|
#define GC_DARWIN_STOP_WORLD_H
|
|
#if!defined(GC_DARWIN_THREADS)
|
|
#error darwin_stop_world.h included without GC_DARWIN_THREADS defined
|
|
#endif
|
|
#include <mach/mach.h>
|
|
#include <mach/thread_act.h>
|
|
EXTERN_C_BEGIN
|
|
struct thread_stop_info {
|
|
mach_port_t mach_thread;
|
|
ptr_t stack_ptr;
|
|
};
|
|
#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
|
|
#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
|
|
#ifdef NEED_PROC_MAPS
|
|
STATIC ssize_t GC_repeat_read(int fd,char*buf,size_t count)
|
|
{
|
|
#define READ read
|
|
size_t num_read=0;
|
|
ASSERT_CANCEL_DISABLED();
|
|
while (num_read < count){
|
|
ssize_t result=READ(fd,buf+num_read,count - num_read);
|
|
if (result < 0)return result;
|
|
if (result==0)break;
|
|
num_read+=result;
|
|
}
|
|
#undef READ
|
|
return num_read;
|
|
}
|
|
#ifdef THREADS
|
|
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=read(f,buf,GET_FILE_LEN_BUF_SZ);
|
|
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;
|
|
result=GC_get_file_len(f);
|
|
close(f);
|
|
return result;
|
|
}
|
|
#endif
|
|
GC_INNER 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
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
#ifdef THREADS
|
|
maps_size=GC_get_maps_len();
|
|
if (0==maps_size)return 0;
|
|
#else
|
|
maps_size=4000;
|
|
#endif
|
|
do {
|
|
int f;
|
|
while (maps_size>=maps_buf_sz){
|
|
#ifdef LINT2
|
|
GC_noop1((word)maps_buf);
|
|
#else
|
|
GC_scratch_recycle_no_gww(maps_buf,maps_buf_sz);
|
|
#endif
|
|
while (maps_size>=maps_buf_sz)maps_buf_sz*=2;
|
|
maps_buf=GC_scratch_alloc(maps_buf_sz);
|
|
#ifdef THREADS
|
|
maps_size=GC_get_maps_len();
|
|
if (0==maps_size)return 0;
|
|
#endif
|
|
if (maps_buf==0)return 0;
|
|
}
|
|
GC_ASSERT(maps_buf_sz>=maps_size+1);
|
|
f=open("/proc/self/maps",O_RDONLY);
|
|
if (-1==f)return 0;
|
|
#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){
|
|
close(f);
|
|
return 0;
|
|
}
|
|
maps_size+=result;
|
|
} while ((size_t)result==maps_buf_sz-1);
|
|
close(f);
|
|
#ifdef THREADS
|
|
if (maps_size > old_maps_size){
|
|
WARN("Unexpected asynchronous/proc/self/maps growth"
|
|
" (to %" WARN_PRIdPTR " 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;
|
|
}
|
|
#if (defined(DYNAMIC_LOADING)&&defined(USE_PROC_FOR_LIBRARIES))||defined(IA64)||defined(INCLUDE_LINUX_THREAD_DESCR)||defined(REDIRECT_MALLOC)
|
|
GC_INNER char*GC_parse_map_entry(char*buf_ptr,ptr_t*start,ptr_t*end,
|
|
char**prot,unsigned int*maj_dev,
|
|
char**mapping_name)
|
|
{
|
|
unsigned char*start_start,*end_start,*maj_dev_start;
|
|
unsigned char*p;
|
|
if (buf_ptr==NULL||*buf_ptr=='\0'){
|
|
return NULL;
|
|
}
|
|
p=(unsigned char*)buf_ptr;
|
|
while (isspace(*p))++p;
|
|
start_start=p;
|
|
GC_ASSERT(isxdigit(*start_start));
|
|
*start=(ptr_t)strtoul((char*)start_start,(char**)&p,16);
|
|
GC_ASSERT(*p=='-');
|
|
++p;
|
|
end_start=p;
|
|
GC_ASSERT(isxdigit(*end_start));
|
|
*end=(ptr_t)strtoul((char*)end_start,(char**)&p,16);
|
|
GC_ASSERT(isspace(*p));
|
|
while (isspace(*p))++p;
|
|
GC_ASSERT(*p=='r'||*p=='-');
|
|
*prot=(char*)p;
|
|
while (!isspace(*p))++p;
|
|
while (isspace(*p))p++;
|
|
GC_ASSERT(isxdigit(*p));
|
|
while (!isspace(*p))++p;
|
|
while (isspace(*p))p++;
|
|
maj_dev_start=p;
|
|
GC_ASSERT(isxdigit(*maj_dev_start));
|
|
*maj_dev=strtoul((char*)maj_dev_start,NULL,16);
|
|
if (mapping_name==0){
|
|
while (*p&&*p++!='\n');
|
|
} else {
|
|
while (*p&&*p!='\n'&&*p!='/'&&*p!='[')p++;
|
|
*mapping_name=(char*)p;
|
|
while (*p&&*p++!='\n');
|
|
}
|
|
return (char*)p;
|
|
}
|
|
#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)
|
|
{
|
|
char*prot;
|
|
ptr_t my_start,my_end;
|
|
unsigned int maj_dev;
|
|
char*maps=GC_get_maps();
|
|
char*buf_ptr=maps;
|
|
if (0==maps)return(FALSE);
|
|
for (;;){
|
|
buf_ptr=GC_parse_map_entry(buf_ptr,&my_start,&my_end,
|
|
&prot,&maj_dev,0);
|
|
if (buf_ptr==NULL)return FALSE;
|
|
if (prot[1]=='w'&&maj_dev==0){
|
|
if ((word)my_end > (word)addr&&(word)my_start<=(word)addr){
|
|
*startp=my_start;
|
|
*endp=my_end;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
#if defined(REDIRECT_MALLOC)
|
|
GC_INNER GC_bool GC_text_mapping(char*nm,ptr_t*startp,ptr_t*endp)
|
|
{
|
|
size_t nm_len=strlen(nm);
|
|
char*prot;
|
|
char*map_path;
|
|
ptr_t my_start,my_end;
|
|
unsigned int maj_dev;
|
|
char*maps=GC_get_maps();
|
|
char*buf_ptr=maps;
|
|
if (0==maps)return(FALSE);
|
|
for (;;){
|
|
buf_ptr=GC_parse_map_entry(buf_ptr,&my_start,&my_end,
|
|
&prot,&maj_dev,&map_path);
|
|
if (buf_ptr==NULL)return FALSE;
|
|
if (prot[0]=='r'&&prot[1]=='-'&&prot[2]=='x'){
|
|
char*p=map_path;
|
|
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
|
|
#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
|
|
#if defined(SEARCH_FOR_DATA_START)
|
|
#if defined(LINUX)||defined(HURD)
|
|
EXTERN_C_BEGIN
|
|
#pragma weak __data_start
|
|
#pragma weak data_start
|
|
extern int __data_start[],data_start[];
|
|
EXTERN_C_END
|
|
#endif
|
|
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)
|
|
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
|
|
if (GC_no_dls){
|
|
GC_data_start=data_end;
|
|
return;
|
|
}
|
|
GC_data_start=(ptr_t)GC_find_limit(data_end,FALSE);
|
|
}
|
|
#endif
|
|
#ifdef ECOS
|
|
#ifndef ECOS_GC_MEMORY_SIZE
|
|
#define ECOS_GC_MEMORY_SIZE (448*1024)
|
|
#endif
|
|
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
|
|
#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)
|
|
{
|
|
GC_data_start=(ptr_t)GC_find_limit(&environ,FALSE);
|
|
}
|
|
#endif
|
|
#if defined(ADDRESS_SANITIZER)&&(defined(UNIX_LIKE)||defined(NEED_FIND_LIMIT)||defined(MPROTECT_VDB))&&!defined(CUSTOM_ASAN_DEF_OPTIONS)
|
|
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 <sys/syscall.h>
|
|
EXTERN_C_BEGIN
|
|
extern sigset_t __syscall(quad_t,...);
|
|
EXTERN_C_END
|
|
STATIC ptr_t GC_find_limit_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;
|
|
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;
|
|
GC_noop1((word)(*result));
|
|
}
|
|
}
|
|
#ifdef THREADS
|
|
__syscall(SYS_sigprocmask,SIG_UNBLOCK,sigmask(SIGPROF));
|
|
#endif
|
|
sigaction(SIGSEGV,&old_segv_act,0);
|
|
return(result);
|
|
}
|
|
#endif
|
|
static volatile int firstpass;
|
|
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;
|
|
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;
|
|
GC_noop1((word)(*result));
|
|
}
|
|
}
|
|
sigaction(SIGSEGV,&old_segv_act,0);
|
|
return(result);
|
|
}
|
|
#endif
|
|
#ifdef OS2
|
|
#include <stddef.h>
|
|
#if!defined(__IBMC__)&&!defined(__WATCOMC__)
|
|
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
|
|
#ifndef WORD
|
|
#define WORD unsigned short
|
|
#endif
|
|
#ifndef DWORD
|
|
#define DWORD unsigned long
|
|
#endif
|
|
#define EXE386 1
|
|
#include <newexe.h>
|
|
#include <exe386.h>
|
|
#endif
|
|
#define INCL_DOSEXCEPTIONS
|
|
#define INCL_DOSPROCESS
|
|
#define INCL_DOSERRORS
|
|
#define INCL_DOSMODULEMGR
|
|
#define INCL_DOSMEMMGR
|
|
#include <os2.h>
|
|
#endif
|
|
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))
|
|
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;
|
|
verInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
|
|
if (!GetVersionEx(&verInfo))
|
|
ABORT("GetVersionEx failed");
|
|
if (verInfo.dwPlatformId==VER_PLATFORM_WIN32_CE&&
|
|
verInfo.dwMajorVersion < 6){
|
|
GC_sysinfo.lpMaximumApplicationAddress=(LPVOID)((word)32<<20);
|
|
#ifdef THREADS
|
|
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)
|
|
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;
|
|
if (!GC_page_size)GC_setpagesize();
|
|
trunc_sp=(ptr_t)((word)GC_approx_sp()&~(GC_page_size - 1));
|
|
size=GC_get_writable_length(trunc_sp,0);
|
|
GC_ASSERT(size!=0);
|
|
sb->mem_base=trunc_sp+size;
|
|
return GC_SUCCESS;
|
|
}
|
|
#else
|
|
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
|
|
#define HAVE_GET_STACK_BASE
|
|
#else
|
|
GC_INNER void GC_setpagesize(void)
|
|
{
|
|
#if defined(MPROTECT_VDB)||defined(PROC_VDB)||defined(USE_MMAP)
|
|
GC_page_size=(size_t)GETPAGESIZE();
|
|
#if!defined(CPPCHECK)
|
|
if (0==GC_page_size)
|
|
ABORT("getpagesize failed");
|
|
#endif
|
|
#else
|
|
GC_page_size=HBLKSIZE;
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef HAIKU
|
|
#include <kernel/OS.h>
|
|
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
|
|
#ifdef OS2
|
|
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*sb)
|
|
{
|
|
PTIB ptib;
|
|
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
|
|
#ifdef AMIGA
|
|
#define GC_AMIGA_SB
|
|
#undef GC_AMIGA_SB
|
|
#define GET_MAIN_STACKBASE_SPECIAL
|
|
#endif
|
|
#if defined(NEED_FIND_LIMIT)||defined(UNIX_LIKE)
|
|
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)||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
|
|
act.sa_flags=SA_RESTART|SA_NODEFER;
|
|
#else
|
|
act.sa_flags=SA_RESTART;
|
|
#endif
|
|
(void)sigemptyset(&act.sa_mask);
|
|
#ifdef GC_IRIX_THREADS
|
|
(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)||defined(HURD)||defined(NETBSD)||defined(FREEBSD)
|
|
(void)sigaction(SIGBUS,&act,&old_bus_act);
|
|
#endif
|
|
#endif
|
|
#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
|
|
#if defined(NEED_FIND_LIMIT)||(defined(USE_PROC_FOR_LIBRARIES)&&defined(THREADS))
|
|
#define MIN_PAGE_SIZE 256
|
|
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)
|
|
{
|
|
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)||defined(HURD)||defined(NETBSD)
|
|
(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
|
|
}
|
|
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;
|
|
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;
|
|
} else {
|
|
if ((word)result<=(word)bound+MIN_PAGE_SIZE){
|
|
result=bound - MIN_PAGE_SIZE;
|
|
break;
|
|
}
|
|
result-=MIN_PAGE_SIZE;
|
|
}
|
|
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
|
|
#ifdef HPUX_MAIN_STACKBOTTOM
|
|
#include <sys/param.h>
|
|
#include <sys/pstat.h>
|
|
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;
|
|
}
|
|
#ifdef STACK_GROWS_UP
|
|
return (ptr_t)GC_find_limit(GC_approx_sp(),FALSE);
|
|
#else
|
|
return (ptr_t)GC_find_limit(GC_approx_sp(),TRUE);
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef HPUX_STACKBOTTOM
|
|
#include <sys/param.h>
|
|
#include <sys/pstat.h>
|
|
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;
|
|
}
|
|
}
|
|
return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1)
|
|
&~(BACKING_STORE_ALIGNMENT - 1));
|
|
}
|
|
#endif
|
|
#ifdef LINUX_STACKBOTTOM
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#define STAT_SKIP 27
|
|
#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){
|
|
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);
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
STATIC ptr_t GC_linux_main_stack_base(void)
|
|
{
|
|
#ifndef STAT_READ
|
|
#define STAT_BUF_SIZE 4096
|
|
#define STAT_READ read
|
|
#endif
|
|
char stat_buf[STAT_BUF_SIZE];
|
|
int f;
|
|
word result;
|
|
int i,buf_offset=0,len;
|
|
#ifdef USE_LIBC_PRIVATES
|
|
if (0!=&__libc_stack_end&&0!=__libc_stack_end){
|
|
#if defined(IA64)
|
|
if (((word)__libc_stack_end&0xfff)+0x10 < 0x1000){
|
|
return __libc_stack_end+0x10;
|
|
}
|
|
#elif defined(SPARC)
|
|
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 (f < 0)
|
|
ABORT("Couldn't read/proc/self/stat");
|
|
len=STAT_READ(f,stat_buf,STAT_BUF_SIZE);
|
|
close(f);
|
|
for (i=0;i < STAT_SKIP;++i){
|
|
while (buf_offset < len&&isspace(stat_buf[buf_offset++])){
|
|
}
|
|
while (buf_offset < len&&!isspace(stat_buf[buf_offset++])){
|
|
}
|
|
}
|
|
while (buf_offset < len&&isspace(stat_buf[buf_offset])){
|
|
buf_offset++;
|
|
}
|
|
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("Absurd stack bottom value");
|
|
return (ptr_t)result;
|
|
}
|
|
#endif
|
|
#ifdef FREEBSD_STACKBOTTOM
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
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
|
|
#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__)
|
|
#include <emscripten.h>
|
|
static void*emscripten_stack_base;
|
|
static void scan_stack_cb(void*begin,void*end)
|
|
{
|
|
(void)begin;
|
|
emscripten_stack_base=end;
|
|
}
|
|
ptr_t GC_get_main_stack_base(void)
|
|
{
|
|
emscripten_scan_stack(scan_stack_cb);
|
|
return (ptr_t)emscripten_stack_base;
|
|
}
|
|
#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 <pthread.h>
|
|
#ifdef HAVE_PTHREAD_NP_H
|
|
#include <pthread_np.h>
|
|
#endif
|
|
#elif defined(DARWIN)&&!defined(NO_PTHREAD_GET_STACKADDR_NP)
|
|
#include <pthread.h>
|
|
#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
|
|
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
|
|
#if (defined(HAVE_PTHREAD_ATTR_GET_NP)||defined(HAVE_PTHREAD_GETATTR_NP))&&defined(THREADS)&&!defined(HAVE_GET_STACK_BASE)
|
|
#include <pthread.h>
|
|
#ifdef HAVE_PTHREAD_NP_H
|
|
#include <pthread_np.h>
|
|
#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
|
|
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
|
|
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 {
|
|
b->reg_base=GC_find_limit_with_bound(bsp,FALSE,next_stack);
|
|
}
|
|
RESTORE_CANCEL(cancel_state);
|
|
}
|
|
UNLOCK();
|
|
#endif
|
|
return GC_SUCCESS;
|
|
}
|
|
#define HAVE_GET_STACK_BASE
|
|
#endif
|
|
#if defined(GC_DARWIN_THREADS)&&!defined(NO_PTHREAD_GET_STACKADDR_NP)
|
|
#include <pthread.h>
|
|
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*b)
|
|
{
|
|
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
|
|
#ifdef GC_OPENBSD_THREADS
|
|
#include <sys/signal.h>
|
|
#include <pthread.h>
|
|
#include <pthread_np.h>
|
|
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
|
|
#if defined(GC_SOLARIS_THREADS)&&!defined(_STRICT_STDC)
|
|
#include <thread.h>
|
|
#include <signal.h>
|
|
#include <pthread.h>
|
|
static pthread_t stackbase_main_self=0;
|
|
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)
|
|
{
|
|
b->mem_base=stackbase_main_ss_sp;
|
|
GC_ASSERT(b->mem_base!=NULL);
|
|
return GC_SUCCESS;
|
|
}
|
|
if (thr_stksegment(&s)){
|
|
ABORT("thr_stksegment failed");
|
|
}
|
|
GC_ASSERT((word)GC_approx_sp()HOTTER_THAN (word)s.ss_sp);
|
|
if (!stackbase_main_self&&thr_main()!=0)
|
|
{
|
|
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
|
|
#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
|
|
#ifndef HAVE_GET_STACK_BASE
|
|
#ifdef NEED_FIND_LIMIT
|
|
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);
|
|
#ifdef STACK_GROWS_DOWN
|
|
b->mem_base=GC_find_limit(GC_approx_sp(),TRUE);
|
|
#ifdef IA64
|
|
b->reg_base=GC_find_limit(GC_save_regs_in_stack(),FALSE);
|
|
#endif
|
|
#else
|
|
b->mem_base=GC_find_limit(GC_approx_sp(),FALSE);
|
|
#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
|
|
#endif
|
|
#ifndef GET_MAIN_STACKBASE_SPECIAL
|
|
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
|
|
#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;
|
|
struct e32_exe hdr386;
|
|
struct o32_obj seg;
|
|
int nsegs;
|
|
#if defined(CPPCHECK)
|
|
hdrdos.padding[0]=0;
|
|
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
|
|
#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
|
|
#define GC_ULONG_PTR word
|
|
typedef UINT (WINAPI*GetWriteWatch_type)(
|
|
DWORD,PVOID,GC_ULONG_PTR,
|
|
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')){
|
|
done=TRUE;
|
|
return;
|
|
}
|
|
#else
|
|
if (str!=NULL&&*str=='0'&&*(str+1)=='\0'){
|
|
done=TRUE;
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef MSWINRT_FLAVOR
|
|
{
|
|
MEMORY_BASIC_INFORMATION memInfo;
|
|
SIZE_T result=VirtualQuery(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){
|
|
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;
|
|
if ((*(GetWriteWatch_type)(word)GetWriteWatch_func)(
|
|
WRITE_WATCH_FLAG_RESET,page,
|
|
GC_page_size,pages,&count,
|
|
&page_size)!=0){
|
|
GetWriteWatch_func=0;
|
|
} else {
|
|
GetWriteWatch_alloc_flag=MEM_WRITE_WATCH;
|
|
}
|
|
VirtualFree(page,0,MEM_RELEASE);
|
|
} else {
|
|
GetWriteWatch_func=0;
|
|
}
|
|
}
|
|
#ifndef SMALL_CONFIG
|
|
if (!GetWriteWatch_func){
|
|
GC_COND_LOG_PRINTF("Did not find a usable GetWriteWatch()\n");
|
|
} else {
|
|
GC_COND_LOG_PRINTF("Using GetWriteWatch()\n");
|
|
}
|
|
#endif
|
|
done=TRUE;
|
|
}
|
|
#else
|
|
#define GetWriteWatch_alloc_flag 0
|
|
#endif
|
|
#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)
|
|
#ifdef MSWIN32
|
|
GC_INNER GC_bool GC_no_win32_dlls=FALSE;
|
|
GC_INNER GC_bool GC_wnt=FALSE;
|
|
GC_INNER void GC_init_win32(void)
|
|
{
|
|
#if defined(_WIN64)||(defined(_MSC_VER)&&_MSC_VER>=1800)
|
|
GC_wnt=TRUE;
|
|
#else
|
|
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){
|
|
GC_unmap_threshold=0;
|
|
}
|
|
#endif
|
|
}
|
|
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||(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
|
|
#if defined(USE_WINALLOC)&&!defined(REDIRECT_MALLOC)
|
|
STATIC size_t GC_max_root_size=100000;
|
|
STATIC struct GC_malloc_heap_list {
|
|
void*allocation_base;
|
|
struct GC_malloc_heap_list*next;
|
|
}*GC_malloc_heap_l=0;
|
|
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;
|
|
candidate=GC_get_allocation_base(new_l);
|
|
if (GC_is_malloc_heap_base(candidate)){
|
|
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;
|
|
}
|
|
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
|
|
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)break;
|
|
p=(LPVOID)new_limit;
|
|
}
|
|
if (base!=limit)GC_add_roots_inner(base,limit,FALSE);
|
|
}
|
|
#endif
|
|
void GC_register_data_segments(void)
|
|
{
|
|
#ifdef MSWIN32
|
|
GC_register_root_section((ptr_t)&GC_pages_executable);
|
|
#endif
|
|
}
|
|
#else
|
|
#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);
|
|
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);
|
|
GC_setup_temporary_fault_handler();
|
|
if (SETJMP(GC_jmp_buf)==0){
|
|
#ifdef AO_HAVE_fetch_and_add
|
|
volatile AO_t zero=0;
|
|
(void)AO_fetch_and_add((volatile AO_t*)result,zero);
|
|
#else
|
|
char v=*result;
|
|
#if defined(CPPCHECK)
|
|
GC_noop1((word)&v);
|
|
#endif
|
|
*result=v;
|
|
#endif
|
|
GC_reset_fault_handler();
|
|
} else {
|
|
GC_reset_fault_handler();
|
|
result=(char*)GC_find_limit(DATAEND,FALSE);
|
|
}
|
|
return ( ptr_t)result;
|
|
}
|
|
#endif
|
|
#ifdef DATASTART_USES_BSDGETDATASTART
|
|
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);
|
|
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){
|
|
for (;next_page < (word)DATAEND;next_page+=(word)max_page_size)
|
|
*(volatile char*)next_page;
|
|
GC_reset_fault_handler();
|
|
} else {
|
|
GC_reset_fault_handler();
|
|
result=(ptr_t)GC_find_limit(DATAEND,FALSE);
|
|
}
|
|
return(result);
|
|
}
|
|
#endif
|
|
#ifdef AMIGA
|
|
#define GC_AMIGA_DS
|
|
#undef GC_AMIGA_DS
|
|
#elif defined(OPENBSD)
|
|
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
|
|
#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(PCR)&&!defined(MACOS)
|
|
#if defined(REDIRECT_MALLOC)&&defined(GC_SOLARIS_THREADS)
|
|
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){
|
|
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);
|
|
GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
|
|
(ptr_t)LMGetCurrentA5(),FALSE);
|
|
#else
|
|
#if defined(__MWERKS__)
|
|
#if!__POWERPC__
|
|
extern void*GC_MacGetDataStart(void);
|
|
#if __option(far_data)
|
|
extern void*GC_MacGetDataEnd(void);
|
|
#endif
|
|
GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
|
|
(ptr_t)LMGetCurrentA5(),FALSE);
|
|
#if __option(far_data)
|
|
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
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#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
|
|
#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);
|
|
#undef IGNORE_PAGES_EXECUTABLE
|
|
if (EXPECT(MAP_FAILED==result,FALSE)){
|
|
if (HEAP_START==last_addr&&GC_pages_executable&&EACCES==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){
|
|
munmap(result,~GC_page_size - (size_t)result+1);
|
|
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
|
|
#endif
|
|
#if defined(USE_MMAP)
|
|
ptr_t GC_unix_get_mem(size_t bytes)
|
|
{
|
|
return GC_unix_mmap_get_mem(bytes);
|
|
}
|
|
#else
|
|
STATIC ptr_t GC_unix_sbrk_get_mem(size_t bytes)
|
|
{
|
|
ptr_t result;
|
|
#ifdef IRIX5
|
|
__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;
|
|
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
|
|
{
|
|
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
|
|
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)
|
|
static GC_bool sbrk_failed=FALSE;
|
|
ptr_t result=0;
|
|
if (GC_pages_executable){
|
|
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){
|
|
result=GC_unix_sbrk_get_mem(bytes);
|
|
}
|
|
return result;
|
|
#else
|
|
return GC_unix_sbrk_get_mem(bytes);
|
|
#endif
|
|
}
|
|
#endif
|
|
#endif
|
|
#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);
|
|
}
|
|
if (result==0)return(os2_alloc(bytes));
|
|
return(result);
|
|
}
|
|
#endif
|
|
#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;
|
|
word i;
|
|
GC_ASSERT(GC_page_size!=0);
|
|
bytes=ROUNDUP_PAGESIZE(bytes);
|
|
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){
|
|
size_t res_bytes=
|
|
SIZET_SAT_ADD(bytes,(size_t)GC_sysinfo.dwAllocationGranularity-1)
|
|
&~((size_t)GC_sysinfo.dwAllocationGranularity-1);
|
|
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 (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++;
|
|
}
|
|
result=(ptr_t)VirtualAlloc(result,bytes,MEM_COMMIT,
|
|
GC_pages_executable?PAGE_EXECUTE_READWRITE:
|
|
PAGE_READWRITE);
|
|
#undef IGNORE_PAGES_EXECUTABLE
|
|
if (result!=NULL){
|
|
if (HBLKDISPL(result)!=0)ABORT("Bad VirtualAlloc result");
|
|
GC_heap_lengths[i]+=bytes;
|
|
}
|
|
return(result);
|
|
}
|
|
#elif (defined(USE_WINALLOC)&&!defined(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;
|
|
#else
|
|
#define GC_mem_top_down 0
|
|
#endif
|
|
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){
|
|
result=(ptr_t)GlobalAlloc(0,SIZET_SAT_ADD(bytes,HBLKSIZE));
|
|
result=(ptr_t)(((word)result+HBLKSIZE - 1)
|
|
&~(word)(HBLKSIZE - 1));
|
|
} else
|
|
#endif
|
|
{
|
|
#ifdef MPROTECT_VDB
|
|
#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
|
|
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
|
|
if (HBLKDISPL(result)!=0)ABORT("Bad VirtualAlloc result");
|
|
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
|
|
#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
|
|
#else
|
|
GlobalFree(GC_heap_bases[GC_n_heap_bases]);
|
|
#endif
|
|
GC_heap_bases[GC_n_heap_bases]=0;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
#ifndef CYGWIN32
|
|
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
|
|
}
|
|
#endif
|
|
#ifdef AMIGA
|
|
#define GC_AMIGA_AM
|
|
#undef GC_AMIGA_AM
|
|
#endif
|
|
#if defined(HAIKU)
|
|
#include <stdlib.h>
|
|
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
|
|
#ifdef USE_MUNMAP
|
|
#if!defined(NN_PLATFORM_CTR)&&!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(MSWIN_XBOX1)
|
|
#include <unistd.h>
|
|
#ifdef SN_TARGET_PS3
|
|
#include <sys/memory.h>
|
|
#else
|
|
#include <sys/mman.h>
|
|
#endif
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#endif
|
|
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;
|
|
}
|
|
GC_INNER void GC_unmap(ptr_t start,size_t bytes)
|
|
{
|
|
ptr_t start_addr=GC_unmap_start(start,bytes);
|
|
ptr_t end_addr=GC_unmap_end(start,bytes);
|
|
word len=end_addr - start_addr;
|
|
if (0==start_addr)return;
|
|
#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;
|
|
}
|
|
#elif defined(SN_TARGET_PS3)
|
|
ps3_free_mem(start_addr,len);
|
|
#else
|
|
{
|
|
#if defined(AIX)||defined(CYGWIN32)||defined(HAIKU)||defined(HPUX)
|
|
if (mprotect(start_addr,len,PROT_NONE))
|
|
ABORT("mprotect(PROT_NONE)failed");
|
|
#elif defined(__EMSCRIPTEN__)
|
|
#else
|
|
void*result=mmap(start_addr,len,PROT_NONE,
|
|
MAP_PRIVATE|MAP_FIXED|OPT_MAP_ANON,
|
|
zero_fd,0);
|
|
if (result!=(void*)start_addr)
|
|
ABORT("mmap(PROT_NONE)failed");
|
|
#if defined(CPPCHECK)||defined(LINT2)
|
|
GC_noop1((word)result);
|
|
#endif
|
|
#endif
|
|
}
|
|
GC_unmapped_bytes+=len;
|
|
#endif
|
|
}
|
|
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;
|
|
#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;
|
|
}
|
|
#else
|
|
{
|
|
#if defined(NACL)||defined(NETBSD)
|
|
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);
|
|
if (result!=(void*)start_addr)
|
|
ABORT("mmap as mprotect failed");
|
|
#if defined(CPPCHECK)||defined(LINT2)
|
|
GC_noop1((word)result);
|
|
#endif
|
|
#else
|
|
if (mprotect(start_addr,len,(PROT_READ|PROT_WRITE)
|
|
|(GC_pages_executable?PROT_EXEC:0))!=0){
|
|
ABORT_ARG3("mprotect remapping failed",
|
|
" at %p (length %lu),errcode=%d",
|
|
(void*)start_addr,(unsigned long)len,errno);
|
|
}
|
|
#endif
|
|
}
|
|
#undef IGNORE_PAGES_EXECUTABLE
|
|
GC_unmapped_bytes-=len;
|
|
#endif
|
|
}
|
|
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;
|
|
size_t len;
|
|
GC_ASSERT(start1+bytes1==start2);
|
|
if (0==start1_addr)start_addr=GC_unmap_start(start1,bytes1+bytes2);
|
|
if (0==start2_addr)end_addr=GC_unmap_end(start1,bytes1+bytes2);
|
|
if (0==start_addr)return;
|
|
len=end_addr - start_addr;
|
|
#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
|
|
if (len!=0){
|
|
#if defined(AIX)||defined(CYGWIN32)||defined(HAIKU)||defined(HPUX)
|
|
if (mprotect(start_addr,len,PROT_NONE))
|
|
ABORT("mprotect(PROT_NONE)failed");
|
|
#else
|
|
void*result=mmap(start_addr,len,PROT_NONE,
|
|
MAP_PRIVATE|MAP_FIXED|OPT_MAP_ANON,
|
|
zero_fd,0);
|
|
if (result!=(void*)start_addr)
|
|
ABORT("mmap(PROT_NONE)failed");
|
|
#if defined(CPPCHECK)||defined(LINT2)
|
|
GC_noop1((word)result);
|
|
#endif
|
|
#endif
|
|
GC_unmapped_bytes+=len;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifndef THREADS
|
|
#if defined(__EMSCRIPTEN__)
|
|
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)
|
|
{
|
|
emscripten_scan_registers(scan_regs_cb);
|
|
}
|
|
#else
|
|
#define GC_default_push_other_roots 0
|
|
#endif
|
|
#else
|
|
#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);
|
|
}
|
|
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;
|
|
STATIC void GC_CALLBACK GC_default_push_other_roots(void)
|
|
{
|
|
if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false,
|
|
GC_push_old_obj,0)
|
|
!=PCR_ERes_okay){
|
|
ABORT("Old object enumeration failed");
|
|
}
|
|
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
|
|
STATIC void GC_CALLBACK GC_default_push_other_roots(void)
|
|
{
|
|
GC_push_all_stacks();
|
|
}
|
|
#endif
|
|
#endif
|
|
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(CHECKSUMS)&&defined(GWW_VDB))||defined(PROC_VDB)
|
|
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
|
|
#ifdef GWW_VDB
|
|
#define GC_GWW_BUF_LEN (MAXHINCR*HBLKSIZE/4096)
|
|
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;
|
|
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;
|
|
} else 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);
|
|
}
|
|
#ifdef CHECKSUMS
|
|
GC_ASSERT(!output_unneeded);
|
|
GC_or_pages(GC_written_pages,GC_grungy_pages);
|
|
#endif
|
|
}
|
|
#else
|
|
#define GC_GWW_AVAILABLE()FALSE
|
|
#endif
|
|
#ifdef DEFAULT_VDB
|
|
GC_INNER GC_bool GC_dirty_init(void)
|
|
{
|
|
GC_VERBOSE_LOG_PRINTF("Initializing DEFAULT_VDB...\n");
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
#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)
|
|
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
|
|
#endif
|
|
#ifdef MPROTECT_VDB
|
|
#ifdef DARWIN
|
|
#include <mach/vm_map.h>
|
|
STATIC mach_port_t GC_task_self=0;
|
|
#define PROTECT(addr,len)if (vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len),FALSE,VM_PROT_READ|(GC_pages_executable?VM_PROT_EXECUTE:0))==KERN_SUCCESS){} else ABORT("vm_protect(PROTECT)failed")
|
|
#define UNPROTECT(addr,len)if (vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len),FALSE,(VM_PROT_READ|VM_PROT_WRITE)|(GC_pages_executable?VM_PROT_EXECUTE:0))==KERN_SUCCESS){} else ABORT("vm_protect(UNPROTECT)failed")
|
|
#elif!defined(USE_WINALLOC)
|
|
#include <sys/mman.h>
|
|
#include <signal.h>
|
|
#if!defined(CYGWIN32)&&!defined(HAIKU)
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
#define PROTECT(addr,len)if (mprotect((caddr_t)(addr),(size_t)(len),PROT_READ|(GC_pages_executable?PROT_EXEC:0))>=0){ } else ABORT("mprotect failed")
|
|
#define UNPROTECT(addr,len)if (mprotect((caddr_t)(addr),(size_t)(len),(PROT_READ|PROT_WRITE)|(GC_pages_executable?PROT_EXEC:0))>=0){ } else ABORT(GC_pages_executable?"un-mprotect executable page failed" " (probably disabled by OS)":"un-mprotect failed")
|
|
#undef IGNORE_PAGES_EXECUTABLE
|
|
#else
|
|
#ifndef MSWINCE
|
|
#include <signal.h>
|
|
#endif
|
|
static DWORD protect_junk;
|
|
#define PROTECT(addr,len)if (VirtualProtect((addr),(len),GC_pages_executable?PAGE_EXECUTE_READ:PAGE_READONLY,&protect_junk)){ } else ABORT_ARG1("VirtualProtect failed",":errcode=0x%X",(unsigned)GetLastError())
|
|
#define UNPROTECT(addr,len)if (VirtualProtect((addr),(len),GC_pages_executable?PAGE_EXECUTE_READWRITE:PAGE_READWRITE,&protect_junk)){ } else ABORT("un-VirtualProtect failed")
|
|
#endif
|
|
#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
|
|
#if defined(__GLIBC__)
|
|
#if __GLIBC__ < 2||__GLIBC__==2&&__GLIBC_MINOR__ < 2
|
|
#error glibc too old?
|
|
#endif
|
|
#endif
|
|
#ifndef DARWIN
|
|
STATIC SIG_HNDLR_PTR GC_old_segv_handler=0;
|
|
#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
|
|
#endif
|
|
#ifdef THREADS
|
|
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
|
|
#ifndef DARWIN
|
|
#if!defined(MSWIN32)&&!defined(MSWINCE)
|
|
#include <errno.h>
|
|
#if defined(FREEBSD)||defined(HURD)||defined(HPUX)
|
|
#define SIG_OK (sig==SIGBUS||sig==SIGSEGV)
|
|
#else
|
|
#define SIG_OK (sig==SIGSEGV)
|
|
#endif
|
|
#if defined(FREEBSD)
|
|
#ifndef SEGV_ACCERR
|
|
#define SEGV_ACCERR 2
|
|
#endif
|
|
#if defined(AARCH64)||defined(ARM32)||defined(MIPS)
|
|
#define CODE_OK (si->si_code==SEGV_ACCERR)
|
|
#elif defined(POWERPC)
|
|
#define AIM
|
|
#include <machine/trap.h>
|
|
#define CODE_OK (si->si_code==EXC_DSI||si->si_code==SEGV_ACCERR)
|
|
#else
|
|
#define CODE_OK (si->si_code==BUS_PAGE_FAULT||si->si_code==SEGV_ACCERR)
|
|
#endif
|
|
#elif defined(OSF1)
|
|
#define CODE_OK (si->si_code==2)
|
|
#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
|
|
#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 <ucontext.h>
|
|
#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)
|
|
STATIC LONG WINAPI GC_write_fault_handler(
|
|
struct _EXCEPTION_POINTERS*exc_info)
|
|
#endif
|
|
{
|
|
#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
|
|
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){
|
|
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
|
|
{
|
|
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 {
|
|
#if defined(MSWIN32)||defined(MSWINCE)
|
|
return((*old_handler)(exc_info));
|
|
#else
|
|
if (used_si)
|
|
((SIG_HNDLR_PTR)old_handler)(sig,si,raw_sc);
|
|
else
|
|
((PLAIN_HNDLR_PTR)(signed_word)old_handler)(sig);
|
|
return;
|
|
#endif
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
#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
|
|
#endif
|
|
#if!defined(DARWIN)
|
|
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)
|
|
(void)sigaddset(&act.sa_mask,GC_get_suspend_signal());
|
|
#endif
|
|
#endif
|
|
GC_VERBOSE_LOG_PRINTF(
|
|
"Initializing mprotect virtual dirty bit implementation\n");
|
|
if (GC_page_size % HBLKSIZE!=0){
|
|
ABORT("Page size not multiple of HBLKSIZE");
|
|
}
|
|
#if!defined(MSWIN32)&&!defined(MSWINCE)
|
|
#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(__GLIBC__)||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
|
|
#endif
|
|
} else if (GC_old_bus_handler!=(SIG_HNDLR_PTR)(signed_word)SIG_DFL){
|
|
GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n");
|
|
}
|
|
#endif
|
|
#endif
|
|
#if defined(GWW_VDB)
|
|
if (GC_gww_dirty_init())
|
|
return TRUE;
|
|
#endif
|
|
#if defined(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)
|
|
#endif
|
|
#if defined(CPPCHECK)&&defined(ADDRESS_SANITIZER)
|
|
GC_noop1((word)&__asan_default_options);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
GC_API int GC_CALL GC_incremental_protection_needs(void)
|
|
{
|
|
GC_ASSERT(GC_is_initialized);
|
|
if (GC_page_size==HBLKSIZE){
|
|
return GC_PROTECTS_POINTER_HEAP;
|
|
} else {
|
|
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;
|
|
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)){
|
|
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;
|
|
} 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef PROC_VDB
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/stat.h>
|
|
#ifdef GC_NO_SYS_FAULT_H
|
|
#define PG_MODIFIED 1
|
|
struct prpageheader {
|
|
int dummy[2];
|
|
unsigned long pr_nmap;
|
|
unsigned long pr_npage;
|
|
};
|
|
struct prasmap {
|
|
char*pr_vaddr;
|
|
size_t pr_npage;
|
|
char dummy1[64+8];
|
|
unsigned pr_mflags;
|
|
unsigned pr_pagesize;
|
|
int dummy2[2];
|
|
};
|
|
#else
|
|
#include <sys/fault.h>
|
|
#include <sys/procfs.h>
|
|
#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=0;
|
|
GC_INNER GC_bool GC_dirty_init(void)
|
|
{
|
|
char buf[40];
|
|
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));
|
|
}
|
|
(void)snprintf(buf,sizeof(buf),"/proc/%ld/pagedata",(long)getpid());
|
|
buf[sizeof(buf)- 1]='\0';
|
|
GC_proc_fd=open(buf,O_RDONLY);
|
|
if (GC_proc_fd < 0){
|
|
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);
|
|
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)
|
|
{
|
|
#define READ read
|
|
int nmaps;
|
|
char*bufp=GC_proc_buf;
|
|
int i;
|
|
BZERO(GC_grungy_pages,sizeof(GC_grungy_pages));
|
|
if (READ(GC_proc_fd,bufp,GC_proc_buf_size)<=0){
|
|
size_t new_size=2*GC_proc_buf_size;
|
|
char*new_buf;
|
|
WARN("/proc read failed:GC_proc_buf_size=%" WARN_PRIdPTR "\n",
|
|
(signed_word)GC_proc_buf_size);
|
|
new_buf=GC_scratch_alloc(new_size);
|
|
if (new_buf!=0){
|
|
GC_scratch_recycle_no_gww(bufp,GC_proc_buf_size);
|
|
GC_proc_buf=bufp=new_buf;
|
|
GC_proc_buf_size=new_size;
|
|
}
|
|
if (READ(GC_proc_fd,bufp,GC_proc_buf_size)<=0){
|
|
WARN("Insufficient space for/proc read\n",0);
|
|
if (!output_unneeded)
|
|
memset(GC_grungy_pages,0xff,sizeof (page_hash_table));
|
|
memset(GC_written_pages,0xff,sizeof(page_hash_table));
|
|
return;
|
|
}
|
|
}
|
|
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
|
|
GC_or_pages(GC_written_pages,GC_grungy_pages);
|
|
#undef READ
|
|
}
|
|
#endif
|
|
#ifdef PCR_VDB
|
|
#include "vd/PCR_VD.h"
|
|
#define NPAGES (32*1024)
|
|
PCR_VD_DB GC_grungy_bits[NPAGES];
|
|
STATIC ptr_t GC_vd_base=NULL;
|
|
GC_INNER GC_bool GC_dirty_init(void)
|
|
{
|
|
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
|
|
#ifndef GC_DISABLE_INCREMENTAL
|
|
GC_INNER GC_bool GC_manual_vdb=FALSE;
|
|
GC_INNER void GC_dirty_inner(const void*p)
|
|
{
|
|
word index=PHT_HASH(p);
|
|
#if defined(MPROTECT_VDB)
|
|
GC_ASSERT(GC_manual_vdb);
|
|
#endif
|
|
async_set_pht_entry_from_index(GC_dirty_pages,index);
|
|
}
|
|
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(( void*)GC_dirty_pages,GC_grungy_pages,
|
|
sizeof(GC_dirty_pages));
|
|
BZERO(( 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(PCR_VDB)
|
|
{
|
|
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
|
|
}
|
|
GC_INNER GC_bool GC_page_was_dirty(struct hblk*h)
|
|
{
|
|
#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;
|
|
#endif
|
|
return NULL==HDR(h)
|
|
||get_pht_entry_from_index(GC_grungy_pages,PHT_HASH(h));
|
|
}
|
|
#if defined(CHECKSUMS)||defined(PROC_VDB)
|
|
GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk*h)
|
|
{
|
|
#if defined(GWW_VDB)||defined(PROC_VDB)
|
|
#ifdef MPROTECT_VDB
|
|
if (!GC_GWW_AVAILABLE())
|
|
return TRUE;
|
|
#endif
|
|
return NULL==HDR(h)
|
|
||get_pht_entry_from_index(GC_written_pages,PHT_HASH(h));
|
|
#else
|
|
(void)h;
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
#endif
|
|
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;
|
|
struct hblk*h_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(GC_dirty_pages,PHT_HASH(h_trunc))){
|
|
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
|
|
(void)h;(void)nblocks;(void)is_ptrfree;
|
|
#endif
|
|
}
|
|
#endif
|
|
#if defined(MPROTECT_VDB)&&defined(DARWIN)
|
|
#include <mach/mach.h>
|
|
#include <mach/mach_error.h>
|
|
#include <mach/exception.h>
|
|
#include <mach/task.h>
|
|
#include <pthread.h>
|
|
EXTERN_C_BEGIN
|
|
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
|
|
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={
|
|
{
|
|
(void (*)(void))catch_exception_raise,
|
|
(void (*)(void))catch_exception_raise_state,
|
|
(void (*)(void))catch_exception_raise_state_identity
|
|
},
|
|
#ifdef THREADS
|
|
0,
|
|
#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
|
|
#define ID_STOP 1
|
|
#define ID_RESUME 2
|
|
#define ID_ACK 3
|
|
STATIC GC_mprotect_state_t GC_mprotect_state=GC_MP_NORMAL;
|
|
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;
|
|
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");
|
|
}
|
|
STATIC void GC_mprotect_thread_reply(void)
|
|
{
|
|
GC_msg_t msg;
|
|
mach_msg_return_t r;
|
|
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
|
|
#define GC_mprotect_state GC_MP_NORMAL
|
|
#endif
|
|
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;
|
|
struct mp_reply_s reply;
|
|
struct mp_msg_s msg;
|
|
mach_msg_id_t id;
|
|
if ((word)arg==GC_WORD_MAX)return 0;
|
|
#if defined(CPPCHECK)
|
|
reply.data[0]=0;
|
|
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
|
|
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
|
|
default:
|
|
if(!exc_server(&msg.head,&reply.head))
|
|
ABORT("exc_server failed");
|
|
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){
|
|
#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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef BROKEN_EXCEPTION_HANDLING
|
|
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");
|
|
if (GC_sigbus_count>=8){
|
|
ABORT("Got more than 8 SIGBUSs in a row!");
|
|
} else {
|
|
GC_sigbus_count++;
|
|
WARN("Ignoring SIGBUS\n",0);
|
|
}
|
|
}
|
|
#endif
|
|
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){
|
|
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);
|
|
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
|
|
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
|
|
if (pthread_create(&thread,&attr,GC_mprotect_thread,NULL)!=0)
|
|
ABORT("pthread_create failed");
|
|
(void)pthread_attr_destroy(&attr);
|
|
#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;
|
|
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
|
|
#if defined(CPPCHECK)
|
|
GC_noop1((word)GC_ports.os_callback[0]);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
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;
|
|
default:
|
|
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)
|
|
#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
|
|
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
|
|
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){
|
|
#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
|
|
}
|
|
addr=(char*)exc_state.DARWIN_EXC_STATE_DAR;
|
|
if (!is_header_found_async(addr)){
|
|
#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);
|
|
EXIT();
|
|
#else
|
|
return FWD();
|
|
#endif
|
|
}
|
|
#ifdef BROKEN_EXCEPTION_HANDLING
|
|
GC_sigbus_count=0;
|
|
#endif
|
|
GC_ASSERT(GC_page_size!=0);
|
|
if (GC_mprotect_state==GC_MP_NORMAL){
|
|
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){
|
|
} else {
|
|
GC_err_printf("KERN_PROTECTION_FAILURE while world is stopped\n");
|
|
return FWD();
|
|
}
|
|
return KERN_SUCCESS;
|
|
}
|
|
#undef FWD
|
|
#ifndef NO_DESC_CATCH_EXCEPTION_RAISE
|
|
__asm__(".desc _catch_exception_raise,0x10");
|
|
__asm__(".desc _catch_exception_raise_state,0x10");
|
|
__asm__(".desc _catch_exception_raise_state_identity,0x10");
|
|
#endif
|
|
#endif
|
|
#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
|
|
#ifdef ECOS
|
|
#undef sbrk
|
|
#endif
|
|
GC_API void GC_CALL GC_set_pages_executable(int value)
|
|
{
|
|
GC_ASSERT(!GC_is_initialized);
|
|
GC_pages_executable=(GC_bool)(value!=0);
|
|
}
|
|
GC_API int GC_CALL GC_get_pages_executable(void)
|
|
{
|
|
#ifdef IGNORE_PAGES_EXECUTABLE
|
|
return 1;
|
|
#else
|
|
return (int)GC_pages_executable;
|
|
#endif
|
|
}
|
|
#if defined(I386)&&defined(LINUX)&&defined(SAVE_CALL_CHAIN)
|
|
#include <features.h>
|
|
struct frame {
|
|
struct frame*fr_savfp;
|
|
long fr_savpc;
|
|
#if NARGS > 0
|
|
long fr_arg[NARGS];
|
|
#endif
|
|
};
|
|
#endif
|
|
#if defined(SPARC)
|
|
#if defined(LINUX)
|
|
#include <features.h>
|
|
#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 <sys/sparc/frame.h>
|
|
#elif defined(OPENBSD)
|
|
#include <frame.h>
|
|
#elif defined(FREEBSD)||defined(NETBSD)
|
|
#include <machine/frame.h>
|
|
#else
|
|
#include <sys/frame.h>
|
|
#endif
|
|
#if NARGS > 6
|
|
#error We only know how to get the first 6 arguments
|
|
#endif
|
|
#endif
|
|
#ifdef NEED_CALLINFO
|
|
#ifdef LINUX
|
|
#include <unistd.h>
|
|
#endif
|
|
#endif
|
|
#if defined(GC_HAVE_BUILTIN_BACKTRACE)
|
|
#ifdef _MSC_VER
|
|
#ifndef GC_MSVC_DBG_H
|
|
#define GC_MSVC_DBG_H
|
|
#include <stdlib.h>
|
|
#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);
|
|
MSVC_DBG_EXPORT int backtrace(void*addresses[],int count);
|
|
MSVC_DBG_EXPORT char**backtrace_symbols(void*const addresses[],int count);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
#else
|
|
#include <execinfo.h>
|
|
#endif
|
|
#endif
|
|
#ifdef SAVE_CALL_CHAIN
|
|
#if NARGS==0&&NFRAMES % 2==0&&defined(GC_HAVE_BUILTIN_BACKTRACE)
|
|
#ifdef REDIRECT_MALLOC
|
|
#ifdef THREADS
|
|
__thread
|
|
#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
|
|
#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());
|
|
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
|
|
#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
|
|
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
|
|
}
|
|
if (nframes < NFRAMES)info[nframes].ci_pc=0;
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef NEED_CALLINFO
|
|
GC_INNER void GC_print_callers(struct callinfo info[NFRAMES])
|
|
{
|
|
int i;
|
|
static int reentry_count=0;
|
|
DCL_LOCK_STATE;
|
|
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){
|
|
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
|
|
{
|
|
(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)
|
|
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;
|
|
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;
|
|
break;
|
|
}
|
|
exe_name[ret_code]='\0';
|
|
found_exe_name=TRUE;
|
|
}
|
|
(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;
|
|
{
|
|
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
|
|
- COVERT_DATAFLOW(result_buf))
|
|
:result_len)==0){
|
|
stop=TRUE;
|
|
}
|
|
}
|
|
if (result_len < RESULT_SZ - 25){
|
|
(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]);
|
|
#endif
|
|
name=result_buf;
|
|
} while (0);
|
|
#endif
|
|
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);
|
|
#endif
|
|
}
|
|
#if defined(LINUX)&&!defined(SMALL_CONFIG)
|
|
if (stop)
|
|
break;
|
|
#endif
|
|
}
|
|
LOCK();
|
|
--reentry_count;
|
|
UNLOCK();
|
|
}
|
|
#endif
|
|
#if defined(LINUX)&&defined(__ELF__)&&!defined(SMALL_CONFIG)
|
|
void GC_print_address_map(void)
|
|
{
|
|
char*maps;
|
|
GC_err_printf("----------Begin address map----------\n");
|
|
maps=GC_get_maps();
|
|
GC_err_puts(maps!=NULL?maps:"Failed to get map!\n");
|
|
GC_err_printf("----------End address map----------\n");
|
|
}
|
|
#endif
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
#ifndef THREADS
|
|
#error "invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS"
|
|
#endif
|
|
#ifndef GC_THREAD_LOCAL_ALLOC_H
|
|
#define GC_THREAD_LOCAL_ALLOC_H
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
#if defined(USE_HPUX_TLS)
|
|
#error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS
|
|
#endif
|
|
#include <stdlib.h>
|
|
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__)
|
|
#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
|
|
#elif (defined(LINUX)&&!defined(ARM32)&&!defined(AVR32)&&GC_GNUC_PREREQ(3,3)&&!(defined(__clang__)&&defined(HOST_ANDROID)))||(defined(FREEBSD)||(defined(NETBSD)&&__NetBSD_Version__>=600000000)&&(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
|
|
#else
|
|
#define USE_COMPILER_TLS
|
|
#endif
|
|
#else
|
|
#define USE_CUSTOM_SPECIFIC
|
|
#endif
|
|
#endif
|
|
#ifndef THREAD_FREELISTS_KINDS
|
|
#ifdef ENABLE_DISCLAIM
|
|
#define THREAD_FREELISTS_KINDS (NORMAL+2)
|
|
#else
|
|
#define THREAD_FREELISTS_KINDS (NORMAL+1)
|
|
#endif
|
|
#endif
|
|
typedef struct thread_local_freelists {
|
|
void*_freelists[THREAD_FREELISTS_KINDS][TINY_FREELISTS];
|
|
#define ptrfree_freelists _freelists[PTRFREE]
|
|
#define normal_freelists _freelists[NORMAL]
|
|
#ifdef GC_GCJ_SUPPORT
|
|
void*gcj_freelists[TINY_FREELISTS];
|
|
#define ERROR_FL ((void*)GC_WORD_MAX)
|
|
#endif
|
|
#define DIRECT_GRANULES (HBLKSIZE/GRANULE_BYTES)
|
|
}*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)
|
|
#define GC_remove_specific_after_fork(key,t)(void)0
|
|
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)
|
|
#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)
|
|
#ifndef TLS_OUT_OF_INDEXES
|
|
#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)
|
|
#define GC_remove_specific_after_fork(key,t)(void)0
|
|
typedef DWORD GC_key_t;
|
|
#elif defined(USE_CUSTOM_SPECIFIC)
|
|
EXTERN_C_END
|
|
#ifndef GC_SPECIFIC_H
|
|
#define GC_SPECIFIC_H
|
|
#include <errno.h>
|
|
EXTERN_C_BEGIN
|
|
#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
|
|
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
|
|
typedef struct thread_specific_entry {
|
|
volatile AO_t qtid;
|
|
ts_entry_value_t value;
|
|
struct thread_specific_entry*next;
|
|
pthread_t thread;
|
|
} tse;
|
|
#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];
|
|
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);
|
|
GC_INNER void*GC_slow_getspecific(tsd*key,word qtid,
|
|
tse*volatile*cache_entry);
|
|
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;
|
|
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
|
|
EXTERN_C_BEGIN
|
|
#else
|
|
#error implement me
|
|
#endif
|
|
GC_INNER void GC_init_thread_local(GC_tlfs p);
|
|
GC_INNER void GC_destroy_thread_local(GC_tlfs p);
|
|
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
|
|
#ifndef GC_ATTR_TLS_FAST
|
|
#define GC_ATTR_TLS_FAST
|
|
#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;
|
|
EXTERN_C_END
|
|
#endif
|
|
#endif
|
|
#include <stdlib.h>
|
|
#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;
|
|
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));
|
|
qptr=&(obj_link(fl));
|
|
while ((word)(q=*qptr)>=HBLKSIZE)
|
|
qptr=&(obj_link(q));
|
|
GC_ASSERT(0==q);
|
|
*qptr=*gfl;
|
|
*gfl=fl;
|
|
}
|
|
}
|
|
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]);
|
|
}
|
|
fl[i]=(ptr_t)HBLKSIZE;
|
|
}
|
|
#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
|
|
static void reset_thread_key(void*v){
|
|
pthread_setspecific(GC_thread_key,v);
|
|
}
|
|
#else
|
|
#define reset_thread_key 0
|
|
#endif
|
|
GC_INNER void GC_init_thread_local(GC_tlfs p)
|
|
{
|
|
int i,j,res;
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
if (!EXPECT(keys_initialized,TRUE)){
|
|
GC_ASSERT((word)&GC_thread_key % sizeof(word)==0);
|
|
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
|
|
}
|
|
#ifdef GC_GCJ_SUPPORT
|
|
p->gcj_freelists[0]=ERROR_FL;
|
|
#endif
|
|
}
|
|
GC_INNER void GC_destroy_thread_local(GC_tlfs p)
|
|
{
|
|
int k;
|
|
GC_STATIC_ASSERT(THREAD_FREELISTS_KINDS<=MAXOBJKINDS);
|
|
for (k=0;k < THREAD_FREELISTS_KINDS;++k){
|
|
if (k==(int)GC_n_kinds)
|
|
break;
|
|
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)){
|
|
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
|
|
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;});
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
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){
|
|
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)
|
|
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
|
|
#endif
|
|
#ifndef GC_PTHREAD_SUPPORT_H
|
|
#define GC_PTHREAD_SUPPORT_H
|
|
#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS)
|
|
#if defined(GC_DARWIN_THREADS)
|
|
#else
|
|
#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(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)
|
|
volatile AO_t last_stop_count;
|
|
#endif
|
|
ptr_t stack_ptr;
|
|
#ifdef NACL
|
|
#ifdef ARM32
|
|
#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(SN_TARGET_ORBIS)
|
|
#define ORBIS_GC_REG_STORAGE_SIZE 27
|
|
word registers[ORBIS_GC_REG_STORAGE_SIZE];
|
|
#endif
|
|
};
|
|
GC_INNER void GC_stop_init(void);
|
|
EXTERN_C_END
|
|
#endif
|
|
#endif
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
#endif
|
|
#ifdef THREAD_SANITIZER
|
|
#endif
|
|
EXTERN_C_BEGIN
|
|
typedef struct GC_Thread_Rep {
|
|
#ifdef THREAD_SANITIZER
|
|
char dummy[sizeof(oh)];
|
|
#endif
|
|
struct GC_Thread_Rep*next;
|
|
pthread_t id;
|
|
#ifdef USE_TKILL_ON_ANDROID
|
|
pid_t kernel_id;
|
|
#endif
|
|
struct thread_stop_info stop_info;
|
|
#if defined(GC_ENABLE_SUSPEND_THREAD)&&!defined(GC_DARWIN_THREADS)&&!defined(GC_OPENBSD_UTHREADS)&&!defined(NACL)
|
|
volatile AO_t suspended_ext;
|
|
#endif
|
|
unsigned char flags;
|
|
#define FINISHED 1
|
|
#define DETACHED 2
|
|
#define MAIN_THREAD 4
|
|
#define DISABLED_GC 0x10
|
|
unsigned char thread_blocked;
|
|
unsigned short finalizer_skipped;
|
|
unsigned char finalizer_nested;
|
|
ptr_t stack_end;
|
|
ptr_t altstack;
|
|
word altstack_size;
|
|
ptr_t stack;
|
|
word stack_size;
|
|
#if defined(GC_DARWIN_THREADS)&&!defined(DARWIN_DONT_PARSE_STACK)
|
|
ptr_t topOfStack;
|
|
#endif
|
|
#ifdef IA64
|
|
ptr_t backing_store_end;
|
|
ptr_t backing_store_ptr;
|
|
#endif
|
|
struct GC_traced_stack_sect_s*traced_stack_sect;
|
|
void*status;
|
|
#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
|
|
#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
|
|
#ifdef GC_PTHREAD_START_STANDALONE
|
|
#define GC_INNER_PTHRSTART
|
|
#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
|
|
#endif
|
|
#if defined(GC_DARWIN_THREADS)
|
|
#include <sys/sysctl.h>
|
|
#include <mach/machine.h>
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
#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){
|
|
frame=(StackFrame*)frame->savedSP;
|
|
if ((frame->savedLR&~0x3)==0||(frame->savedLR&~0x3)==~0x3UL)
|
|
break;
|
|
}
|
|
#ifdef DEBUG_THREADS_EXTRA
|
|
GC_log_printf("FindTopOfStack finish at sp=%p\n",(void*)frame);
|
|
#endif
|
|
return (ptr_t)frame;
|
|
}
|
|
#endif
|
|
#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_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();
|
|
#endif
|
|
}
|
|
#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
|
|
#define kCFCoreFoundationVersionNumber_iOS_8_0 1140.1
|
|
#endif
|
|
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 {
|
|
kern_return_t kern_result;
|
|
GC_THREAD_STATE_T state;
|
|
#if defined(ARM32)&&defined(ARM_THREAD_STATE32)
|
|
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
|
|
{
|
|
mach_msg_type_number_t thread_state_count=GC_MACH_THREAD_STATE_COUNT;
|
|
kern_result=thread_get_state(thread,GC_MACH_THREAD_STATE,
|
|
(natural_t*)&state,
|
|
&thread_state_count);
|
|
}
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("thread_get_state returns value=%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));
|
|
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));
|
|
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]));
|
|
#endif
|
|
{
|
|
int j;
|
|
for (j=0;j < 7;j++)
|
|
GC_push_one(state.THREAD_FLD(r[j]));
|
|
j++;
|
|
for (;j<=12;j++)
|
|
GC_push_one(state.THREAD_FLD(r[j]));
|
|
}
|
|
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]));
|
|
}
|
|
}
|
|
GC_push_one(state.THREAD_FLD(lr));
|
|
#elif defined(CPPCHECK)
|
|
lo=NULL;
|
|
#else
|
|
#error FIXME for non-arm/ppc/x86 architectures
|
|
#endif
|
|
}
|
|
#ifdef DARWIN_DONT_PARSE_STACK
|
|
*phi=(p->flags&MAIN_THREAD)!=0?GC_stackbottom:p->stack_end;
|
|
#endif
|
|
#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
|
|
{
|
|
*paltstack_lo=NULL;
|
|
}
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Darwin:Stack for thread %p=[%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;
|
|
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);
|
|
}
|
|
nthreads++;
|
|
if (thread==my_thread)
|
|
found_me=TRUE;
|
|
mach_port_deallocate(my_task,thread);
|
|
}
|
|
vm_deallocate(my_task,(vm_address_t)act_list,
|
|
sizeof(thread_t)*listcount);
|
|
} else
|
|
#endif
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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
|
|
#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;
|
|
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)
|
|
#endif
|
|
){
|
|
mach_port_deallocate(my_task,thread);
|
|
continue;
|
|
}
|
|
found=FALSE;
|
|
{
|
|
int last_found=j;
|
|
while (++j < old_count)
|
|
if (old_list[j]==thread){
|
|
found=TRUE;
|
|
break;
|
|
}
|
|
if (!found){
|
|
for (j=0;j < last_found;j++)
|
|
if (old_list[j]==thread){
|
|
found=TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (found){
|
|
mach_port_deallocate(my_task,thread);
|
|
continue;
|
|
}
|
|
if (GC_mach_threads_count==GC_MAX_MACH_THREADS)
|
|
ABORT("Too many threads");
|
|
GC_mach_threads[GC_mach_threads_count].thread=thread;
|
|
GC_mach_threads[GC_mach_threads_count].suspended=FALSE;
|
|
changed=TRUE;
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Suspending %p\n",(void*)(word)thread);
|
|
#endif
|
|
GC_acquire_dirty_lock();
|
|
do {
|
|
kern_result=thread_suspend(thread);
|
|
} while (kern_result==KERN_ABORTED);
|
|
GC_release_dirty_lock();
|
|
if (kern_result!=KERN_SUCCESS){
|
|
GC_mach_threads[GC_mach_threads_count].suspended=FALSE;
|
|
} else {
|
|
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_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){
|
|
GC_acquire_mark_lock();
|
|
GC_ASSERT(GC_fl_builder_count==0);
|
|
}
|
|
#endif
|
|
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;
|
|
GC_mach_threads_count=0;
|
|
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){
|
|
vm_deallocate(my_task,(vm_address_t)prev_list,
|
|
sizeof(thread_t)*prevcount);
|
|
}
|
|
prev_list=act_list;
|
|
prevcount=listcount;
|
|
}
|
|
} while (changed);
|
|
GC_ASSERT(prev_list!=0);
|
|
vm_deallocate(my_task,(vm_address_t)act_list,
|
|
sizeof(thread_t)*listcount);
|
|
#endif
|
|
} 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;
|
|
#if defined(CPPCHECK)&&defined(DEBUG_THREADS)
|
|
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
|
|
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);
|
|
}
|
|
}
|
|
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;
|
|
while (++j < (int)listcount){
|
|
if (act_list[j]==thread)
|
|
break;
|
|
}
|
|
if (j>=(int)listcount){
|
|
for (j=0;j < last_found;j++){
|
|
if (act_list[j]==thread)
|
|
break;
|
|
}
|
|
}
|
|
if (j!=last_found){
|
|
GC_thread_resume(thread);
|
|
}
|
|
} else {
|
|
#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
|
|
} 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
|
|
#if!defined(MACOS)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)&&!defined(_WIN32_WCE)&&!defined(__CC_ARM)
|
|
#include <sys/types.h>
|
|
#endif
|
|
#undef GC_MUST_RESTORE_REDEFINED_DLOPEN
|
|
#if defined(GC_PTHREADS)&&!defined(GC_NO_DLOPEN)&&!defined(GC_NO_THREAD_REDIRECTS)&&!defined(GC_USE_LD_WRAP)
|
|
#undef dlopen
|
|
#define GC_MUST_RESTORE_REDEFINED_DLOPEN
|
|
#endif
|
|
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 <stdio.h>
|
|
#ifdef SOLARISDL
|
|
#include <sys/elf.h>
|
|
#include <dlfcn.h>
|
|
#include <link.h>
|
|
#endif
|
|
#if defined(NETBSD)
|
|
#include <sys/param.h>
|
|
#include <dlfcn.h>
|
|
#include <machine/elf_machdep.h>
|
|
#define ELFSIZE ARCH_ELFSIZE
|
|
#endif
|
|
#if defined(OPENBSD)
|
|
#include <sys/param.h>
|
|
#if (OpenBSD>=200519)&&!defined(HAVE_DL_ITERATE_PHDR)
|
|
#define HAVE_DL_ITERATE_PHDR
|
|
#endif
|
|
#endif
|
|
#if defined(SCO_ELF)||defined(DGUX)||defined(HURD)||defined(NACL)||(defined(__ELF__)&&(defined(LINUX)||defined(FREEBSD)||defined(NETBSD)||defined(OPENBSD)))
|
|
#include <stddef.h>
|
|
#if!defined(OPENBSD)&&!defined(HOST_ANDROID)
|
|
#include <elf.h>
|
|
#endif
|
|
#ifdef HOST_ANDROID
|
|
#ifdef BIONIC_ELFDATA_REDEF_BUG
|
|
#include <asm/elf.h>
|
|
#include <linux/elf-em.h>
|
|
#undef ELF_DATA
|
|
#undef EM_ALPHA
|
|
#endif
|
|
#include <link.h>
|
|
#if!defined(GC_DONT_DEFINE_LINK_MAP)&&!(__ANDROID_API__>=21)
|
|
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
|
|
#include <link.h>
|
|
EXTERN_C_END
|
|
#endif
|
|
#endif
|
|
#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;
|
|
#ifdef SUNOS53_SHARED_LIB
|
|
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)){
|
|
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;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cachedResult;
|
|
}
|
|
#endif
|
|
#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN
|
|
#define dlopen GC_dlopen
|
|
#endif
|
|
#if defined(SOLARISDL)
|
|
#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
|
|
#endif
|
|
#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 <string.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#define MAPS_BUF_SIZE (32*1024)
|
|
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(char*maps)
|
|
{
|
|
char*prot;
|
|
char*buf_ptr=maps;
|
|
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 (;;){
|
|
buf_ptr=GC_parse_map_entry(buf_ptr,&start,&end,&prot,
|
|
&maj_dev,0);
|
|
if (NULL==buf_ptr)
|
|
break;
|
|
if (prot[1]=='w'){
|
|
if ((word)start<=(word)GC_stackbottom
|
|
&&(word)end>=(word)GC_stackbottom){
|
|
continue;
|
|
}
|
|
#ifdef THREADS
|
|
if (GC_segment_is_thread_stack(start,end))continue;
|
|
#endif
|
|
if ((word)end<=(word)least_ha
|
|
||(word)start>=(word)greatest_ha){
|
|
GC_add_roots_inner(start,end,TRUE);
|
|
continue;
|
|
}
|
|
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]=='-'){
|
|
GC_remove_roots_subregion(start,end);
|
|
}
|
|
}
|
|
}
|
|
GC_INNER void GC_register_dynamic_libraries(void)
|
|
{
|
|
char*maps=GC_get_maps();
|
|
if (NULL==maps)
|
|
ABORT("Failed to read/proc for library registration");
|
|
GC_register_map_entries(maps);
|
|
}
|
|
GC_INNER GC_bool GC_register_main_static_data(void)
|
|
{
|
|
return FALSE;
|
|
}
|
|
#define HAVE_REGISTER_MAIN_STATIC_DATA
|
|
#else
|
|
#if __GLIBC__ > 2||(__GLIBC__==2&&__GLIBC_MINOR__ > 2)||(__GLIBC__==2&&__GLIBC_MINOR__==2&&defined(DT_CONFIG))||defined(HOST_ANDROID)
|
|
#ifndef HAVE_DL_ITERATE_PHDR
|
|
#define HAVE_DL_ITERATE_PHDR
|
|
#endif
|
|
#ifdef HOST_ANDROID
|
|
EXTERN_C_BEGIN
|
|
extern int dl_iterate_phdr(int (*cb)(struct dl_phdr_info*,
|
|
size_t,void*),
|
|
void*data);
|
|
EXTERN_C_END
|
|
#endif
|
|
#endif
|
|
#if defined(__DragonFly__)||defined(__FreeBSD_kernel__)||(defined(FREEBSD)&&__FreeBSD__>=7)
|
|
#ifndef HAVE_DL_ITERATE_PHDR
|
|
#define HAVE_DL_ITERATE_PHDR
|
|
#endif
|
|
#define DL_ITERATE_PHDR_STRONG
|
|
#elif defined(HAVE_DL_ITERATE_PHDR)
|
|
EXTERN_C_BEGIN
|
|
#pragma weak dl_iterate_phdr
|
|
EXTERN_C_END
|
|
#endif
|
|
#if defined(HAVE_DL_ITERATE_PHDR)
|
|
#ifdef PT_GNU_RELRO
|
|
#define MAX_LOAD_SEGS MAX_ROOT_SETS
|
|
static struct load_segment {
|
|
ptr_t start;
|
|
ptr_t end;
|
|
ptr_t start2;
|
|
ptr_t end2;
|
|
} load_segs[MAX_LOAD_SEGS];
|
|
static int n_load_segs;
|
|
static GC_bool load_segs_overflow;
|
|
#endif
|
|
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;
|
|
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
|
|
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
|
|
}
|
|
}
|
|
#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){
|
|
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);
|
|
load_segs[j].end2=load_segs[j].end;
|
|
load_segs[j].end=start;
|
|
load_segs[j].start2=end;
|
|
}
|
|
break;
|
|
}
|
|
if (0==j&&0==GC_has_static_roots)
|
|
WARN("Failed to find PT_GNU_RELRO segment"
|
|
" inside PT_LOAD region\n",0);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
*(int*)ptr=1;
|
|
return 0;
|
|
}
|
|
GC_INNER GC_bool GC_register_main_static_data(void)
|
|
{
|
|
#ifdef DL_ITERATE_PHDR_STRONG
|
|
return FALSE;
|
|
#else
|
|
return 0==COVERT_DATAFLOW(dl_iterate_phdr);
|
|
#endif
|
|
}
|
|
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;
|
|
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;
|
|
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);
|
|
GC_add_roots_inner(datastart,dataend,TRUE);
|
|
#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,TRUE);
|
|
#endif
|
|
}
|
|
return TRUE;
|
|
}
|
|
#define HAVE_REGISTER_MAIN_STATIC_DATA
|
|
#else
|
|
#if defined(NETBSD)||defined(OPENBSD)
|
|
#include <sys/exec_elf.h>
|
|
#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 <elf.h>
|
|
#endif
|
|
#ifndef HOST_ANDROID
|
|
#include <link.h>
|
|
#endif
|
|
#endif
|
|
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)){
|
|
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){
|
|
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;
|
|
if (rd!=NULL){
|
|
struct link_map*lm=rd->r_map;
|
|
if (lm!=NULL)
|
|
cachedResult=lm->l_next;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
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
|
|
#endif
|
|
#if defined(IRIX5)||(defined(USE_PROC_FOR_LIBRARIES)&&!defined(LINUX))
|
|
#include <sys/procfs.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <elf.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#ifndef _sigargs
|
|
#define IRIX6
|
|
#endif
|
|
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;
|
|
int needed_sz=0;
|
|
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
|
|
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;
|
|
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;
|
|
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;
|
|
#ifndef IRIX6
|
|
if (addr_map[i].pr_off==0&&strncmp(start,ELFMAG,4)==0){
|
|
caddr_t arg;
|
|
int obj;
|
|
#define MAP_IRR_SZ 10
|
|
static ptr_t map_irr[MAP_IRR_SZ];
|
|
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
|
|
GC_add_roots_inner(start,limit,TRUE);
|
|
irrelevant:;
|
|
}
|
|
if (close(fd)< 0)ABORT("Couldn't close/proc file");
|
|
fd=-1;
|
|
}
|
|
#endif
|
|
#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)
|
|
#include <stdlib.h>
|
|
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){
|
|
return;
|
|
}
|
|
GC_add_roots_inner(base,limit,TRUE);
|
|
#endif
|
|
}
|
|
#ifdef DYNAMIC_LOADING
|
|
GC_INNER GC_bool GC_register_main_static_data(void)
|
|
{
|
|
#if defined(MSWINCE)||defined(CYGWIN32)
|
|
return FALSE;
|
|
#else
|
|
return GC_no_win32_dlls;
|
|
#endif
|
|
}
|
|
#define HAVE_REGISTER_MAIN_STATIC_DATA
|
|
#endif
|
|
#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
|
|
#if defined(MSWINCE)||defined(CYGWIN32)
|
|
#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){
|
|
new_limit=(char*)
|
|
(((DWORD)p+GC_sysinfo.dwAllocationGranularity)
|
|
&~(GC_sysinfo.dwAllocationGranularity-1));
|
|
} else
|
|
#endif
|
|
{
|
|
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
|
|
||(!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)break;
|
|
p=(LPVOID)new_limit;
|
|
}
|
|
GC_cond_add_roots(base,limit);
|
|
}
|
|
#endif
|
|
#if defined(ALPHA)&&defined(OSF1)
|
|
#include <loader.h>
|
|
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();
|
|
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);
|
|
if (moduleid==LDR_NULL_MODULE)
|
|
break;
|
|
if (status!=0){
|
|
ABORT_ARG3("ldr_next_module failed",
|
|
":status=%d,errcode=%d (%s)",status,errno,
|
|
errno < sys_nerr?sys_errlist[errno]:"");
|
|
}
|
|
status=ldr_inq_module(mypid,moduleid,&moduleinfo,
|
|
sizeof(moduleinfo),&modulereturnsize);
|
|
if (status!=0)
|
|
ABORT("ldr_inq_module failed");
|
|
if (moduleinfo.lmi_flags&LDR_MAIN)
|
|
continue;
|
|
#ifdef DL_VERBOSE
|
|
GC_log_printf("---Module---\n");
|
|
GC_log_printf("Module ID\t=%16ld\n",moduleinfo.lmi_modid);
|
|
GC_log_printf("Count of regions=%16d\n",moduleinfo.lmi_nregion);
|
|
GC_log_printf("flags for module=%16lx\n",moduleinfo.lmi_flags);
|
|
GC_log_printf("module pathname\t=\"%s\"\n",moduleinfo.lmi_name);
|
|
#endif
|
|
for (region=0;region < moduleinfo.lmi_nregion;region++){
|
|
status=ldr_inq_region(mypid,moduleid,region,®ioninfo,
|
|
sizeof(regioninfo),®ionreturnsize);
|
|
if (status!=0)
|
|
ABORT("ldr_inq_region failed");
|
|
if (!(regioninfo.lri_prot&LDR_W))
|
|
continue;
|
|
#ifdef DL_VERBOSE
|
|
GC_log_printf("--- Region---\n");
|
|
GC_log_printf("Region number\t=%16ld\n",
|
|
regioninfo.lri_region_no);
|
|
GC_log_printf("Protection flags=%016x\n",regioninfo.lri_prot);
|
|
GC_log_printf("Virtual address\t=%16p\n",regioninfo.lri_vaddr);
|
|
GC_log_printf("Mapped address\t=%16p\n",
|
|
regioninfo.lri_mapaddr);
|
|
GC_log_printf("Region size\t=%16ld\n",regioninfo.lri_size);
|
|
GC_log_printf("Region name\t=\"%s\"\n",regioninfo.lri_name);
|
|
#endif
|
|
GC_add_roots_inner((char*)regioninfo.lri_mapaddr,
|
|
(char*)regioninfo.lri_mapaddr+regioninfo.lri_size,
|
|
TRUE);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(HPUX)
|
|
#include <errno.h>
|
|
#include <dl.h>
|
|
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;
|
|
while (TRUE){
|
|
struct shl_descriptor*shl_desc;
|
|
int status=shl_get(index,&shl_desc);
|
|
if (status!=0){
|
|
#ifdef GC_HPUX_THREADS
|
|
break;
|
|
#else
|
|
if (errno==EINVAL){
|
|
break;
|
|
} 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("\tfilename\t=\"%s\"\n",shl_desc->filename);
|
|
GC_log_printf("\tindex\t\t=%d\n",index);
|
|
GC_log_printf("\thandle\t\t=%08x\n",
|
|
(unsigned long)shl_desc->handle);
|
|
GC_log_printf("\ttext seg.start\t=%08x\n",shl_desc->tstart);
|
|
GC_log_printf("\ttext seg.end\t=%08x\n",shl_desc->tend);
|
|
GC_log_printf("\tdata seg.start\t=%08x\n",shl_desc->dstart);
|
|
GC_log_printf("\tdata seg.end\t=%08x\n",shl_desc->dend);
|
|
GC_log_printf("\tref.count\t=%lu\n",shl_desc->ref_count);
|
|
#endif
|
|
GC_add_roots_inner((char*)shl_desc->dstart,
|
|
(char*)shl_desc->dend,TRUE);
|
|
index++;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef AIX
|
|
#include <alloca.h>
|
|
#include <sys/ldr.h>
|
|
#include <sys/errno.h>
|
|
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
|
|
#ifdef DARWIN
|
|
#ifndef __private_extern__
|
|
#define __private_extern__ extern
|
|
#include <mach-o/dyld.h>
|
|
#undef __private_extern__
|
|
#else
|
|
#include <mach-o/dyld.h>
|
|
#endif
|
|
#include <mach-o/getsect.h>
|
|
STATIC const struct dyld_sections_s {
|
|
const char*seg;
|
|
const char*sect;
|
|
} GC_dyld_sections[]={
|
|
{ SEG_DATA,SECT_DATA },
|
|
{ SEG_DATA,"__static_data" },
|
|
{ SEG_DATA,SECT_BSS },
|
|
{ SEG_DATA,SECT_COMMON },
|
|
{ SEG_DATA,"__zobj_data" },
|
|
{ SEG_DATA,"__zobj_bss" }
|
|
};
|
|
STATIC const char*const GC_dyld_add_sect_fmts[]={
|
|
"__bss%u",
|
|
"__pu_bss%u",
|
|
"__zo_bss%u",
|
|
"__zo_pu_bss%u"
|
|
};
|
|
#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;
|
|
}
|
|
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();
|
|
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();
|
|
}
|
|
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("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
|
|
}
|
|
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);
|
|
}
|
|
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)
|
|
{
|
|
}
|
|
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
|
|
_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);
|
|
initialized=TRUE;
|
|
#ifdef NO_DYLD_BIND_FULLY_IMAGE
|
|
#else
|
|
if (GC_no_dls)return;
|
|
if (GETENV("DYLD_BIND_AT_LAUNCH")==0){
|
|
#ifdef DARWIN_DEBUG
|
|
GC_log_printf("Forcing full bind of GC code...\n");
|
|
#endif
|
|
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)
|
|
{
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
#if defined(HAIKU)
|
|
#include <kernel/image.h>
|
|
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
|
|
#elif defined(PCR)
|
|
GC_INNER void GC_register_dynamic_libraries(void)
|
|
{
|
|
PCR_IL_LoadedFile*p=PCR_IL_GetLastLoadedFile();
|
|
PCR_IL_LoadedSegment*q;
|
|
while (p!=NIL&&!(p->lf_commitPoint)){
|
|
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
|
|
#if!defined(HAVE_REGISTER_MAIN_STATIC_DATA)&&defined(DYNAMIC_LOADING)
|
|
GC_INNER GC_bool GC_register_main_static_data(void)
|
|
{
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
GC_API void GC_CALL GC_register_has_static_roots_callback(
|
|
GC_has_static_roots_func callback)
|
|
{
|
|
GC_has_static_roots=callback;
|
|
}
|
|
#if defined(GC_PTHREADS)&&!defined(GC_NO_DLOPEN)
|
|
#undef GC_MUST_RESTORE_REDEFINED_DLOPEN
|
|
#if defined(dlopen)&&!defined(GC_USE_LD_WRAP)
|
|
#undef dlopen
|
|
#define GC_MUST_RESTORE_REDEFINED_DLOPEN
|
|
#endif
|
|
#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
|
|
#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_gc_for_dlopen();
|
|
#endif
|
|
result=REAL_DLFUNC(dlopen)(path,mode);
|
|
#ifndef USE_PROC_FOR_LIBRARIES
|
|
GC_enable();
|
|
#endif
|
|
return(result);
|
|
}
|
|
#ifdef GC_USE_LD_WRAP
|
|
GC_API void*GC_dlopen(const char*path,int mode)
|
|
{
|
|
return dlopen(path,mode);
|
|
}
|
|
#endif
|
|
#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN
|
|
#define dlopen GC_dlopen
|
|
#endif
|
|
#endif
|
|
#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)
|
|
#include <stdio.h>
|
|
#ifdef AMIGA
|
|
#ifndef __GNUC__
|
|
#include <dos.h>
|
|
#else
|
|
#include <machine/reg.h>
|
|
#endif
|
|
#endif
|
|
#if defined(MACOS)&&defined(__MWERKS__)
|
|
#if defined(POWERPC)
|
|
#define NONVOLATILE_GPR_COUNT 19
|
|
struct ppc_registers {
|
|
unsigned long gprs[NONVOLATILE_GPR_COUNT];
|
|
};
|
|
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
|
|
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
|
|
asm static void PushMacRegisters(void)
|
|
{
|
|
sub.w #4,sp
|
|
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)
|
|
move.l a6,(sp)
|
|
jsr GC_push_one
|
|
#endif
|
|
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
|
|
rts
|
|
}
|
|
#endif
|
|
#endif
|
|
#if defined(SPARC)||defined(IA64)
|
|
GC_INNER ptr_t GC_save_regs_ret_val=NULL;
|
|
#endif
|
|
#undef HAVE_PUSH_REGS
|
|
#if defined(USE_ASM_PUSH_REGS)
|
|
#define HAVE_PUSH_REGS
|
|
#else
|
|
#ifdef STACK_NOT_SCANNED
|
|
void GC_push_regs(void)
|
|
{
|
|
}
|
|
#define HAVE_PUSH_REGS
|
|
#elif defined(M68K)&&defined(AMIGA)
|
|
void GC_push_regs(void)
|
|
{
|
|
#ifdef __GNUC__
|
|
asm("subq.w&0x4,%sp");
|
|
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");
|
|
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");
|
|
#else
|
|
GC_push_one(getreg(REG_A2));
|
|
GC_push_one(getreg(REG_A3));
|
|
#ifndef __SASC
|
|
GC_push_one(getreg(REG_A4));
|
|
#endif
|
|
GC_push_one(getreg(REG_A5));
|
|
GC_push_one(getreg(REG_A6));
|
|
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
|
|
}
|
|
#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
|
|
#endif
|
|
#endif
|
|
#if defined(HAVE_PUSH_REGS)&&defined(THREADS)
|
|
#error GC_push_regs cannot be used with threads
|
|
#undef HAVE_PUSH_REGS
|
|
#endif
|
|
#if!defined(HAVE_PUSH_REGS)&&defined(UNIX_LIKE)
|
|
#include <signal.h>
|
|
#ifndef NO_GETCONTEXT
|
|
#if defined(DARWIN)&&(MAC_OS_X_VERSION_MAX_ALLOWED>=1060)
|
|
#include <sys/ucontext.h>
|
|
#else
|
|
#include <ucontext.h>
|
|
#endif
|
|
#ifdef GETCONTEXT_FPU_EXCMASK_BUG
|
|
#include <fenv.h>
|
|
#endif
|
|
#endif
|
|
#endif
|
|
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__)
|
|
#else
|
|
#if defined(UNIX_LIKE)&&!defined(NO_GETCONTEXT)
|
|
static signed char getcontext_works=0;
|
|
ucontext_t ctxt;
|
|
#ifdef GETCONTEXT_FPU_EXCMASK_BUG
|
|
#ifdef X86_64
|
|
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);
|
|
} 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;
|
|
__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
|
|
if (feenableexcept(except_mask)< 0)
|
|
ABORT("feenableexcept failed");
|
|
#endif
|
|
#endif
|
|
#if defined(SPARC)||defined(IA64)
|
|
GC_save_regs_ret_val=GC_save_regs_in_stack();
|
|
#endif
|
|
if (NULL==context)
|
|
#endif
|
|
{
|
|
#if defined(HAVE_BUILTIN_UNWIND_INIT)
|
|
__builtin_unwind_init();
|
|
#elif defined(NO_CRT)&&defined(MSWIN32)
|
|
CONTEXT ctx;
|
|
RtlCaptureContext(&ctx);
|
|
#else
|
|
jmp_buf regs;
|
|
word*i=(word*)®s;
|
|
ptr_t lim=(ptr_t)(®s)+sizeof(regs);
|
|
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);
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
fn(arg,( void*)context);
|
|
GC_noop1(COVERT_DATAFLOW(&dummy));
|
|
}
|
|
#endif
|
|
#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS)&&!defined(GC_DARWIN_THREADS)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)
|
|
#ifdef NACL
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
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)
|
|
#include <pthread_np.h>
|
|
#else
|
|
#include <signal.h>
|
|
#include <semaphore.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#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
|
|
#undef pthread_sigmask
|
|
#ifdef GC_ENABLE_SUSPEND_THREAD
|
|
static void*GC_CALLBACK suspend_self_inner(void*client_data);
|
|
#endif
|
|
#ifdef DEBUG_THREADS
|
|
#ifndef NSIG
|
|
#if defined(MAXSIG)
|
|
#define NSIG (MAXSIG+1)
|
|
#elif defined(_NSIG)
|
|
#define NSIG _NSIG
|
|
#elif defined(__SIGRTMAX)
|
|
#define NSIG (__SIGRTMAX+1)
|
|
#else
|
|
#error define NSIG
|
|
#endif
|
|
#endif
|
|
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
|
|
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
|
|
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;
|
|
STATIC volatile AO_t GC_world_is_stopped=FALSE;
|
|
#if defined(GC_OSF1_THREADS)||defined(THREAD_SANITIZER)||defined(ADDRESS_SANITIZER)||defined(MEMORY_SANITIZER)
|
|
STATIC GC_bool GC_retry_signals=TRUE;
|
|
#else
|
|
STATIC GC_bool GC_retry_signals=FALSE;
|
|
#endif
|
|
#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
|
|
#else
|
|
#define SIG_THR_RESTART SIGXCPU
|
|
#endif
|
|
#endif
|
|
#define SIGNAL_UNSET (-1)
|
|
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)
|
|
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
|
|
STATIC sem_t GC_suspend_ack_sem;
|
|
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)
|
|
if (0==sig)return;
|
|
#endif
|
|
ABORT("Bad signal in suspend_handler");
|
|
}
|
|
#if defined(IA64)||defined(HP_PA)||defined(M68K)
|
|
GC_with_callee_saves_pushed(GC_suspend_handler_inner,NULL);
|
|
#else
|
|
{
|
|
#ifdef NO_SA_SIGACTION
|
|
void*context=0;
|
|
#endif
|
|
GC_suspend_handler_inner(NULL,context);
|
|
}
|
|
#endif
|
|
errno=old_errno;
|
|
}
|
|
#ifdef BASE_ATOMIC_OPS_EMULATED
|
|
#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
|
|
#ifdef THREAD_SANITIZER
|
|
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)
|
|
{
|
|
#ifdef SPARC
|
|
ao_store_async((volatile AO_t*)&me->stop_info.stack_ptr,
|
|
(AO_t)GC_save_regs_in_stack());
|
|
#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;
|
|
IF_CANCEL(int cancel_state;)
|
|
AO_t my_stop_count=ao_load_acquire_async(&GC_stop_count);
|
|
DISABLE_CANCEL(cancel_state);
|
|
#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 (ao_load_async(&me->suspended_ext)){
|
|
GC_store_stack_ptr(me);
|
|
sem_post(&GC_suspend_ack_sem);
|
|
suspend_self_inner(me);
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Continuing %p on GC_resume_thread\n",(void*)self);
|
|
#endif
|
|
RESTORE_CANCEL(cancel_state);
|
|
return;
|
|
}
|
|
#endif
|
|
if (((word)me->stop_info.last_stop_count&~(word)THREAD_RESTARTED)
|
|
==(word)my_stop_count){
|
|
if (!GC_retry_signals){
|
|
WARN("Duplicate suspend signal in thread %p\n",self);
|
|
}
|
|
RESTORE_CANCEL(cancel_state);
|
|
return;
|
|
}
|
|
GC_store_stack_ptr(me);
|
|
#ifdef THREAD_SANITIZER
|
|
{
|
|
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 in suspend handler");
|
|
}
|
|
#endif
|
|
sem_post(&GC_suspend_ack_sem);
|
|
ao_store_release_async(&me->stop_info.last_stop_count,my_stop_count);
|
|
do {
|
|
sigsuspend (&suspend_handler_mask);
|
|
} while (ao_load_acquire_async(&GC_world_is_stopped)
|
|
&&ao_load_async(&GC_stop_count)==my_stop_count);
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Continuing %p\n",(void*)self);
|
|
#endif
|
|
#ifndef GC_NETBSD_THREADS_WORKAROUND
|
|
if (GC_retry_signals)
|
|
#endif
|
|
{
|
|
sem_post(&GC_suspend_ack_sem);
|
|
#ifdef GC_NETBSD_THREADS_WORKAROUND
|
|
if (GC_retry_signals)
|
|
#endif
|
|
{
|
|
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)){
|
|
if (errno!=EINTR)
|
|
ABORT("sem_wait failed");
|
|
}
|
|
}
|
|
#ifdef GC_ASSERTIONS
|
|
sem_getvalue(&GC_suspend_ack_sem,&i);
|
|
GC_ASSERT(0==i);
|
|
#endif
|
|
}
|
|
static int resend_lost_signals(int n_live_threads,
|
|
int (*suspend_restart_all)(void))
|
|
{
|
|
#define WAIT_UNIT 3000
|
|
#define RETRY_INTERVAL 100000
|
|
if (n_live_threads > 0){
|
|
unsigned long wait_usecs=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();
|
|
GC_COND_LOG_PRINTF("Resent %d signals after timeout\n",newly_sent);
|
|
sem_getvalue(&GC_suspend_ack_sem,&ack_count);
|
|
if (newly_sent < n_live_threads - ack_count){
|
|
WARN("Lost some threads while stopping or starting world?!\n",0);
|
|
n_live_threads=ack_count+newly_sent;
|
|
}
|
|
wait_usecs=0;
|
|
}
|
|
#ifdef LINT2
|
|
#undef WAIT_UNIT
|
|
#define WAIT_UNIT 1
|
|
sched_yield();
|
|
#elif defined(CPPCHECK)
|
|
{
|
|
struct timespec ts;
|
|
ts.tv_sec=0;
|
|
ts.tv_nsec=WAIT_UNIT*1000;
|
|
(void)nanosleep(&ts,NULL);
|
|
}
|
|
#else
|
|
usleep(WAIT_UNIT);
|
|
#endif
|
|
wait_usecs+=WAIT_UNIT;
|
|
}
|
|
}
|
|
return n_live_threads;
|
|
}
|
|
STATIC void GC_restart_handler(int sig)
|
|
{
|
|
#if defined(DEBUG_THREADS)
|
|
int old_errno=errno;
|
|
#endif
|
|
if (sig!=GC_sig_thr_restart)
|
|
ABORT("Bad signal in restart handler");
|
|
#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);
|
|
EXTERN_C_END
|
|
static int android_thread_kill(pid_t tid,int sig)
|
|
{
|
|
int ret;
|
|
int old_errno=errno;
|
|
ret=tkill(tid,sig);
|
|
if (ret < 0){
|
|
ret=errno;
|
|
errno=old_errno;
|
|
}
|
|
return ret;
|
|
}
|
|
#define THREAD_SYSTEM_ID(t)(t)->kernel_id
|
|
#define RAISE_SIGNAL(t,sig)android_thread_kill(THREAD_SYSTEM_ID(t),sig)
|
|
#else
|
|
#define THREAD_SYSTEM_ID(t)(t)->id
|
|
#define RAISE_SIGNAL(t,sig)pthread_kill(THREAD_SYSTEM_ID(t),sig)
|
|
#endif
|
|
#ifdef GC_ENABLE_SUSPEND_THREAD
|
|
#include <sys/time.h>
|
|
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);
|
|
}
|
|
static void*GC_CALLBACK suspend_self_inner(void*client_data){
|
|
GC_thread me=(GC_thread)client_data;
|
|
while (ao_load_acquire_async(&me->suspended_ext)){
|
|
GC_brief_async_signal_safe_sleep();
|
|
}
|
|
return NULL;
|
|
}
|
|
GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID thread){
|
|
GC_thread t;
|
|
IF_CANCEL(int cancel_state;)
|
|
DCL_LOCK_STATE;
|
|
LOCK();
|
|
t=GC_lookup_thread((pthread_t)thread);
|
|
if (t==NULL||t->suspended_ext){
|
|
UNLOCK();
|
|
return;
|
|
}
|
|
AO_store_release(&t->suspended_ext,TRUE);
|
|
if (THREAD_EQUAL((pthread_t)thread,pthread_self())){
|
|
UNLOCK();
|
|
(void)GC_do_blocking(suspend_self_inner,t);
|
|
return;
|
|
}
|
|
if ((t->flags&FINISHED)!=0){
|
|
UNLOCK();
|
|
return;
|
|
}
|
|
DISABLE_CANCEL(cancel_state);
|
|
#ifdef PARALLEL_MARK
|
|
if (GC_parallel)
|
|
GC_wait_for_reclaim();
|
|
#endif
|
|
if (GC_manual_vdb){
|
|
GC_acquire_dirty_lock();
|
|
}
|
|
switch (RAISE_SIGNAL(t,GC_sig_suspend)){
|
|
case 0:
|
|
break;
|
|
default:
|
|
ABORT("pthread_kill failed");
|
|
}
|
|
GC_ASSERT(GC_thr_initialized);
|
|
while (sem_wait(&GC_suspend_ack_sem)!=0){
|
|
if (errno!=EINTR)
|
|
ABORT("sem_wait for handler failed (suspend_self)");
|
|
}
|
|
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)
|
|
AO_store(&t->suspended_ext,FALSE);
|
|
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->suspended_ext)
|
|
is_suspended=(int)TRUE;
|
|
UNLOCK();
|
|
return is_suspended;
|
|
}
|
|
#endif
|
|
#undef ao_load_acquire_async
|
|
#undef ao_load_async
|
|
#undef ao_store_async
|
|
#undef ao_store_release_async
|
|
#endif
|
|
#ifdef IA64
|
|
#define IF_IA64(x)x
|
|
#else
|
|
#define IF_IA64(x)
|
|
#endif
|
|
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,hi;
|
|
IF_IA64(ptr_t bs_lo;ptr_t bs_hi;)
|
|
struct GC_traced_stack_sect_s*traced_stack_sect;
|
|
pthread_t self=pthread_self();
|
|
word total_size=0;
|
|
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=(ptr_t)GC_save_regs_in_stack();
|
|
#else
|
|
lo=GC_approx_sp();
|
|
#endif
|
|
found_me=TRUE;
|
|
IF_IA64(bs_hi=(ptr_t)GC_save_regs_in_stack();)
|
|
} else {
|
|
lo=(ptr_t)AO_load((volatile AO_t*)&p->stop_info.stack_ptr);
|
|
IF_IA64(bs_hi=p->backing_store_ptr;)
|
|
if (traced_stack_sect!=NULL
|
|
&&traced_stack_sect->saved_stack_ptr==lo){
|
|
traced_stack_sect=traced_stack_sect->prev;
|
|
}
|
|
}
|
|
if ((p->flags&MAIN_THREAD)==0){
|
|
hi=p->stack_end;
|
|
IF_IA64(bs_lo=p->backing_store_end);
|
|
} else {
|
|
hi=GC_stackbottom;
|
|
IF_IA64(bs_lo=BACKING_STORE_BASE;)
|
|
}
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Stack for thread %p=[%p,%p)\n",
|
|
(void*)p->id,(void*)lo,(void*)hi);
|
|
#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){
|
|
hi=p->altstack+p->altstack_size;
|
|
}
|
|
GC_push_all_stack_sections(lo,hi,traced_stack_sect);
|
|
#ifdef STACK_GROWS_UP
|
|
total_size+=lo - hi;
|
|
#else
|
|
total_size+=hi - lo;
|
|
#endif
|
|
#ifdef NACL
|
|
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 IA64
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Reg stack for thread %p=[%p,%p)\n",
|
|
(void*)p->id,(void*)bs_lo,(void*)bs_hi);
|
|
#endif
|
|
GC_push_all_register_sections(bs_lo,bs_hi,
|
|
THREAD_EQUAL(p->id,self),
|
|
traced_stack_sect);
|
|
total_size+=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
|
|
pthread_t GC_stopping_thread;
|
|
int GC_stopping_pid=0;
|
|
#endif
|
|
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)continue;
|
|
#ifndef GC_OPENBSD_UTHREADS
|
|
#ifdef GC_ENABLE_SUSPEND_THREAD
|
|
if (p->suspended_ext)continue;
|
|
#endif
|
|
if (AO_load(&p->stop_info.last_stop_count)==GC_stop_count)
|
|
continue;
|
|
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
|
|
result=RAISE_SIGNAL(p,GC_sig_suspend);
|
|
switch(result){
|
|
case ESRCH:
|
|
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));
|
|
break;
|
|
default:
|
|
ABORT_ARG1("pthread_kill failed at suspend",
|
|
":errcode=%d",result);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
#ifndef NACL_PARK_WAIT_NANOSECONDS
|
|
#define NACL_PARK_WAIT_NANOSECONDS (100*1000)
|
|
#endif
|
|
#define NANOS_PER_SECOND (1000UL*1000*1000)
|
|
unsigned long num_sleeps=0;
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("pthread_stop_world:num_threads=%d\n",
|
|
GC_nacl_num_gc_threads - 1);
|
|
#endif
|
|
GC_nacl_thread_parker=pthread_self();
|
|
GC_nacl_park_threads_now=1;
|
|
if (GC_manual_vdb)
|
|
GC_acquire_dirty_lock();
|
|
while (1){
|
|
int num_threads_parked=0;
|
|
struct timespec ts;
|
|
int num_used=0;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
if (num_threads_parked>=GC_nacl_num_gc_threads - 1)
|
|
break;
|
|
ts.tv_sec=0;
|
|
ts.tv_nsec=NACL_PARK_WAIT_NANOSECONDS;
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Sleep waiting for %d threads to park...\n",
|
|
GC_nacl_num_gc_threads - num_threads_parked - 1);
|
|
#endif
|
|
nanosleep(&ts,0);
|
|
if (++num_sleeps > NANOS_PER_SECOND/NACL_PARK_WAIT_NANOSECONDS){
|
|
WARN("GC appears stalled waiting for %" WARN_PRIdPTR
|
|
" threads to park...\n",
|
|
GC_nacl_num_gc_threads - num_threads_parked - 1);
|
|
num_sleeps=0;
|
|
}
|
|
}
|
|
if (GC_manual_vdb)
|
|
GC_release_dirty_lock();
|
|
#endif
|
|
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
|
|
#ifdef PARALLEL_MARK
|
|
if (GC_parallel){
|
|
GC_acquire_mark_lock();
|
|
GC_ASSERT(GC_fl_builder_count==0);
|
|
}
|
|
#endif
|
|
#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)));
|
|
if (GC_manual_vdb){
|
|
GC_acquire_dirty_lock();
|
|
}
|
|
AO_store_release(&GC_world_is_stopped,TRUE);
|
|
n_live_threads=GC_suspend_all();
|
|
if (GC_retry_signals)
|
|
n_live_threads=resend_lost_signals(n_live_threads,GC_suspend_all);
|
|
suspend_restart_barrier(n_live_threads);
|
|
if (GC_manual_vdb)
|
|
GC_release_dirty_lock();
|
|
#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();
|
|
if (GC_nacl_thread_parker==self)
|
|
return;
|
|
if (GC_nacl_thread_idx < 0)
|
|
return;
|
|
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){
|
|
}
|
|
GC_nacl_thread_parked[GC_nacl_thread_idx]=0;
|
|
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)
|
|
{
|
|
__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));
|
|
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
|
|
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->suspended_ext)continue;
|
|
#endif
|
|
if (GC_retry_signals
|
|
&&AO_load(&p->stop_info.last_stop_count)
|
|
==(AO_t)((word)GC_stop_count|THREAD_RESTARTED))
|
|
continue;
|
|
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:
|
|
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
|
|
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);
|
|
#endif
|
|
n_live_threads=GC_restart_all();
|
|
#ifdef GC_OPENBSD_UTHREADS
|
|
(void)n_live_threads;
|
|
#elif defined(GC_NETBSD_THREADS_WORKAROUND)
|
|
if (GC_retry_signals)
|
|
n_live_threads=resend_lost_signals(n_live_threads,GC_restart_all);
|
|
suspend_restart_barrier(n_live_threads);
|
|
#else
|
|
if (GC_retry_signals){
|
|
n_live_threads=resend_lost_signals(n_live_threads,GC_restart_all);
|
|
suspend_restart_barrier(n_live_threads);
|
|
}
|
|
#endif
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("World started\n");
|
|
#endif
|
|
#else
|
|
#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);
|
|
#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);
|
|
#ifndef NO_SA_SIGACTION
|
|
act.sa_sigaction=GC_suspend_handler;
|
|
#else
|
|
act.sa_handler=GC_suspend_handler;
|
|
#endif
|
|
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");
|
|
}
|
|
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");
|
|
str=GETENV("GC_RETRY_SIGNALS");
|
|
if (str!=NULL){
|
|
if (*str=='0'&&*(str+1)=='\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
|
|
GC_unblock_gc_signals();
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS)
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)
|
|
#if!defined(GC_RTEMS_PTHREADS)
|
|
#include <sys/mman.h>
|
|
#endif
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#endif
|
|
#include <signal.h>
|
|
#if defined(GC_DARWIN_THREADS)
|
|
#ifndef GC_DARWIN_SEMAPHORE_H
|
|
#define GC_DARWIN_SEMAPHORE_H
|
|
#if!defined(GC_DARWIN_THREADS)
|
|
#error darwin_semaphore.h included with GC_DARWIN_THREADS not defined
|
|
#endif
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
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;
|
|
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
|
|
}
|
|
#endif
|
|
#endif
|
|
#else
|
|
#include <semaphore.h>
|
|
#endif
|
|
#if defined(GC_DARWIN_THREADS)||defined(GC_FREEBSD_THREADS)
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
#if defined(GC_NETBSD_THREADS)||defined(GC_OPENBSD_THREADS)
|
|
#include <sys/param.h>
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
#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;
|
|
#endif
|
|
#if defined(GC_DGUX386_THREADS)
|
|
#include <sys/dg_sys_info.h>
|
|
#include <sys/_int_psem.h>
|
|
typedef unsigned int sem_t;
|
|
#endif
|
|
#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_)
|
|
#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 <dlfcn.h>
|
|
#define WRAP_FUNC(f)f
|
|
#define REAL_FUNC(f)GC_real_##f
|
|
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
|
|
#define REAL_FUNC(f)__d10_##f
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#if defined(GC_USE_LD_WRAP)||defined(GC_USE_DLOPEN_WRAP)
|
|
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_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
|
|
#ifdef GC_HAVE_PTHREAD_EXIT
|
|
GC_API GC_PTHREAD_EXIT_ATTRIBUTE void GC_pthread_exit(void*retval)
|
|
{
|
|
pthread_exit(retval);
|
|
}
|
|
#endif
|
|
#endif
|
|
#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);
|
|
}
|
|
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()
|
|
#define ASSERT_SYMS_INITIALIZED()GC_ASSERT(GC_syms_initialized)
|
|
#else
|
|
#define INIT_REAL_SYMS()(void)0
|
|
#define ASSERT_SYMS_INITIALIZED()GC_ASSERT(parallel_initialized)
|
|
#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;
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
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)
|
|
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
|
|
#endif
|
|
#ifdef PARALLEL_MARK
|
|
#ifndef MAX_MARKERS
|
|
#define MAX_MARKERS 16
|
|
#endif
|
|
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};
|
|
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
|
|
#ifdef HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG
|
|
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",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];
|
|
int len=sizeof("GC-marker-")- 1;
|
|
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
|
|
(void)pthread_setname_np(name_buf);
|
|
#else
|
|
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;
|
|
DISABLE_CANCEL(cancel_state);
|
|
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
|
|
GC_acquire_mark_lock();
|
|
if (0==--GC_fl_builder_count)
|
|
GC_notify_all_builder();
|
|
for (;;++my_mark_no){
|
|
if (my_mark_no < GC_mark_no||my_mark_no > GC_mark_no+2){
|
|
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 CAN_HANDLE_FORK
|
|
static int available_markers_m1=0;
|
|
static pthread_cond_t mark_cv;
|
|
#else
|
|
#define available_markers_m1 GC_markers_m1
|
|
static pthread_cond_t mark_cv=PTHREAD_COND_INITIALIZER;
|
|
#endif
|
|
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_DONT_HOLD_LOCK());
|
|
if (available_markers_m1<=0)return;
|
|
#ifdef CAN_HANDLE_FORK
|
|
if (GC_parallel)return;
|
|
{
|
|
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();
|
|
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
|
|
{
|
|
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){
|
|
if (pthread_attr_setstacksize(&attr,MIN_STACK_SIZE)!=0)
|
|
ABORT("pthread_attr_setstacksize failed");
|
|
}
|
|
}
|
|
#endif
|
|
#ifndef NO_MARKER_SPECIAL_SIGMASK
|
|
if (sigfillset(&set)!=0)
|
|
ABORT("sigfillset failed");
|
|
#if!defined(GC_DARWIN_THREADS)&&!defined(GC_OPENBSD_UTHREADS)&&!defined(NACL)
|
|
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,"
|
|
" errno=%" WARN_PRIdPTR "\n",errno);
|
|
GC_markers_m1=0;
|
|
(void)pthread_attr_destroy(&attr);
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef CAN_HANDLE_FORK
|
|
GC_markers_m1=available_markers_m1;
|
|
#endif
|
|
for (i=0;i < available_markers_m1;++i){
|
|
if (0!=REAL_FUNC(pthread_create)(GC_mark_threads+i,&attr,
|
|
GC_mark_thread,(void*)(word)i)){
|
|
WARN("Marker thread creation failed,errno=%" WARN_PRIdPTR "\n",
|
|
errno);
|
|
GC_markers_m1=i;
|
|
break;
|
|
}
|
|
}
|
|
#ifndef NO_MARKER_SPECIAL_SIGMASK
|
|
if (REAL_FUNC(pthread_sigmask)(SIG_SETMASK,&oldset,NULL)< 0){
|
|
WARN("pthread_sigmask restore failed,errno=%" WARN_PRIdPTR "\n",
|
|
errno);
|
|
}
|
|
#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
|
|
GC_INNER GC_bool GC_thr_initialized=FALSE;
|
|
GC_INNER volatile GC_thread GC_threads[THREAD_TABLE_SZ]={0};
|
|
void GC_push_thread_structures(void)
|
|
{
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
GC_PUSH_ALL_SYM(GC_threads);
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
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
|
|
static struct GC_Thread_Rep first_thread;
|
|
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(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);
|
|
}
|
|
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
|
|
#ifdef NACL
|
|
GC_nacl_shutdown_gc_thread();
|
|
GC_nacl_gc_thread_self=NULL;
|
|
#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);
|
|
}
|
|
}
|
|
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
|
|
}
|
|
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);
|
|
}
|
|
GC_INNER void GC_reset_finalizer_nested(void)
|
|
{
|
|
GC_thread me=GC_lookup_thread(pthread_self());
|
|
me->finalizer_nested=0;
|
|
}
|
|
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){
|
|
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;
|
|
}
|
|
#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC)
|
|
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_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;
|
|
}
|
|
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 {
|
|
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
|
|
#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;
|
|
}
|
|
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){
|
|
me=p;
|
|
p->next=0;
|
|
#ifdef GC_DARWIN_THREADS
|
|
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;
|
|
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)){
|
|
GC_remove_specific_after_fork(GC_thread_key,p->id);
|
|
}
|
|
#endif
|
|
#if!defined(THREAD_SANITIZER)||!defined(CAN_CALL_ATFORK)
|
|
if (p!=&first_thread)GC_INTERNAL_FREE(p);
|
|
#endif
|
|
}
|
|
}
|
|
store_to_threads_table(hv,me);
|
|
}
|
|
}
|
|
#endif
|
|
#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
|
|
if ((word)p->stack_end > (word)lo
|
|
&&(word)p->stack_end<=(word)hi)
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
#ifdef IA64
|
|
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
|
|
#ifndef STAT_READ
|
|
#define STAT_BUF_SIZE 4096
|
|
#define STAT_READ read
|
|
#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;
|
|
}
|
|
#elif defined(GC_IRIX_THREADS)
|
|
GC_INLINE int GC_get_nprocs(void)
|
|
{
|
|
int nprocs=(int)sysconf(_SC_NPROC_ONLN);
|
|
return nprocs > 0?nprocs:1;
|
|
}
|
|
#elif defined(GC_LINUX_THREADS)
|
|
STATIC int GC_get_nprocs(void)
|
|
{
|
|
char stat_buf[STAT_BUF_SIZE];
|
|
int f;
|
|
int result,i,len;
|
|
f=open("/proc/stat",O_RDONLY);
|
|
if (f < 0){
|
|
WARN("Couldn't read/proc/stat\n",0);
|
|
return 1;
|
|
}
|
|
len=STAT_READ(f,stat_buf,STAT_BUF_SIZE);
|
|
close(f);
|
|
result=1;
|
|
for (i=0;i < len - 100;++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)
|
|
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)
|
|
numCpus=-1;
|
|
else
|
|
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
|
|
#define GC_get_nprocs()1
|
|
#endif
|
|
#if defined(ARM32)&&defined(GC_LINUX_THREADS)&&!defined(NACL)
|
|
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;
|
|
len=STAT_READ(f,stat_buf,sizeof(stat_buf));
|
|
close(f);
|
|
if (len < 2||stat_buf[0]!='0'||stat_buf[len - 1]!='\n'){
|
|
return 0;
|
|
} else if (len==2){
|
|
return 1;
|
|
} else if (stat_buf[1]!='-'){
|
|
return 0;
|
|
}
|
|
stat_buf[len - 1]='\0';
|
|
return atoi(&stat_buf[2])+1;
|
|
}
|
|
#endif
|
|
STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all)
|
|
{
|
|
DCL_LOCK_STATE;
|
|
#if!defined(THREAD_SANITIZER)||!defined(CAN_CALL_ATFORK)
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
#endif
|
|
ASSERT_CANCEL_DISABLED();
|
|
if (GC_incremental&&GC_collection_in_progress()){
|
|
word old_gc_no=GC_gc_no;
|
|
while (GC_incremental&&GC_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
|
|
IF_CANCEL(static int fork_cancel_state;)
|
|
#if defined(GC_ASSERTIONS)&&defined(CAN_CALL_ATFORK)
|
|
GC_ATTR_NO_SANITIZE_THREAD
|
|
#endif
|
|
static void fork_prepare_proc(void)
|
|
{
|
|
LOCK();
|
|
DISABLE_CANCEL(fork_cancel_state);
|
|
#if defined(PARALLEL_MARK)
|
|
if (GC_parallel)
|
|
GC_wait_for_reclaim();
|
|
#endif
|
|
GC_wait_for_gc_completion(TRUE);
|
|
#if defined(PARALLEL_MARK)
|
|
if (GC_parallel)
|
|
GC_acquire_mark_lock();
|
|
#endif
|
|
GC_acquire_dirty_lock();
|
|
}
|
|
#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)
|
|
GC_release_mark_lock();
|
|
#endif
|
|
RESTORE_CANCEL(fork_cancel_state);
|
|
UNLOCK();
|
|
}
|
|
#if defined(GC_ASSERTIONS)&&defined(CAN_CALL_ATFORK)
|
|
GC_ATTR_NO_SANITIZE_THREAD
|
|
#endif
|
|
static void fork_child_proc(void)
|
|
{
|
|
GC_release_dirty_lock();
|
|
#if defined(PARALLEL_MARK)
|
|
if (GC_parallel)
|
|
GC_release_mark_lock();
|
|
#endif
|
|
GC_remove_all_threads_but_me();
|
|
#ifdef PARALLEL_MARK
|
|
GC_parallel=FALSE;
|
|
#endif
|
|
RESTORE_CANCEL(fork_cancel_state);
|
|
UNLOCK();
|
|
#ifdef USE_PTHREAD_LOCKS
|
|
GC_ASSERT(I_DONT_HOLD_LOCK());
|
|
(void)pthread_mutex_destroy(&GC_allocate_ml);
|
|
if (0!=pthread_mutex_init(&GC_allocate_ml,NULL))
|
|
ABORT("pthread_mutex_init failed (in child)");
|
|
#endif
|
|
}
|
|
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
|
|
#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;
|
|
#endif
|
|
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
|
|
}
|
|
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
|
|
if (GC_handle_fork){
|
|
#ifdef CAN_CALL_ATFORK
|
|
if (pthread_atfork(fork_prepare_proc,fork_parent_proc,
|
|
fork_child_proc)==0){
|
|
GC_handle_fork=1;
|
|
} else
|
|
#endif
|
|
if (GC_handle_fork!=-1)
|
|
ABORT("pthread_atfork failed");
|
|
}
|
|
#endif
|
|
#ifdef INCLUDE_LINUX_THREAD_DESCR
|
|
{
|
|
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 {
|
|
GC_add_roots_inner(main_thread_start,main_thread_end,FALSE);
|
|
}
|
|
}
|
|
#endif
|
|
{
|
|
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
|
|
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;
|
|
}
|
|
}
|
|
#ifndef GC_DARWIN_THREADS
|
|
GC_stop_init();
|
|
#endif
|
|
{
|
|
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
|
|
#endif
|
|
)
|
|
{
|
|
GC_nprocs=GC_get_nprocs();
|
|
}
|
|
if (GC_nprocs<=0){
|
|
WARN("GC_get_nprocs()returned %" WARN_PRIdPTR "\n",GC_nprocs);
|
|
GC_nprocs=2;
|
|
#ifdef PARALLEL_MARK
|
|
available_markers_m1=0;
|
|
#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){
|
|
markers=GC_nprocs;
|
|
#if defined(GC_MIN_MARKERS)&&!defined(CPPCHECK)
|
|
if (markers < GC_MIN_MARKERS)
|
|
markers=GC_MIN_MARKERS;
|
|
#endif
|
|
if (markers > MAX_MARKERS)
|
|
markers=MAX_MARKERS;
|
|
}
|
|
available_markers_m1=markers - 1;
|
|
}
|
|
#endif
|
|
}
|
|
GC_COND_LOG_PRINTF("Number of processors=%d\n",GC_nprocs);
|
|
#ifdef PARALLEL_MARK
|
|
if (available_markers_m1<=0){
|
|
GC_parallel=FALSE;
|
|
GC_COND_LOG_PRINTF(
|
|
"Single marker thread,turning off parallel marking\n");
|
|
} else {
|
|
setup_mark_lock();
|
|
}
|
|
#endif
|
|
}
|
|
GC_INNER void GC_init_parallel(void)
|
|
{
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
DCL_LOCK_STATE;
|
|
#endif
|
|
if (parallel_initialized)return;
|
|
parallel_initialized=TRUE;
|
|
if (!GC_is_initialized)GC_init();
|
|
#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_INNER void GC_do_blocking_inner(ptr_t data,void*context GC_ATTR_UNUSED)
|
|
{
|
|
struct blocking_data*d=(struct blocking_data*)data;
|
|
pthread_t self=pthread_self();
|
|
GC_thread me;
|
|
#if defined(SPARC)||defined(IA64)
|
|
ptr_t stack_ptr=GC_save_regs_in_stack();
|
|
#endif
|
|
#if defined(GC_DARWIN_THREADS)&&!defined(DARWIN_DONT_PARSE_STACK)
|
|
GC_bool topOfStackUnset=FALSE;
|
|
#endif
|
|
DCL_LOCK_STATE;
|
|
LOCK();
|
|
me=GC_lookup_thread(self);
|
|
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){
|
|
topOfStackUnset=TRUE;
|
|
me->topOfStack=GC_FindTopOfStack(0);
|
|
}
|
|
#endif
|
|
#ifdef IA64
|
|
me->backing_store_ptr=stack_ptr;
|
|
#endif
|
|
me->thread_blocked=(unsigned char)TRUE;
|
|
UNLOCK();
|
|
d->client_data=(d->fn)(d->client_data);
|
|
LOCK();
|
|
#if defined(CPPCHECK)
|
|
GC_noop1((word)&me->thread_blocked);
|
|
#endif
|
|
me->thread_blocked=FALSE;
|
|
#if defined(GC_DARWIN_THREADS)&&!defined(DARWIN_DONT_PARSE_STACK)
|
|
if (topOfStackUnset)
|
|
me->topOfStack=NULL;
|
|
#endif
|
|
UNLOCK();
|
|
}
|
|
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)
|
|
t=GC_lookup_thread(pthread_self());
|
|
GC_ASSERT((t->flags&FINISHED)==0);
|
|
GC_ASSERT(!(t->thread_blocked)
|
|
&&NULL==t->traced_stack_sect);
|
|
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;
|
|
}
|
|
}
|
|
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);
|
|
if ((me->flags&MAIN_THREAD)==0){
|
|
sb->mem_base=me->stack_end;
|
|
#ifdef IA64
|
|
sb->reg_base=me->backing_store_end;
|
|
#endif
|
|
} else {
|
|
sb->mem_base=GC_stackbottom;
|
|
#ifdef IA64
|
|
sb->reg_base=GC_register_stackbottom;
|
|
#endif
|
|
}
|
|
UNLOCK();
|
|
return (void*)me;
|
|
}
|
|
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;
|
|
DCL_LOCK_STATE;
|
|
LOCK();
|
|
me=GC_lookup_thread(self);
|
|
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 {
|
|
if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect))
|
|
GC_stackbottom=(ptr_t)COVERT_DATAFLOW(&stacksect);
|
|
}
|
|
if (!me->thread_blocked){
|
|
UNLOCK();
|
|
client_data=fn(client_data);
|
|
GC_noop1(COVERT_DATAFLOW(&stacksect));
|
|
return client_data;
|
|
}
|
|
stacksect.saved_stack_ptr=me->stop_info.stack_ptr;
|
|
#ifdef IA64
|
|
stacksect.backing_store_end=GC_save_regs_in_stack();
|
|
stacksect.saved_backing_store_ptr=me->backing_store_ptr;
|
|
#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);
|
|
#if defined(CPPCHECK)
|
|
GC_noop1((word)me->traced_stack_sect);
|
|
#endif
|
|
LOCK();
|
|
me->traced_stack_sect=stacksect.prev;
|
|
#ifdef IA64
|
|
me->backing_store_ptr=stacksect.saved_backing_store_ptr;
|
|
#endif
|
|
me->thread_blocked=(unsigned char)TRUE;
|
|
me->stop_info.stack_ptr=stacksect.saved_stack_ptr;
|
|
UNLOCK();
|
|
return client_data;
|
|
}
|
|
STATIC void GC_unregister_my_thread_inner(GC_thread me)
|
|
{
|
|
#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
|
|
#if defined(GC_HAVE_PTHREAD_EXIT)||!defined(GC_NO_PTHREAD_CANCEL)
|
|
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)
|
|
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);
|
|
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;
|
|
}
|
|
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;
|
|
ASSERT_SYMS_INITIALIZED();
|
|
LOCK();
|
|
t=GC_lookup_thread(thread);
|
|
UNLOCK();
|
|
result=REAL_FUNC(pthread_join)(thread,retval);
|
|
#if defined(GC_FREEBSD_THREADS)
|
|
if (result==EINTR)result=0;
|
|
#endif
|
|
if (result==0){
|
|
LOCK();
|
|
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;
|
|
ASSERT_SYMS_INITIALIZED();
|
|
LOCK();
|
|
t=GC_lookup_thread(thread);
|
|
UNLOCK();
|
|
result=REAL_FUNC(pthread_detach)(thread);
|
|
if (result==0){
|
|
LOCK();
|
|
t->flags|=DETACHED;
|
|
if ((t->flags&FINISHED)!=0){
|
|
GC_delete_gc_thread(t);
|
|
}
|
|
UNLOCK();
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
#ifndef GC_NO_PTHREAD_CANCEL
|
|
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);
|
|
if (t!=NULL&&(t->flags&DISABLED_GC)==0){
|
|
t->flags|=DISABLED_GC;
|
|
GC_dont_gc++;
|
|
}
|
|
UNLOCK();
|
|
#endif
|
|
return REAL_FUNC(pthread_cancel)(thread);
|
|
}
|
|
#endif
|
|
#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);
|
|
if (me!=0&&(me->flags&DISABLED_GC)==0){
|
|
me->flags|=DISABLED_GC;
|
|
GC_dont_gc++;
|
|
}
|
|
UNLOCK();
|
|
REAL_FUNC(pthread_exit)(retval);
|
|
}
|
|
#endif
|
|
GC_INNER GC_bool GC_in_thread_creation=FALSE;
|
|
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;
|
|
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
|
|
GC_unblock_gc_signals();
|
|
#endif
|
|
return me;
|
|
}
|
|
GC_API void GC_CALL GC_allow_register_threads(void)
|
|
{
|
|
GC_ASSERT(GC_lookup_thread(pthread_self())!=0);
|
|
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;
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
GC_init_thread_local(&(me->tlfs));
|
|
#endif
|
|
UNLOCK();
|
|
return GC_SUCCESS;
|
|
} else if ((me->flags&FINISHED)!=0){
|
|
#ifdef GC_DARWIN_THREADS
|
|
me->stop_info.mach_thread=mach_thread_self();
|
|
#endif
|
|
GC_record_stack_base(me,sb);
|
|
me->flags&=~FINISHED;
|
|
#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK
|
|
GC_unblock_gc_signals();
|
|
#endif
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
GC_init_thread_local(&(me->tlfs));
|
|
#endif
|
|
UNLOCK();
|
|
return GC_SUCCESS;
|
|
} else {
|
|
UNLOCK();
|
|
return GC_DUPLICATE;
|
|
}
|
|
}
|
|
struct start_info {
|
|
void*(*start_routine)(void*);
|
|
void*arg;
|
|
word flags;
|
|
sem_t registered;
|
|
};
|
|
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));
|
|
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_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;
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
#endif
|
|
if (NULL==attr){
|
|
detachstate=PTHREAD_CREATE_JOINABLE;
|
|
} else {
|
|
pthread_attr_getdetachstate(attr,&detachstate);
|
|
}
|
|
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
|
|
set_need_to_lock();
|
|
result=REAL_FUNC(pthread_create)(new_thread,attr,GC_start_routine,
|
|
&si);
|
|
if (0==result){
|
|
IF_CANCEL(int cancel_state;)
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Started thread %p\n",(void*)(*new_thread));
|
|
#endif
|
|
DISABLE_CANCEL(cancel_state);
|
|
while (0!=sem_wait(&si.registered)){
|
|
#if defined(GC_HAIKU_THREADS)
|
|
if (EACCES==errno)continue;
|
|
#endif
|
|
if (EINTR!=errno)ABORT("sem_wait failed");
|
|
}
|
|
RESTORE_CANCEL(cancel_state);
|
|
}
|
|
sem_destroy(&si.registered);
|
|
return(result);
|
|
}
|
|
#endif
|
|
#if defined(USE_SPIN_LOCK)||!defined(NO_PTHREAD_TRYLOCK)
|
|
#define GC_PAUSE_SPIN_CYCLES 10
|
|
STATIC void GC_pause(void)
|
|
{
|
|
int i;
|
|
for (i=0;i < GC_PAUSE_SPIN_CYCLES;++i){
|
|
#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
|
|
#endif
|
|
GC_INNER volatile GC_bool GC_collecting=FALSE;
|
|
#if (!defined(USE_SPIN_LOCK)&&!defined(NO_PTHREAD_TRYLOCK))||defined(PARALLEL_MARK)
|
|
#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
|
|
#ifdef LOCK_STATS
|
|
(void)AO_fetch_and_add1(&GC_block_count);
|
|
#endif
|
|
pthread_mutex_lock(lock);
|
|
}
|
|
#endif
|
|
#if defined(AO_HAVE_char_load)&&!defined(BASE_ATOMIC_OPS_EMULATED)
|
|
#define is_collecting()((GC_bool)AO_char_load((unsigned char*)&GC_collecting))
|
|
#else
|
|
#define is_collecting()GC_collecting
|
|
#endif
|
|
#if defined(USE_SPIN_LOCK)
|
|
GC_INNER volatile AO_TS_t GC_allocate_lock=AO_TS_INITIALIZER;
|
|
#define low_spin_max 30
|
|
#define high_spin_max SPIN_MAX
|
|
static volatile AO_t spin_max=low_spin_max;
|
|
static volatile AO_t last_spins=0;
|
|
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){
|
|
AO_store(&last_spins,(AO_t)i);
|
|
AO_store(&spin_max,(AO_t)high_spin_max);
|
|
return;
|
|
}
|
|
}
|
|
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
|
|
if (i < SLEEP_THRESHOLD){
|
|
sched_yield();
|
|
} else {
|
|
struct timespec ts;
|
|
if (i > 24)i=24;
|
|
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
|
|
#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
|
|
#ifdef GLIBC_2_1_MUTEX_HACK
|
|
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 pthread_cond_t builder_cv=PTHREAD_COND_INITIALIZER;
|
|
#ifdef GLIBC_2_19_TSX_BUG
|
|
static int 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){
|
|
return -1;
|
|
}
|
|
if (*endp!='.'){
|
|
*pminor=-1;
|
|
} else {
|
|
value=strtoul(endp+1,&endp,10);
|
|
*pminor=(int)value;
|
|
if (*pminor < 0||(unsigned)(*pminor)!=value){
|
|
return -1;
|
|
}
|
|
}
|
|
return major;
|
|
}
|
|
#endif
|
|
static void setup_mark_lock(void)
|
|
{
|
|
#ifdef GLIBC_2_19_TSX_BUG
|
|
pthread_mutexattr_t mattr;
|
|
int glibc_minor=-1;
|
|
int glibc_major=parse_version(&glibc_minor,gnu_get_libc_version());
|
|
if (glibc_major > 2||(glibc_major==2&&glibc_minor>=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");
|
|
}
|
|
}
|
|
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();
|
|
}
|
|
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
|
|
#ifdef PTHREAD_REGISTER_CANCEL_WEAK_STUBS
|
|
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
|
|
#if defined(USE_CUSTOM_SPECIFIC)
|
|
static const tse invalid_tse={INVALID_QTID,0,0,INVALID_THREADID};
|
|
GC_INNER int GC_key_create_inner(tsd**key_ptr)
|
|
{
|
|
int i;
|
|
int ret;
|
|
tsd*result;
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
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]=( 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++;
|
|
entry=(volatile tse*)MALLOC_CLEAR(sizeof(tse));
|
|
GC_dont_gc--;
|
|
if (0==entry)return ENOMEM;
|
|
pthread_mutex_lock(&(key->lock));
|
|
entry->next=key->hash[hash_val].p;
|
|
entry->thread=self;
|
|
entry->value=TS_HIDE_VALUE(value);
|
|
GC_ASSERT(entry->qtid==INVALID_QTID);
|
|
AO_store_release(&key->hash[hash_val].ao,(AO_t)entry);
|
|
GC_dirty(( void*)entry);
|
|
GC_dirty(key->hash+hash_val);
|
|
if (pthread_mutex_unlock(&key->lock)!=0)
|
|
ABORT("pthread_mutex_unlock failed (setspecific)");
|
|
return 0;
|
|
}
|
|
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
|
|
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;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
#ifdef LINT2
|
|
GC_noop1((word)entry);
|
|
#endif
|
|
if (pthread_mutex_unlock(&key->lock)!=0)
|
|
ABORT("pthread_mutex_unlock failed (remove_specific after fork)");
|
|
}
|
|
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;
|
|
entry->qtid=(AO_t)qtid;
|
|
*cache_ptr=entry;
|
|
return TS_REVEAL_PTR(entry->value);
|
|
}
|
|
#ifdef GC_ASSERTIONS
|
|
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
|
|
#endif
|
|
#if defined(GC_WIN32_THREADS)
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
#endif
|
|
#if!defined(USE_PTHREAD_LOCKS)
|
|
GC_INNER CRITICAL_SECTION GC_allocate_ml;
|
|
#ifdef GC_ASSERTIONS
|
|
GC_INNER DWORD GC_lock_holder=NO_THREAD;
|
|
#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 <errno.h>
|
|
#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 <pthread.h>
|
|
#ifdef CAN_CALL_ATFORK
|
|
#include <unistd.h>
|
|
#endif
|
|
#elif!defined(MSWINCE)
|
|
#include <process.h>
|
|
#include <errno.h>
|
|
#endif
|
|
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
|
|
#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)
|
|
#ifdef GC_DISCOVER_TASK_THREADS
|
|
#define GC_win32_dll_threads TRUE
|
|
#else
|
|
STATIC GC_bool GC_win32_dll_threads=FALSE;
|
|
#endif
|
|
#else
|
|
#ifndef GC_NO_THREADS_DISCOVERY
|
|
#define GC_NO_THREADS_DISCOVERY
|
|
#endif
|
|
#define GC_win32_dll_threads FALSE
|
|
#undef MAX_THREADS
|
|
#define MAX_THREADS 1
|
|
#endif
|
|
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_API void GC_CALL GC_use_threads_discovery(void)
|
|
{
|
|
#ifdef GC_NO_THREADS_DISCOVERY
|
|
ABORT("GC DllMain-based thread registration unsupported");
|
|
#else
|
|
GC_ASSERT(!parallel_initialized);
|
|
#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;
|
|
LONG long_in_use;
|
|
#endif
|
|
struct GC_Thread_Rep*next;
|
|
} tm;
|
|
DWORD id;
|
|
#ifdef MSWINCE
|
|
#define THREAD_HANDLE(t)(HANDLE)(word)(t)->id
|
|
#else
|
|
HANDLE handle;
|
|
#define THREAD_HANDLE(t)(t)->handle
|
|
#endif
|
|
ptr_t stack_base;
|
|
ptr_t last_stack_min;
|
|
#ifdef IA64
|
|
ptr_t backing_store_end;
|
|
ptr_t backing_store_ptr;
|
|
#elif defined(I386)
|
|
ptr_t initial_stack_base;
|
|
#endif
|
|
ptr_t thread_blocked_sp;
|
|
struct GC_traced_stack_sect_s*traced_stack_sect;
|
|
unsigned short finalizer_skipped;
|
|
unsigned char finalizer_nested;
|
|
unsigned char suspended;
|
|
#ifdef GC_PTHREADS
|
|
unsigned char flags;
|
|
#define FINISHED 1
|
|
#define DETACHED 2
|
|
#define KNOWN_FINISHED(t)(((t)->flags)&FINISHED)
|
|
pthread_t pthread_id;
|
|
void*status;
|
|
#else
|
|
#define KNOWN_FINISHED(t)0
|
|
#endif
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
struct thread_local_freelists tlfs;
|
|
#endif
|
|
#ifdef RETRY_GET_THREAD_CONTEXT
|
|
ptr_t context_sp;
|
|
word context_regs[PUSHED_REGS_COUNT];
|
|
#endif
|
|
};
|
|
typedef struct GC_Thread_Rep*GC_thread;
|
|
typedef volatile struct GC_Thread_Rep*GC_vthread;
|
|
#ifndef GC_NO_THREADS_DISCOVERY
|
|
STATIC DWORD GC_main_thread=0;
|
|
STATIC volatile AO_t GC_attached_thread=FALSE;
|
|
STATIC volatile GC_bool GC_please_stop=FALSE;
|
|
#elif defined(GC_ASSERTIONS)
|
|
STATIC GC_bool GC_please_stop=FALSE;
|
|
#endif
|
|
#if defined(WRAP_MARK_SOME)&&!defined(GC_PTHREADS)
|
|
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))
|
|
return TRUE;
|
|
#else
|
|
AO_nop_full();
|
|
if (AO_load(&GC_attached_thread)){
|
|
AO_store(&GC_attached_thread,FALSE);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
#ifndef MAX_THREADS
|
|
#define MAX_THREADS 512
|
|
#endif
|
|
volatile struct GC_Thread_Rep dll_thread_table[MAX_THREADS];
|
|
STATIC volatile LONG GC_max_thread_index=0;
|
|
#ifndef THREAD_TABLE_SZ
|
|
#define THREAD_TABLE_SZ 256
|
|
#endif
|
|
#define THREAD_TABLE_INDEX(id)(int)((((id)>>8)^(id))% THREAD_TABLE_SZ)
|
|
STATIC GC_thread GC_threads[THREAD_TABLE_SZ];
|
|
static struct GC_Thread_Rep first_thread;
|
|
static GC_bool first_thread_used=FALSE;
|
|
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->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;
|
|
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");
|
|
}
|
|
STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base*sb,
|
|
DWORD thread_id)
|
|
{
|
|
GC_vthread me;
|
|
#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;
|
|
for (i=0;
|
|
InterlockedExchange(&dll_thread_table[i].tm.long_in_use,1)!=0;
|
|
i++){
|
|
if (i==MAX_THREADS - 1)
|
|
ABORT("Too many threads");
|
|
}
|
|
while (i > GC_max_thread_index){
|
|
InterlockedIncrement((IE_t)&GC_max_thread_index);
|
|
}
|
|
if (GC_max_thread_index>=MAX_THREADS){
|
|
GC_max_thread_index=MAX_THREADS - 1;
|
|
}
|
|
me=dll_thread_table+i;
|
|
} else
|
|
#endif
|
|
{
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
GC_in_thread_creation=TRUE;
|
|
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->pthread_id=pthread_self();
|
|
#endif
|
|
#ifndef MSWINCE
|
|
if (!DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),
|
|
GetCurrentProcess(),
|
|
(HANDLE*)&(me->handle),
|
|
0,FALSE,
|
|
DUPLICATE_SAME_ACCESS)){
|
|
ABORT_ARG1("DuplicateHandle failed",
|
|
":errcode=0x%X",(unsigned)GetLastError());
|
|
}
|
|
#endif
|
|
me->last_stack_min=ADDR_LIMIT;
|
|
GC_record_stack_base(me,sb);
|
|
me->id=thread_id;
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
GC_init_thread_local((GC_tlfs)(&(me->tlfs)));
|
|
#endif
|
|
#ifndef GC_NO_THREADS_DISCOVERY
|
|
if (GC_win32_dll_threads){
|
|
if (GC_please_stop){
|
|
AO_store(&GC_attached_thread,TRUE);
|
|
AO_nop_full();
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
GC_ASSERT(!GC_please_stop);
|
|
}
|
|
return (GC_thread)(me);
|
|
}
|
|
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;
|
|
}
|
|
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);
|
|
i++){
|
|
}
|
|
return i<=my_max?(GC_thread)(dll_thread_table+i):NULL;
|
|
} else
|
|
#endif
|
|
{
|
|
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)
|
|
#endif
|
|
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_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){
|
|
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;
|
|
}
|
|
#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC)
|
|
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_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;
|
|
}
|
|
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)
|
|
{
|
|
}
|
|
#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 <pthread.h>
|
|
#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
|
|
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){
|
|
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
|
|
{
|
|
DWORD id=((GC_thread)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!=(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);
|
|
}
|
|
}
|
|
}
|
|
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_PRIdPTR "\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)
|
|
{
|
|
GC_ASSERT(GC_lookup_thread_inner(GetCurrentThreadId())!=0);
|
|
#if!defined(GC_ALWAYS_MULTITHREADED)&&!defined(PARALLEL_MARK)&&!defined(GC_NO_THREADS_DISCOVERY)
|
|
parallel_initialized=TRUE;
|
|
#endif
|
|
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");
|
|
LOCK();
|
|
me=GC_lookup_thread_inner(thread_id);
|
|
if (me==0){
|
|
#ifdef GC_PTHREADS
|
|
me=GC_register_my_thread_inner(sb,thread_id);
|
|
#if defined(CPPCHECK)
|
|
GC_noop1(me->flags);
|
|
#endif
|
|
me->flags|=DETACHED;
|
|
#else
|
|
GC_register_my_thread_inner(sb,thread_id);
|
|
#endif
|
|
UNLOCK();
|
|
return GC_SUCCESS;
|
|
} else
|
|
#ifdef GC_PTHREADS
|
|
if ((me->flags&FINISHED)!=0){
|
|
GC_record_stack_base(me,sb);
|
|
me->flags&=~FINISHED;
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
GC_init_thread_local((GC_tlfs)(&me->tlfs));
|
|
#endif
|
|
UNLOCK();
|
|
return GC_SUCCESS;
|
|
} else
|
|
#endif
|
|
{
|
|
UNLOCK();
|
|
return GC_DUPLICATE;
|
|
}
|
|
}
|
|
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;
|
|
do {
|
|
ENTER_GC();
|
|
GC_in_thread_creation=TRUE;
|
|
GC_collect_a_little_inner(1);
|
|
GC_in_thread_creation=FALSE;
|
|
EXIT_GC();
|
|
UNLOCK();
|
|
Sleep(0);
|
|
LOCK();
|
|
} while (GC_incremental&&GC_collection_in_progress()
|
|
&&(wait_for_all||old_gc_no==GC_gc_no));
|
|
}
|
|
}
|
|
GC_API int GC_CALL GC_unregister_my_thread(void)
|
|
{
|
|
DCL_LOCK_STATE;
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Unregistering thread 0x%lx\n",(long)GetCurrentThreadId());
|
|
#endif
|
|
if (GC_win32_dll_threads){
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
GC_ASSERT(FALSE);
|
|
#else
|
|
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
|
|
{
|
|
GC_delete_thread(thread_id);
|
|
}
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
GC_remove_specific(GC_thread_key);
|
|
#endif
|
|
UNLOCK();
|
|
}
|
|
return GC_SUCCESS;
|
|
}
|
|
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;
|
|
UNLOCK();
|
|
d->client_data=(d->fn)(d->client_data);
|
|
LOCK();
|
|
#if defined(CPPCHECK)
|
|
GC_noop1((word)me->thread_blocked_sp);
|
|
#endif
|
|
me->thread_blocked_sp=NULL;
|
|
UNLOCK();
|
|
}
|
|
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();
|
|
me=GC_lookup_thread_inner(thread_id);
|
|
CHECK_LOOKUP_MY_THREAD(me);
|
|
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){
|
|
UNLOCK();
|
|
client_data=fn(client_data);
|
|
GC_noop1(COVERT_DATAFLOW(&stacksect));
|
|
return client_data;
|
|
}
|
|
stacksect.saved_stack_ptr=me->thread_blocked_sp;
|
|
#ifdef IA64
|
|
stacksect.backing_store_end=GC_save_regs_in_stack();
|
|
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);
|
|
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;
|
|
}
|
|
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){
|
|
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);
|
|
t->stack_base=(ptr_t)sb->mem_base;
|
|
t->last_stack_min=ADDR_LIMIT;
|
|
#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);
|
|
sb->mem_base=me->stack_base;
|
|
#ifdef IA64
|
|
sb->reg_base=me->backing_store_end;
|
|
#endif
|
|
UNLOCK();
|
|
return (void*)me;
|
|
}
|
|
#ifdef GC_PTHREADS
|
|
#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)
|
|
#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)]
|
|
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));
|
|
i++){
|
|
}
|
|
return i<=my_max?(GC_thread)(dll_thread_table+i):NULL;
|
|
} else
|
|
#endif
|
|
{
|
|
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
|
|
#ifdef CAN_HANDLE_FORK
|
|
STATIC void GC_remove_all_threads_but_me(void)
|
|
{
|
|
int hv;
|
|
GC_thread me=NULL;
|
|
DWORD thread_id;
|
|
pthread_t pthread_id=pthread_self();
|
|
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){
|
|
me=p;
|
|
p->tm.next=0;
|
|
} else {
|
|
#ifdef THREAD_LOCAL_ALLOC
|
|
if ((p->flags&FINISHED)==0){
|
|
GC_remove_specific_after_fork(GC_thread_key,p->pthread_id);
|
|
}
|
|
#endif
|
|
if (&first_thread!=p)
|
|
GC_INTERNAL_FREE(p);
|
|
}
|
|
}
|
|
GC_threads[hv]=NULL;
|
|
}
|
|
GC_ASSERT(me!=NULL);
|
|
thread_id=GetCurrentThreadId();
|
|
GC_threads[THREAD_TABLE_INDEX(thread_id)]=me;
|
|
me->id=thread_id;
|
|
#ifndef MSWINCE
|
|
if (!DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),
|
|
GetCurrentProcess(),(HANDLE*)&me->handle,
|
|
0,FALSE,
|
|
DUPLICATE_SAME_ACCESS))
|
|
ABORT("DuplicateHandle failed");
|
|
#endif
|
|
#if defined(THREAD_LOCAL_ALLOC)&&!defined(USE_CUSTOM_SPECIFIC)
|
|
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;
|
|
}
|
|
#endif
|
|
GC_remove_all_threads_but_me();
|
|
UNLOCK();
|
|
}
|
|
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
|
|
void GC_push_thread_structures(void)
|
|
{
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
#ifndef GC_NO_THREADS_DISCOVERY
|
|
if (GC_win32_dll_threads){
|
|
} else
|
|
#endif
|
|
{
|
|
GC_PUSH_ALL_SYM(GC_threads);
|
|
}
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
GC_PUSH_ALL_SYM(GC_thread_key);
|
|
#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 BOOL isWow64;
|
|
#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
|
|
STATIC void GC_suspend(GC_thread t)
|
|
{
|
|
#ifndef MSWINCE
|
|
DWORD exitCode;
|
|
#endif
|
|
#ifdef RETRY_GET_THREAD_CONTEXT
|
|
int retry_cnt=0;
|
|
#define MAX_SUSPEND_THREAD_RETRIES (1000*1000)
|
|
#endif
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("Suspending 0x%x\n",(int)t->id);
|
|
#endif
|
|
UNPROTECT_THREAD(t);
|
|
GC_acquire_dirty_lock();
|
|
#ifdef MSWINCE
|
|
while (SuspendThread(THREAD_HANDLE(t))==(DWORD)-1){
|
|
GC_release_dirty_lock();
|
|
Sleep(10);
|
|
GC_acquire_dirty_lock();
|
|
}
|
|
#elif defined(RETRY_GET_THREAD_CONTEXT)
|
|
for (;;){
|
|
if (GetExitCodeThread(t->handle,&exitCode)
|
|
&&exitCode!=STILL_ACTIVE){
|
|
GC_release_dirty_lock();
|
|
#ifdef GC_PTHREADS
|
|
t->stack_base=0;
|
|
#else
|
|
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)){
|
|
t->context_sp=copy_ptr_regs(t->context_regs,&context);
|
|
break;
|
|
}
|
|
if (ResumeThread(t->handle)==(DWORD)-1)
|
|
ABORT("ResumeThread failed in suspend loop");
|
|
}
|
|
if (retry_cnt > 1){
|
|
GC_release_dirty_lock();
|
|
Sleep(0);
|
|
GC_acquire_dirty_lock();
|
|
}
|
|
if (++retry_cnt>=MAX_SUSPEND_THREAD_RETRIES)
|
|
ABORT("SuspendThread loop failed");
|
|
}
|
|
#else
|
|
if (GetExitCodeThread(t->handle,&exitCode)
|
|
&&exitCode!=STILL_ACTIVE){
|
|
GC_release_dirty_lock();
|
|
#ifdef GC_PTHREADS
|
|
t->stack_base=0;
|
|
#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;
|
|
#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());
|
|
#ifdef PARALLEL_MARK
|
|
if (GC_parallel){
|
|
GC_acquire_mark_lock();
|
|
GC_ASSERT(GC_fl_builder_count==0);
|
|
}
|
|
#endif
|
|
#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);
|
|
#ifdef GC_ASSERTIONS
|
|
GC_write_disabled=TRUE;
|
|
#endif
|
|
#endif
|
|
#ifndef GC_NO_THREADS_DISCOVERY
|
|
if (GC_win32_dll_threads){
|
|
int i;
|
|
int my_max;
|
|
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
|
|
{
|
|
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 {
|
|
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
|
|
#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
|
|
static ptr_t last_address=0;
|
|
static MEMORY_BASIC_INFORMATION last_info;
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
#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;
|
|
#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){
|
|
#ifdef RETRY_GET_THREAD_CONTEXT
|
|
word*regs=thread->context_regs;
|
|
if (thread->suspended){
|
|
sp=thread->context_sp;
|
|
} else
|
|
#else
|
|
word regs[PUSHED_REGS_COUNT];
|
|
#endif
|
|
{
|
|
CONTEXT context;
|
|
context.ContextFlags=GET_THREAD_CONTEXT_FLAGS;
|
|
if (GetThreadContext(THREAD_HANDLE(thread),&context)){
|
|
sp=copy_ptr_regs(regs,&context);
|
|
} else {
|
|
#ifdef RETRY_GET_THREAD_CONTEXT
|
|
sp=thread->context_sp;
|
|
if (NULL==sp){
|
|
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);
|
|
if (isWow64){
|
|
DWORD ContextFlags=(DWORD)regs[0];
|
|
WORD SegFs=(WORD)regs[1];
|
|
if ((ContextFlags&CONTEXT_EXCEPTION_REPORTING)!=0
|
|
&&(ContextFlags&(CONTEXT_EXCEPTION_ACTIVE
|
|
))!=0){
|
|
LDT_ENTRY selector;
|
|
PNT_TIB tib;
|
|
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));
|
|
#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
|
|
&&((word)thread->stack_base<=(word)tib->StackLimit
|
|
||(word)tib->StackBase < (word)thread->stack_base)){
|
|
WARN("GetThreadContext might return stale register values"
|
|
" including ESP=%p\n",sp);
|
|
} else {
|
|
sp=(ptr_t)tib->StackLimit;
|
|
}
|
|
}
|
|
#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
|
|
}
|
|
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);
|
|
} else
|
|
#endif
|
|
{
|
|
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 {
|
|
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 {
|
|
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;
|
|
if ((word)sp < (word)stack_min
|
|
||(word)sp>=(word)thread->stack_base)
|
|
stack_min=GC_get_stack_min(thread->last_stack_min);
|
|
} else {
|
|
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 (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
|
|
GC_push_all_stack(stack_min,thread->stack_base);
|
|
}
|
|
return thread->stack_base - sp;
|
|
}
|
|
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
|
|
{
|
|
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];
|
|
#ifdef IA64
|
|
static ptr_t marker_bsp[MAX_MARKERS - 1];
|
|
#endif
|
|
static ptr_t marker_last_stack_min[MAX_MARKERS - 1];
|
|
#endif
|
|
GC_INNER void GC_get_next_stack(char*start,char*limit,
|
|
char**lo,char**hi)
|
|
{
|
|
int i;
|
|
char*current_min=ADDR_LIMIT;
|
|
ptr_t*plast_stack_min=NULL;
|
|
GC_thread thread=NULL;
|
|
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){
|
|
plast_stack_min=(ptr_t*)
|
|
&dll_thread_table[i].last_stack_min;
|
|
current_min=s;
|
|
#if defined(CPPCHECK)
|
|
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){
|
|
plast_stack_min=&t->last_stack_min;
|
|
thread=t;
|
|
current_min=s;
|
|
}
|
|
}
|
|
}
|
|
#ifdef PARALLEL_MARK
|
|
for (i=0;i < GC_markers_m1;++i){
|
|
ptr_t s=marker_sp[i];
|
|
#ifdef IA64
|
|
#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;
|
|
}
|
|
}
|
|
#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);
|
|
return;
|
|
}
|
|
#endif
|
|
if ((word)current_min > (word)limit&&!may_be_in_stack(limit)){
|
|
*lo=ADDR_LIMIT;
|
|
return;
|
|
}
|
|
if (*plast_stack_min==ADDR_LIMIT
|
|
||!may_be_in_stack(*plast_stack_min)){
|
|
*lo=GC_get_stack_min(current_min);
|
|
} else {
|
|
*lo=GC_get_stack_min(*plast_stack_min);
|
|
}
|
|
if (thread!=NULL){
|
|
UNPROTECT_THREAD(thread);
|
|
}
|
|
*plast_stack_min=*lo;
|
|
}
|
|
#ifdef PARALLEL_MARK
|
|
#if defined(GC_PTHREADS)&&!defined(GC_PTHREADS_PARAMARK)
|
|
#if!defined(__MINGW32__)
|
|
#define GC_PTHREADS_PARAMARK
|
|
#endif
|
|
#endif
|
|
#if!defined(GC_PTHREADS_PARAMARK)
|
|
STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1]={0};
|
|
STATIC DWORD GC_marker_Id[MAX_MARKERS - 1]={0};
|
|
#endif
|
|
#if defined(GC_PTHREADS)&&defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)
|
|
static void set_marker_thread_name(unsigned id)
|
|
{
|
|
char name_buf[16];
|
|
int len=sizeof("GC-marker-")- 1;
|
|
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)
|
|
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;
|
|
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;
|
|
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
|
|
#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;
|
|
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
|
|
GC_acquire_mark_lock();
|
|
if (0==--GC_fl_builder_count)
|
|
GC_notify_all_builder();
|
|
for (;;++my_mark_no){
|
|
if (my_mark_no - GC_mark_no > (word)2){
|
|
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
|
|
#ifdef CAN_HANDLE_FORK
|
|
static int available_markers_m1=0;
|
|
#else
|
|
#define available_markers_m1 GC_markers_m1
|
|
#endif
|
|
#ifdef GC_PTHREADS_PARAMARK
|
|
#include <pthread.h>
|
|
#if defined(GC_ASSERTIONS)&&!defined(NUMERIC_THREAD_ID)
|
|
#define NUMERIC_THREAD_ID(id)(unsigned long)(word)GC_PTHREAD_PTRVAL(id)
|
|
#endif
|
|
#ifdef CAN_HANDLE_FORK
|
|
static pthread_cond_t mark_cv;
|
|
#else
|
|
static pthread_cond_t mark_cv=PTHREAD_COND_INITIALIZER;
|
|
#endif
|
|
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_DONT_HOLD_LOCK());
|
|
if (available_markers_m1<=0)return;
|
|
#ifdef CAN_HANDLE_FORK
|
|
if (GC_parallel)return;
|
|
{
|
|
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
|
|
if (sigfillset(&set)!=0)
|
|
ABORT("sigfillset failed");
|
|
if (pthread_sigmask(SIG_BLOCK,&set,&oldset)< 0){
|
|
WARN("pthread_sigmask set failed,no markers started,"
|
|
" errno=%" WARN_PRIdPTR "\n",errno);
|
|
GC_markers_m1=0;
|
|
(void)pthread_attr_destroy(&attr);
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef CAN_HANDLE_FORK
|
|
GC_markers_m1=available_markers_m1;
|
|
#endif
|
|
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);
|
|
GC_markers_m1=i;
|
|
break;
|
|
}
|
|
}
|
|
#ifndef NO_MARKER_SPECIAL_SIGMASK
|
|
if (pthread_sigmask(SIG_SETMASK,&oldset,NULL)< 0){
|
|
WARN("pthread_sigmask restore failed,errno=%" WARN_PRIdPTR "\n",
|
|
errno);
|
|
}
|
|
#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
|
|
static pthread_mutex_t mark_mutex=PTHREAD_MUTEX_INITIALIZER;
|
|
static pthread_cond_t builder_cv=PTHREAD_COND_INITIALIZER;
|
|
#ifdef LOCK_STATS
|
|
volatile AO_t GC_block_count=0;
|
|
#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
|
|
if (pthread_mutex_lock(&mark_mutex)!=0){
|
|
ABORT("pthread_mutex_lock failed");
|
|
}
|
|
#ifdef LOCK_STATS
|
|
(void)AO_fetch_and_add1(&GC_block_count);
|
|
#endif
|
|
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");
|
|
}
|
|
}
|
|
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
|
|
#ifndef MARK_THREAD_STACK_SIZE
|
|
#define MARK_THREAD_STACK_SIZE 0
|
|
#endif
|
|
static HANDLE mark_mutex_event=(HANDLE)0;
|
|
static HANDLE builder_cv=(HANDLE)0;
|
|
static HANDLE mark_cv=(HANDLE)0;
|
|
GC_INNER void GC_start_mark_threads_inner(void)
|
|
{
|
|
int i;
|
|
GC_ASSERT(I_DONT_HOLD_LOCK());
|
|
if (available_markers_m1<=0)return;
|
|
GC_ASSERT(GC_fl_builder_count==0);
|
|
for (i=0;i < GC_markers_m1;++i){
|
|
if ((GC_marker_cv[i]=CreateEvent(NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL))==(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;
|
|
handle=CreateThread(NULL,
|
|
MARK_THREAD_STACK_SIZE,
|
|
GC_mark_thread,(LPVOID)(word)i,
|
|
0,&thread_id);
|
|
if (handle==NULL){
|
|
WARN("Marker thread creation failed\n",0);
|
|
break;
|
|
} else {
|
|
CloseHandle(handle);
|
|
}
|
|
#else
|
|
GC_uintptr_t handle;
|
|
unsigned thread_id;
|
|
marker_last_stack_min[i]=ADDR_LIMIT;
|
|
handle=_beginthreadex(NULL,
|
|
MARK_THREAD_STACK_SIZE,GC_mark_thread,
|
|
(void*)(word)i,0,&thread_id);
|
|
if (!handle||handle==(GC_uintptr_t)-1L){
|
|
WARN("Marker thread creation failed\n",0);
|
|
break;
|
|
} else {
|
|
}
|
|
#endif
|
|
}
|
|
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
|
|
STATIC LONG GC_mark_mutex_state=0;
|
|
#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)
|
|
{
|
|
#ifndef THREAD_SANITIZER
|
|
GC_ASSERT(GC_mark_lock_holder!=GetCurrentThreadId());
|
|
#endif
|
|
if (InterlockedExchange(&GC_mark_mutex_state,1)!=0){
|
|
#ifdef LOCK_STATS
|
|
(void)AO_fetch_and_add1(&GC_block_count);
|
|
#endif
|
|
while (InterlockedExchange(&GC_mark_mutex_state,
|
|
-1)!=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)< 0){
|
|
if (SetEvent(mark_mutex_event)==FALSE)
|
|
ABORT("SetEvent failed");
|
|
}
|
|
}
|
|
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");
|
|
}
|
|
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){
|
|
if (SetEvent(GC_marker_Id[i]!=thread_id?GC_marker_cv[i]:
|
|
mark_cv)==FALSE)
|
|
ABORT("SetEvent failed");
|
|
}
|
|
}
|
|
#endif
|
|
static unsigned required_markers_cnt=0;
|
|
#endif
|
|
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);
|
|
#ifdef DEBUG_THREADS
|
|
GC_log_printf("thread 0x%lx starting...\n",(long)GetCurrentThreadId());
|
|
#endif
|
|
GC_free(arg);
|
|
#if!defined(__GNUC__)&&!defined(NO_CRT)
|
|
ret=NULL;
|
|
__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();
|
|
#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));
|
|
HANDLE thread_h;
|
|
if (NULL==args){
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
args->start=lpStartAddress;
|
|
args->param=lpParameter;
|
|
GC_dirty(args);
|
|
REACHABLE_AFTER_DIRTY(lpParameter);
|
|
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();
|
|
#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));
|
|
if (NULL==args){
|
|
errno=EAGAIN;
|
|
return 0;
|
|
}
|
|
args->start=(LPTHREAD_START_ROUTINE)start_address;
|
|
args->param=arglist;
|
|
GC_dirty(args);
|
|
REACHABLE_AFTER_DIRTY(arglist);
|
|
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
|
|
#ifdef GC_WINMAIN_REDIRECT
|
|
#if defined(MSWINCE)&&defined(UNDER_CE)
|
|
#define WINMAIN_LPTSTR LPWSTR
|
|
#else
|
|
#define WINMAIN_LPTSTR LPSTR
|
|
#endif
|
|
#undef WinMain
|
|
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_waitForSingleObjectInfinite(void*handle)
|
|
{
|
|
return (void*)(word)WaitForSingleObject((HANDLE)handle,INFINITE);
|
|
}
|
|
#ifndef WINMAIN_THREAD_STACK_SIZE
|
|
#define WINMAIN_THREAD_STACK_SIZE 0
|
|
#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;
|
|
GC_INIT();
|
|
thread_h=GC_CreateThread(NULL,
|
|
WINMAIN_THREAD_STACK_SIZE,
|
|
main_thread_start,&args,0,
|
|
&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_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
|
|
}
|
|
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=GetModuleHandle(TEXT("kernel32.dll"));
|
|
#endif
|
|
GC_ASSERT(I_HOLD_LOCK());
|
|
if (GC_thr_initialized)return;
|
|
GC_ASSERT((word)&GC_threads % sizeof(word)==0);
|
|
#ifdef GC_NO_THREADS_DISCOVERY
|
|
#define GC_main_thread GetCurrentThreadId()
|
|
#else
|
|
GC_main_thread=GetCurrentThreadId();
|
|
#endif
|
|
GC_thr_initialized=TRUE;
|
|
#ifdef CAN_HANDLE_FORK
|
|
if (GC_handle_fork){
|
|
#ifdef CAN_CALL_ATFORK
|
|
if (pthread_atfork(fork_prepare_proc,fork_parent_proc,
|
|
fork_child_proc)==0){
|
|
GC_handle_fork=1;
|
|
} else
|
|
#endif
|
|
if (GC_handle_fork!=-1)
|
|
ABORT("pthread_atfork failed");
|
|
}
|
|
#endif
|
|
#ifdef WOW64_THREAD_CONTEXT_WORKAROUND
|
|
if (hK32){
|
|
FARPROC pfn=GetProcAddress(hK32,"IsWow64Process");
|
|
if (pfn
|
|
&&!(*(BOOL (WINAPI*)(HANDLE,BOOL*))(word)pfn)(
|
|
GetCurrentProcess(),&isWow64))
|
|
isWow64=FALSE;
|
|
}
|
|
#endif
|
|
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){
|
|
#ifdef MSWINCE
|
|
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
|
|
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)
|
|
if (markers < GC_MIN_MARKERS)
|
|
markers=GC_MIN_MARKERS;
|
|
#endif
|
|
if (markers > MAX_MARKERS)
|
|
markers=MAX_MARKERS;
|
|
}
|
|
available_markers_m1=markers - 1;
|
|
}
|
|
if (GC_win32_dll_threads||available_markers_m1<=0){
|
|
GC_parallel=FALSE;
|
|
GC_COND_LOG_PRINTF(
|
|
"Single marker thread,turning off parallel marking\n");
|
|
} else {
|
|
#ifndef GC_PTHREADS_PARAMARK
|
|
mark_mutex_event=CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,NULL);
|
|
builder_cv=CreateEvent(NULL,
|
|
TRUE,
|
|
FALSE,NULL);
|
|
mark_cv=CreateEvent(NULL,TRUE,
|
|
FALSE,NULL);
|
|
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
|
|
GC_ASSERT(0==GC_lookup_thread_inner(GC_main_thread));
|
|
GC_register_my_thread_inner(&sb,GC_main_thread);
|
|
#undef GC_main_thread
|
|
}
|
|
#ifdef GC_PTHREADS
|
|
struct start_info {
|
|
void*(*start_routine)(void*);
|
|
void*arg;
|
|
GC_bool detached;
|
|
};
|
|
GC_API int GC_pthread_join(pthread_t pthread_id,void**retval)
|
|
{
|
|
int result;
|
|
#ifndef GC_WIN32_PTHREADS
|
|
GC_thread t;
|
|
#endif
|
|
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
|
|
#ifndef GC_WIN32_PTHREADS
|
|
while ((t=GC_lookup_pthread(pthread_id))==0)
|
|
Sleep(10);
|
|
#endif
|
|
result=pthread_join(pthread_id,retval);
|
|
if (0==result){
|
|
#ifdef GC_WIN32_PTHREADS
|
|
GC_thread t=GC_lookup_pthread(pthread_id);
|
|
if (NULL==t)ABORT("Thread not registered");
|
|
#endif
|
|
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;
|
|
}
|
|
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();
|
|
GC_ASSERT(!GC_win32_dll_threads);
|
|
si=(struct start_info*)GC_malloc_uncollectable(
|
|
sizeof(struct start_info));
|
|
if (NULL==si)
|
|
return EAGAIN;
|
|
si->start_routine=start_routine;
|
|
si->arg=arg;
|
|
GC_dirty(si);
|
|
REACHABLE_AFTER_DIRTY(arg);
|
|
if (attr!=0&&
|
|
pthread_attr_getdetachstate(attr,&si->detached)
|
|
==PTHREAD_CREATE_DETACHED){
|
|
si->detached=TRUE;
|
|
}
|
|
#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
|
|
set_need_to_lock();
|
|
result=pthread_create(new_thread,attr,GC_pthread_start,si);
|
|
if (result){
|
|
GC_free(si);
|
|
}
|
|
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);
|
|
LOCK();
|
|
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;
|
|
UNLOCK();
|
|
start=si->start_routine;
|
|
start_arg=si->arg;
|
|
GC_free(si);
|
|
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 {
|
|
me->flags|=FINISHED;
|
|
}
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
GC_remove_specific(GC_thread_key);
|
|
#endif
|
|
UNLOCK();
|
|
}
|
|
#ifndef GC_NO_PTHREAD_SIGMASK
|
|
GC_API int GC_pthread_sigmask(int how,const sigset_t*set,
|
|
sigset_t*oset)
|
|
{
|
|
return pthread_sigmask(how,set,oset);
|
|
}
|
|
#endif
|
|
GC_API int GC_pthread_detach(pthread_t thread)
|
|
{
|
|
int result;
|
|
GC_thread t;
|
|
DCL_LOCK_STATE;
|
|
GC_ASSERT(!GC_win32_dll_threads);
|
|
while ((t=GC_lookup_pthread(thread))==NULL)
|
|
Sleep(10);
|
|
result=pthread_detach(thread);
|
|
if (result==0){
|
|
LOCK();
|
|
t->flags|=DETACHED;
|
|
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)
|
|
#ifdef GC_INSIDE_DLL
|
|
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;
|
|
if (!GC_win32_dll_threads&¶llel_initialized)return TRUE;
|
|
switch (reason){
|
|
case DLL_THREAD_ATTACH:
|
|
#ifdef PARALLEL_MARK
|
|
if (GC_parallel){
|
|
break;
|
|
}
|
|
#endif
|
|
case DLL_PROCESS_ATTACH:
|
|
thread_id=GetCurrentThreadId();
|
|
if (parallel_initialized&&GC_main_thread!=thread_id){
|
|
#ifdef PARALLEL_MARK
|
|
ABORT("Cannot initialize parallel marker from DllMain");
|
|
#else
|
|
struct GC_stack_base sb;
|
|
#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);
|
|
#endif
|
|
}
|
|
break;
|
|
case DLL_THREAD_DETACH:
|
|
GC_ASSERT(parallel_initialized);
|
|
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_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;
|
|
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();
|
|
}
|
|
#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)
|
|
GC_INNER void GC_lock(void)
|
|
{
|
|
pthread_mutex_lock(&GC_allocate_ml);
|
|
}
|
|
#endif
|
|
#if defined(THREAD_LOCAL_ALLOC)
|
|
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)
|
|
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
|
|
#endif
|
|
#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
|
|
#endif
|
|
#endif
|
|
#ifndef GC_PTHREAD_START_STANDALONE
|
|
#if defined(__GNUC__)&&defined(__linux__)
|
|
#undef __EXCEPTIONS
|
|
#endif
|
|
#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS)
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
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);
|
|
#ifndef NACL
|
|
pthread_cleanup_pop(1);
|
|
#endif
|
|
return result;
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifndef GC_NO_THREAD_REDIRECTS
|
|
#define GC_PTHREAD_REDIRECTS_ONLY
|
|
#ifndef GC_PTHREAD_REDIRECTS_H
|
|
#define GC_PTHREAD_REDIRECTS_H
|
|
#if defined(GC_H)&&defined(GC_PTHREADS)
|
|
#ifndef GC_PTHREAD_REDIRECTS_ONLY
|
|
#include <pthread.h>
|
|
#ifndef GC_NO_DLOPEN
|
|
#include <dlfcn.h>
|
|
#endif
|
|
#ifndef GC_NO_PTHREAD_SIGMASK
|
|
#include <signal.h>
|
|
#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*,int);
|
|
#endif
|
|
#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,const sigset_t*,
|
|
sigset_t*);
|
|
#endif
|
|
#endif
|
|
#ifndef GC_PTHREAD_CREATE_CONST
|
|
#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*);
|
|
GC_API int GC_pthread_join(pthread_t,void**);
|
|
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
|
|
}
|
|
#endif
|
|
#endif
|
|
#if!defined(GC_NO_THREAD_REDIRECTS)&&!defined(GC_USE_LD_WRAP)
|
|
#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
|
|
#endif
|
|
#endif
|
|
#endif
|