mirror of
https://github.com/2003scape/deep-c-rsc.git
synced 2024-03-22 05:49:51 -04:00
3932 lines
102 KiB
C
Executable File
3932 lines
102 KiB
C
Executable File
/*
|
|
* @(#) tinygc.c -- TinyGC (Tiny Garbage Collector) source.
|
|
* Copyright (C) 2006-2010 Ivan Maidanski <ivmai@mail.ru> All rights reserved.
|
|
**
|
|
* Version: 2.6
|
|
* See also files: gc.h, gc_gcj.h, gc_mark.h, javaxfc.h
|
|
* Required: any ANSI C compiler (assume GC-safe compilation).
|
|
*/
|
|
|
|
/*
|
|
* This is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
**
|
|
* This software is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License (GPL) for more details.
|
|
**
|
|
* Linking this library statically or dynamically with other modules is
|
|
* making a combined work based on this library. Thus, the terms and
|
|
* conditions of the GNU General Public License cover the whole
|
|
* combination.
|
|
**
|
|
* As a special exception, the copyright holders of this library give you
|
|
* permission to link this library with independent modules to produce an
|
|
* executable, regardless of the license terms of these independent
|
|
* modules, and to copy and distribute the resulting executable under
|
|
* terms of your choice, provided that you also meet, for each linked
|
|
* independent module, the terms and conditions of the license of that
|
|
* module. An independent module is a module which is not derived from
|
|
* or based on this library. If you modify this library, you may extend
|
|
* this exception to your version of the library, but you are not
|
|
* obligated to do so. If you do not wish to do so, delete this
|
|
* exception statement from your version.
|
|
*/
|
|
|
|
/*
|
|
* Control macros: ALL_INTERIOR_POINTERS, DONT_ADD_BYTE_AT_END,
|
|
* FINALIZE_ON_DEMAND, GC_DLL, GC_DONT_EXPAND, GC_GCJ_SUPPORT, GC_GETENV_SKIP,
|
|
* GC_IGNORE_GCJ_INFO, GC_MISC_EXCLUDE, GC_NO_DLINKS, GC_NO_FNLZ,
|
|
* GC_NO_GCBASE, GC_NO_INACTIVE, GC_NO_REGISTER_DLINK,
|
|
* GC_OMIT_REGISTER_KEYWORD, GC_PRINT_MSGS, GC_THREADS, GC_USE_GETTIMEOFDAY,
|
|
* GC_USE_WIN32_SYSTEMTIME, GC_WIN32_THREADS, GC_WIN32_WCE,
|
|
* JAVA_FINALIZATION_NOT_NEEDED.
|
|
**
|
|
* Macros for tuning (also see in gc.h): CONST, GC_ASYNC_PUSHREGS_BEGIN,
|
|
* GC_ASYNC_PUSHREGS_END, GC_CLIBDECL, GC_CORE_API, GC_CORE_CALL,
|
|
* GC_CORE_FREE, GC_CORE_MALLOC, GC_DATASTATIC, GC_DATASTART, GC_DATASTART2,
|
|
* GC_DATAEND, GC_DATAEND2, GC_FATAL_ABORT, GC_FREE_SPACE_DIVISOR,
|
|
* GC_MAX_RETRIES, GC_FASTCALL, GC_INLINE_STATIC, GC_LAZYREFILL_BIGCNT,
|
|
* GC_LAZYREFILL_COUNT, GC_LOG2_OFFIGNORE, GC_NEW_LINE, GC_PUSHREGS_BEGIN,
|
|
* GC_PUSHREGS_END, GC_SIG_SUSPEND, GC_STACKBOTTOM, GC_STACKBOTTOMVAR,
|
|
* GC_STACKLEN, GC_STACKLENVAR, GC_STATIC, GC_THREAD_MUTEX_DEFATTR,
|
|
* GC_THREAD_YIELD, GC_WIN32_CONTEXT_SP_NAME, GC_YIELD_MAX_ATTEMPT, INLINE,
|
|
* MARK_DESCR_OFFSET.
|
|
*/
|
|
|
|
#ifndef _SETJMP_H
|
|
#include <setjmp.h>
|
|
/* int setjmp(jmp_buf); */
|
|
#endif
|
|
|
|
#ifndef _STDLIB_H
|
|
#include <stdlib.h>
|
|
/* long atol(const char *); */
|
|
/* void exit(int); */
|
|
/* void free(void *); */
|
|
/* char *getenv(const char *); */
|
|
/* void *malloc(size_t); */
|
|
#endif
|
|
|
|
#ifndef _STRING_H
|
|
#include <string.h>
|
|
/* void *memset(void *, int, size_t); */
|
|
#endif
|
|
|
|
#ifndef _LIMITS_H
|
|
#include <limits.h>
|
|
#endif
|
|
|
|
#ifdef GC_WIN32_THREADS
|
|
|
|
#ifndef _WINDOWS_H
|
|
#include <windows.h>
|
|
/* BOOL CloseHandle(HANDLE); */
|
|
/* HANDLE CreateEvent(SECURITY_ATTRIBUTES *, BOOL, BOOL, LPCTSTR); */
|
|
/* BOOL DuplicateHandle(HANDLE, HANDLE, HANDLE, HANDLE *, DWORD, BOOL, DWORD); */
|
|
/* HANDLE GetCurrentProcess(void); */
|
|
/* HANDLE GetCurrentThread(void); */
|
|
/* DWORD GetCurrentThreadId(void); */
|
|
/* BOOL GetThreadContext(HANDLE, CONTEXT *); */
|
|
/* LONG InterlockedExchange(LONG *, LONG); */
|
|
/* DWORD ResumeThread(HANDLE); */
|
|
/* BOOL SetEvent(HANDLE); */
|
|
/* void Sleep(DWORD); */
|
|
/* DWORD SuspendThread(HANDLE); */
|
|
/* DWORD WaitForSingleObject(HANDLE, DWORD); */
|
|
#endif
|
|
|
|
#ifndef GC_THREADS
|
|
#define GC_THREADS 1
|
|
#endif
|
|
|
|
#else /* GC_WIN32_THREADS */
|
|
|
|
#ifdef GC_THREADS
|
|
|
|
#ifndef _ERRNO_H
|
|
#include <errno.h>
|
|
/* int errno; */
|
|
#endif
|
|
|
|
#ifndef _SIGNAL_H
|
|
#include <signal.h>
|
|
/* void (*signal(int, void (*)(int)))(int); */
|
|
#endif
|
|
|
|
#ifndef _PTHREAD_H
|
|
#include <pthread.h>
|
|
/* int pthread_kill(pthread_t, int); */
|
|
/* int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); */
|
|
/* int pthread_mutex_lock(pthread_mutex_t *); */
|
|
/* int pthread_mutex_unlock(pthread_mutex_t *); */
|
|
/* pthread_t pthread_self(void); */
|
|
#endif
|
|
|
|
#ifndef _SCHED_H
|
|
#include <sched.h>
|
|
/* int sched_yield(void); */
|
|
#endif
|
|
|
|
#ifdef pthread_usleep_np
|
|
/* #include <pthread.h> */
|
|
/* unsigned pthread_usleep_np(unsigned); */
|
|
#else
|
|
#ifndef _UNISTD_H
|
|
#include <unistd.h>
|
|
/* int usleep(useconds_t); */
|
|
#endif
|
|
#define pthread_usleep_np usleep
|
|
#endif
|
|
|
|
#endif /* GC_THREADS */
|
|
|
|
#endif /* ! GC_WIN32_THREADS */
|
|
|
|
#ifdef GC_PRINT_MSGS
|
|
|
|
#ifndef _STDIO_H
|
|
#include <stdio.h>
|
|
/* int fprintf(FILE *, const char *, ...); */
|
|
/* FILE * const stderr; */
|
|
/* FILE * const stdout; */
|
|
#endif
|
|
|
|
#ifdef GC_USE_WIN32_SYSTEMTIME
|
|
|
|
#ifndef _WINDOWS_H
|
|
#include <windows.h>
|
|
/* void GetSystemTime(SYSTEMTIME *); */
|
|
#endif
|
|
|
|
#define GC_CURTIME_T SYSTEMTIME
|
|
#define GC_CURTIME_GETMS(pcurt) (GetSystemTime(pcurt), ((((unsigned long)(pcurt)->wDay * 24 + (unsigned long)(pcurt)->wHour) * 60 + (unsigned long)(pcurt)->wMinute) * 60 + (unsigned long)(pcurt)->wSecond) * 1000 + (unsigned long)(pcurt)->wMilliseconds)
|
|
|
|
#else /* GC_USE_WIN32_SYSTEMTIME */
|
|
|
|
#ifdef GC_USE_GETTIMEOFDAY
|
|
|
|
#ifndef _SYS_TIME_H
|
|
#include <sys/time.h>
|
|
/* int gettimeofday(struct timeval *, void *); */
|
|
#endif
|
|
|
|
#define GC_CURTIME_T struct timeval
|
|
|
|
#ifdef _SVID_GETTOD
|
|
#define GC_CURTIME_GETMS(pcurt) (gettimeofday((void *)(pcurt)), (unsigned long)(pcurt)->tv_sec * 1000 + (unsigned long)(pcurt)->tv_usec / 1000)
|
|
#else
|
|
#define GC_CURTIME_GETMS(pcurt) (gettimeofday((void *)(pcurt), NULL), (unsigned long)(pcurt)->tv_sec * 1000 + (unsigned long)(pcurt)->tv_usec / 1000)
|
|
#endif
|
|
|
|
#else /* GC_USE_GETTIMEOFDAY */
|
|
|
|
#ifndef _TIME_H
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#ifndef _SYS_TIMEB_H
|
|
#include <sys/timeb.h>
|
|
/* void ftime(struct timeb *); */
|
|
#endif
|
|
|
|
#define GC_CURTIME_T struct timeb
|
|
#define GC_CURTIME_GETMS(pcurt) (ftime(pcurt), (unsigned long)(pcurt)->time * 1000 + (unsigned long)(pcurt)->millitm)
|
|
|
|
#endif /* ! GC_USE_GETTIMEOFDAY */
|
|
|
|
#endif /* ! GC_USE_WIN32_SYSTEMTIME */
|
|
|
|
#define GC_SIZE_TO_ULKB(size) ((unsigned long)((size) >> 10))
|
|
|
|
#ifndef GC_NEW_LINE
|
|
#define GC_NEW_LINE "\n"
|
|
#endif
|
|
|
|
#endif /* GC_PRINT_MSGS */
|
|
|
|
#ifndef GC_API
|
|
#ifdef GC_DLL
|
|
#define GC_API __declspec(dllexport)
|
|
#endif
|
|
#endif
|
|
|
|
#include "gc.h"
|
|
|
|
#include "gc_gcj.h"
|
|
|
|
#include "gc_mark.h"
|
|
|
|
#include "javaxfc.h"
|
|
|
|
#ifndef NULL
|
|
#define NULL (void *)0
|
|
#endif
|
|
|
|
#ifndef CHAR_BIT
|
|
#define CHAR_BIT 8
|
|
#endif
|
|
|
|
#ifndef CONST
|
|
#define CONST const
|
|
#endif
|
|
|
|
#ifndef GC_FATAL_ABORT
|
|
#define GC_FATAL_ABORT exit(-1) /* abort(), DebugBreak() */
|
|
#endif
|
|
|
|
#ifndef GC_DATASTATIC
|
|
#define GC_DATASTATIC static
|
|
#endif
|
|
|
|
#ifndef GC_STATIC
|
|
#define GC_STATIC static
|
|
#endif
|
|
|
|
#ifndef GC_INLINE_STATIC
|
|
#ifdef INLINE
|
|
#define GC_INLINE_STATIC GC_STATIC INLINE
|
|
#else
|
|
#define GC_INLINE_STATIC GC_STATIC __inline
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GC_FASTCALL
|
|
#define GC_FASTCALL __fastcall
|
|
#endif
|
|
|
|
#ifndef GC_CORE_API
|
|
#define GC_CORE_API extern
|
|
#endif
|
|
|
|
#ifndef GC_CORE_CALL
|
|
#define GC_CORE_CALL GC_CALL
|
|
#endif
|
|
|
|
#ifdef GC_PUSHREGS_BEGIN
|
|
#ifndef GC_PUSHREGS_END
|
|
#define GC_PUSHREGS_END (void)0
|
|
#endif
|
|
#else
|
|
#define GC_PUSHREGS_BEGIN jmp_buf buf; (void)setjmp(buf)
|
|
#ifndef GC_PUSHREGS_END
|
|
#define GC_PUSHREGS_END GC_noop1((GC_word)(&buf))
|
|
#endif
|
|
#endif
|
|
|
|
#define GC_MEM_BZERO(ptr, size) (void)memset(ptr, '\0', (size_t)(size))
|
|
|
|
#ifdef GC_THREADS
|
|
|
|
#ifdef GC_WIN32_THREADS
|
|
|
|
#ifndef GC_WIN32_CONTEXT_SP_NAME
|
|
#ifdef _M_AMD64
|
|
#define GC_WIN32_CONTEXT_SP_NAME Rsp
|
|
#else
|
|
#ifdef _M_X64
|
|
#define GC_WIN32_CONTEXT_SP_NAME Rsp
|
|
#else
|
|
#ifdef __x86_64
|
|
#define GC_WIN32_CONTEXT_SP_NAME Rsp
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GC_WIN32_CONTEXT_SP_NAME
|
|
#ifdef _M_ALPHA
|
|
#define GC_WIN32_CONTEXT_SP_NAME IntSp
|
|
#else
|
|
#ifdef _ALPHA_
|
|
#define GC_WIN32_CONTEXT_SP_NAME IntSp
|
|
#else
|
|
#ifdef _M_MRX000
|
|
#define GC_WIN32_CONTEXT_SP_NAME IntSp
|
|
#else
|
|
#ifdef _MIPS_
|
|
#define GC_WIN32_CONTEXT_SP_NAME IntSp
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GC_WIN32_CONTEXT_SP_NAME
|
|
#ifdef _M_ARM
|
|
#define GC_WIN32_CONTEXT_SP_NAME Sp
|
|
#else
|
|
#ifdef _ARM_
|
|
#define GC_WIN32_CONTEXT_SP_NAME Sp
|
|
#else
|
|
#ifdef _M_PPC
|
|
#define GC_WIN32_CONTEXT_SP_NAME Gpr1
|
|
#else
|
|
#ifdef _PPC_
|
|
#define GC_WIN32_CONTEXT_SP_NAME Gpr1
|
|
#else
|
|
#ifdef _M_SH
|
|
#define GC_WIN32_CONTEXT_SP_NAME R15
|
|
#else
|
|
#ifdef SHx
|
|
#define GC_WIN32_CONTEXT_SP_NAME R15
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GC_WIN32_CONTEXT_SP_NAME
|
|
#define GC_WIN32_CONTEXT_SP_NAME Esp /* x86 */
|
|
#endif
|
|
|
|
#ifndef GC_THREAD_YIELD
|
|
#define GC_THREAD_YIELD Sleep(10) /* "long" yield */
|
|
#endif
|
|
|
|
#define GC_THREAD_ID_T DWORD
|
|
|
|
#ifdef GC_WIN32_WCE
|
|
#define GC_THREAD_HANDLE(stkroot) ((HANDLE)(GC_word)(stkroot)->thread_id)
|
|
#else
|
|
#define GC_THREAD_HANDLE(stkroot) ((stkroot)->thread_handle)
|
|
#endif
|
|
|
|
#else /* GC_WIN32_THREADS */
|
|
|
|
#ifndef GC_SIG_SUSPEND
|
|
#ifdef SIGPWR
|
|
#define GC_SIG_SUSPEND SIGPWR
|
|
#else
|
|
#ifdef SIGUSR1
|
|
#define GC_SIG_SUSPEND SIGUSR1
|
|
#else
|
|
#define GC_SIG_SUSPEND SIGILL
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GC_CLIBDECL
|
|
#ifdef __CLIB
|
|
#define GC_CLIBDECL __CLIB
|
|
#else
|
|
#ifdef _USERENTRY
|
|
#define GC_CLIBDECL _USERENTRY
|
|
#else
|
|
#ifdef _RTL_FUNC
|
|
#define GC_CLIBDECL _RTL_FUNC
|
|
#else
|
|
#define GC_CLIBDECL __cdecl
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef GC_ASYNC_PUSHREGS_BEGIN
|
|
#ifndef GC_ASYNC_PUSHREGS_END
|
|
#define GC_ASYNC_PUSHREGS_END (void)0
|
|
#endif
|
|
#else
|
|
#define GC_ASYNC_PUSHREGS_BEGIN GC_PUSHREGS_BEGIN
|
|
#ifndef GC_ASYNC_PUSHREGS_END
|
|
#define GC_ASYNC_PUSHREGS_END GC_PUSHREGS_END
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GC_THREAD_MUTEX_DEFATTR
|
|
#ifdef pthread_mutexattr_default
|
|
#define GC_THREAD_MUTEX_DEFATTR pthread_mutexattr_default
|
|
#else
|
|
#define GC_THREAD_MUTEX_DEFATTR NULL
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GC_THREAD_YIELD
|
|
#define GC_THREAD_YIELD (void)sched_yield()
|
|
#endif
|
|
|
|
#ifndef GC_YIELD_MAX_ATTEMPT
|
|
#define GC_YIELD_MAX_ATTEMPT 2
|
|
#endif
|
|
|
|
#define GC_ERRNO_SET(value) (void)(errno = (value))
|
|
|
|
#define GC_THREAD_ID_T pthread_t
|
|
|
|
#endif /* ! GC_WIN32_THREADS */
|
|
|
|
#endif /* GC_THREADS */
|
|
|
|
#ifndef MARK_DESCR_OFFSET
|
|
#define MARK_DESCR_OFFSET sizeof(GC_word)
|
|
#endif
|
|
|
|
#ifndef GC_LOG2_OFFIGNORE
|
|
#define GC_LOG2_OFFIGNORE 8 /* must be at least 3 */
|
|
#endif
|
|
|
|
#ifndef GC_FREE_SPACE_DIVISOR
|
|
#define GC_FREE_SPACE_DIVISOR 3
|
|
#endif
|
|
|
|
#ifndef GC_MAX_RETRIES
|
|
#define GC_MAX_RETRIES 2
|
|
#endif
|
|
|
|
#ifndef GC_LAZYREFILL_COUNT
|
|
#define GC_LAZYREFILL_COUNT 10 /* must be at least 3 */
|
|
#endif
|
|
|
|
#ifndef GC_LAZYREFILL_BIGCNT
|
|
#define GC_LAZYREFILL_BIGCNT 1024
|
|
#endif
|
|
|
|
#ifdef GC_OMIT_REGISTER_KEYWORD
|
|
#define GC_REGISTER_KEYWORD /* empty */
|
|
#else
|
|
#define GC_REGISTER_KEYWORD register
|
|
#endif
|
|
|
|
#define GC_DEFAULT_LOG2_OBJSIZE 8
|
|
#define GC_DEFAULT_LOG2_SIZE 3
|
|
|
|
#define GC_MEM_SIZELIMIT ((GC_word)((~(size_t)0) >> 1) - ((GC_word)1 << (sizeof(int) << 1)))
|
|
|
|
#define GC_ATOMIC_MASK (((~(GC_word)0) >> 1) + 1)
|
|
|
|
#ifdef GC_GCJ_SUPPORT
|
|
#define GC_HASDSLEN_MASK (((GC_word)GC_ATOMIC_MASK) >> 1)
|
|
#else
|
|
#define GC_HASDSLEN_MASK 0
|
|
#endif
|
|
|
|
#define GC_NEVER_COLLECT (int)((((unsigned)-1) >> 1) + 1)
|
|
|
|
#ifndef GC_NO_DLINKS
|
|
#define GC_HIDE_POINTER(ptr) (~(GC_word)(ptr))
|
|
#endif
|
|
|
|
#define GC_RANDOM_SEED(gcdata) (((gcdata)->total_heapsize ^ (gcdata)->allocd_before_gc) + ((gcdata)->bytes_allocd ^ (gcdata)->marked_bytes) + ((gcdata)->free_bytes ^ (gcdata)->obj_htable.pending_free_size))
|
|
#define GC_HASH_INDEX(word_value, seed, log2_size) ((((word_value) ^ (seed)) * (GC_word)0x9E3779B1L) >> (sizeof(GC_word) * CHAR_BIT - (log2_size)))
|
|
#define GC_HASH_RESIZECOND(count, log2_size) (((GC_word)3 << ((log2_size) - 2)) <= (count))
|
|
|
|
#define GC_LEAVE(gcdata) GC_leave()
|
|
|
|
#ifdef GC_CORE_MALLOC
|
|
GC_CORE_API void *GC_CORE_CALL GC_CORE_MALLOC(size_t size);
|
|
#else
|
|
#define GC_CORE_MALLOC malloc
|
|
#endif
|
|
|
|
#ifdef GC_CORE_FREE
|
|
GC_CORE_API void GC_CORE_CALL GC_CORE_FREE(void *ptr);
|
|
#else
|
|
#define GC_CORE_FREE free
|
|
#endif
|
|
|
|
#ifdef GC_STACKBOTTOMVAR
|
|
extern char *GC_STACKBOTTOMVAR;
|
|
#endif
|
|
|
|
#ifdef GC_STACKLENVAR
|
|
extern GC_word GC_STACKLENVAR;
|
|
#endif
|
|
|
|
#ifndef GC_STACKBOTTOM
|
|
#ifdef GC_STACKBOTTOMVAR
|
|
#define GC_STACKBOTTOM GC_STACKBOTTOMVAR
|
|
#else
|
|
#define GC_STACKBOTTOM 0
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GC_STACKLEN
|
|
#ifdef GC_STACKLENVAR
|
|
#define GC_STACKLEN GC_STACKLENVAR
|
|
#else
|
|
#define GC_STACKLEN 0
|
|
#endif
|
|
#endif
|
|
|
|
struct GC_objlink_s
|
|
{
|
|
void *obj;
|
|
struct GC_objlink_s *next;
|
|
GC_word atomic_and_size;
|
|
};
|
|
|
|
struct GC_obj_htable_s
|
|
{
|
|
struct GC_objlink_s **hroots;
|
|
struct GC_objlink_s *free_list;
|
|
struct GC_objlink_s *marked_list;
|
|
struct GC_objlink_s *follow_list;
|
|
struct GC_objlink_s *unlinked_list;
|
|
GC_word min_obj_addr;
|
|
GC_word max_obj_addr;
|
|
GC_word count;
|
|
GC_word log2_size;
|
|
GC_word pending_free_size;
|
|
};
|
|
|
|
#ifndef GC_NO_DLINKS
|
|
|
|
struct GC_dlink_s
|
|
{
|
|
struct GC_dlink_s *next;
|
|
struct GC_objlink_s *objlink;
|
|
GC_word hidden_link;
|
|
};
|
|
|
|
struct GC_dlink_htable_s
|
|
{
|
|
struct GC_dlink_s **hroots;
|
|
struct GC_dlink_s *free_list;
|
|
GC_word count;
|
|
GC_word log2_size;
|
|
GC_word seed;
|
|
};
|
|
|
|
#endif /* ! GC_NO_DLINKS */
|
|
|
|
#ifndef GC_NO_FNLZ
|
|
|
|
struct GC_fnlz_s
|
|
{
|
|
struct GC_fnlz_s *next;
|
|
struct GC_objlink_s *objlink;
|
|
void *client_data;
|
|
GC_finalization_proc fn;
|
|
};
|
|
|
|
struct GC_fnlz_htable_s
|
|
{
|
|
struct GC_fnlz_s **hroots;
|
|
struct GC_fnlz_s *ready_fnlz;
|
|
struct GC_fnlz_s *single_free;
|
|
GC_word count;
|
|
GC_word log2_size;
|
|
GC_word seed;
|
|
int has_client_ptrs;
|
|
};
|
|
|
|
#endif /* ! GC_NO_FNLZ */
|
|
|
|
#ifndef GC_NO_INACTIVE
|
|
|
|
struct GC_activation_frame_s
|
|
{
|
|
GC_word inactive_sp;
|
|
CONST struct GC_activation_frame_s *prev;
|
|
};
|
|
|
|
#endif /* ! GC_NO_INACTIVE */
|
|
|
|
struct GC_dataroot_s
|
|
{
|
|
struct GC_dataroot_s *next;
|
|
GC_word begin_addr;
|
|
GC_word end_addr;
|
|
};
|
|
|
|
struct GC_stkroot_s
|
|
{
|
|
GC_word begin_addr;
|
|
GC_word end_addr;
|
|
#ifndef GC_NO_INACTIVE
|
|
CONST struct GC_activation_frame_s *activation_frame;
|
|
#endif
|
|
#ifdef GC_THREADS
|
|
struct GC_stkroot_s *next;
|
|
GC_THREAD_ID_T thread_id;
|
|
#ifdef GC_WIN32_THREADS
|
|
#ifndef GC_WIN32_WCE
|
|
HANDLE thread_handle;
|
|
#endif
|
|
#else
|
|
volatile int suspend_ack;
|
|
#endif
|
|
#endif
|
|
#ifndef GC_NO_INACTIVE
|
|
int inactive;
|
|
#endif
|
|
#ifndef GC_NO_FNLZ
|
|
int inside_fnlz;
|
|
#endif
|
|
};
|
|
|
|
#ifdef GC_THREADS
|
|
|
|
struct GC_stkroot_htable_s
|
|
{
|
|
struct GC_stkroot_s **hroots;
|
|
GC_word count;
|
|
GC_word log2_size;
|
|
GC_word seed;
|
|
};
|
|
|
|
#endif /* GC_THREADS */
|
|
|
|
struct GC_gcdata_s
|
|
{
|
|
struct GC_obj_htable_s obj_htable;
|
|
void *objlinks_block_list;
|
|
#ifndef GC_NO_DLINKS
|
|
struct GC_dlink_htable_s dlink_htable;
|
|
#endif
|
|
#ifndef GC_NO_FNLZ
|
|
struct GC_fnlz_htable_s fnlz_htable;
|
|
GC_word notifier_gc_no;
|
|
GC_word bytes_finalized;
|
|
#endif
|
|
struct GC_stkroot_s *cur_stack;
|
|
struct GC_dataroot_s *dataroots;
|
|
GC_word dataroot_size;
|
|
GC_word expanded_heapsize;
|
|
GC_word total_heapsize;
|
|
GC_word allocd_before_gc;
|
|
GC_word bytes_allocd;
|
|
GC_word marked_bytes;
|
|
GC_word free_bytes;
|
|
GC_word followscan_size;
|
|
#ifdef GC_THREADS
|
|
struct GC_stkroot_htable_s stkroot_htable;
|
|
#endif
|
|
int recycling;
|
|
#ifdef GC_GCJ_SUPPORT
|
|
#ifndef GC_GETENV_SKIP
|
|
#ifndef GC_IGNORE_GCJ_INFO
|
|
int ignore_gcj_info;
|
|
#endif
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
volatile GC_word GC_noop_sink;
|
|
|
|
GC_DATASTATIC GC_word GC_gc_no = 0;
|
|
|
|
GC_DATASTATIC GC_word GC_free_space_divisor = (GC_FREE_SPACE_DIVISOR);
|
|
GC_DATASTATIC GC_word GC_max_retries = (GC_MAX_RETRIES);
|
|
|
|
GC_DATASTATIC GC_finalizer_notifier_proc GC_finalizer_notifier = 0;
|
|
|
|
GC_DATASTATIC GC_start_callback_proc GC_start_call_back = 0;
|
|
|
|
#ifdef ALL_INTERIOR_POINTERS
|
|
GC_DATASTATIC int GC_all_interior_pointers = 1;
|
|
#else
|
|
GC_DATASTATIC int GC_all_interior_pointers = 0;
|
|
#endif
|
|
|
|
#ifdef FINALIZE_ON_DEMAND
|
|
GC_DATASTATIC int GC_finalize_on_demand = 1;
|
|
#else
|
|
GC_DATASTATIC int GC_finalize_on_demand = 0;
|
|
#endif
|
|
|
|
GC_DATASTATIC int GC_dont_gc = 0;
|
|
|
|
#ifdef GC_DONT_EXPAND
|
|
GC_DATASTATIC int GC_dont_expand = 1;
|
|
#else
|
|
GC_DATASTATIC int GC_dont_expand = 0;
|
|
#endif
|
|
|
|
GC_STATIC int GC_CALLBACK GC_never_stop_func(void);
|
|
GC_DATASTATIC GC_stop_func GC_default_stop_func = GC_never_stop_func;
|
|
|
|
#ifndef GC_MISC_EXCLUDE
|
|
GC_STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word arg);
|
|
GC_DATASTATIC GC_warn_proc GC_current_warn_proc =
|
|
GC_default_warn_proc; /* ignored */
|
|
#endif
|
|
|
|
GC_DATASTATIC int GC_stack_grows_up = 0;
|
|
|
|
GC_DATASTATIC struct GC_gcdata_s *GC_gcdata_global = NULL;
|
|
|
|
GC_DATASTATIC GC_word GC_max_heapsize = ~(GC_word)0;
|
|
|
|
GC_DATASTATIC CONST struct GC_objlink_s GC_nil_objlink = { NULL, NULL, 0 };
|
|
|
|
#ifndef GC_NO_DLINKS
|
|
GC_DATASTATIC CONST struct GC_dlink_s GC_nil_dlink = { NULL, NULL, 0 };
|
|
#endif
|
|
|
|
#ifndef GC_NO_FNLZ
|
|
GC_DATASTATIC CONST struct GC_fnlz_s GC_nil_fnlz = { NULL, NULL, NULL, 0 };
|
|
#endif
|
|
|
|
#ifdef GC_PRINT_MSGS
|
|
GC_DATASTATIC int GC_verbose_gc = 0;
|
|
#endif
|
|
|
|
#ifdef GC_THREADS
|
|
|
|
#ifdef GC_WIN32_THREADS
|
|
|
|
struct GC_mutex_s
|
|
{
|
|
LONG state;
|
|
HANDLE event;
|
|
};
|
|
|
|
GC_DATASTATIC struct GC_mutex_s GC_allocate_ml = { 0, 0 };
|
|
|
|
#else /* GC_WIN32_THREADS */
|
|
|
|
volatile int GC_inside_collect = -1;
|
|
|
|
GC_DATASTATIC pthread_mutex_t GC_allocate_ml;
|
|
|
|
GC_STATIC void GC_CLIBDECL GC_suspend_handler(int sig);
|
|
|
|
#endif /* ! GC_WIN32_THREADS */
|
|
|
|
GC_STATIC void GC_FASTCALL GC_stkroot_add(struct GC_gcdata_s *gcdata,
|
|
GC_THREAD_ID_T thread_id, struct GC_stkroot_s *new_stkroot);
|
|
GC_STATIC void GC_FASTCALL GC_stkroot_tblresize(struct GC_gcdata_s *gcdata,
|
|
struct GC_stkroot_s **new_hroots, GC_word new_log2_size);
|
|
|
|
#else /* GC_THREADS */
|
|
|
|
GC_DATASTATIC int GC_allocate_ml = 0;
|
|
|
|
#endif /* ! GC_THREADS */
|
|
|
|
GC_STATIC void *GC_FASTCALL GC_alloc_hroots(struct GC_gcdata_s *gcdata,
|
|
GC_word new_log2_size, CONST void *nil_ptr);
|
|
GC_STATIC void *GC_FASTCALL GC_core_malloc_with_gc(struct GC_gcdata_s *gcdata,
|
|
GC_word size, int *pres);
|
|
GC_STATIC int GC_FASTCALL GC_heap_expand(struct GC_gcdata_s *gcdata,
|
|
GC_word incsize);
|
|
GC_STATIC void *GC_FASTCALL GC_inner_core_malloc(struct GC_gcdata_s *gcdata,
|
|
GC_word size, int dont_expand);
|
|
GC_STATIC int GC_FASTCALL GC_roots_add(struct GC_gcdata_s *gcdata,
|
|
GC_word begin_addr, GC_word end_addr);
|
|
|
|
void GC_noop1(GC_word value)
|
|
{
|
|
GC_noop_sink = value;
|
|
}
|
|
|
|
GC_word GC_approx_sp(void)
|
|
{
|
|
volatile GC_word value;
|
|
value = (GC_word)(&value);
|
|
GC_noop1(value);
|
|
return value;
|
|
}
|
|
|
|
GC_STATIC int GC_FASTCALL GC_roots_autodetect(struct GC_gcdata_s *gcdata)
|
|
{
|
|
int res = GC_roots_add(gcdata, 0, 0);
|
|
#ifdef GC_DATASTART
|
|
#ifdef GC_DATAEND
|
|
res |= GC_roots_add(gcdata, (GC_word)GC_DATASTART, (GC_word)GC_DATAEND);
|
|
#endif
|
|
#endif
|
|
#ifdef GC_DATASTART2
|
|
#ifdef GC_DATAEND2
|
|
res |= GC_roots_add(gcdata, (GC_word)GC_DATASTART2, (GC_word)GC_DATAEND2);
|
|
#endif
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
GC_INLINE_STATIC GC_word GC_FASTCALL GC_stack_detectbase(void)
|
|
{
|
|
return (GC_word)(GC_STACKBOTTOM) + (GC_STACKLEN);
|
|
}
|
|
|
|
GC_INLINE_STATIC GC_word GC_FASTCALL GC_stack_approx_size(
|
|
CONST struct GC_gcdata_s *gcdata)
|
|
{
|
|
struct GC_stkroot_s *cur_stack = gcdata->cur_stack;
|
|
GC_word totalsize = cur_stack != NULL ? cur_stack->end_addr -
|
|
cur_stack->begin_addr : sizeof(GC_word);
|
|
#ifdef GC_THREADS
|
|
totalsize = gcdata->stkroot_htable.count * totalsize;
|
|
#endif
|
|
return totalsize;
|
|
}
|
|
|
|
GC_INLINE_STATIC int GC_FASTCALL GC_guess_collect(
|
|
CONST struct GC_gcdata_s *gcdata, GC_word objsize)
|
|
{
|
|
return (((GC_stack_approx_size(gcdata) + gcdata->followscan_size) << 1) +
|
|
gcdata->dataroot_size +
|
|
((GC_word)sizeof(GC_word) << gcdata->obj_htable.log2_size) +
|
|
#ifndef GC_NO_DLINKS
|
|
((GC_word)sizeof(GC_word) << gcdata->dlink_htable.log2_size) +
|
|
#endif
|
|
((gcdata->marked_bytes + gcdata->bytes_allocd -
|
|
gcdata->followscan_size) >> 2)) / GC_free_space_divisor <=
|
|
#ifndef GC_NO_FNLZ
|
|
gcdata->bytes_finalized +
|
|
#endif
|
|
gcdata->bytes_allocd + objsize ? 1 : 0;
|
|
}
|
|
|
|
GC_INLINE_STATIC GC_word GC_FASTCALL GC_guess_expand_size(
|
|
CONST struct GC_gcdata_s *gcdata, GC_word objsize)
|
|
{
|
|
GC_word space_divisor = GC_free_space_divisor + 1;
|
|
return (gcdata->marked_bytes + gcdata->bytes_allocd) / space_divisor >=
|
|
gcdata->free_bytes ? gcdata->free_bytes * space_divisor +
|
|
(gcdata->bytes_allocd >> 3) + (objsize << 2) +
|
|
gcdata->dataroot_size : 0;
|
|
}
|
|
|
|
GC_STATIC void GC_FASTCALL GC_abort_badptr(CONST void *ptr)
|
|
{
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr, " GC: Illegal pointer specified: 0x%lX." GC_NEW_LINE,
|
|
(unsigned long)((GC_word)ptr));
|
|
#else
|
|
GC_noop1((GC_word)ptr);
|
|
#endif
|
|
GC_FATAL_ABORT;
|
|
}
|
|
|
|
GC_INLINE_STATIC int GC_FASTCALL GC_config_set(struct GC_gcdata_s *gcdata)
|
|
{
|
|
int res = 0;
|
|
#ifdef GC_GETENV_SKIP
|
|
#ifdef GC_PRINT_MSGS
|
|
GC_verbose_gc = 1;
|
|
#endif
|
|
GC_noop1((GC_word)gcdata);
|
|
#else
|
|
char *str;
|
|
GC_word value;
|
|
if ((str = getenv("GC_ALL_INTERIOR_POINTERS")) != NULL && *str)
|
|
GC_all_interior_pointers = *str != '0' || *(str + 1) ? 1 : 0;
|
|
if ((str = getenv("GC_DONT_GC")) != NULL && *str)
|
|
GC_dont_gc = GC_NEVER_COLLECT;
|
|
#ifdef GC_GCJ_SUPPORT
|
|
#ifndef GC_IGNORE_GCJ_INFO
|
|
if ((str = getenv("GC_IGNORE_GCJ_INFO")) != NULL && *str)
|
|
gcdata->ignore_gcj_info = 1;
|
|
#endif
|
|
#endif
|
|
#ifdef GC_PRINT_MSGS
|
|
if ((str = getenv("GC_PRINT_STATS")) != NULL && *str)
|
|
GC_verbose_gc = 1;
|
|
#endif
|
|
if (((str = getenv("GC_FREE_SPACE_DIVISOR")) != NULL && *str &&
|
|
((GC_free_space_divisor = (GC_word)atol(str)) == 0 ||
|
|
GC_free_space_divisor == ~(GC_word)0)) ||
|
|
((str = getenv("GC_MAXIMUM_HEAP_SIZE")) != NULL && *str &&
|
|
(GC_max_heapsize = (GC_word)atol(str)) == 0) ||
|
|
((str = getenv("GC_INITIAL_HEAP_SIZE")) != NULL && *str &&
|
|
((value = (GC_word)atol(str)) - (GC_word)1 >= GC_max_heapsize ||
|
|
(gcdata->total_heapsize < value && GC_heap_expand(gcdata,
|
|
value - gcdata->total_heapsize) < 0))))
|
|
res = -1;
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
GC_STATIC int GC_CALLBACK GC_never_stop_func(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
GC_API GC_word GC_CALL GC_get_gc_no(void)
|
|
{
|
|
return GC_gc_no;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_finalize_on_demand(int value)
|
|
{
|
|
GC_finalize_on_demand = value;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_java_finalization(int value)
|
|
{
|
|
if (!value)
|
|
GC_abort_badptr(NULL);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_max_heap_size(GC_word size)
|
|
{
|
|
GC_max_heapsize = size ? size : ~(GC_word)0;
|
|
}
|
|
|
|
#ifndef GC_MISC_EXCLUDE
|
|
|
|
GC_API void GC_CALL GC_set_free_space_divisor(GC_word value)
|
|
{
|
|
if (!value || value == ~(GC_word)0)
|
|
GC_abort_badptr(NULL);
|
|
GC_free_space_divisor = value;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_all_interior_pointers(int value)
|
|
{
|
|
GC_all_interior_pointers = value;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_dont_expand(int value)
|
|
{
|
|
GC_dont_expand = value;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_no_dls(int value)
|
|
{
|
|
/* dummy */
|
|
GC_noop1((GC_word)value);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_dont_precollect(int value)
|
|
{
|
|
/* dummy */
|
|
GC_noop1((GC_word)value);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int value)
|
|
{
|
|
/* dummy */
|
|
GC_noop1((GC_word)value);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_max_retries(GC_word value)
|
|
{
|
|
GC_max_retries = value;
|
|
}
|
|
|
|
GC_STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word arg)
|
|
{
|
|
/* dummy */
|
|
GC_noop1((GC_word)msg ^ arg);
|
|
}
|
|
|
|
GC_API void GC_CALLBACK GC_ignore_warn_proc(char *msg, GC_word arg)
|
|
{
|
|
GC_default_warn_proc(msg, arg);
|
|
}
|
|
|
|
#endif /* ! GC_MISC_EXCLUDE */
|
|
|
|
GC_API void *GC_CALL GC_call_with_stack_base(GC_stack_base_func fn,
|
|
void *client_data)
|
|
{
|
|
GC_word stack_data;
|
|
struct GC_stack_base sb;
|
|
sb.mem_base = (void *)&stack_data;
|
|
return (*fn)(&sb, client_data);
|
|
}
|
|
|
|
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
|
|
{
|
|
if (sb == NULL)
|
|
GC_abort_badptr(NULL);
|
|
return GC_UNIMPLEMENTED;
|
|
}
|
|
|
|
GC_INLINE_STATIC struct GC_gcdata_s *GC_FASTCALL GC_gcdata_alloc(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
if ((gcdata = GC_CORE_MALLOC(sizeof(struct GC_gcdata_s))) != NULL)
|
|
{
|
|
GC_MEM_BZERO(gcdata, sizeof(struct GC_gcdata_s));
|
|
if ((gcdata->obj_htable.hroots = GC_alloc_hroots(gcdata,
|
|
GC_DEFAULT_LOG2_OBJSIZE, &GC_nil_objlink)) != NULL
|
|
#ifndef GC_NO_DLINKS
|
|
&& (gcdata->dlink_htable.hroots = GC_alloc_hroots(gcdata,
|
|
GC_DEFAULT_LOG2_SIZE, &GC_nil_dlink)) != NULL
|
|
#endif
|
|
#ifndef GC_NO_FNLZ
|
|
&& (gcdata->fnlz_htable.hroots = GC_alloc_hroots(gcdata,
|
|
GC_DEFAULT_LOG2_SIZE, &GC_nil_fnlz)) != NULL
|
|
#endif
|
|
#ifdef GC_THREADS
|
|
&& (gcdata->stkroot_htable.hroots = GC_alloc_hroots(gcdata,
|
|
GC_DEFAULT_LOG2_SIZE, NULL)) != NULL
|
|
#endif
|
|
)
|
|
{
|
|
gcdata->obj_htable.min_obj_addr = ~(GC_word)0;
|
|
gcdata->obj_htable.max_obj_addr = (GC_word)1 << GC_LOG2_OFFIGNORE;
|
|
gcdata->obj_htable.log2_size = GC_DEFAULT_LOG2_OBJSIZE;
|
|
#ifndef GC_NO_DLINKS
|
|
gcdata->dlink_htable.log2_size = GC_DEFAULT_LOG2_SIZE;
|
|
#endif
|
|
#ifndef GC_NO_FNLZ
|
|
gcdata->fnlz_htable.log2_size = GC_DEFAULT_LOG2_SIZE;
|
|
gcdata->notifier_gc_no = GC_gc_no;
|
|
#endif
|
|
#ifdef GC_THREADS
|
|
gcdata->stkroot_htable.log2_size = GC_DEFAULT_LOG2_SIZE;
|
|
#endif
|
|
}
|
|
else gcdata = NULL;
|
|
}
|
|
return gcdata;
|
|
}
|
|
|
|
GC_STATIC int GC_FASTCALL GC_heap_expand(struct GC_gcdata_s *gcdata,
|
|
GC_word incsize)
|
|
{
|
|
void *ptr;
|
|
GC_word free_bytes = gcdata->free_bytes;
|
|
GC_word total_heapsize = gcdata->total_heapsize;
|
|
GC_word max_heapsize;
|
|
int res = -1;
|
|
incsize = incsize > ((GC_word)sizeof(GC_word) << GC_LOG2_OFFIGNORE) ?
|
|
(incsize + (sizeof(GC_word) - 1)) & ~(sizeof(GC_word) - 1) :
|
|
(GC_word)sizeof(GC_word) << GC_LOG2_OFFIGNORE;
|
|
if (free_bytes + incsize <= GC_MEM_SIZELIMIT)
|
|
{
|
|
gcdata->expanded_heapsize = total_heapsize + incsize;
|
|
if ((max_heapsize = GC_max_heapsize) > total_heapsize)
|
|
{
|
|
if (max_heapsize - total_heapsize < incsize)
|
|
incsize = max_heapsize - total_heapsize;
|
|
#ifdef GC_PRINT_MSGS
|
|
if (GC_verbose_gc)
|
|
fprintf(stdout,
|
|
"[GC: Expand by %lu KiB after %lu KiB allocd, %lu KiB free of %lu KiB]"
|
|
GC_NEW_LINE, GC_SIZE_TO_ULKB(incsize),
|
|
GC_SIZE_TO_ULKB(gcdata->bytes_allocd), GC_SIZE_TO_ULKB(free_bytes),
|
|
GC_SIZE_TO_ULKB(total_heapsize));
|
|
#endif
|
|
while ((ptr = GC_CORE_MALLOC((size_t)free_bytes +
|
|
(size_t)incsize)) == NULL)
|
|
if ((incsize = incsize >> 1) == 0)
|
|
break;
|
|
if (ptr != NULL)
|
|
{
|
|
total_heapsize += incsize;
|
|
GC_CORE_FREE(ptr);
|
|
gcdata->expanded_heapsize = total_heapsize;
|
|
gcdata->total_heapsize = total_heapsize;
|
|
gcdata->free_bytes = free_bytes + incsize;
|
|
res = 0;
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifndef GC_MISC_EXCLUDE
|
|
|
|
GC_STATIC void GC_FASTCALL GC_roots_del_inside(struct GC_gcdata_s *gcdata,
|
|
GC_word begin_addr, GC_word end_addr)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_dataroot_s *dataroot;
|
|
GC_REGISTER_KEYWORD struct GC_dataroot_s **pnext = &gcdata->dataroots;
|
|
struct GC_dataroot_s *pred;
|
|
GC_word count;
|
|
while ((dataroot = *pnext) != NULL && dataroot->begin_addr < begin_addr)
|
|
pnext = &dataroot->next;
|
|
if (dataroot != NULL)
|
|
{
|
|
count = 0;
|
|
do
|
|
{
|
|
if (end_addr < dataroot->end_addr)
|
|
break;
|
|
gcdata->dataroot_size -= dataroot->end_addr - dataroot->begin_addr;
|
|
dataroot = (pred = dataroot)->next;
|
|
GC_CORE_FREE(pred);
|
|
count++;
|
|
} while (dataroot != NULL);
|
|
*pnext = dataroot;
|
|
gcdata->free_bytes += count * sizeof(struct GC_dataroot_s);
|
|
}
|
|
}
|
|
|
|
GC_INLINE_STATIC int GC_FASTCALL GC_roots_exclude(struct GC_gcdata_s *gcdata,
|
|
GC_word begin_addr, GC_word end_addr)
|
|
{
|
|
struct GC_dataroot_s *dataroot = gcdata->dataroots;
|
|
struct GC_dataroot_s *new_dataroot;
|
|
int res = 0;
|
|
while (dataroot != NULL && begin_addr >= dataroot->end_addr)
|
|
dataroot = dataroot->next;
|
|
if (dataroot != NULL && dataroot->begin_addr < end_addr)
|
|
{
|
|
if (dataroot->begin_addr < begin_addr)
|
|
{
|
|
if (end_addr < dataroot->end_addr)
|
|
{
|
|
new_dataroot = GC_core_malloc_with_gc(gcdata,
|
|
sizeof(struct GC_dataroot_s), &res);
|
|
res = -1;
|
|
if (new_dataroot != NULL)
|
|
{
|
|
gcdata->dataroot_size -= end_addr - begin_addr;
|
|
new_dataroot->begin_addr = end_addr;
|
|
new_dataroot->end_addr = dataroot->end_addr;
|
|
new_dataroot->next = dataroot->next;
|
|
dataroot->end_addr = begin_addr;
|
|
dataroot->next = new_dataroot;
|
|
res = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gcdata->dataroot_size -= dataroot->end_addr - begin_addr;
|
|
dataroot->end_addr = begin_addr;
|
|
if ((dataroot = dataroot->next) != NULL &&
|
|
dataroot->begin_addr < end_addr && end_addr < dataroot->end_addr)
|
|
{
|
|
gcdata->dataroot_size -= end_addr - dataroot->begin_addr;
|
|
dataroot->begin_addr = end_addr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gcdata->dataroot_size -= end_addr - dataroot->begin_addr;
|
|
dataroot->begin_addr = end_addr;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#endif /* ! GC_MISC_EXCLUDE */
|
|
|
|
GC_STATIC int GC_FASTCALL GC_roots_add(struct GC_gcdata_s *gcdata,
|
|
GC_word begin_addr, GC_word end_addr)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_dataroot_s *dataroot;
|
|
struct GC_dataroot_s *new_dataroot;
|
|
struct GC_dataroot_s **pnext;
|
|
int res = 0;
|
|
if (begin_addr)
|
|
{
|
|
begin_addr = (begin_addr + (sizeof(GC_word) - 1)) & ~(sizeof(GC_word) - 1);
|
|
if ((end_addr = end_addr & ~(sizeof(GC_word) - 1)) > begin_addr)
|
|
{
|
|
pnext = &gcdata->dataroots;
|
|
while ((dataroot = *pnext) != NULL && dataroot->end_addr < begin_addr)
|
|
pnext = &dataroot->next;
|
|
if (dataroot == NULL || end_addr < dataroot->begin_addr)
|
|
{
|
|
new_dataroot = GC_core_malloc_with_gc(gcdata,
|
|
sizeof(struct GC_dataroot_s), &res);
|
|
res = -1;
|
|
if (new_dataroot != NULL)
|
|
{
|
|
gcdata->dataroot_size += end_addr - begin_addr;
|
|
new_dataroot->begin_addr = begin_addr;
|
|
new_dataroot->end_addr = end_addr;
|
|
new_dataroot->next = dataroot;
|
|
*pnext = new_dataroot;
|
|
res = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (begin_addr < dataroot->begin_addr)
|
|
{
|
|
gcdata->dataroot_size += dataroot->begin_addr - begin_addr;
|
|
dataroot->begin_addr = begin_addr;
|
|
}
|
|
if (dataroot->end_addr < end_addr)
|
|
{
|
|
dataroot = dataroot->next;
|
|
while (dataroot != NULL && end_addr >= dataroot->begin_addr)
|
|
{
|
|
if (dataroot->end_addr >= end_addr)
|
|
end_addr = dataroot->end_addr;
|
|
gcdata->dataroot_size -= dataroot->end_addr - dataroot->begin_addr;
|
|
dataroot = (new_dataroot = dataroot)->next;
|
|
GC_CORE_FREE(new_dataroot);
|
|
gcdata->free_bytes += sizeof(struct GC_dataroot_s);
|
|
}
|
|
(new_dataroot = *pnext)->next = dataroot;
|
|
gcdata->dataroot_size += end_addr - new_dataroot->end_addr;
|
|
new_dataroot->end_addr = end_addr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifdef GC_WIN32_THREADS
|
|
|
|
GC_INLINE_STATIC int GC_FASTCALL GC_win32_block_on_mutex(
|
|
struct GC_mutex_s *pmutex)
|
|
{
|
|
while (InterlockedExchange(&pmutex->state, -1))
|
|
if (WaitForSingleObject(pmutex->event, INFINITE) == WAIT_FAILED)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
#endif /* GC_WIN32_THREADS */
|
|
|
|
GC_STATIC int GC_FASTCALL GC_enter(struct GC_gcdata_s **pgcdata)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_gcdata_s *gcdata;
|
|
GC_REGISTER_KEYWORD struct GC_stkroot_s *cur_stack;
|
|
int res;
|
|
#ifdef GC_THREADS
|
|
struct GC_stkroot_s **new_hroots;
|
|
GC_THREAD_ID_T thread_id;
|
|
GC_word new_log2_size;
|
|
if (
|
|
#ifdef GC_WIN32_THREADS
|
|
(!GC_allocate_ml.event && (GC_allocate_ml.event =
|
|
CreateEvent(NULL, (BOOL)0, (BOOL)0, NULL)) == 0) ||
|
|
(InterlockedExchange(&GC_allocate_ml.state, 1) &&
|
|
GC_win32_block_on_mutex(&GC_allocate_ml) < 0) ||
|
|
(thread_id = GetCurrentThreadId()) == (GC_THREAD_ID_T)-1L
|
|
#else
|
|
(GC_inside_collect == -1 && (pthread_mutex_init(&GC_allocate_ml,
|
|
GC_THREAD_MUTEX_DEFATTR) ? 1 : (GC_inside_collect = 0))) ||
|
|
pthread_mutex_lock(&GC_allocate_ml) ||
|
|
(thread_id = pthread_self()) == (pthread_t)(~(GC_word)0)
|
|
#endif
|
|
)
|
|
{
|
|
*(GC_THREAD_ID_T volatile *)&thread_id = 0;
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr, " GC: Cannot initialize or lock mutex!" GC_NEW_LINE);
|
|
#endif
|
|
GC_FATAL_ABORT;
|
|
}
|
|
#else
|
|
if (++GC_allocate_ml != 1)
|
|
{
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr, " GC: Not re-entrant!" GC_NEW_LINE);
|
|
#endif
|
|
GC_FATAL_ABORT;
|
|
}
|
|
res = GC_UNIMPLEMENTED;
|
|
#endif
|
|
if ((gcdata = GC_gcdata_global) == NULL)
|
|
{
|
|
if ((gcdata = GC_gcdata_alloc()) == NULL || GC_config_set(gcdata) < 0 ||
|
|
GC_roots_autodetect(gcdata) < 0 || (gcdata->cur_stack =
|
|
GC_inner_core_malloc(gcdata, sizeof(struct GC_stkroot_s), 0)) == NULL)
|
|
{
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr,
|
|
" GC: Cannot startup - bad config params or no memory!" GC_NEW_LINE);
|
|
#endif
|
|
GC_FATAL_ABORT;
|
|
}
|
|
if (GC_approx_sp() > (GC_word)pgcdata)
|
|
GC_stack_grows_up = 1;
|
|
cur_stack = gcdata->cur_stack;
|
|
#ifndef GC_NO_INACTIVE
|
|
cur_stack->activation_frame = NULL;
|
|
cur_stack->inactive = 0;
|
|
#endif
|
|
#ifndef GC_NO_FNLZ
|
|
cur_stack->inside_fnlz = 0;
|
|
#endif
|
|
cur_stack->begin_addr = (cur_stack->end_addr = GC_stack_detectbase()) != 0 ?
|
|
cur_stack->end_addr : ~(GC_word)0;
|
|
#ifdef GC_THREADS
|
|
GC_stkroot_add(gcdata, thread_id, cur_stack);
|
|
res = GC_SUCCESS;
|
|
#endif
|
|
GC_gcdata_global = gcdata;
|
|
}
|
|
else
|
|
{
|
|
cur_stack = gcdata->cur_stack;
|
|
#ifdef GC_THREADS
|
|
res = GC_DUPLICATE;
|
|
if (cur_stack == NULL || cur_stack->thread_id != thread_id)
|
|
{
|
|
cur_stack =
|
|
gcdata->stkroot_htable.hroots[GC_HASH_INDEX((GC_word)thread_id,
|
|
gcdata->stkroot_htable.seed, gcdata->stkroot_htable.log2_size)];
|
|
while (cur_stack != NULL && cur_stack->thread_id != thread_id)
|
|
cur_stack = cur_stack->next;
|
|
if ((gcdata->cur_stack = cur_stack) == NULL)
|
|
{
|
|
if (GC_HASH_RESIZECOND(gcdata->stkroot_htable.count,
|
|
gcdata->stkroot_htable.log2_size) &&
|
|
(new_hroots = GC_alloc_hroots(gcdata, new_log2_size =
|
|
gcdata->stkroot_htable.log2_size + 1, NULL)) != NULL)
|
|
GC_stkroot_tblresize(gcdata, new_hroots, new_log2_size);
|
|
res = 0;
|
|
if ((cur_stack = GC_core_malloc_with_gc(gcdata,
|
|
sizeof(struct GC_stkroot_s), &res)) == NULL)
|
|
{
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr, " GC: Cannot register new thread!" GC_NEW_LINE);
|
|
#endif
|
|
GC_FATAL_ABORT;
|
|
}
|
|
#ifndef GC_NO_INACTIVE
|
|
cur_stack->activation_frame = NULL;
|
|
cur_stack->inactive = 0;
|
|
#endif
|
|
#ifndef GC_NO_FNLZ
|
|
cur_stack->inside_fnlz = 0;
|
|
#endif
|
|
cur_stack->begin_addr = ~(GC_word)0;
|
|
cur_stack->end_addr = 0;
|
|
GC_stkroot_add(gcdata, thread_id, cur_stack);
|
|
gcdata->cur_stack = cur_stack;
|
|
res = GC_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (cur_stack->begin_addr >= (GC_word)pgcdata)
|
|
cur_stack->begin_addr = (GC_word)pgcdata - sizeof(GC_word);
|
|
if ((GC_word)pgcdata >= cur_stack->end_addr)
|
|
cur_stack->end_addr = (GC_word)pgcdata + (sizeof(GC_word) << 1);
|
|
*pgcdata = gcdata;
|
|
return res;
|
|
}
|
|
|
|
GC_INLINE_STATIC void GC_FASTCALL GC_leave(void)
|
|
{
|
|
#ifdef GC_THREADS
|
|
#ifdef GC_WIN32_THREADS
|
|
if (InterlockedExchange(&GC_allocate_ml.state, 0) < 0 &&
|
|
!SetEvent(GC_allocate_ml.event))
|
|
GC_FATAL_ABORT;
|
|
#else
|
|
if (pthread_mutex_unlock(&GC_allocate_ml))
|
|
GC_FATAL_ABORT;
|
|
#endif
|
|
#else
|
|
GC_allocate_ml = 0;
|
|
#endif
|
|
}
|
|
|
|
#ifndef GC_NO_INACTIVE
|
|
|
|
GC_INLINE_STATIC int GC_FASTCALL GC_set_inactive_sp(
|
|
struct GC_gcdata_s **pgcdata)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_stkroot_s *cur_stack;
|
|
if ((cur_stack = (*pgcdata)->cur_stack) == NULL || cur_stack->inactive)
|
|
return 0;
|
|
if (GC_stack_grows_up)
|
|
cur_stack->end_addr = (GC_word)pgcdata - sizeof(GC_word);
|
|
else cur_stack->begin_addr = (GC_word)pgcdata + (sizeof(GC_word) << 1);
|
|
cur_stack->inactive = 1;
|
|
return 1;
|
|
}
|
|
|
|
GC_INLINE_STATIC int GC_FASTCALL GC_set_activation_frame(
|
|
struct GC_activation_frame_s *activation_frame,
|
|
struct GC_stkroot_s *cur_stack)
|
|
{
|
|
if (cur_stack == NULL || !cur_stack->inactive)
|
|
return 0;
|
|
activation_frame->inactive_sp = GC_stack_grows_up ? cur_stack->end_addr :
|
|
cur_stack->begin_addr;
|
|
activation_frame->prev = cur_stack->activation_frame;
|
|
cur_stack->inactive = 0;
|
|
cur_stack->activation_frame = activation_frame;
|
|
return 1;
|
|
}
|
|
|
|
GC_INLINE_STATIC void GC_FASTCALL GC_restore_inactive_sp(
|
|
struct GC_stkroot_s *cur_stack,
|
|
CONST struct GC_activation_frame_s *activation_frame)
|
|
{
|
|
cur_stack->activation_frame = activation_frame->prev;
|
|
*(GC_stack_grows_up ? &cur_stack->end_addr :
|
|
&cur_stack->begin_addr) = activation_frame->inactive_sp;
|
|
cur_stack->inactive = 1;
|
|
}
|
|
|
|
#endif /* ! GC_NO_INACTIVE */
|
|
|
|
#ifdef GC_THREADS
|
|
|
|
#ifndef GC_WIN32_THREADS
|
|
|
|
GC_STATIC void GC_FASTCALL GC_thread_yield(int attempt)
|
|
{
|
|
if (attempt >= GC_YIELD_MAX_ATTEMPT)
|
|
(void)pthread_usleep_np((unsigned)(attempt - GC_YIELD_MAX_ATTEMPT) * 1000);
|
|
else GC_THREAD_YIELD;
|
|
}
|
|
|
|
#endif /* ! GC_WIN32_THREADS */
|
|
|
|
#endif /* GC_THREADS */
|
|
|
|
GC_STATIC void GC_FASTCALL GC_mutator_suspend(struct GC_gcdata_s *gcdata)
|
|
{
|
|
#ifdef GC_THREADS
|
|
GC_REGISTER_KEYWORD GC_word addr;
|
|
GC_REGISTER_KEYWORD struct GC_stkroot_s *stkroot;
|
|
#ifndef GC_WIN32_THREADS
|
|
struct GC_stkroot_s **pnext;
|
|
#endif
|
|
struct GC_stkroot_s *cur_stack;
|
|
#ifndef GC_WIN32_THREADS
|
|
int attempt;
|
|
GC_inside_collect = 1;
|
|
#endif
|
|
if ((GC_word)((cur_stack = gcdata->cur_stack) != NULL ? 1 : 0) <
|
|
gcdata->stkroot_htable.count)
|
|
{
|
|
addr = (GC_word)gcdata->stkroot_htable.hroots - sizeof(GC_word);
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(void **)(addr += sizeof(GC_word)) != NULL)
|
|
break;
|
|
}
|
|
if ((GC_word)(stkroot = *(struct GC_stkroot_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
#ifdef GC_WIN32_THREADS
|
|
do
|
|
{
|
|
if (stkroot != cur_stack
|
|
#ifndef GC_NO_INACTIVE
|
|
&& !stkroot->inactive
|
|
#endif
|
|
)
|
|
{
|
|
#ifdef GC_WIN32_WCE
|
|
while (SuspendThread(GC_THREAD_HANDLE(stkroot)) == ~(DWORD)0)
|
|
GC_THREAD_YIELD;
|
|
#else
|
|
if (SuspendThread(stkroot->thread_handle) == ~(DWORD)0)
|
|
{
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr, " GC: Cannot suspend thread!" GC_NEW_LINE);
|
|
#endif
|
|
GC_FATAL_ABORT;
|
|
}
|
|
#endif
|
|
}
|
|
} while ((stkroot = stkroot->next) != NULL);
|
|
#else
|
|
pnext = (struct GC_stkroot_s **)addr;
|
|
do
|
|
{
|
|
if (stkroot != cur_stack
|
|
#ifndef GC_NO_INACTIVE
|
|
&& !stkroot->inactive
|
|
#endif
|
|
)
|
|
{
|
|
stkroot->suspend_ack = 1;
|
|
(void)signal(GC_SIG_SUSPEND, GC_suspend_handler);
|
|
if (pthread_kill(stkroot->thread_id, GC_SIG_SUSPEND))
|
|
{
|
|
*pnext = stkroot->next;
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr,
|
|
" GC: Cannot send signal to thread: 0x%lX." GC_NEW_LINE,
|
|
(unsigned long)((GC_word)stkroot->thread_id));
|
|
#endif
|
|
gcdata->stkroot_htable.count--;
|
|
GC_CORE_FREE(stkroot);
|
|
gcdata->free_bytes += sizeof(struct GC_stkroot_s);
|
|
}
|
|
else pnext = &stkroot->next;
|
|
}
|
|
else pnext = &stkroot->next;
|
|
} while ((stkroot = *pnext) != NULL);
|
|
#endif
|
|
}
|
|
#ifndef GC_WIN32_THREADS
|
|
addr = (GC_word)gcdata->stkroot_htable.hroots - sizeof(GC_word);
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(void **)(addr += sizeof(GC_word)) != NULL)
|
|
break;
|
|
}
|
|
if ((GC_word)(stkroot = *(struct GC_stkroot_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
do
|
|
{
|
|
attempt = 0;
|
|
while (stkroot->suspend_ack)
|
|
GC_thread_yield(attempt++);
|
|
} while ((stkroot = stkroot->next) != NULL);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
GC_noop1((GC_word)gcdata);
|
|
#endif
|
|
}
|
|
|
|
GC_STATIC void GC_FASTCALL GC_mutator_resume(struct GC_gcdata_s *gcdata)
|
|
{
|
|
#ifdef GC_THREADS
|
|
#ifdef GC_WIN32_THREADS
|
|
GC_REGISTER_KEYWORD GC_word addr;
|
|
GC_REGISTER_KEYWORD struct GC_stkroot_s *stkroot;
|
|
struct GC_stkroot_s *cur_stack;
|
|
DWORD res;
|
|
if ((GC_word)((cur_stack = gcdata->cur_stack) != NULL ? 1 : 0) <
|
|
gcdata->stkroot_htable.count)
|
|
{
|
|
addr = (GC_word)gcdata->stkroot_htable.hroots - sizeof(GC_word);
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(void **)(addr += sizeof(GC_word)) != NULL)
|
|
break;
|
|
}
|
|
if ((GC_word)(stkroot = *(struct GC_stkroot_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
do
|
|
{
|
|
if (stkroot != cur_stack &&
|
|
#ifndef GC_NO_INACTIVE
|
|
!stkroot->inactive &&
|
|
#endif
|
|
((res = ResumeThread(GC_THREAD_HANDLE(stkroot))) == ~(DWORD)0 ||
|
|
!res))
|
|
{
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr, " GC: Cannot resume thread!" GC_NEW_LINE);
|
|
#endif
|
|
GC_FATAL_ABORT;
|
|
}
|
|
} while ((stkroot = stkroot->next) != NULL);
|
|
}
|
|
}
|
|
#else
|
|
GC_inside_collect = 0;
|
|
GC_noop1((GC_word)gcdata);
|
|
#endif
|
|
#else
|
|
GC_noop1((GC_word)gcdata);
|
|
#endif
|
|
}
|
|
|
|
GC_STATIC void GC_FASTCALL GC_scan_region(struct GC_gcdata_s *gcdata,
|
|
GC_word begin_addr, GC_word end_addr, int interior_pointers)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word *region;
|
|
GC_REGISTER_KEYWORD GC_word *end_of_region;
|
|
GC_REGISTER_KEYWORD GC_word min_obj_addr;
|
|
GC_REGISTER_KEYWORD GC_word ignore_off;
|
|
GC_REGISTER_KEYWORD GC_word addr;
|
|
struct GC_objlink_s **hroots;
|
|
struct GC_objlink_s *marked_list;
|
|
struct GC_objlink_s *follow_list;
|
|
GC_word log2_size;
|
|
GC_word hmask;
|
|
GC_word count;
|
|
GC_word align_mask;
|
|
if (begin_addr < end_addr)
|
|
{
|
|
hroots = gcdata->obj_htable.hroots;
|
|
marked_list = gcdata->obj_htable.marked_list;
|
|
follow_list = gcdata->obj_htable.follow_list;
|
|
hmask = ((GC_word)1 << (log2_size = gcdata->obj_htable.log2_size)) - 1;
|
|
count = 0;
|
|
align_mask = 0;
|
|
ignore_off = gcdata->obj_htable.max_obj_addr -
|
|
(min_obj_addr = gcdata->obj_htable.min_obj_addr);
|
|
region = (GC_word *)begin_addr;
|
|
end_of_region = (GC_word *)end_addr;
|
|
if (!interior_pointers)
|
|
align_mask = sizeof(GC_word) - 1;
|
|
do
|
|
{
|
|
if (*region - min_obj_addr >= ignore_off)
|
|
do
|
|
{
|
|
if (++region >= end_of_region)
|
|
goto out;
|
|
} while (*region - min_obj_addr >= ignore_off);
|
|
if (((addr = *region) & align_mask) == 0)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *objlink;
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s **pnext;
|
|
if ((GC_word)(objlink = *(pnext = &hroots[(((addr >> log2_size) ^ addr) >>
|
|
GC_LOG2_OFFIGNORE) & hmask]))->obj > addr)
|
|
for (;;)
|
|
{
|
|
if ((GC_word)(objlink = *(pnext = &objlink->next))->obj <= addr)
|
|
break;
|
|
}
|
|
if (interior_pointers)
|
|
{
|
|
if ((objlink->atomic_and_size & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK)) <=
|
|
addr - (GC_word)objlink->obj)
|
|
{
|
|
if ((addr | ~(((GC_word)1 << GC_LOG2_OFFIGNORE) - (GC_word)1)) ==
|
|
~(GC_word)0)
|
|
continue;
|
|
pnext = &hroots[((((addr - ((GC_word)1 << GC_LOG2_OFFIGNORE)) >>
|
|
log2_size) ^ (addr - ((GC_word)1 << GC_LOG2_OFFIGNORE))) >>
|
|
GC_LOG2_OFFIGNORE) & hmask];
|
|
while ((GC_word)(objlink = *pnext)->obj > addr)
|
|
pnext = &objlink->next;
|
|
if ((objlink->atomic_and_size & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK)) <=
|
|
addr - (GC_word)objlink->obj)
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((GC_word)objlink->obj != addr)
|
|
continue;
|
|
}
|
|
*pnext = objlink->next;
|
|
count++;
|
|
if ((objlink->atomic_and_size & GC_ATOMIC_MASK) != 0)
|
|
{
|
|
objlink->next = marked_list;
|
|
marked_list = objlink;
|
|
}
|
|
else
|
|
{
|
|
objlink->next = follow_list;
|
|
follow_list = objlink;
|
|
}
|
|
}
|
|
} while (++region < end_of_region);
|
|
out:
|
|
gcdata->obj_htable.count -= count;
|
|
gcdata->obj_htable.marked_list = marked_list;
|
|
gcdata->obj_htable.follow_list = follow_list;
|
|
}
|
|
}
|
|
|
|
GC_INLINE_STATIC void *GC_FASTCALL GC_roots_scan(struct GC_gcdata_s *gcdata,
|
|
GC_stop_func stop_func)
|
|
{
|
|
struct GC_dataroot_s *dataroot;
|
|
if ((dataroot = gcdata->dataroots) != NULL)
|
|
{
|
|
do
|
|
{
|
|
if ((*stop_func)())
|
|
break;
|
|
GC_scan_region(gcdata, dataroot->begin_addr, dataroot->end_addr,
|
|
GC_all_interior_pointers);
|
|
} while ((dataroot = dataroot->next) != NULL);
|
|
}
|
|
return dataroot;
|
|
}
|
|
|
|
#ifndef GC_NO_INACTIVE
|
|
|
|
#ifdef GC_THREADS
|
|
GC_STATIC
|
|
#else
|
|
GC_INLINE_STATIC
|
|
#endif
|
|
void GC_FASTCALL GC_stack_scan_frames(struct GC_gcdata_s *gcdata,
|
|
GC_word begin_addr, GC_word end_addr,
|
|
CONST struct GC_activation_frame_s *activation_frame)
|
|
{
|
|
if (activation_frame != NULL)
|
|
{
|
|
if (GC_stack_grows_up)
|
|
{
|
|
do
|
|
{
|
|
GC_scan_region(gcdata, ((GC_word)activation_frame +
|
|
(sizeof(struct GC_activation_frame_s) + (sizeof(GC_word) << 1) - 1)) &
|
|
~(sizeof(GC_word) - 1), end_addr & ~(sizeof(GC_word) - 1), 1);
|
|
end_addr = activation_frame->inactive_sp;
|
|
} while ((activation_frame = activation_frame->prev) != NULL);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
GC_scan_region(gcdata, (begin_addr + (sizeof(GC_word) - 1)) &
|
|
~(sizeof(GC_word) - 1), ((GC_word)activation_frame - sizeof(GC_word)) &
|
|
~(sizeof(GC_word) - 1), 1);
|
|
begin_addr = activation_frame->inactive_sp;
|
|
} while ((activation_frame = activation_frame->prev) != NULL);
|
|
}
|
|
}
|
|
GC_scan_region(gcdata, (begin_addr + (sizeof(GC_word) - 1)) &
|
|
~(sizeof(GC_word) - 1), end_addr & ~(sizeof(GC_word) - 1), 1);
|
|
}
|
|
|
|
#endif /* ! GC_NO_INACTIVE */
|
|
|
|
GC_STATIC void GC_FASTCALL GC_stack_scan_cur(struct GC_gcdata_s *gcdata)
|
|
{
|
|
GC_word begin_addr;
|
|
GC_word end_addr;
|
|
struct GC_stkroot_s *cur_stack;
|
|
GC_PUSHREGS_BEGIN;
|
|
if ((cur_stack = gcdata->cur_stack) != NULL)
|
|
{
|
|
begin_addr = cur_stack->begin_addr;
|
|
end_addr = GC_approx_sp();
|
|
if (!GC_stack_grows_up)
|
|
{
|
|
begin_addr = end_addr;
|
|
end_addr = cur_stack->end_addr;
|
|
}
|
|
#ifdef GC_NO_INACTIVE
|
|
GC_scan_region(gcdata, (begin_addr + (sizeof(GC_word) - 1)) &
|
|
~(sizeof(GC_word) - 1), end_addr & ~(sizeof(GC_word) - 1), 1);
|
|
#else
|
|
GC_stack_scan_frames(gcdata, begin_addr, end_addr,
|
|
cur_stack->activation_frame);
|
|
#endif
|
|
}
|
|
GC_PUSHREGS_END;
|
|
}
|
|
|
|
GC_STATIC void GC_FASTCALL GC_scan_followable(struct GC_gcdata_s *gcdata,
|
|
GC_stop_func stop_func)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *objlink;
|
|
GC_word begin_addr;
|
|
while ((objlink = gcdata->obj_htable.follow_list) != NULL && !(*stop_func)())
|
|
{
|
|
gcdata->obj_htable.follow_list = objlink->next;
|
|
objlink->next = gcdata->obj_htable.marked_list;
|
|
begin_addr = (GC_word)(gcdata->obj_htable.marked_list = objlink)->obj;
|
|
#ifdef GC_GCJ_SUPPORT
|
|
#ifdef GC_IGNORE_GCJ_INFO
|
|
GC_scan_region(gcdata, begin_addr, begin_addr + (objlink->atomic_and_size &
|
|
~(GC_ATOMIC_MASK | (sizeof(GC_word) - 1))), GC_all_interior_pointers);
|
|
#else
|
|
GC_scan_region(gcdata, begin_addr,
|
|
(((objlink->atomic_and_size & GC_HASDSLEN_MASK) != 0 ?
|
|
*(GC_word *)(*(GC_word *)begin_addr + MARK_DESCR_OFFSET) :
|
|
objlink->atomic_and_size) & ~(GC_ATOMIC_MASK | (sizeof(GC_word) - 1))) +
|
|
begin_addr, GC_all_interior_pointers);
|
|
#endif
|
|
#else
|
|
GC_scan_region(gcdata, begin_addr, begin_addr + (objlink->atomic_and_size &
|
|
~(GC_ATOMIC_MASK | (sizeof(GC_word) - 1))), GC_all_interior_pointers);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef GC_THREADS
|
|
|
|
#ifndef GC_WIN32_THREADS
|
|
|
|
GC_STATIC void GC_CLIBDECL GC_suspend_handler(int sig)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_gcdata_s *gcdata;
|
|
struct GC_stkroot_s *volatile stkroot;
|
|
pthread_t thread_id;
|
|
int old_errno;
|
|
int attempt;
|
|
if ((gcdata = GC_gcdata_global) != NULL)
|
|
{
|
|
old_errno = errno;
|
|
#ifdef SIG_ACK
|
|
if (signal(sig, GC_suspend_handler) != SIG_DFL)
|
|
(void)signal(sig, SIG_ACK);
|
|
#else
|
|
(void)signal(sig, GC_suspend_handler);
|
|
#endif
|
|
thread_id = pthread_self();
|
|
stkroot =
|
|
gcdata->stkroot_htable.hroots[GC_HASH_INDEX((GC_word)thread_id,
|
|
gcdata->stkroot_htable.seed, gcdata->stkroot_htable.log2_size)];
|
|
while (stkroot != NULL && stkroot->thread_id != thread_id)
|
|
stkroot = stkroot->next;
|
|
if (gcdata->cur_stack != stkroot && stkroot != NULL
|
|
#ifndef GC_NO_INACTIVE
|
|
&& !stkroot->inactive
|
|
#endif
|
|
)
|
|
{
|
|
GC_ASYNC_PUSHREGS_BEGIN;
|
|
*(volatile GC_word *)(GC_stack_grows_up ? &stkroot->end_addr :
|
|
&stkroot->begin_addr) = GC_approx_sp();
|
|
attempt = 0;
|
|
do
|
|
{
|
|
stkroot->suspend_ack = 0;
|
|
GC_thread_yield(attempt++);
|
|
} while (GC_inside_collect);
|
|
GC_ASYNC_PUSHREGS_END;
|
|
}
|
|
GC_ERRNO_SET(old_errno);
|
|
}
|
|
}
|
|
|
|
#endif /* ! GC_WIN32_THREADS */
|
|
|
|
GC_STATIC GC_word GC_FASTCALL GC_stkroot_scan_other(
|
|
struct GC_gcdata_s *gcdata, GC_stop_func stop_func)
|
|
{
|
|
#ifdef GC_WIN32_THREADS
|
|
CONTEXT context;
|
|
#endif
|
|
GC_REGISTER_KEYWORD GC_word addr;
|
|
struct GC_stkroot_s *stkroot = (void *)(~(GC_word)0);
|
|
struct GC_stkroot_s *cur_stack;
|
|
if ((cur_stack = gcdata->cur_stack) == NULL ||
|
|
gcdata->stkroot_htable.count > (GC_word)1)
|
|
{
|
|
addr = (GC_word)gcdata->stkroot_htable.hroots - sizeof(GC_word);
|
|
do
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(void **)(addr += sizeof(GC_word)) != NULL)
|
|
break;
|
|
}
|
|
if ((GC_word)(stkroot = *(struct GC_stkroot_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
do
|
|
{
|
|
if (stkroot != cur_stack)
|
|
{
|
|
if ((*stop_func)())
|
|
break;
|
|
#ifdef GC_WIN32_THREADS
|
|
#ifndef GC_NO_INACTIVE
|
|
if (!stkroot->inactive)
|
|
#endif
|
|
{
|
|
context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
|
|
if (GetThreadContext(GC_THREAD_HANDLE(stkroot), &context))
|
|
{
|
|
GC_scan_region(gcdata, (GC_word)(&context),
|
|
(GC_word)(&context) + (sizeof(context) & ~(sizeof(GC_word) - 1)), 1);
|
|
if ((*stop_func)())
|
|
break;
|
|
*(GC_stack_grows_up ? &stkroot->end_addr : &stkroot->begin_addr) =
|
|
context.GC_WIN32_CONTEXT_SP_NAME;
|
|
}
|
|
#ifdef GC_PRINT_MSGS
|
|
else fprintf(stderr,
|
|
" GC: Cannot get context of thread: 0x%lX." GC_NEW_LINE,
|
|
(unsigned long)stkroot->thread_id);
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef GC_NO_INACTIVE
|
|
GC_scan_region(gcdata, (stkroot->begin_addr + (sizeof(GC_word) - 1)) &
|
|
~(sizeof(GC_word) - 1), stkroot->end_addr & ~(sizeof(GC_word) - 1), 1);
|
|
#else
|
|
GC_stack_scan_frames(gcdata, stkroot->begin_addr, stkroot->end_addr,
|
|
stkroot->activation_frame);
|
|
#endif
|
|
}
|
|
} while ((stkroot = stkroot->next) != NULL);
|
|
} while (stkroot == NULL);
|
|
}
|
|
return ~(GC_word)stkroot;
|
|
}
|
|
|
|
GC_STATIC void GC_FASTCALL GC_stkroot_tblresize(struct GC_gcdata_s *gcdata,
|
|
struct GC_stkroot_s **new_hroots, GC_word new_log2_size)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr =
|
|
(GC_word)gcdata->stkroot_htable.hroots - sizeof(GC_word);
|
|
struct GC_stkroot_s **pnext;
|
|
struct GC_stkroot_s *stkroot;
|
|
struct GC_stkroot_s *new_stkroot;
|
|
GC_word seed = addr + GC_RANDOM_SEED(gcdata);
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(void **)(addr += sizeof(GC_word)) != NULL)
|
|
break;
|
|
}
|
|
if ((GC_word)(stkroot = *(struct GC_stkroot_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
do
|
|
{
|
|
pnext = &new_hroots[GC_HASH_INDEX((GC_word)stkroot->thread_id, seed,
|
|
new_log2_size)];
|
|
stkroot = (new_stkroot = stkroot)->next;
|
|
new_stkroot->next = *pnext;
|
|
*pnext = new_stkroot;
|
|
} while (stkroot != NULL);
|
|
}
|
|
gcdata->stkroot_htable.seed = seed;
|
|
GC_CORE_FREE(gcdata->stkroot_htable.hroots);
|
|
gcdata->free_bytes += ((GC_word)sizeof(GC_word) <<
|
|
gcdata->stkroot_htable.log2_size) + sizeof(GC_word);
|
|
gcdata->stkroot_htable.hroots = new_hroots;
|
|
gcdata->stkroot_htable.log2_size = new_log2_size;
|
|
}
|
|
|
|
GC_STATIC void GC_FASTCALL GC_stkroot_add(struct GC_gcdata_s *gcdata,
|
|
GC_THREAD_ID_T thread_id, struct GC_stkroot_s *new_stkroot)
|
|
{
|
|
struct GC_stkroot_s **pnext =
|
|
&gcdata->stkroot_htable.hroots[GC_HASH_INDEX((GC_word)thread_id,
|
|
gcdata->stkroot_htable.seed, gcdata->stkroot_htable.log2_size)];
|
|
#ifdef GC_WIN32_THREADS
|
|
#ifndef GC_WIN32_WCE
|
|
HANDLE process_handle = GetCurrentProcess();
|
|
if (!DuplicateHandle(process_handle, GetCurrentThread(), process_handle,
|
|
&new_stkroot->thread_handle, 0, (BOOL)0, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
#ifdef GC_PRINT_MSGS
|
|
fprintf(stderr, " GC: Cannot duplicate thread handle!" GC_NEW_LINE);
|
|
#endif
|
|
GC_FATAL_ABORT;
|
|
}
|
|
#endif
|
|
#else
|
|
new_stkroot->suspend_ack = 0;
|
|
#endif
|
|
new_stkroot->next = *pnext;
|
|
new_stkroot->thread_id = thread_id;
|
|
*pnext = new_stkroot;
|
|
gcdata->stkroot_htable.count++;
|
|
#ifndef GC_WIN32_THREADS
|
|
(void)signal(GC_SIG_SUSPEND, GC_suspend_handler);
|
|
#endif
|
|
}
|
|
|
|
GC_INLINE_STATIC void GC_FASTCALL GC_stkroot_delete_cur(
|
|
struct GC_gcdata_s *gcdata)
|
|
{
|
|
struct GC_stkroot_s *cur_stack = gcdata->cur_stack;
|
|
GC_REGISTER_KEYWORD struct GC_stkroot_s **pnext =
|
|
&gcdata->stkroot_htable.hroots[GC_HASH_INDEX((GC_word)cur_stack->thread_id,
|
|
gcdata->stkroot_htable.seed, gcdata->stkroot_htable.log2_size)];
|
|
while (*pnext != cur_stack)
|
|
pnext = &(*pnext)->next;
|
|
*pnext = cur_stack->next;
|
|
gcdata->stkroot_htable.count--;
|
|
#ifdef GC_WIN32_THREADS
|
|
#ifndef GC_WIN32_WCE
|
|
(void)CloseHandle(cur_stack->thread_handle);
|
|
#endif
|
|
#endif
|
|
gcdata->cur_stack = NULL;
|
|
GC_CORE_FREE(cur_stack);
|
|
gcdata->free_bytes += sizeof(struct GC_stkroot_s);
|
|
}
|
|
|
|
#endif /* GC_THREADS */
|
|
|
|
#ifndef GC_NO_DLINKS
|
|
|
|
GC_STATIC void GC_FASTCALL GC_dlink_scan_clear(struct GC_gcdata_s *gcdata)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr =
|
|
(GC_word)gcdata->dlink_htable.hroots - sizeof(GC_word);
|
|
GC_REGISTER_KEYWORD struct GC_dlink_s *dlink;
|
|
struct GC_dlink_s **pnext;
|
|
struct GC_dlink_s *free_list = gcdata->dlink_htable.free_list;
|
|
struct GC_objlink_s **obj_hroots = gcdata->obj_htable.hroots;
|
|
GC_word obj_log2_size;
|
|
GC_word obj_hmask;
|
|
GC_word saved_addr;
|
|
GC_word count = 0;
|
|
obj_hmask = ((GC_word)1 << (obj_log2_size =
|
|
gcdata->obj_htable.log2_size)) - 1;
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(struct GC_dlink_s **)(addr += sizeof(GC_word)) != &GC_nil_dlink)
|
|
break;
|
|
}
|
|
if ((GC_word)(dlink = *(struct GC_dlink_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
pnext = (struct GC_dlink_s **)(saved_addr = addr);
|
|
do
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *objlink;
|
|
addr = (GC_word)dlink->objlink->obj;
|
|
if ((GC_word)(objlink = obj_hroots[(((addr >> obj_log2_size) ^ addr) >>
|
|
GC_LOG2_OFFIGNORE) & obj_hmask])->obj > addr)
|
|
for (;;)
|
|
{
|
|
if ((GC_word)(objlink = objlink->next)->obj <= addr)
|
|
break;
|
|
}
|
|
if ((GC_word)objlink->obj == addr)
|
|
{
|
|
*pnext = dlink->next;
|
|
*(GC_word *)(~dlink->hidden_link) = 0;
|
|
dlink->next = free_list;
|
|
count++;
|
|
free_list = dlink;
|
|
}
|
|
else pnext = &dlink->next;
|
|
} while ((dlink = *pnext) != &GC_nil_dlink);
|
|
addr = saved_addr;
|
|
}
|
|
gcdata->dlink_htable.free_list = free_list;
|
|
gcdata->dlink_htable.count -= count;
|
|
gcdata->free_bytes += count * sizeof(struct GC_dlink_s);
|
|
}
|
|
|
|
GC_STATIC GC_word GC_FASTCALL GC_dlink_free_pending(
|
|
struct GC_gcdata_s *gcdata, GC_word min_free_count)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_dlink_s *dlink;
|
|
GC_word count = 0;
|
|
do
|
|
{
|
|
if ((dlink = gcdata->dlink_htable.free_list) == NULL)
|
|
break;
|
|
gcdata->dlink_htable.free_list = dlink->next;
|
|
GC_CORE_FREE(dlink);
|
|
} while (++count < min_free_count);
|
|
return count;
|
|
}
|
|
|
|
GC_INLINE_STATIC void GC_FASTCALL GC_dlink_tblresize(
|
|
struct GC_gcdata_s *gcdata, struct GC_dlink_s **new_hroots,
|
|
GC_word new_log2_size)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr =
|
|
(GC_word)gcdata->dlink_htable.hroots - sizeof(GC_word);
|
|
struct GC_dlink_s **pnext;
|
|
struct GC_dlink_s *dlink;
|
|
GC_word hidden_link;
|
|
GC_word saved_addr;
|
|
GC_word seed = addr + GC_RANDOM_SEED(gcdata);
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(struct GC_dlink_s **)(addr += sizeof(GC_word)) != &GC_nil_dlink)
|
|
break;
|
|
}
|
|
if ((GC_word)(dlink = *(struct GC_dlink_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
saved_addr = addr;
|
|
do
|
|
{
|
|
hidden_link = dlink->hidden_link;
|
|
pnext = &new_hroots[GC_HASH_INDEX(hidden_link >> GC_LOG2_OFFIGNORE,
|
|
seed, new_log2_size)];
|
|
while (((struct GC_dlink_s *)(addr = (GC_word)(*pnext)))->hidden_link >
|
|
hidden_link)
|
|
pnext = &((struct GC_dlink_s *)addr)->next;
|
|
dlink = (*pnext = dlink)->next;
|
|
(*pnext)->next = (struct GC_dlink_s *)addr;
|
|
} while (dlink != &GC_nil_dlink);
|
|
addr = saved_addr;
|
|
}
|
|
gcdata->dlink_htable.seed = seed;
|
|
GC_CORE_FREE(gcdata->dlink_htable.hroots);
|
|
gcdata->free_bytes += ((GC_word)sizeof(GC_word) <<
|
|
gcdata->dlink_htable.log2_size) + sizeof(GC_word);
|
|
gcdata->dlink_htable.hroots = new_hroots;
|
|
gcdata->dlink_htable.log2_size = new_log2_size;
|
|
}
|
|
|
|
GC_INLINE_STATIC int GC_FASTCALL GC_dlink_add(struct GC_gcdata_s *gcdata,
|
|
GC_word hidden_link, struct GC_objlink_s *objlink,
|
|
struct GC_dlink_s *new_dlink)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_dlink_s *dlink;
|
|
struct GC_dlink_s **pnext;
|
|
if (!gcdata->dlink_htable.count)
|
|
gcdata->dlink_htable.seed += GC_RANDOM_SEED(gcdata);
|
|
pnext = &gcdata->dlink_htable.hroots[GC_HASH_INDEX(hidden_link >>
|
|
GC_LOG2_OFFIGNORE, gcdata->dlink_htable.seed,
|
|
gcdata->dlink_htable.log2_size)];
|
|
while ((dlink = *pnext)->hidden_link > hidden_link)
|
|
pnext = &dlink->next;
|
|
if (dlink->hidden_link == hidden_link)
|
|
{
|
|
dlink->objlink = objlink;
|
|
if (new_dlink != NULL &&
|
|
(dlink = gcdata->dlink_htable.free_list) != new_dlink)
|
|
{
|
|
new_dlink->next = dlink;
|
|
gcdata->dlink_htable.free_list = new_dlink;
|
|
gcdata->free_bytes += sizeof(struct GC_dlink_s);
|
|
}
|
|
return GC_DUPLICATE;
|
|
}
|
|
if (new_dlink == NULL)
|
|
return GC_NO_MEMORY;
|
|
if (gcdata->dlink_htable.free_list == new_dlink)
|
|
{
|
|
gcdata->free_bytes -= sizeof(struct GC_dlink_s);
|
|
gcdata->dlink_htable.free_list = new_dlink->next;
|
|
}
|
|
new_dlink->objlink = objlink;
|
|
new_dlink->next = dlink;
|
|
new_dlink->hidden_link = hidden_link;
|
|
*pnext = new_dlink;
|
|
gcdata->dlink_htable.count++;
|
|
return GC_SUCCESS;
|
|
}
|
|
|
|
GC_STATIC int GC_FASTCALL GC_dlink_delete(struct GC_gcdata_s *gcdata,
|
|
GC_word hidden_link, GC_word min_hidden, GC_word max_hidden)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_dlink_s *dlink;
|
|
struct GC_dlink_s **pnext =
|
|
&gcdata->dlink_htable.hroots[GC_HASH_INDEX(hidden_link >> GC_LOG2_OFFIGNORE,
|
|
gcdata->dlink_htable.seed, gcdata->dlink_htable.log2_size)];
|
|
while ((dlink = *pnext)->hidden_link > max_hidden)
|
|
pnext = &dlink->next;
|
|
if (dlink->hidden_link < min_hidden)
|
|
return 0;
|
|
do
|
|
{
|
|
*pnext = dlink->next;
|
|
gcdata->dlink_htable.count--;
|
|
GC_CORE_FREE(dlink);
|
|
dlink = *pnext;
|
|
gcdata->free_bytes += sizeof(struct GC_dlink_s);
|
|
} while (dlink->hidden_link >= min_hidden);
|
|
return 1;
|
|
}
|
|
|
|
#endif /* ! GC_NO_DLINKS */
|
|
|
|
#ifndef GC_NO_FNLZ
|
|
|
|
GC_STATIC int GC_FASTCALL GC_objlink_mark(struct GC_gcdata_s *gcdata,
|
|
GC_word addr, int interior_pointers)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *objlink;
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s **pnext;
|
|
struct GC_objlink_s **hroots = gcdata->obj_htable.hroots;
|
|
GC_word log2_size = gcdata->obj_htable.log2_size;
|
|
pnext = &hroots[(((addr >> log2_size) ^ addr) >> GC_LOG2_OFFIGNORE) &
|
|
(((GC_word)1 << log2_size) - 1)];
|
|
while ((GC_word)(objlink = *pnext)->obj > addr)
|
|
pnext = &objlink->next;
|
|
if (interior_pointers)
|
|
{
|
|
if ((objlink->atomic_and_size & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK)) <=
|
|
addr - (GC_word)objlink->obj)
|
|
{
|
|
if ((addr | ~(((GC_word)1 << GC_LOG2_OFFIGNORE) - (GC_word)1)) ==
|
|
~(GC_word)0)
|
|
return 0;
|
|
pnext = &hroots[((((addr - ((GC_word)1 << GC_LOG2_OFFIGNORE)) >>
|
|
log2_size) ^ (addr - ((GC_word)1 << GC_LOG2_OFFIGNORE))) >>
|
|
GC_LOG2_OFFIGNORE) & (((GC_word)1 << log2_size) - 1)];
|
|
while ((GC_word)(objlink = *pnext)->obj > addr)
|
|
pnext = &objlink->next;
|
|
if ((objlink->atomic_and_size & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK)) <=
|
|
addr - (GC_word)objlink->obj)
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((GC_word)objlink->obj != addr)
|
|
return 0;
|
|
}
|
|
*pnext = objlink->next;
|
|
gcdata->obj_htable.count--;
|
|
if ((objlink->atomic_and_size & GC_ATOMIC_MASK) != 0)
|
|
{
|
|
objlink->next = gcdata->obj_htable.marked_list;
|
|
gcdata->obj_htable.marked_list = objlink;
|
|
}
|
|
else
|
|
{
|
|
objlink->next = gcdata->obj_htable.follow_list;
|
|
gcdata->obj_htable.follow_list = objlink;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
GC_INLINE_STATIC GC_word GC_FASTCALL GC_fnlz_precollect(
|
|
struct GC_gcdata_s *gcdata, GC_word *pcount)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr;
|
|
GC_REGISTER_KEYWORD struct GC_fnlz_s *fnlz;
|
|
void *client_data;
|
|
struct GC_objlink_s *objlink;
|
|
GC_word bytes_finalized = 0;
|
|
int interior_pointers = GC_all_interior_pointers;
|
|
if ((fnlz = gcdata->fnlz_htable.ready_fnlz) != NULL)
|
|
{
|
|
addr = 0;
|
|
do
|
|
{
|
|
bytes_finalized += (objlink = fnlz->objlink)->atomic_and_size;
|
|
(void)GC_objlink_mark(gcdata, (GC_word)objlink->obj, 0);
|
|
addr++;
|
|
if ((client_data = fnlz->client_data) != NULL)
|
|
(void)GC_objlink_mark(gcdata, (GC_word)client_data, interior_pointers);
|
|
} while ((fnlz = fnlz->next) != NULL);
|
|
*pcount += addr;
|
|
}
|
|
if (gcdata->fnlz_htable.has_client_ptrs)
|
|
{
|
|
addr = (GC_word)gcdata->fnlz_htable.hroots - sizeof(GC_word);
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(struct GC_fnlz_s **)(addr += sizeof(GC_word)) != &GC_nil_fnlz)
|
|
break;
|
|
}
|
|
if ((GC_word)(fnlz = *(struct GC_fnlz_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
do
|
|
{
|
|
if ((client_data = fnlz->client_data) != NULL)
|
|
(void)GC_objlink_mark(gcdata, (GC_word)client_data, interior_pointers);
|
|
} while ((fnlz = fnlz->next) != &GC_nil_fnlz);
|
|
}
|
|
}
|
|
return bytes_finalized & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK);
|
|
}
|
|
|
|
GC_STATIC GC_word GC_FASTCALL GC_fnlz_after_collect(
|
|
struct GC_gcdata_s *gcdata, GC_word *pcount)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr =
|
|
(GC_word)gcdata->fnlz_htable.hroots - sizeof(GC_word);
|
|
GC_REGISTER_KEYWORD struct GC_fnlz_s *fnlz;
|
|
struct GC_fnlz_s **pnext;
|
|
struct GC_fnlz_s *ready_fnlz = gcdata->fnlz_htable.ready_fnlz;
|
|
struct GC_objlink_s *objlink;
|
|
GC_word bytes_finalized = 0;
|
|
GC_word count = 0;
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(struct GC_fnlz_s **)(addr += sizeof(GC_word)) != &GC_nil_fnlz)
|
|
break;
|
|
}
|
|
if ((GC_word)(fnlz = *(struct GC_fnlz_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
pnext = (struct GC_fnlz_s **)addr;
|
|
do
|
|
{
|
|
if (GC_objlink_mark(gcdata, (GC_word)(objlink = fnlz->objlink)->obj, 0))
|
|
{
|
|
*pnext = fnlz->next;
|
|
bytes_finalized += objlink->atomic_and_size;
|
|
fnlz->next = ready_fnlz;
|
|
count++;
|
|
ready_fnlz = fnlz;
|
|
}
|
|
else pnext = &fnlz->next;
|
|
} while ((fnlz = *pnext) != &GC_nil_fnlz);
|
|
}
|
|
gcdata->fnlz_htable.ready_fnlz = ready_fnlz;
|
|
if ((gcdata->fnlz_htable.count -= count) == 0)
|
|
gcdata->fnlz_htable.has_client_ptrs = 0;
|
|
gcdata->free_bytes += count * sizeof(struct GC_fnlz_s);
|
|
*pcount += count;
|
|
return bytes_finalized & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK);
|
|
}
|
|
|
|
GC_INLINE_STATIC void GC_FASTCALL GC_fnlz_tblresize(
|
|
struct GC_gcdata_s *gcdata, struct GC_fnlz_s **new_hroots,
|
|
GC_word new_log2_size)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr =
|
|
(GC_word)gcdata->fnlz_htable.hroots - sizeof(GC_word);
|
|
struct GC_fnlz_s **pnext;
|
|
struct GC_fnlz_s *fnlz;
|
|
struct GC_objlink_s *objlink;
|
|
GC_word saved_addr;
|
|
GC_word seed = addr + GC_RANDOM_SEED(gcdata);
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(struct GC_fnlz_s **)(addr += sizeof(GC_word)) != &GC_nil_fnlz)
|
|
break;
|
|
}
|
|
if ((GC_word)(fnlz = *(struct GC_fnlz_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
saved_addr = addr;
|
|
do
|
|
{
|
|
objlink = fnlz->objlink;
|
|
pnext = &new_hroots[GC_HASH_INDEX((GC_word)objlink /
|
|
(sizeof(GC_word) << 1), seed, new_log2_size)];
|
|
while ((GC_word)((struct GC_fnlz_s *)(addr = (GC_word)(*pnext)))->objlink >
|
|
(GC_word)objlink)
|
|
pnext = &((struct GC_fnlz_s *)addr)->next;
|
|
fnlz = (*pnext = fnlz)->next;
|
|
(*pnext)->next = (struct GC_fnlz_s *)addr;
|
|
} while (fnlz != &GC_nil_fnlz);
|
|
addr = saved_addr;
|
|
}
|
|
gcdata->fnlz_htable.seed = seed;
|
|
GC_CORE_FREE(gcdata->fnlz_htable.hroots);
|
|
gcdata->free_bytes += ((GC_word)sizeof(GC_word) <<
|
|
gcdata->fnlz_htable.log2_size) + sizeof(GC_word);
|
|
gcdata->fnlz_htable.hroots = new_hroots;
|
|
gcdata->fnlz_htable.log2_size = new_log2_size;
|
|
}
|
|
|
|
GC_INLINE_STATIC GC_finalization_proc GC_FASTCALL GC_fnlz_add_del(
|
|
struct GC_gcdata_s *gcdata, struct GC_objlink_s *objlink,
|
|
GC_finalization_proc fn, void *client_data, void **odata)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_fnlz_s *fnlz;
|
|
struct GC_fnlz_s **pnext;
|
|
struct GC_fnlz_s *new_fnlz;
|
|
GC_finalization_proc old_fn;
|
|
if (!gcdata->fnlz_htable.count)
|
|
gcdata->fnlz_htable.seed += GC_RANDOM_SEED(gcdata);
|
|
pnext = &gcdata->fnlz_htable.hroots[GC_HASH_INDEX((GC_word)objlink /
|
|
(sizeof(GC_word) << 1), gcdata->fnlz_htable.seed,
|
|
gcdata->fnlz_htable.log2_size)];
|
|
while ((GC_word)(fnlz = *pnext)->objlink > (GC_word)objlink)
|
|
pnext = &fnlz->next;
|
|
if (fnlz->objlink == objlink)
|
|
{
|
|
*odata = fnlz->client_data;
|
|
old_fn = fnlz->fn;
|
|
if (fn != 0)
|
|
{
|
|
fnlz->client_data = client_data;
|
|
fnlz->fn = fn;
|
|
if (!gcdata->fnlz_htable.has_client_ptrs &&
|
|
(GC_word)client_data >= gcdata->obj_htable.min_obj_addr &&
|
|
(GC_word)client_data < gcdata->obj_htable.max_obj_addr)
|
|
gcdata->fnlz_htable.has_client_ptrs = 1;
|
|
}
|
|
else
|
|
{
|
|
*pnext = fnlz->next;
|
|
if (gcdata->fnlz_htable.single_free != NULL)
|
|
{
|
|
GC_CORE_FREE(fnlz);
|
|
gcdata->free_bytes += sizeof(struct GC_fnlz_s);
|
|
}
|
|
else gcdata->fnlz_htable.single_free = fnlz;
|
|
if (!(--gcdata->fnlz_htable.count))
|
|
gcdata->fnlz_htable.has_client_ptrs = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fn != 0)
|
|
{
|
|
if ((new_fnlz = gcdata->fnlz_htable.single_free) != NULL)
|
|
{
|
|
gcdata->fnlz_htable.single_free = NULL;
|
|
new_fnlz->next = fnlz;
|
|
new_fnlz->objlink = objlink;
|
|
new_fnlz->client_data = client_data;
|
|
new_fnlz->fn = fn;
|
|
*pnext = new_fnlz;
|
|
gcdata->fnlz_htable.count++;
|
|
*odata = NULL;
|
|
if (!gcdata->fnlz_htable.has_client_ptrs &&
|
|
(GC_word)client_data >= gcdata->obj_htable.min_obj_addr &&
|
|
(GC_word)client_data < gcdata->obj_htable.max_obj_addr)
|
|
gcdata->fnlz_htable.has_client_ptrs = 1;
|
|
}
|
|
}
|
|
else *odata = NULL;
|
|
old_fn = 0;
|
|
}
|
|
return old_fn;
|
|
}
|
|
|
|
GC_STATIC GC_finalization_proc GC_FASTCALL GC_fnlz_del_ready(
|
|
struct GC_gcdata_s *gcdata, struct GC_objlink_s **pobjlink, void **odata)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_fnlz_s *fnlz;
|
|
GC_finalization_proc fn = 0;
|
|
if ((fnlz = gcdata->fnlz_htable.ready_fnlz) != NULL)
|
|
{
|
|
*pobjlink = fnlz->objlink;
|
|
gcdata->fnlz_htable.ready_fnlz = fnlz->next;
|
|
*odata = fnlz->client_data;
|
|
fn = fnlz->fn;
|
|
GC_CORE_FREE(fnlz);
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
#ifndef JAVA_FINALIZATION_NOT_NEEDED
|
|
|
|
GC_INLINE_STATIC void GC_FASTCALL GC_fnlz_ready_all(
|
|
struct GC_gcdata_s *gcdata)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr =
|
|
(GC_word)gcdata->fnlz_htable.hroots - sizeof(GC_word);
|
|
GC_REGISTER_KEYWORD struct GC_fnlz_s *fnlz;
|
|
struct GC_fnlz_s *ready_fnlz = gcdata->fnlz_htable.ready_fnlz;
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(struct GC_fnlz_s **)(addr += sizeof(GC_word)) != &GC_nil_fnlz)
|
|
break;
|
|
}
|
|
if ((GC_word)(fnlz = *(struct GC_fnlz_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
while (fnlz->next != &GC_nil_fnlz)
|
|
fnlz = fnlz->next;
|
|
fnlz->next = ready_fnlz;
|
|
ready_fnlz = *(struct GC_fnlz_s **)addr;
|
|
*(CONST struct GC_fnlz_s **)addr = &GC_nil_fnlz;
|
|
}
|
|
gcdata->free_bytes += gcdata->fnlz_htable.count * sizeof(struct GC_fnlz_s);
|
|
gcdata->fnlz_htable.ready_fnlz = ready_fnlz;
|
|
gcdata->fnlz_htable.count = 0;
|
|
gcdata->fnlz_htable.has_client_ptrs = 0;
|
|
}
|
|
|
|
#endif /* ! JAVA_FINALIZATION_NOT_NEEDED */
|
|
|
|
#endif /* ! GC_NO_FNLZ */
|
|
|
|
GC_INLINE_STATIC void GC_FASTCALL GC_objlink_add(struct GC_gcdata_s *gcdata,
|
|
void *obj, GC_word objsize, GC_word vtable)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *objlink;
|
|
struct GC_objlink_s **pnext;
|
|
struct GC_objlink_s *new_objlink;
|
|
gcdata->obj_htable.unlinked_list =
|
|
(new_objlink = gcdata->obj_htable.unlinked_list)->next;
|
|
if (vtable)
|
|
{
|
|
GC_MEM_BZERO(obj, objsize);
|
|
gcdata->followscan_size += objsize & ~(sizeof(GC_word) - 1);
|
|
#ifdef GC_GCJ_SUPPORT
|
|
#ifdef GC_IGNORE_GCJ_INFO
|
|
if (vtable != ~(GC_word)0)
|
|
*(GC_word *)obj = vtable;
|
|
new_objlink->atomic_and_size = objsize;
|
|
#else
|
|
new_objlink->atomic_and_size =
|
|
vtable != ~(GC_word)0 && (*(GC_word *)obj = vtable,
|
|
#ifndef GC_GETENV_SKIP
|
|
!gcdata->ignore_gcj_info &&
|
|
#endif
|
|
*(GC_word *)(vtable + MARK_DESCR_OFFSET) != GC_DS_LENGTH) ?
|
|
objsize | GC_HASDSLEN_MASK : objsize;
|
|
#endif
|
|
#else
|
|
new_objlink->atomic_and_size = objsize;
|
|
#endif
|
|
}
|
|
else new_objlink->atomic_and_size = objsize | GC_ATOMIC_MASK;
|
|
new_objlink->obj = obj;
|
|
if ((GC_word)(objlink = *(pnext =
|
|
&gcdata->obj_htable.hroots[(((((GC_word)obj) >>
|
|
gcdata->obj_htable.log2_size) ^ (GC_word)obj) >> GC_LOG2_OFFIGNORE) &
|
|
(((GC_word)1 << gcdata->obj_htable.log2_size) - 1)]))->obj >
|
|
(GC_word)obj)
|
|
for (;;)
|
|
{
|
|
if ((GC_word)(objlink = *(pnext = &objlink->next))->obj <= (GC_word)obj)
|
|
break;
|
|
}
|
|
new_objlink->next = objlink;
|
|
*pnext = new_objlink;
|
|
if (gcdata->obj_htable.min_obj_addr >= (GC_word)obj)
|
|
gcdata->obj_htable.min_obj_addr = (GC_word)obj;
|
|
if ((GC_word)obj + ((GC_word)1 << GC_LOG2_OFFIGNORE) >
|
|
gcdata->obj_htable.max_obj_addr)
|
|
gcdata->obj_htable.max_obj_addr =
|
|
(GC_word)obj + ((GC_word)1 << GC_LOG2_OFFIGNORE);
|
|
gcdata->obj_htable.count++;
|
|
}
|
|
|
|
GC_STATIC GC_word GC_FASTCALL GC_objlink_remove_all(
|
|
struct GC_gcdata_s *gcdata)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr =
|
|
(GC_word)gcdata->obj_htable.hroots - sizeof(GC_word);
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *objlink;
|
|
GC_word removed_bytes = 0;
|
|
GC_word count = 0;
|
|
struct GC_objlink_s *free_list = gcdata->obj_htable.free_list;
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (*(struct GC_objlink_s **)(addr += sizeof(GC_word)) != &GC_nil_objlink)
|
|
break;
|
|
}
|
|
if ((GC_word)(objlink = *(struct GC_objlink_s **)addr) == ~(GC_word)0)
|
|
break;
|
|
removed_bytes += objlink->atomic_and_size;
|
|
while (objlink->next != &GC_nil_objlink)
|
|
{
|
|
removed_bytes += (objlink = objlink->next)->atomic_and_size;
|
|
count++;
|
|
}
|
|
objlink->next = free_list;
|
|
count++;
|
|
free_list = *(struct GC_objlink_s **)addr;
|
|
*(CONST struct GC_objlink_s **)addr = &GC_nil_objlink;
|
|
}
|
|
gcdata->marked_bytes -= removed_bytes & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK);
|
|
gcdata->obj_htable.free_list = free_list;
|
|
gcdata->obj_htable.count = 0;
|
|
gcdata->obj_htable.min_obj_addr = ~(GC_word)0;
|
|
gcdata->obj_htable.max_obj_addr = (GC_word)1 << GC_LOG2_OFFIGNORE;
|
|
return count;
|
|
}
|
|
|
|
#ifdef GC_NO_FNLZ
|
|
#ifdef GC_NO_GCBASE
|
|
GC_INLINE_STATIC
|
|
#else
|
|
GC_STATIC
|
|
#endif
|
|
#else
|
|
GC_STATIC
|
|
#endif
|
|
struct GC_objlink_s *GC_FASTCALL GC_objlink_get(struct GC_gcdata_s *gcdata,
|
|
void *displaced_pointer)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *objlink;
|
|
struct GC_objlink_s **hroots = gcdata->obj_htable.hroots;
|
|
GC_word log2_size = gcdata->obj_htable.log2_size;
|
|
objlink = hroots[(((((GC_word)displaced_pointer) >> log2_size) ^
|
|
(GC_word)displaced_pointer) >> GC_LOG2_OFFIGNORE) &
|
|
(((GC_word)1 << log2_size) - 1)];
|
|
while ((GC_word)objlink->obj > (GC_word)displaced_pointer)
|
|
objlink = objlink->next;
|
|
if ((objlink->atomic_and_size & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK)) <=
|
|
(GC_word)displaced_pointer - (GC_word)objlink->obj)
|
|
{
|
|
objlink = hroots[(((((GC_word)displaced_pointer -
|
|
(((GC_word)1 << GC_LOG2_OFFIGNORE) - (GC_word)1)) >>
|
|
log2_size) ^ ((GC_word)displaced_pointer -
|
|
(((GC_word)1 << GC_LOG2_OFFIGNORE) - (GC_word)1))) >>
|
|
GC_LOG2_OFFIGNORE) & (((GC_word)1 << log2_size) - 1)];
|
|
while ((GC_word)objlink->obj > (GC_word)displaced_pointer)
|
|
objlink = objlink->next;
|
|
if ((objlink->atomic_and_size & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK)) <=
|
|
(GC_word)displaced_pointer - (GC_word)objlink->obj)
|
|
objlink = NULL;
|
|
}
|
|
return objlink;
|
|
}
|
|
|
|
#ifdef GC_NO_FNLZ
|
|
#ifdef GC_NO_GCBASE
|
|
GC_INLINE_STATIC
|
|
#else
|
|
GC_STATIC
|
|
#endif
|
|
#else
|
|
GC_STATIC
|
|
#endif
|
|
struct GC_objlink_s *GC_FASTCALL GC_objlink_refill_find(
|
|
struct GC_gcdata_s *gcdata, void *displaced_pointer)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *marked_list;
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s **pnext;
|
|
struct GC_objlink_s *objlink;
|
|
struct GC_objlink_s **hroots;
|
|
GC_word min_obj_addr;
|
|
GC_word max_obj_addr;
|
|
GC_word log2_size;
|
|
GC_word hmask;
|
|
GC_word count;
|
|
if ((void *)(marked_list = gcdata->obj_htable.follow_list) !=
|
|
(void *)gcdata->obj_htable.marked_list)
|
|
{
|
|
hroots = gcdata->obj_htable.hroots;
|
|
min_obj_addr = gcdata->obj_htable.min_obj_addr;
|
|
max_obj_addr = gcdata->obj_htable.max_obj_addr -
|
|
((GC_word)1 << GC_LOG2_OFFIGNORE);
|
|
if (marked_list == NULL)
|
|
marked_list = gcdata->obj_htable.marked_list;
|
|
hmask = ((GC_word)1 << (log2_size = gcdata->obj_htable.log2_size)) - 1;
|
|
count = 0;
|
|
do
|
|
{
|
|
do
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr = (GC_word)marked_list->obj;
|
|
if (min_obj_addr >= addr)
|
|
min_obj_addr = addr;
|
|
if (max_obj_addr <= addr)
|
|
max_obj_addr = addr;
|
|
pnext = &hroots[(((addr >> log2_size) ^ addr) >>
|
|
GC_LOG2_OFFIGNORE) & hmask];
|
|
while ((GC_word)(objlink = *pnext)->obj > addr)
|
|
pnext = &objlink->next;
|
|
count++;
|
|
if ((GC_word)displaced_pointer - addr <
|
|
(marked_list->atomic_and_size & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK)))
|
|
break;
|
|
marked_list = (*pnext = marked_list)->next;
|
|
(*pnext)->next = objlink;
|
|
} while (marked_list != NULL);
|
|
if (marked_list != NULL)
|
|
break;
|
|
if (gcdata->obj_htable.follow_list == NULL)
|
|
{
|
|
gcdata->obj_htable.marked_list = NULL;
|
|
break;
|
|
}
|
|
gcdata->obj_htable.follow_list = NULL;
|
|
} while ((marked_list = gcdata->obj_htable.marked_list) != NULL);
|
|
gcdata->obj_htable.min_obj_addr = min_obj_addr;
|
|
gcdata->obj_htable.max_obj_addr =
|
|
max_obj_addr + ((GC_word)1 << GC_LOG2_OFFIGNORE);
|
|
gcdata->obj_htable.count += count;
|
|
if (marked_list != NULL)
|
|
{
|
|
if (gcdata->obj_htable.follow_list != NULL)
|
|
gcdata->obj_htable.follow_list = marked_list->next;
|
|
else gcdata->obj_htable.marked_list = marked_list->next;
|
|
*pnext = marked_list;
|
|
marked_list->next = objlink;
|
|
}
|
|
}
|
|
return marked_list;
|
|
}
|
|
|
|
GC_STATIC GC_word GC_FASTCALL GC_objlink_some_refill(
|
|
struct GC_gcdata_s *gcdata, GC_word max_count)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *marked_list;
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s **pnext;
|
|
struct GC_objlink_s *objlink;
|
|
struct GC_objlink_s **hroots;
|
|
GC_word min_obj_addr;
|
|
GC_word max_obj_addr;
|
|
GC_word log2_size;
|
|
GC_word hmask;
|
|
GC_word count = 0;
|
|
if ((void *)(marked_list = gcdata->obj_htable.follow_list) !=
|
|
(void *)gcdata->obj_htable.marked_list)
|
|
{
|
|
hroots = gcdata->obj_htable.hroots;
|
|
min_obj_addr = gcdata->obj_htable.min_obj_addr;
|
|
max_obj_addr = gcdata->obj_htable.max_obj_addr -
|
|
((GC_word)1 << GC_LOG2_OFFIGNORE);
|
|
if (marked_list == NULL)
|
|
marked_list = gcdata->obj_htable.marked_list;
|
|
hmask = ((GC_word)1 << (log2_size = gcdata->obj_htable.log2_size)) - 1;
|
|
count = max_count;
|
|
do
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word addr = (GC_word)marked_list->obj;
|
|
if (min_obj_addr >= addr)
|
|
min_obj_addr = addr;
|
|
if (max_obj_addr <= addr)
|
|
max_obj_addr = addr;
|
|
if ((GC_word)(objlink = *(pnext = &hroots[(((addr >> log2_size) ^ addr) >>
|
|
GC_LOG2_OFFIGNORE) & hmask]))->obj > addr)
|
|
for (;;)
|
|
{
|
|
if ((GC_word)(objlink = *(pnext = &objlink->next))->obj <= addr)
|
|
break;
|
|
}
|
|
marked_list = (*pnext = marked_list)->next;
|
|
(*pnext)->next = objlink;
|
|
} while (--count && marked_list != NULL);
|
|
gcdata->obj_htable.min_obj_addr = min_obj_addr;
|
|
count = max_count - count;
|
|
gcdata->obj_htable.max_obj_addr =
|
|
max_obj_addr + ((GC_word)1 << GC_LOG2_OFFIGNORE);
|
|
gcdata->obj_htable.count += count;
|
|
if (gcdata->obj_htable.follow_list != NULL)
|
|
gcdata->obj_htable.follow_list = marked_list;
|
|
else gcdata->obj_htable.marked_list = marked_list;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
GC_STATIC GC_word GC_FASTCALL GC_objlink_free_pending(
|
|
struct GC_gcdata_s *gcdata, GC_word min_free_bytes)
|
|
{
|
|
GC_REGISTER_KEYWORD struct GC_objlink_s *objlink;
|
|
#ifndef GC_NO_DLINKS
|
|
GC_word max_hidden;
|
|
GC_word min_hidden;
|
|
#endif
|
|
GC_word objsize;
|
|
GC_word totalsize = 0;
|
|
GC_word count = 0;
|
|
while ((objlink = gcdata->obj_htable.free_list) != NULL)
|
|
{
|
|
objsize = objlink->atomic_and_size;
|
|
#ifndef GC_NO_DLINKS
|
|
if (gcdata->dlink_htable.count)
|
|
{
|
|
min_hidden = GC_HIDE_POINTER((char *)objlink->obj +
|
|
(objsize & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK |
|
|
(sizeof(GC_word) - 1))) - (GC_word)sizeof(GC_word));
|
|
if ((max_hidden = GC_HIDE_POINTER(objlink->obj)) >= min_hidden)
|
|
{
|
|
(void)GC_dlink_delete(gcdata, max_hidden, min_hidden, max_hidden);
|
|
if (((min_hidden ^ max_hidden) &
|
|
~(((GC_word)1 << GC_LOG2_OFFIGNORE) - 1)) != 0)
|
|
(void)GC_dlink_delete(gcdata, max_hidden -
|
|
((GC_word)1 << GC_LOG2_OFFIGNORE), min_hidden, max_hidden);
|
|
}
|
|
}
|
|
#endif
|
|
gcdata->obj_htable.free_list = objlink->next;
|
|
GC_CORE_FREE(objlink->obj);
|
|
if ((objsize & GC_ATOMIC_MASK) == 0)
|
|
gcdata->followscan_size -= objsize & ~(GC_HASDSLEN_MASK |
|
|
(sizeof(GC_word) - 1));
|
|
objlink->obj = NULL;
|
|
objlink->next = gcdata->obj_htable.unlinked_list;
|
|
gcdata->obj_htable.unlinked_list = objlink;
|
|
totalsize += objsize;
|
|
count++;
|
|
if ((gcdata->free_bytes +=
|
|
objsize & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK)) >= min_free_bytes)
|
|
break;
|
|
}
|
|
gcdata->obj_htable.pending_free_size -=
|
|
totalsize & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK);
|
|
return count;
|
|
}
|
|
|
|
GC_STATIC void GC_FASTCALL GC_collect_unreachable(struct GC_gcdata_s *gcdata,
|
|
GC_stop_func stop_func)
|
|
{
|
|
GC_word oldcnt;
|
|
GC_word obj_count;
|
|
GC_start_callback_proc start_fn;
|
|
#ifndef GC_NO_FNLZ
|
|
GC_word ready_count = 0;
|
|
#endif
|
|
struct GC_objlink_s **new_hroots;
|
|
int stopped;
|
|
#ifdef GC_PRINT_MSGS
|
|
#ifndef GC_NO_DLINKS
|
|
GC_word dlinks_count = gcdata->dlink_htable.count;
|
|
#endif
|
|
GC_CURTIME_T curt;
|
|
unsigned long time_ms = 0;
|
|
#endif
|
|
if (gcdata->dataroots != NULL)
|
|
{
|
|
if ((start_fn = GC_start_call_back) != 0)
|
|
(*start_fn)();
|
|
#ifdef GC_PRINT_MSGS
|
|
if (GC_verbose_gc)
|
|
{
|
|
fprintf(stdout,
|
|
"[GC: #%lu Scan %lu KiB after %lu KiB allocd, %lu KiB free of %lu KiB]"
|
|
GC_NEW_LINE, (unsigned long)(GC_gc_no + 1),
|
|
GC_SIZE_TO_ULKB(GC_stack_approx_size(gcdata) + gcdata->dataroot_size +
|
|
gcdata->followscan_size), GC_SIZE_TO_ULKB(gcdata->bytes_allocd),
|
|
GC_SIZE_TO_ULKB(gcdata->free_bytes),
|
|
GC_SIZE_TO_ULKB(gcdata->total_heapsize));
|
|
time_ms = GC_CURTIME_GETMS(&curt);
|
|
}
|
|
obj_count = gcdata->obj_htable.count;
|
|
#endif
|
|
if (!(*stop_func)())
|
|
{
|
|
stopped = 0;
|
|
while (GC_objlink_some_refill(gcdata, GC_LAZYREFILL_BIGCNT))
|
|
if ((stopped = (*stop_func)()) != 0)
|
|
break;
|
|
obj_count = gcdata->obj_htable.count;
|
|
if (!stopped)
|
|
{
|
|
#ifndef GC_NO_FNLZ
|
|
oldcnt = GC_fnlz_precollect(gcdata, &ready_count);
|
|
#endif
|
|
if (
|
|
#ifdef GC_NO_FNLZ
|
|
obj_count
|
|
#else
|
|
gcdata->obj_htable.count &&
|
|
((!oldcnt && !gcdata->fnlz_htable.count) ||
|
|
(stopped = (*stop_func)()) == 0)
|
|
#endif
|
|
)
|
|
{
|
|
GC_mutator_suspend(gcdata);
|
|
GC_stack_scan_cur(gcdata);
|
|
stopped = 1;
|
|
if (GC_roots_scan(gcdata, stop_func) == NULL
|
|
#ifdef GC_THREADS
|
|
&& !GC_stkroot_scan_other(gcdata, stop_func)
|
|
#endif
|
|
)
|
|
{
|
|
GC_scan_followable(gcdata, stop_func);
|
|
if (gcdata->obj_htable.follow_list == NULL)
|
|
stopped = 0;
|
|
}
|
|
GC_mutator_resume(gcdata);
|
|
#ifdef GC_NO_FNLZ
|
|
#ifndef GC_NO_DLINKS
|
|
if (!stopped && gcdata->dlink_htable.count)
|
|
GC_dlink_scan_clear(gcdata);
|
|
#endif
|
|
#else
|
|
if (!stopped)
|
|
{
|
|
#ifndef GC_NO_DLINKS
|
|
if (gcdata->dlink_htable.count)
|
|
GC_dlink_scan_clear(gcdata);
|
|
#endif
|
|
if (gcdata->fnlz_htable.count)
|
|
oldcnt += GC_fnlz_after_collect(gcdata, &ready_count);
|
|
gcdata->bytes_finalized = oldcnt;
|
|
}
|
|
#endif
|
|
}
|
|
if (!stopped)
|
|
{
|
|
GC_gc_no++;
|
|
#ifndef GC_NO_FNLZ
|
|
GC_scan_followable(gcdata, GC_never_stop_func);
|
|
#endif
|
|
oldcnt = obj_count;
|
|
gcdata->marked_bytes += gcdata->bytes_allocd;
|
|
if (gcdata->obj_htable.count)
|
|
{
|
|
gcdata->obj_htable.pending_free_size += gcdata->marked_bytes;
|
|
obj_count -= GC_objlink_remove_all(gcdata);
|
|
gcdata->obj_htable.pending_free_size -= gcdata->marked_bytes;
|
|
}
|
|
if ((oldcnt >> 1) < obj_count &&
|
|
GC_HASH_RESIZECOND(oldcnt, gcdata->obj_htable.log2_size) &&
|
|
(new_hroots = GC_alloc_hroots(gcdata,
|
|
gcdata->obj_htable.log2_size + 1, &GC_nil_objlink)) != NULL)
|
|
{
|
|
GC_CORE_FREE(gcdata->obj_htable.hroots);
|
|
gcdata->free_bytes += ((GC_word)sizeof(GC_word) <<
|
|
gcdata->obj_htable.log2_size) + sizeof(GC_word);
|
|
gcdata->obj_htable.hroots = new_hroots;
|
|
gcdata->obj_htable.log2_size++;
|
|
}
|
|
gcdata->allocd_before_gc += gcdata->bytes_allocd;
|
|
gcdata->bytes_allocd = 0;
|
|
}
|
|
}
|
|
}
|
|
if (!gcdata->bytes_allocd)
|
|
{
|
|
gcdata->recycling = 1;
|
|
#ifdef GC_PRINT_MSGS
|
|
if (GC_verbose_gc)
|
|
{
|
|
fprintf(stdout,
|
|
"[GC: Done in %lu ms, %lu KiB used by %lu objs, %lu KiB used by GC]"
|
|
GC_NEW_LINE, GC_CURTIME_GETMS(&curt) - time_ms,
|
|
GC_SIZE_TO_ULKB(gcdata->marked_bytes), (unsigned long)obj_count,
|
|
GC_SIZE_TO_ULKB(gcdata->total_heapsize - gcdata->marked_bytes -
|
|
gcdata->free_bytes));
|
|
#ifndef GC_NO_DLINKS
|
|
if (dlinks_count)
|
|
fprintf(stdout,
|
|
"[GC: %lu disappearing links cleared of %lu registered]" GC_NEW_LINE,
|
|
(unsigned long)(dlinks_count - gcdata->dlink_htable.count),
|
|
(unsigned long)dlinks_count);
|
|
#endif
|
|
#ifndef GC_NO_FNLZ
|
|
if ((oldcnt = gcdata->fnlz_htable.count + ready_count) != 0)
|
|
fprintf(stdout,
|
|
"[GC: %lu finalizers ready of %lu registered]" GC_NEW_LINE,
|
|
(unsigned long)ready_count, (unsigned long)oldcnt);
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
GC_STATIC void *GC_FASTCALL GC_inner_core_malloc(struct GC_gcdata_s *gcdata,
|
|
GC_word size, int dont_expand)
|
|
{
|
|
GC_word free_bytes;
|
|
void *ptr = NULL;
|
|
if (size <= (~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK) < GC_MEM_SIZELIMIT ?
|
|
~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK) : GC_MEM_SIZELIMIT) &&
|
|
((free_bytes = gcdata->free_bytes) > size || (!dont_expand &&
|
|
gcdata->total_heapsize - (free_bytes - size) <= GC_max_heapsize)))
|
|
{
|
|
do
|
|
{
|
|
ptr = GC_CORE_MALLOC((size_t)size);
|
|
} while ((GC_word)ptr > ~((GC_word)1 << GC_LOG2_OFFIGNORE) ||
|
|
((GC_word)ptr & (sizeof(GC_word) - 1)) != 0);
|
|
if (ptr != NULL)
|
|
{
|
|
if (free_bytes < size)
|
|
{
|
|
gcdata->total_heapsize += size - free_bytes;
|
|
gcdata->free_bytes = 0;
|
|
}
|
|
else gcdata->free_bytes = free_bytes - size;
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
GC_STATIC void *GC_FASTCALL GC_alloc_hroots(struct GC_gcdata_s *gcdata,
|
|
GC_word new_log2_size, CONST void *nil_ptr)
|
|
{
|
|
GC_REGISTER_KEYWORD GC_word size = (GC_word)sizeof(GC_word) << new_log2_size;
|
|
void *ptr;
|
|
if ((ptr = GC_inner_core_malloc(gcdata, size + sizeof(GC_word),
|
|
GC_dont_expand)) != NULL || ((
|
|
#ifndef GC_NO_DLINKS
|
|
GC_dlink_free_pending(gcdata, ~(GC_word)0) +
|
|
#endif
|
|
GC_objlink_free_pending(gcdata, ~(GC_word)0) != 0 || GC_dont_expand) &&
|
|
(ptr = GC_inner_core_malloc(gcdata, size + sizeof(GC_word), 0)) != NULL))
|
|
{
|
|
*(GC_word *)((char *)ptr + size) = ~(GC_word)0;
|
|
if (nil_ptr != NULL)
|
|
{
|
|
while ((size -= sizeof(GC_word)) != 0)
|
|
*(CONST void **)((char *)ptr + size) = nil_ptr;
|
|
*(CONST void **)ptr = nil_ptr;
|
|
}
|
|
else GC_MEM_BZERO(ptr, size);
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
GC_STATIC void *GC_FASTCALL GC_core_malloc_with_gc(struct GC_gcdata_s *gcdata,
|
|
GC_word size, int *pres)
|
|
{
|
|
GC_REGISTER_KEYWORD void *ptr;
|
|
#ifndef GC_NO_FNLZ
|
|
struct GC_stkroot_s *cur_stack;
|
|
#endif
|
|
GC_word count;
|
|
if ((ptr = GC_inner_core_malloc(gcdata, size, GC_dont_expand)) == NULL)
|
|
{
|
|
if (*pres >= 0)
|
|
{
|
|
*pres = 0;
|
|
if ((!GC_objlink_free_pending(gcdata, ~(GC_word)0) ||
|
|
(ptr = GC_inner_core_malloc(gcdata, size, GC_dont_expand)) == NULL) &&
|
|
!GC_dont_gc)
|
|
{
|
|
GC_collect_unreachable(gcdata, GC_never_stop_func);
|
|
*pres = -1;
|
|
#ifndef GC_NO_FNLZ
|
|
if ((cur_stack = gcdata->cur_stack) != NULL)
|
|
cur_stack->inside_fnlz = 0;
|
|
#endif
|
|
}
|
|
}
|
|
if (ptr == NULL && ((count = GC_objlink_free_pending(gcdata, size)) == 0 ||
|
|
(ptr = GC_inner_core_malloc(gcdata, size, GC_dont_expand)) == NULL))
|
|
{
|
|
#ifndef GC_NO_DLINKS
|
|
if (GC_dlink_free_pending(gcdata, ~(GC_word)0))
|
|
{
|
|
*pres = 1;
|
|
if ((ptr = GC_inner_core_malloc(gcdata, size, 0)) != NULL)
|
|
*pres = 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (!count || GC_dont_expand)
|
|
ptr = GC_inner_core_malloc(gcdata, size, 0);
|
|
}
|
|
#ifdef GC_PRINT_MSGS
|
|
if (ptr == NULL && GC_verbose_gc)
|
|
fprintf(stderr,
|
|
" GC: Out of memory! Cannot allocate %lu bytes." GC_NEW_LINE,
|
|
(unsigned long)size);
|
|
#endif
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
GC_STATIC int GC_FASTCALL GC_alloc_objlinks(struct GC_gcdata_s *gcdata,
|
|
int *pres)
|
|
{
|
|
GC_REGISTER_KEYWORD void *objlinks_block_list;
|
|
GC_word count = (GC_word)1 << (gcdata->obj_htable.log2_size - 2);
|
|
if ((objlinks_block_list = GC_core_malloc_with_gc(gcdata,
|
|
count * sizeof(struct GC_objlink_s) + sizeof(GC_word), pres)) == NULL)
|
|
return 0;
|
|
*(void **)objlinks_block_list = gcdata->objlinks_block_list;
|
|
gcdata->objlinks_block_list = objlinks_block_list;
|
|
if (*pres >= 0)
|
|
*pres = 0;
|
|
((struct GC_objlink_s *)(objlinks_block_list =
|
|
(char *)objlinks_block_list + sizeof(GC_word)))->obj = NULL;
|
|
((struct GC_objlink_s *)objlinks_block_list)->next =
|
|
gcdata->obj_htable.unlinked_list;
|
|
while (--count)
|
|
{
|
|
((struct GC_objlink_s *)((GC_word)objlinks_block_list +
|
|
sizeof(struct GC_objlink_s)))->next = objlinks_block_list;
|
|
((struct GC_objlink_s *)(objlinks_block_list =
|
|
(char *)objlinks_block_list + sizeof(struct GC_objlink_s)))->obj = NULL;
|
|
}
|
|
gcdata->obj_htable.unlinked_list = objlinks_block_list;
|
|
return 1;
|
|
}
|
|
|
|
GC_STATIC void *GC_FASTCALL GC_general_malloc(struct GC_gcdata_s **pgcdata,
|
|
GC_word objsize, GC_word vtable)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
void *obj = NULL;
|
|
struct GC_objlink_s **new_hroots;
|
|
GC_word retry;
|
|
int res;
|
|
#ifndef GC_NO_FNLZ
|
|
struct GC_objlink_s *objlink;
|
|
void *client_data;
|
|
GC_finalization_proc fn;
|
|
GC_finalizer_notifier_proc notifier_fn;
|
|
#endif
|
|
if (objsize)
|
|
{
|
|
retry = ~(GC_word)0;
|
|
#ifndef GC_NO_FNLZ
|
|
objlink = NULL;
|
|
client_data = NULL;
|
|
fn = 0;
|
|
#endif
|
|
#ifndef DONT_ADD_BYTE_AT_END
|
|
if (objsize < ((GC_word)1 << GC_LOG2_OFFIGNORE) && GC_all_interior_pointers)
|
|
objsize++;
|
|
#endif
|
|
do
|
|
{
|
|
res = 1;
|
|
GC_enter(pgcdata);
|
|
gcdata = *pgcdata;
|
|
if (retry == ~(GC_word)0)
|
|
{
|
|
if (
|
|
#ifdef GC_NO_FNLZ
|
|
GC_HASH_RESIZECOND(gcdata->obj_htable.count,
|
|
gcdata->obj_htable.log2_size) &&
|
|
#else
|
|
GC_HASH_RESIZECOND(gcdata->obj_htable.count,
|
|
gcdata->obj_htable.log2_size +
|
|
(GC_word)gcdata->cur_stack->inside_fnlz) &&
|
|
#endif
|
|
gcdata->bytes_allocd)
|
|
{
|
|
if (GC_dont_gc || gcdata->dataroots == NULL)
|
|
{
|
|
if ((GC_dont_gc == GC_NEVER_COLLECT || gcdata->dataroots == NULL) &&
|
|
(new_hroots = GC_alloc_hroots(gcdata,
|
|
gcdata->obj_htable.log2_size + 1, &GC_nil_objlink)) != NULL)
|
|
{
|
|
(void)GC_objlink_free_pending(gcdata, ~(GC_word)0);
|
|
gcdata->obj_htable.free_list = gcdata->obj_htable.marked_list;
|
|
retry = gcdata->marked_bytes;
|
|
(void)GC_objlink_remove_all(gcdata);
|
|
gcdata->marked_bytes = retry;
|
|
gcdata->obj_htable.marked_list = gcdata->obj_htable.free_list;
|
|
retry = ~(GC_word)0;
|
|
gcdata->obj_htable.free_list = NULL;
|
|
GC_CORE_FREE(gcdata->obj_htable.hroots);
|
|
gcdata->free_bytes += ((GC_word)sizeof(GC_word) <<
|
|
gcdata->obj_htable.log2_size) + sizeof(GC_word);
|
|
gcdata->obj_htable.hroots = new_hroots;
|
|
gcdata->obj_htable.log2_size++;
|
|
res = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GC_collect_unreachable(gcdata, GC_default_stop_func);
|
|
res = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!gcdata->recycling &&
|
|
#ifndef GC_NO_FNLZ
|
|
!gcdata->cur_stack->inside_fnlz &&
|
|
#endif
|
|
!GC_dont_gc)
|
|
{
|
|
if (GC_guess_collect(gcdata, objsize))
|
|
{
|
|
GC_collect_unreachable(gcdata, GC_default_stop_func);
|
|
res = -1;
|
|
}
|
|
else retry = 0;
|
|
}
|
|
if (gcdata->bytes_allocd &&
|
|
(retry = GC_guess_expand_size(gcdata, objsize)) != 0)
|
|
{
|
|
if (!GC_dont_expand &&
|
|
gcdata->expanded_heapsize <= gcdata->total_heapsize)
|
|
(void)GC_heap_expand(gcdata, retry);
|
|
retry = 0;
|
|
}
|
|
}
|
|
}
|
|
if (gcdata->obj_htable.unlinked_list != NULL ||
|
|
GC_alloc_objlinks(gcdata, &res))
|
|
obj = GC_core_malloc_with_gc(gcdata, objsize, &res);
|
|
#ifndef GC_NO_FNLZ
|
|
notifier_fn = 0;
|
|
if (gcdata->fnlz_htable.ready_fnlz != NULL && GC_finalize_on_demand &&
|
|
gcdata->notifier_gc_no != GC_gc_no)
|
|
{
|
|
gcdata->notifier_gc_no = GC_gc_no;
|
|
notifier_fn = GC_finalizer_notifier;
|
|
}
|
|
#endif
|
|
if (obj != NULL)
|
|
{
|
|
gcdata->bytes_allocd += objsize;
|
|
GC_objlink_add(gcdata, obj, objsize, vtable);
|
|
if (!retry && gcdata->recycling && res > 0)
|
|
{
|
|
res = (int)GC_objlink_some_refill(gcdata, GC_LAZYREFILL_COUNT);
|
|
if (gcdata->obj_htable.free_list != NULL &&
|
|
(res += (int)GC_objlink_free_pending(gcdata, 0) << 1) > 1 &&
|
|
(res > 2 || GC_objlink_free_pending(gcdata,
|
|
gcdata->free_bytes + ((GC_word)1 << GC_LOG2_OFFIGNORE)) == 1))
|
|
(void)GC_objlink_free_pending(gcdata, 0);
|
|
#ifndef GC_NO_FNLZ
|
|
if (notifier_fn == 0 && !GC_dont_gc && !GC_finalize_on_demand &&
|
|
++gcdata->cur_stack->inside_fnlz == 1)
|
|
{
|
|
if ((fn = GC_fnlz_del_ready(gcdata, &objlink, &client_data)) != 0)
|
|
res = 1;
|
|
else gcdata->cur_stack->inside_fnlz = 0;
|
|
}
|
|
#endif
|
|
if (!res
|
|
#ifndef GC_NO_DLINKS
|
|
&& (gcdata->dlink_htable.free_list == NULL ||
|
|
!GC_dlink_free_pending(gcdata,
|
|
objsize / (sizeof(struct GC_dlink_s) - sizeof(GC_word)) + 1))
|
|
#endif
|
|
)
|
|
{
|
|
gcdata->recycling = 0;
|
|
#ifdef GC_PRINT_MSGS
|
|
if (GC_verbose_gc)
|
|
fprintf(stdout,
|
|
"[GC: Recycled, %lu + %lu /A/ KiB in use, %lu KiB free of %lu KiB]"
|
|
GC_NEW_LINE, GC_SIZE_TO_ULKB(gcdata->followscan_size),
|
|
GC_SIZE_TO_ULKB(gcdata->marked_bytes + gcdata->bytes_allocd -
|
|
gcdata->followscan_size), GC_SIZE_TO_ULKB(gcdata->free_bytes),
|
|
GC_SIZE_TO_ULKB(gcdata->total_heapsize));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#ifndef GC_NO_FNLZ
|
|
else
|
|
{
|
|
if (notifier_fn == 0 && !GC_finalize_on_demand)
|
|
fn = GC_fnlz_del_ready(gcdata, &objlink, &client_data);
|
|
}
|
|
#endif
|
|
*pgcdata = NULL;
|
|
GC_LEAVE(gcdata);
|
|
#ifdef GC_NO_FNLZ
|
|
if (obj != NULL || res <= 0)
|
|
break;
|
|
#else
|
|
if (fn != 0)
|
|
{
|
|
(*fn)(objlink->obj, client_data);
|
|
if (obj != NULL)
|
|
{
|
|
GC_enter(pgcdata);
|
|
gcdata = *pgcdata;
|
|
gcdata->cur_stack->inside_fnlz = 0;
|
|
*pgcdata = NULL;
|
|
GC_LEAVE(gcdata);
|
|
break;
|
|
}
|
|
fn = 0;
|
|
if (!GC_finalize_on_demand)
|
|
for (;;)
|
|
{
|
|
GC_enter(pgcdata);
|
|
gcdata = *pgcdata;
|
|
(void)GC_objlink_some_refill(gcdata, GC_LAZYREFILL_BIGCNT);
|
|
fn = GC_fnlz_del_ready(gcdata, &objlink, &client_data);
|
|
*pgcdata = NULL;
|
|
GC_LEAVE(gcdata);
|
|
if (fn == 0)
|
|
break;
|
|
(*fn)(objlink->obj, client_data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (notifier_fn != 0)
|
|
(*notifier_fn)();
|
|
if (obj != NULL || (notifier_fn == 0 && res <= 0))
|
|
break;
|
|
}
|
|
#endif
|
|
if (retry == ~(GC_word)0)
|
|
retry = 0;
|
|
} while (++retry <= GC_max_retries);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
GC_API void *GC_CALL GC_malloc(size_t size)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
return GC_general_malloc(&gcdata, (GC_word)size, ~(GC_word)0);
|
|
}
|
|
|
|
GC_API void *GC_CALL GC_malloc_atomic(size_t size)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
return GC_general_malloc(&gcdata, (GC_word)size, 0);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_init(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
GC_finalizer_notifier = fn;
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc fn)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
GC_start_call_back = fn;
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_stop_func(GC_stop_func fn)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
if (fn == 0)
|
|
GC_abort_badptr(NULL);
|
|
GC_enter(&gcdata);
|
|
GC_default_stop_func = fn;
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API void *GC_CALL GC_do_blocking(GC_fn_type fn, void *client_data)
|
|
{
|
|
#ifndef GC_NO_INACTIVE
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
if (!GC_set_inactive_sp(&gcdata))
|
|
GC_abort_badptr(NULL);
|
|
GC_LEAVE(gcdata);
|
|
#endif
|
|
client_data = (*fn)(client_data);
|
|
#ifndef GC_NO_INACTIVE
|
|
GC_enter(&gcdata);
|
|
gcdata->cur_stack->inactive = 0;
|
|
GC_LEAVE(gcdata);
|
|
#endif
|
|
return client_data;
|
|
}
|
|
|
|
GC_API void *GC_CALL GC_call_with_gc_active(GC_fn_type fn, void *client_data)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
#ifdef GC_NO_INACTIVE
|
|
GC_enter(&gcdata);
|
|
GC_LEAVE(gcdata);
|
|
return (*fn)(client_data);
|
|
#else
|
|
struct GC_activation_frame_s frame;
|
|
GC_enter(&gcdata);
|
|
if (!GC_set_activation_frame(&frame, gcdata->cur_stack))
|
|
{
|
|
GC_LEAVE(gcdata);
|
|
return (*fn)(client_data);
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
client_data = (*fn)(client_data);
|
|
GC_enter(&gcdata);
|
|
GC_restore_inactive_sp(gcdata->cur_stack, &frame);
|
|
GC_LEAVE(gcdata);
|
|
return client_data;
|
|
#endif
|
|
}
|
|
|
|
#ifdef GC_THREADS
|
|
|
|
GC_API void GC_CALL GC_allow_register_threads(void)
|
|
{
|
|
/* dummy */
|
|
}
|
|
|
|
GC_API int GC_CALL GC_register_my_thread(CONST struct GC_stack_base *sb)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
struct GC_stkroot_s *cur_stack;
|
|
GC_word stack_addr;
|
|
int res;
|
|
res = GC_enter(&gcdata);
|
|
if ((stack_addr = (GC_word)sb->mem_base) != 0)
|
|
{
|
|
if ((cur_stack = gcdata->cur_stack)->begin_addr > stack_addr)
|
|
cur_stack->begin_addr = stack_addr;
|
|
if (cur_stack->end_addr < stack_addr)
|
|
cur_stack->end_addr = stack_addr;
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
return res;
|
|
}
|
|
|
|
GC_API int GC_CALL GC_unregister_my_thread(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
GC_stkroot_delete_cur(gcdata);
|
|
GC_LEAVE(gcdata);
|
|
return GC_SUCCESS;
|
|
}
|
|
|
|
#endif /* GC_THREADS */
|
|
|
|
GC_API void *GC_CALL GC_call_with_alloc_lock(GC_fn_type fn, void *client_data)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
client_data = (*fn)(client_data);
|
|
GC_LEAVE(gcdata);
|
|
return client_data;
|
|
}
|
|
|
|
#ifndef GC_MISC_EXCLUDE
|
|
|
|
GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_finalizer_notifier_proc fn;
|
|
GC_enter(&gcdata);
|
|
fn = GC_finalizer_notifier;
|
|
GC_LEAVE(gcdata);
|
|
return fn;
|
|
}
|
|
|
|
GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_start_callback_proc fn;
|
|
GC_enter(&gcdata);
|
|
fn = GC_start_call_back;
|
|
GC_LEAVE(gcdata);
|
|
return fn;
|
|
}
|
|
|
|
GC_API GC_stop_func GC_CALL GC_get_stop_func(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_stop_func fn;
|
|
GC_enter(&gcdata);
|
|
fn = GC_default_stop_func;
|
|
GC_LEAVE(gcdata);
|
|
return fn;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc fn)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
if (fn == 0)
|
|
GC_abort_badptr(NULL);
|
|
GC_enter(&gcdata);
|
|
GC_current_warn_proc = fn;
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_warn_proc fn;
|
|
GC_enter(&gcdata);
|
|
fn = GC_current_warn_proc;
|
|
GC_LEAVE(gcdata);
|
|
return fn;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_enable_incremental(void)
|
|
{
|
|
/* dummy */
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API int GC_CALL GC_expand_hp(size_t incsize)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
int res;
|
|
GC_enter(&gcdata);
|
|
res = GC_heap_expand(gcdata, (GC_word)incsize);
|
|
GC_LEAVE(gcdata);
|
|
return res >= 0 ? 1 : 0;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_disable(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
GC_dont_gc++;
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_enable(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
GC_dont_gc--;
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API int GC_CALL GC_should_invoke_finalizers(void)
|
|
{
|
|
int res = 0;
|
|
#ifndef GC_NO_FNLZ
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
if (gcdata->fnlz_htable.ready_fnlz != NULL)
|
|
res = 1;
|
|
GC_LEAVE(gcdata);
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
GC_API size_t GC_CALL GC_get_bytes_since_gc(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_word size;
|
|
GC_enter(&gcdata);
|
|
size = gcdata->bytes_allocd;
|
|
GC_LEAVE(gcdata);
|
|
return (size_t)size;
|
|
}
|
|
|
|
GC_API size_t GC_CALL GC_get_total_bytes(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_word size;
|
|
GC_enter(&gcdata);
|
|
size = gcdata->allocd_before_gc + gcdata->bytes_allocd;
|
|
GC_LEAVE(gcdata);
|
|
return (size_t)size;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_remove_roots(void *low_addr, void *high_addr_plus_1)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
if (low_addr != NULL)
|
|
{
|
|
low_addr = (void *)(((GC_word)low_addr + (sizeof(GC_word) - 1)) &
|
|
~(sizeof(GC_word) - 1));
|
|
high_addr_plus_1 =
|
|
(void *)((GC_word)high_addr_plus_1 & ~(sizeof(GC_word) - 1));
|
|
GC_enter(&gcdata);
|
|
if ((GC_word)low_addr < (GC_word)high_addr_plus_1)
|
|
GC_roots_del_inside(gcdata, (GC_word)low_addr, (GC_word)high_addr_plus_1);
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
}
|
|
|
|
GC_API void GC_CALL GC_clear_roots(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
GC_roots_del_inside(gcdata, sizeof(GC_word),
|
|
~(GC_word)(sizeof(GC_word) - 1));
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_exclude_static_roots(void *low_addr,
|
|
void *high_addr_plus_1)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
if (low_addr != NULL)
|
|
{
|
|
low_addr = (void *)(((GC_word)low_addr + (sizeof(GC_word) - 1)) &
|
|
~(sizeof(GC_word) - 1));
|
|
high_addr_plus_1 =
|
|
(void *)((GC_word)high_addr_plus_1 & ~(sizeof(GC_word) - 1));
|
|
GC_enter(&gcdata);
|
|
if ((GC_word)low_addr < (GC_word)high_addr_plus_1)
|
|
{
|
|
GC_roots_del_inside(gcdata, (GC_word)low_addr, (GC_word)high_addr_plus_1);
|
|
(void)GC_roots_exclude(gcdata, (GC_word)low_addr,
|
|
(GC_word)high_addr_plus_1);
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
}
|
|
|
|
#endif /* ! GC_MISC_EXCLUDE */
|
|
|
|
GC_API void GC_CALL GC_add_roots(void *low_addr, void *high_addr_plus_1)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_enter(&gcdata);
|
|
(void)GC_roots_add(gcdata, (GC_word)low_addr, (GC_word)high_addr_plus_1);
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API size_t GC_CALL GC_get_heap_size(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_word size;
|
|
GC_enter(&gcdata);
|
|
size = gcdata->marked_bytes + gcdata->bytes_allocd +
|
|
#ifndef GC_NO_DLINKS
|
|
gcdata->dlink_htable.count * sizeof(struct GC_dlink_s) +
|
|
#endif
|
|
#ifndef GC_NO_FNLZ
|
|
gcdata->fnlz_htable.count * sizeof(struct GC_fnlz_s) +
|
|
#endif
|
|
gcdata->obj_htable.pending_free_size + gcdata->free_bytes;
|
|
GC_LEAVE(gcdata);
|
|
return (size_t)size;
|
|
}
|
|
|
|
GC_API size_t GC_CALL GC_get_free_bytes(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_word size;
|
|
GC_enter(&gcdata);
|
|
size = gcdata->free_bytes;
|
|
GC_LEAVE(gcdata);
|
|
return (size_t)size;
|
|
}
|
|
|
|
GC_API int GC_CALL GC_try_to_collect(GC_stop_func stop_func)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
int res = 1;
|
|
#ifndef GC_NO_FNLZ
|
|
GC_finalizer_notifier_proc notifier_fn = 0;
|
|
#endif
|
|
if (stop_func == 0)
|
|
GC_abort_badptr(NULL);
|
|
GC_enter(&gcdata);
|
|
if (!GC_dont_gc && gcdata->bytes_allocd)
|
|
{
|
|
GC_collect_unreachable(gcdata, stop_func);
|
|
res = 0;
|
|
if (!gcdata->bytes_allocd)
|
|
{
|
|
res = 1;
|
|
#ifndef GC_NO_FNLZ
|
|
gcdata->cur_stack->inside_fnlz = 0;
|
|
if (gcdata->fnlz_htable.ready_fnlz != NULL && GC_finalize_on_demand)
|
|
{
|
|
gcdata->notifier_gc_no = GC_gc_no;
|
|
notifier_fn = GC_finalizer_notifier;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
#ifndef GC_NO_FNLZ
|
|
if (notifier_fn != 0)
|
|
(*notifier_fn)();
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
GC_API void GC_CALL GC_gcollect_and_unmap(void)
|
|
{
|
|
(void)GC_try_to_collect(GC_never_stop_func);
|
|
}
|
|
|
|
GC_API void GC_CALL GC_gcollect(void)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
#ifndef GC_NO_FNLZ
|
|
GC_finalizer_notifier_proc notifier_fn = 0;
|
|
#endif
|
|
GC_enter(&gcdata);
|
|
if (!GC_dont_gc && gcdata->bytes_allocd)
|
|
{
|
|
GC_collect_unreachable(gcdata, GC_default_stop_func);
|
|
#ifndef GC_NO_FNLZ
|
|
if (!gcdata->bytes_allocd)
|
|
{
|
|
gcdata->cur_stack->inside_fnlz = 0;
|
|
if (gcdata->fnlz_htable.ready_fnlz != NULL && GC_finalize_on_demand)
|
|
{
|
|
gcdata->notifier_gc_no = GC_gc_no;
|
|
notifier_fn = GC_finalizer_notifier;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
#ifndef GC_NO_FNLZ
|
|
if (notifier_fn != 0)
|
|
(*notifier_fn)();
|
|
#endif
|
|
}
|
|
|
|
GC_API void *GC_CALL GC_base(void *displaced_pointer)
|
|
{
|
|
#ifdef GC_NO_GCBASE
|
|
if (displaced_pointer != NULL)
|
|
GC_abort_badptr(displaced_pointer);
|
|
#else
|
|
struct GC_gcdata_s *gcdata;
|
|
struct GC_objlink_s *objlink;
|
|
if (displaced_pointer != NULL)
|
|
{
|
|
GC_enter(&gcdata);
|
|
displaced_pointer =
|
|
(objlink = GC_objlink_get(gcdata, displaced_pointer)) != NULL ||
|
|
(objlink = GC_objlink_refill_find(gcdata, displaced_pointer)) != NULL ?
|
|
objlink->obj : NULL;
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
#endif
|
|
return displaced_pointer;
|
|
}
|
|
|
|
GC_API int GC_CALL GC_general_register_disappearing_link(void **link,
|
|
void *obj)
|
|
{
|
|
#ifndef GC_NO_REGISTER_DLINK
|
|
struct GC_gcdata_s *gcdata;
|
|
struct GC_objlink_s *objlink;
|
|
#ifdef GC_NO_DLINKS
|
|
GC_word objsize;
|
|
#else
|
|
struct GC_dlink_s **new_hroots;
|
|
struct GC_dlink_s *new_dlink;
|
|
GC_word new_log2_size;
|
|
#endif
|
|
#endif
|
|
int res = 0;
|
|
if (((GC_word)link & (sizeof(GC_word) - 1)) != 0 || link == NULL)
|
|
GC_abort_badptr(link);
|
|
#ifndef GC_NO_REGISTER_DLINK
|
|
GC_enter(&gcdata);
|
|
if (obj != NULL)
|
|
{
|
|
#ifdef GC_NO_DLINKS
|
|
if ((objlink = GC_objlink_get(gcdata, (void *)link)) != NULL ||
|
|
(objlink = GC_objlink_refill_find(gcdata, (void *)link)) != NULL)
|
|
{
|
|
obj = NULL;
|
|
if (((objsize = objlink->atomic_and_size) & GC_ATOMIC_MASK) != 0)
|
|
{
|
|
gcdata->followscan_size += objsize & ~(GC_ATOMIC_MASK |
|
|
(sizeof(GC_word) - 1));
|
|
#ifndef GC_GCJ_SUPPORT
|
|
objlink->atomic_and_size = objsize & ~GC_ATOMIC_MASK;
|
|
#endif
|
|
}
|
|
#ifdef GC_GCJ_SUPPORT
|
|
objlink->atomic_and_size = objsize & ~(GC_ATOMIC_MASK | GC_HASDSLEN_MASK);
|
|
#endif
|
|
}
|
|
#else
|
|
if ((objlink = GC_objlink_get(gcdata, obj)) != NULL ||
|
|
(objlink = GC_objlink_refill_find(gcdata, obj)) != NULL)
|
|
{
|
|
if (objlink->obj == obj)
|
|
{
|
|
res = GC_dlink_add(gcdata, GC_HIDE_POINTER(link), objlink,
|
|
(new_dlink = gcdata->dlink_htable.free_list) != NULL &&
|
|
gcdata->free_bytes >= sizeof(struct GC_dlink_s) ? new_dlink :
|
|
((GC_HASH_RESIZECOND(gcdata->dlink_htable.count,
|
|
gcdata->dlink_htable.log2_size) &&
|
|
(new_hroots = GC_alloc_hroots(gcdata, new_log2_size =
|
|
gcdata->dlink_htable.log2_size + 1, &GC_nil_dlink)) != NULL ?
|
|
(GC_dlink_tblresize(gcdata, new_hroots, new_log2_size), 0) : 0),
|
|
GC_core_malloc_with_gc(gcdata, sizeof(struct GC_dlink_s), &res)));
|
|
obj = NULL;
|
|
}
|
|
}
|
|
else obj = NULL;
|
|
#endif
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
#endif
|
|
if (obj != NULL)
|
|
GC_abort_badptr(obj);
|
|
return res;
|
|
}
|
|
|
|
#ifndef GC_MISC_EXCLUDE
|
|
|
|
GC_API int GC_CALL GC_unregister_disappearing_link(void **link)
|
|
{
|
|
#ifndef GC_NO_DLINKS
|
|
struct GC_gcdata_s *gcdata;
|
|
GC_word hidden_link;
|
|
#endif
|
|
int res = 0;
|
|
#ifdef GC_NO_DLINKS
|
|
GC_noop1((GC_word)link);
|
|
#else
|
|
GC_enter(&gcdata);
|
|
if (gcdata->dlink_htable.count && (hidden_link = GC_HIDE_POINTER(link)) != 0)
|
|
res = GC_dlink_delete(gcdata, hidden_link, hidden_link, hidden_link);
|
|
GC_LEAVE(gcdata);
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
#endif /* ! GC_MISC_EXCLUDE */
|
|
|
|
GC_API void GC_CALL GC_register_finalizer_no_order(void *obj,
|
|
GC_finalization_proc fn, void *client_data, GC_finalization_proc *ofn,
|
|
void **odata)
|
|
{
|
|
#ifdef GC_NO_FNLZ
|
|
if (obj != NULL && fn != 0)
|
|
GC_noop1((GC_word)client_data);
|
|
if (ofn != NULL)
|
|
*ofn = 0;
|
|
if (odata != NULL)
|
|
*odata = NULL;
|
|
#else
|
|
struct GC_gcdata_s *gcdata;
|
|
struct GC_objlink_s *objlink;
|
|
struct GC_fnlz_s **new_hroots;
|
|
void *old_data;
|
|
GC_word new_log2_size;
|
|
int res = 0;
|
|
if (odata == NULL)
|
|
odata = &old_data;
|
|
GC_enter(&gcdata);
|
|
if (obj != NULL && ((objlink = GC_objlink_get(gcdata, obj)) != NULL ||
|
|
(objlink = GC_objlink_refill_find(gcdata, obj)) != NULL))
|
|
{
|
|
if (objlink->obj == obj)
|
|
{
|
|
if (fn != 0)
|
|
{
|
|
if (GC_HASH_RESIZECOND(gcdata->fnlz_htable.count,
|
|
gcdata->fnlz_htable.log2_size) &&
|
|
(new_hroots = GC_alloc_hroots(gcdata, new_log2_size =
|
|
gcdata->fnlz_htable.log2_size + 1, &GC_nil_fnlz)) != NULL)
|
|
GC_fnlz_tblresize(gcdata, new_hroots, new_log2_size);
|
|
if (gcdata->fnlz_htable.single_free == NULL)
|
|
res = (gcdata->fnlz_htable.single_free = GC_core_malloc_with_gc(gcdata,
|
|
sizeof(struct GC_fnlz_s), &res)) != NULL ? 1 : -1;
|
|
}
|
|
obj = NULL;
|
|
if ((fn = GC_fnlz_add_del(gcdata, objlink, fn, client_data, odata)) != 0)
|
|
{
|
|
if (ofn != NULL)
|
|
*ofn = fn;
|
|
fn = 0;
|
|
}
|
|
else
|
|
{
|
|
if ((res >> 1) == 0)
|
|
{
|
|
if (ofn != NULL)
|
|
*ofn = 0;
|
|
if (res > 0 && !GC_finalize_on_demand &&
|
|
++gcdata->cur_stack->inside_fnlz == 1 &&
|
|
(fn = GC_fnlz_del_ready(gcdata, &objlink, &client_data)) == 0)
|
|
gcdata->cur_stack->inside_fnlz = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
obj = NULL;
|
|
if (ofn != NULL)
|
|
*ofn = 0;
|
|
fn = 0;
|
|
*odata = NULL;
|
|
objlink = NULL;
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
if (obj != NULL)
|
|
GC_abort_badptr(obj);
|
|
if (fn != 0)
|
|
{
|
|
(*fn)(objlink->obj, client_data);
|
|
GC_enter(&gcdata);
|
|
gcdata->cur_stack->inside_fnlz = 0;
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
GC_API int GC_CALL GC_invoke_finalizers(void)
|
|
{
|
|
GC_word count = 0;
|
|
#ifndef GC_NO_FNLZ
|
|
struct GC_gcdata_s *gcdata;
|
|
struct GC_objlink_s *objlink = NULL;
|
|
void *client_data = NULL;
|
|
GC_finalization_proc fn;
|
|
for (;;)
|
|
{
|
|
GC_enter(&gcdata);
|
|
if ((fn = GC_fnlz_del_ready(gcdata, &objlink, &client_data)) != 0)
|
|
{
|
|
if (!count)
|
|
gcdata->cur_stack->inside_fnlz++;
|
|
}
|
|
else
|
|
{
|
|
if (count)
|
|
gcdata->cur_stack->inside_fnlz = 0;
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
if (fn == 0)
|
|
break;
|
|
count++;
|
|
(*fn)(objlink->obj, client_data);
|
|
}
|
|
#endif
|
|
return (int)count;
|
|
}
|
|
|
|
#ifdef GC_GCJ_SUPPORT
|
|
|
|
GC_API void GC_CALL GC_init_gcj_malloc(int mp_index, void *mp)
|
|
{
|
|
/* dummy */
|
|
struct GC_gcdata_s *gcdata;
|
|
if (mp != 0)
|
|
GC_noop1((GC_word)mp_index);
|
|
GC_enter(&gcdata);
|
|
GC_LEAVE(gcdata);
|
|
}
|
|
|
|
GC_API void *GC_CALL GC_gcj_malloc(size_t size, void *vtable)
|
|
{
|
|
struct GC_gcdata_s *gcdata;
|
|
if ((*(GC_word *)((char *)vtable + MARK_DESCR_OFFSET) & GC_DS_TAGS) !=
|
|
GC_DS_LENGTH || size < sizeof(GC_word))
|
|
GC_abort_badptr(vtable);
|
|
return GC_general_malloc(&gcdata, (GC_word)size, (GC_word)vtable);
|
|
}
|
|
|
|
#endif /* GC_GCJ_SUPPORT */
|
|
|
|
#ifndef JAVA_FINALIZATION_NOT_NEEDED
|
|
|
|
GC_API void GC_CALL GC_finalize_all(void)
|
|
{
|
|
#ifndef GC_NO_FNLZ
|
|
struct GC_gcdata_s *gcdata;
|
|
struct GC_objlink_s *objlink = NULL;
|
|
void *client_data = NULL;
|
|
GC_word count = 0;
|
|
GC_finalization_proc fn;
|
|
for (;;)
|
|
{
|
|
GC_enter(&gcdata);
|
|
if (!count && gcdata->fnlz_htable.count)
|
|
GC_fnlz_ready_all(gcdata);
|
|
if ((fn = GC_fnlz_del_ready(gcdata, &objlink, &client_data)) != 0)
|
|
{
|
|
if (!count)
|
|
gcdata->cur_stack->inside_fnlz++;
|
|
}
|
|
else
|
|
{
|
|
if (count)
|
|
gcdata->cur_stack->inside_fnlz = 0;
|
|
}
|
|
GC_LEAVE(gcdata);
|
|
if (fn != 0)
|
|
{
|
|
count++;
|
|
(*fn)(objlink->obj, client_data);
|
|
}
|
|
else
|
|
{
|
|
if (!count)
|
|
break;
|
|
count = 0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif /* ! JAVA_FINALIZATION_NOT_NEEDED */
|