You've already forked linux-packaging-mono
Imported Upstream version 4.2.0.179
Former-commit-id: 0a113cb3a6feb7873f632839b1307cc6033cd595
This commit is contained in:
committed by
Jo Shields
parent
183bba2c9a
commit
6992685b86
76
mono/sgen/Makefile.am
Normal file
76
mono/sgen/Makefile.am
Normal file
@@ -0,0 +1,76 @@
|
||||
AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/mono $(LIBGC_CPPFLAGS) $(GLIB_CFLAGS) $(SHARED_CFLAGS)
|
||||
|
||||
if SUPPORT_SGEN
|
||||
if DISABLE_EXECUTABLES
|
||||
shared_libraries = libmonosgen.la
|
||||
else
|
||||
if SHARED_MONO
|
||||
shared_libraries = libmonosgen.la
|
||||
endif
|
||||
endif
|
||||
libraries = $(shared_libraries) libmonosgen-static.la
|
||||
endif
|
||||
|
||||
if DISABLE_EXECUTABLES
|
||||
noinst_LTLIBRARIES = $(shared_libraries)
|
||||
else
|
||||
noinst_LTLIBRARIES = $(libraries)
|
||||
endif
|
||||
|
||||
monosgen_sources = \
|
||||
gc-internal-agnostic.h \
|
||||
sgen-alloc.c \
|
||||
sgen-archdep.h \
|
||||
sgen-cardtable.c \
|
||||
sgen-cardtable.h \
|
||||
sgen-client.h \
|
||||
sgen-conf.h \
|
||||
sgen-copy-object.h \
|
||||
sgen-debug.c \
|
||||
sgen-descriptor.c \
|
||||
sgen-descriptor.h \
|
||||
sgen-fin-weak-hash.c \
|
||||
sgen-gc.c \
|
||||
sgen-gc.h \
|
||||
sgen-gray.c \
|
||||
sgen-gray.h \
|
||||
sgen-hash-table.c \
|
||||
sgen-hash-table.h \
|
||||
sgen-internal.c \
|
||||
sgen-layout-stats.c \
|
||||
sgen-layout-stats.h \
|
||||
sgen-los.c \
|
||||
sgen-major-copy-object.h \
|
||||
sgen-marksweep-drain-gray-stack.h \
|
||||
sgen-marksweep-scan-object-concurrent.h \
|
||||
sgen-marksweep.c \
|
||||
sgen-memory-governor.c \
|
||||
sgen-memory-governor.h \
|
||||
sgen-minor-copy-object.h \
|
||||
sgen-minor-scan-object.h \
|
||||
sgen-nursery-allocator.c \
|
||||
sgen-pinning-stats.c \
|
||||
sgen-pinning.c \
|
||||
sgen-pinning.h \
|
||||
sgen-pointer-queue.c \
|
||||
sgen-pointer-queue.h \
|
||||
sgen-protocol-def.h \
|
||||
sgen-protocol.c \
|
||||
sgen-protocol.h \
|
||||
sgen-qsort.c \
|
||||
sgen-qsort.h \
|
||||
sgen-scan-object.h \
|
||||
sgen-simple-nursery.c \
|
||||
sgen-split-nursery.c \
|
||||
sgen-tagged-pointer.h \
|
||||
sgen-thread-pool.c \
|
||||
sgen-thread-pool.h \
|
||||
sgen-workers.c \
|
||||
sgen-workers.h
|
||||
|
||||
libmonosgen_la_SOURCES = $(monosgen_sources)
|
||||
libmonosgen_la_CFLAGS = $(SGEN_DEFINES)
|
||||
|
||||
libmonosgen_static_la_SOURCES = $(libmonosgen_la_SOURCES)
|
||||
libmonosgen_static_la_CFLAGS = $(SGEN_DEFINES)
|
||||
libmonosgen_static_la_LDFLAGS = -static
|
||||
1155
mono/sgen/Makefile.in
Normal file
1155
mono/sgen/Makefile.in
Normal file
File diff suppressed because it is too large
Load Diff
83
mono/sgen/gc-internal-agnostic.h
Normal file
83
mono/sgen/gc-internal-agnostic.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* gc-internal-agnostic.h: Mono-agnostic GC interface.
|
||||
*
|
||||
* Copyright (C) 2015 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MONO_METADATA_GCINTERNALAGNOSTIC_H__
|
||||
#define __MONO_METADATA_GCINTERNALAGNOSTIC_H__
|
||||
|
||||
#include <config.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mono/utils/mono-compiler.h"
|
||||
#include "mono/utils/parse.h"
|
||||
#include "mono/utils/memfuncs.h"
|
||||
#ifdef HAVE_SGEN_GC
|
||||
#include "mono/sgen/sgen-conf.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
guint minor_gc_count;
|
||||
guint major_gc_count;
|
||||
guint64 minor_gc_time;
|
||||
guint64 major_gc_time;
|
||||
guint64 major_gc_time_concurrent;
|
||||
} GCStats;
|
||||
|
||||
extern GCStats gc_stats;
|
||||
|
||||
#ifdef HAVE_SGEN_GC
|
||||
typedef SgenDescriptor MonoGCDescriptor;
|
||||
#define MONO_GC_DESCRIPTOR_NULL SGEN_DESCRIPTOR_NULL
|
||||
#else
|
||||
typedef void* MonoGCDescriptor;
|
||||
#define MONO_GC_DESCRIPTOR_NULL NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Try to register a foreign thread with the GC, if we fail or the backend
|
||||
* can't cope with this concept - we return FALSE.
|
||||
*/
|
||||
extern gboolean mono_gc_register_thread (void *baseptr);
|
||||
|
||||
gboolean mono_gc_parse_environment_string_extract_number (const char *str, size_t *out);
|
||||
|
||||
MonoGCDescriptor mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size);
|
||||
MonoGCDescriptor mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size);
|
||||
|
||||
/* simple interface for data structures needed in the runtime */
|
||||
MonoGCDescriptor mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits);
|
||||
|
||||
/* Return a root descriptor for a root with all refs */
|
||||
MonoGCDescriptor mono_gc_make_root_descr_all_refs (int numbits);
|
||||
|
||||
/* Return the bitmap encoded by a descriptor */
|
||||
gsize* mono_gc_get_bitmap_for_descr (MonoGCDescriptor descr, int *numbits);
|
||||
|
||||
/*
|
||||
These functions must be used when it's possible that either destination is not
|
||||
word aligned or size is not a multiple of word size.
|
||||
*/
|
||||
void mono_gc_bzero_atomic (void *dest, size_t size);
|
||||
void mono_gc_bzero_aligned (void *dest, size_t size);
|
||||
void mono_gc_memmove_atomic (void *dest, const void *src, size_t size);
|
||||
void mono_gc_memmove_aligned (void *dest, const void *src, size_t size);
|
||||
|
||||
FILE *mono_gc_get_logfile (void);
|
||||
|
||||
#endif
|
||||
569
mono/sgen/sgen-alloc.c
Normal file
569
mono/sgen/sgen-alloc.c
Normal file
File diff suppressed because it is too large
Load Diff
209
mono/sgen/sgen-archdep.h
Normal file
209
mono/sgen/sgen-archdep.h
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* sgen-archdep.h: Architecture dependent parts of SGen.
|
||||
*
|
||||
* Copyright 2001-2003 Ximian, Inc
|
||||
* Copyright 2003-2010 Novell, Inc.
|
||||
* Copyright (C) 2012 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef __MONO_SGENARCHDEP_H__
|
||||
#define __MONO_SGENARCHDEP_H__
|
||||
|
||||
#include <mono/utils/mono-context.h>
|
||||
|
||||
/*
|
||||
* Define either USE_MONO_CTX, or
|
||||
* ARCH_SIGCTX_SP/ARCH_SIGCTX_IP/ARCH_STORE_REGS/ARCH_COPY_SIGCTX_REGS.
|
||||
* Define ARCH_NUM_REGS to be the number of general registers in MonoContext, or the
|
||||
* number of registers stored by ARCH_STORE_REGS.
|
||||
*/
|
||||
|
||||
#if defined(MONO_CROSS_COMPILE)
|
||||
|
||||
#define REDZONE_SIZE 0
|
||||
|
||||
#define ARCH_NUM_REGS 0
|
||||
#define ARCH_STORE_REGS(ptr)
|
||||
#define ARCH_SIGCTX_SP(ctx) NULL
|
||||
#define ARCH_SIGCTX_IP(ctx) NULL
|
||||
#define ARCH_COPY_SIGCTX_REGS(a,ctx)
|
||||
|
||||
#elif defined(TARGET_X86)
|
||||
|
||||
#define REDZONE_SIZE 0
|
||||
|
||||
#define ARCH_NUM_REGS 8
|
||||
|
||||
#ifndef MONO_ARCH_HAS_MONO_CONTEXT
|
||||
#error 0
|
||||
#endif
|
||||
|
||||
#define USE_MONO_CTX
|
||||
|
||||
#elif defined(TARGET_AMD64)
|
||||
|
||||
#define REDZONE_SIZE 128
|
||||
|
||||
#define ARCH_NUM_REGS 16
|
||||
#define USE_MONO_CTX
|
||||
|
||||
#elif defined(TARGET_POWERPC)
|
||||
|
||||
#define REDZONE_SIZE 224
|
||||
|
||||
#define ARCH_NUM_REGS 32
|
||||
#ifdef __APPLE__
|
||||
#define ARCH_STORE_REGS(ptr) \
|
||||
__asm__ __volatile__( \
|
||||
"stmw r0, 0(%0)\n" \
|
||||
: \
|
||||
: "b" (ptr) \
|
||||
)
|
||||
#else
|
||||
#define ARCH_STORE_REGS(ptr) \
|
||||
__asm__ __volatile__( \
|
||||
"stmw 0, 0(%0)\n" \
|
||||
: \
|
||||
: "b" (ptr) \
|
||||
)
|
||||
#endif
|
||||
#define ARCH_SIGCTX_SP(ctx) (UCONTEXT_REG_Rn((ctx), 1))
|
||||
#define ARCH_SIGCTX_IP(ctx) (UCONTEXT_REG_NIP((ctx)))
|
||||
#define ARCH_COPY_SIGCTX_REGS(a,ctx) do { \
|
||||
int __i; \
|
||||
for (__i = 0; __i < 32; ++__i) \
|
||||
((a)[__i]) = (gpointer) UCONTEXT_REG_Rn((ctx), __i); \
|
||||
} while (0)
|
||||
|
||||
/* MS_BLOCK_SIZE must be a multiple of the system pagesize, which for some
|
||||
archs is 64k. */
|
||||
#if defined(TARGET_POWERPC64) && _CALL_ELF == 2
|
||||
#define ARCH_MIN_MS_BLOCK_SIZE (64*1024)
|
||||
#define ARCH_MIN_MS_BLOCK_SIZE_SHIFT 16
|
||||
#endif
|
||||
|
||||
#elif defined(TARGET_ARM)
|
||||
|
||||
#define REDZONE_SIZE 0
|
||||
#define USE_MONO_CTX
|
||||
|
||||
/* We dont store ip, sp */
|
||||
#define ARCH_NUM_REGS 14
|
||||
|
||||
#elif defined(TARGET_ARM64)
|
||||
|
||||
#ifdef __linux__
|
||||
#define REDZONE_SIZE 0
|
||||
#elif defined(__APPLE__)
|
||||
#define REDZONE_SIZE 128
|
||||
#else
|
||||
#error "Not implemented."
|
||||
#endif
|
||||
#define USE_MONO_CTX
|
||||
#define ARCH_NUM_REGS 31
|
||||
|
||||
#elif defined(__mips__)
|
||||
|
||||
#define REDZONE_SIZE 0
|
||||
|
||||
#define USE_MONO_CTX
|
||||
#define ARCH_NUM_REGS 32
|
||||
|
||||
#elif defined(__s390x__)
|
||||
|
||||
#define REDZONE_SIZE 0
|
||||
|
||||
#define USE_MONO_CTX
|
||||
#define ARCH_NUM_REGS 16
|
||||
|
||||
#elif defined(__sparc__)
|
||||
|
||||
#define REDZONE_SIZE 0
|
||||
|
||||
/* Don't bother with %g0 (%r0), it's always hard-coded to zero */
|
||||
#define ARCH_NUM_REGS 15
|
||||
#ifdef __sparcv9
|
||||
#define ARCH_STORE_REGS(ptr) \
|
||||
__asm__ __volatile__( \
|
||||
"st %%g1,[%0]\n\t" \
|
||||
"st %%g2,[%0+0x08]\n\t" \
|
||||
"st %%g3,[%0+0x10]\n\t" \
|
||||
"st %%g4,[%0+0x18]\n\t" \
|
||||
"st %%g5,[%0+0x20]\n\t" \
|
||||
"st %%g6,[%0+0x28]\n\t" \
|
||||
"st %%g7,[%0+0x30]\n\t" \
|
||||
"st %%o0,[%0+0x38]\n\t" \
|
||||
"st %%o1,[%0+0x40]\n\t" \
|
||||
"st %%o2,[%0+0x48]\n\t" \
|
||||
"st %%o3,[%0+0x50]\n\t" \
|
||||
"st %%o4,[%0+0x58]\n\t" \
|
||||
"st %%o5,[%0+0x60]\n\t" \
|
||||
"st %%o6,[%0+0x68]\n\t" \
|
||||
"st %%o7,[%0+0x70]\n\t" \
|
||||
: \
|
||||
: "r" (ptr) \
|
||||
: "memory" \
|
||||
)
|
||||
#else
|
||||
#define ARCH_STORE_REGS(ptr) \
|
||||
__asm__ __volatile__( \
|
||||
"st %%g1,[%0]\n\t" \
|
||||
"st %%g2,[%0+0x04]\n\t" \
|
||||
"st %%g3,[%0+0x08]\n\t" \
|
||||
"st %%g4,[%0+0x0c]\n\t" \
|
||||
"st %%g5,[%0+0x10]\n\t" \
|
||||
"st %%g6,[%0+0x14]\n\t" \
|
||||
"st %%g7,[%0+0x18]\n\t" \
|
||||
"st %%o0,[%0+0x1c]\n\t" \
|
||||
"st %%o1,[%0+0x20]\n\t" \
|
||||
"st %%o2,[%0+0x24]\n\t" \
|
||||
"st %%o3,[%0+0x28]\n\t" \
|
||||
"st %%o4,[%0+0x2c]\n\t" \
|
||||
"st %%o5,[%0+0x30]\n\t" \
|
||||
"st %%o6,[%0+0x34]\n\t" \
|
||||
"st %%o7,[%0+0x38]\n\t" \
|
||||
: \
|
||||
: "r" (ptr) \
|
||||
: "memory" \
|
||||
)
|
||||
#endif
|
||||
|
||||
#ifndef REG_SP
|
||||
#define REG_SP REG_O6
|
||||
#endif
|
||||
|
||||
#define ARCH_SIGCTX_SP(ctx) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_SP])
|
||||
#define ARCH_SIGCTX_IP(ctx) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_PC])
|
||||
#define ARCH_COPY_SIGCTX_REGS(a,ctx) do { \
|
||||
(a)[0] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_G1]); \
|
||||
(a)[1] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_G2]); \
|
||||
(a)[2] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_G3]); \
|
||||
(a)[3] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_G4]); \
|
||||
(a)[4] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_G5]); \
|
||||
(a)[5] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_G6]); \
|
||||
(a)[6] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_G7]); \
|
||||
(a)[7] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_O0]); \
|
||||
(a)[8] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_O1]); \
|
||||
(a)[9] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_O2]); \
|
||||
(a)[10] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_O3]); \
|
||||
(a)[11] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_O4]); \
|
||||
(a)[12] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_O5]); \
|
||||
(a)[13] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_O6]); \
|
||||
(a)[14] = (gpointer) (((ucontext_t *)(ctx))->uc_mcontext.gregs [REG_O7]); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __MONO_SGENARCHDEP_H__ */
|
||||
610
mono/sgen/sgen-cardtable.c
Normal file
610
mono/sgen/sgen-cardtable.c
Normal file
File diff suppressed because it is too large
Load Diff
144
mono/sgen/sgen-cardtable.h
Normal file
144
mono/sgen/sgen-cardtable.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2001-2003 Ximian, Inc
|
||||
* Copyright 2003-2010 Novell, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef __MONO_SGEN_CARD_TABLE_INLINES_H__
|
||||
#define __MONO_SGEN_CARD_TABLE_INLINES_H__
|
||||
|
||||
/*WARNING: This function returns the number of cards regardless of overflow in case of overlapping cards.*/
|
||||
mword sgen_card_table_number_of_cards_in_range (mword address, mword size);
|
||||
|
||||
void sgen_card_table_reset_region (mword start, mword end);
|
||||
void* sgen_card_table_align_pointer (void *ptr);
|
||||
void sgen_card_table_mark_range (mword address, mword size);
|
||||
void sgen_cardtable_scan_object (GCObject *obj, mword obj_size, guint8 *cards,
|
||||
gboolean mod_union, ScanCopyContext ctx);
|
||||
|
||||
gboolean sgen_card_table_get_card_data (guint8 *dest, mword address, mword cards);
|
||||
|
||||
guint8* sgen_card_table_alloc_mod_union (char *obj, mword obj_size);
|
||||
void sgen_card_table_free_mod_union (guint8 *mod_union, char *obj, mword obj_size);
|
||||
|
||||
void sgen_card_table_update_mod_union_from_cards (guint8 *dest, guint8 *start_card, size_t num_cards);
|
||||
void sgen_card_table_update_mod_union (guint8 *dest, char *obj, mword obj_size, size_t *out_num_cards);
|
||||
|
||||
guint8* sgen_get_card_table_configuration (int *shift_bits, gpointer *mask);
|
||||
|
||||
void sgen_card_table_init (SgenRememberedSet *remset);
|
||||
|
||||
/*How many bytes a single card covers*/
|
||||
#define CARD_BITS 9
|
||||
|
||||
/* How many bits of the address space is covered by the card table.
|
||||
* If this value is smaller than the number of address bits, card aliasing is required.
|
||||
*/
|
||||
#define CARD_TABLE_BITS 32
|
||||
|
||||
#define CARD_SIZE_IN_BYTES (1 << CARD_BITS)
|
||||
#define CARD_COUNT_BITS (CARD_TABLE_BITS - CARD_BITS)
|
||||
#define CARD_COUNT_IN_BYTES (1 << CARD_COUNT_BITS)
|
||||
#define CARD_MASK ((1 << CARD_COUNT_BITS) - 1)
|
||||
|
||||
#if SIZEOF_VOID_P * 8 > CARD_TABLE_BITS
|
||||
#define SGEN_HAVE_OVERLAPPING_CARDS 1
|
||||
#endif
|
||||
|
||||
extern guint8 *sgen_cardtable;
|
||||
|
||||
|
||||
#ifdef SGEN_HAVE_OVERLAPPING_CARDS
|
||||
|
||||
static inline guint8*
|
||||
sgen_card_table_get_card_address (mword address)
|
||||
{
|
||||
return sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK);
|
||||
}
|
||||
|
||||
extern guint8 *sgen_shadow_cardtable;
|
||||
|
||||
#define SGEN_SHADOW_CARDTABLE_END (sgen_shadow_cardtable + CARD_COUNT_IN_BYTES)
|
||||
|
||||
static inline guint8*
|
||||
sgen_card_table_get_shadow_card_address (mword address)
|
||||
{
|
||||
return sgen_shadow_cardtable + ((address >> CARD_BITS) & CARD_MASK);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
sgen_card_table_card_begin_scanning (mword address)
|
||||
{
|
||||
return *sgen_card_table_get_shadow_card_address (address) != 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
sgen_card_table_prepare_card_for_scanning (guint8 *card)
|
||||
{
|
||||
}
|
||||
|
||||
#define sgen_card_table_get_card_scan_address sgen_card_table_get_shadow_card_address
|
||||
|
||||
#else
|
||||
|
||||
static inline guint8*
|
||||
sgen_card_table_get_card_address (mword address)
|
||||
{
|
||||
return sgen_cardtable + (address >> CARD_BITS);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
sgen_card_table_card_begin_scanning (mword address)
|
||||
{
|
||||
guint8 *card = sgen_card_table_get_card_address (address);
|
||||
gboolean res = *card;
|
||||
*card = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void
|
||||
sgen_card_table_prepare_card_for_scanning (guint8 *card)
|
||||
{
|
||||
*card = 0;
|
||||
}
|
||||
|
||||
#define sgen_card_table_get_card_scan_address sgen_card_table_get_card_address
|
||||
|
||||
#endif
|
||||
|
||||
static inline gboolean
|
||||
sgen_card_table_address_is_marked (mword address)
|
||||
{
|
||||
return *sgen_card_table_get_card_address (address) != 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
sgen_card_table_mark_address (mword address)
|
||||
{
|
||||
*sgen_card_table_get_card_address (address) = 1;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
sgen_card_table_get_card_offset (char *ptr, char *base)
|
||||
{
|
||||
return (ptr - base) >> CARD_BITS;
|
||||
}
|
||||
|
||||
#endif
|
||||
305
mono/sgen/sgen-client.h
Normal file
305
mono/sgen/sgen-client.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* sgen-client.h: SGen client interface.
|
||||
*
|
||||
* Copyright (C) 2014 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "mono/sgen/sgen-pointer-queue.h"
|
||||
|
||||
/*
|
||||
* Init whatever needs initing. This is called relatively early in SGen initialization.
|
||||
* Must initialized the small ID for the current thread.
|
||||
*/
|
||||
void sgen_client_init (void);
|
||||
|
||||
/*
|
||||
* The slow path for getting an object's size. We're passing in the vtable because we've
|
||||
* already fetched it.
|
||||
*/
|
||||
mword sgen_client_slow_object_get_size (GCVTable vtable, GCObject* o);
|
||||
|
||||
/*
|
||||
* Fill the given range with a dummy object. If the range is too short to be filled with an
|
||||
* object, null it. Return `TRUE` if the range was filled with an object, `FALSE` if it was
|
||||
* nulled.
|
||||
*/
|
||||
gboolean sgen_client_array_fill_range (char *start, size_t size);
|
||||
|
||||
/*
|
||||
* This is called if the nursery clearing policy at `clear-at-gc`, which is usually only
|
||||
* used for debugging. If `size` is large enough for the memory to have been filled with a
|
||||
* dummy, object, zero its header. Note that there might not actually be a header there.
|
||||
*/
|
||||
void sgen_client_zero_array_fill_header (void *p, size_t size);
|
||||
|
||||
/*
|
||||
* Return whether the given object is an array fill dummy object.
|
||||
*/
|
||||
gboolean sgen_client_object_is_array_fill (GCObject *o);
|
||||
|
||||
/*
|
||||
* Return whether the given finalizable object's finalizer is critical, i.e., needs to run
|
||||
* after all non-critical finalizers have run.
|
||||
*/
|
||||
gboolean sgen_client_object_has_critical_finalizer (GCObject *obj);
|
||||
|
||||
/*
|
||||
* Called after an object is enqueued for finalization. This is a very low-level callback.
|
||||
* It should almost certainly be a NOP.
|
||||
*
|
||||
* FIXME: Can we merge this with `sgen_client_object_has_critical_finalizer()`?
|
||||
*/
|
||||
void sgen_client_object_queued_for_finalization (GCObject *obj);
|
||||
|
||||
/*
|
||||
* Run the given object's finalizer.
|
||||
*/
|
||||
void sgen_client_run_finalize (GCObject *obj);
|
||||
|
||||
/*
|
||||
* Is called after a collection if there are objects to finalize. The world is still
|
||||
* stopped. This will usually notify the finalizer thread that it needs to run.
|
||||
*/
|
||||
void sgen_client_finalize_notify (void);
|
||||
|
||||
/*
|
||||
* Returns TRUE if no ephemerons have been marked. Will be called again if it returned
|
||||
* FALSE. If ephemerons are not supported, just return TRUE.
|
||||
*/
|
||||
gboolean sgen_client_mark_ephemerons (ScanCopyContext ctx);
|
||||
|
||||
/*
|
||||
* Clear ephemeron pairs with unreachable keys.
|
||||
* We pass the copy func so we can figure out if an array was promoted or not.
|
||||
*/
|
||||
void sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx);
|
||||
|
||||
/*
|
||||
* This is called for objects that are larger than one card. If it's possible to scan only
|
||||
* parts of the object based on which cards are marked, do so and return TRUE. Otherwise,
|
||||
* return FALSE.
|
||||
*/
|
||||
gboolean sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, gboolean mod_union, ScanCopyContext ctx);
|
||||
|
||||
/*
|
||||
* Called after nursery objects have been pinned. No action is necessary.
|
||||
*/
|
||||
void sgen_client_nursery_objects_pinned (void **definitely_pinned, int count);
|
||||
|
||||
/*
|
||||
* Called at a semi-random point during minor collections. No action is necessary.
|
||||
*/
|
||||
void sgen_client_collecting_minor (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue);
|
||||
|
||||
/*
|
||||
* Called at semi-random points during major collections. No action is necessary.
|
||||
*/
|
||||
void sgen_client_collecting_major_1 (void);
|
||||
void sgen_client_collecting_major_2 (void);
|
||||
void sgen_client_collecting_major_3 (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue);
|
||||
|
||||
/*
|
||||
* Called after a LOS object has been pinned. No action is necessary.
|
||||
*/
|
||||
void sgen_client_pinned_los_object (GCObject *obj);
|
||||
|
||||
/*
|
||||
* Called for every degraded allocation. No action is necessary.
|
||||
*/
|
||||
void sgen_client_degraded_allocation (size_t size);
|
||||
|
||||
/*
|
||||
* Called whenever the amount of memory allocated for the managed heap changes. No action
|
||||
* is necessary.
|
||||
*/
|
||||
void sgen_client_total_allocated_heap_changed (size_t allocated_heap_size);
|
||||
|
||||
/*
|
||||
* Called when an object allocation fails. The suggested action is to abort the program.
|
||||
*
|
||||
* FIXME: Don't we want to return a BOOL here that indicates whether to retry the
|
||||
* allocation?
|
||||
*/
|
||||
void sgen_client_out_of_memory (size_t size);
|
||||
|
||||
/*
|
||||
* If the client has registered any internal memory types, this must return a string
|
||||
* describing the given type. Only used for debugging.
|
||||
*/
|
||||
const char* sgen_client_description_for_internal_mem_type (int type);
|
||||
|
||||
/*
|
||||
* Only used for debugging. `sgen_client_vtable_get_namespace()` may return NULL.
|
||||
*/
|
||||
gboolean sgen_client_vtable_is_inited (GCVTable vtable);
|
||||
const char* sgen_client_vtable_get_namespace (GCVTable vtable);
|
||||
const char* sgen_client_vtable_get_name (GCVTable vtable);
|
||||
|
||||
/*
|
||||
* Called before starting collections. The world is already stopped. No action is
|
||||
* necessary.
|
||||
*/
|
||||
void sgen_client_pre_collection_checks (void);
|
||||
|
||||
/*
|
||||
* Must set the thread's thread info to `info`. If the thread's small ID was not already
|
||||
* initialized in `sgen_client_init()` (for the main thread, usually), it must be done here.
|
||||
*
|
||||
* `stack_bottom_fallback` is the value passed through via `sgen_thread_register()`.
|
||||
*/
|
||||
void sgen_client_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback);
|
||||
|
||||
void sgen_client_thread_unregister (SgenThreadInfo *p);
|
||||
|
||||
/*
|
||||
* Called on each worker thread when it starts up. Must initialize the thread's small ID.
|
||||
*/
|
||||
void sgen_client_thread_register_worker (void);
|
||||
|
||||
/*
|
||||
* The least this function needs to do is scan all registers and thread stacks. To do this
|
||||
* conservatively, use `sgen_conservatively_pin_objects_from()`.
|
||||
*/
|
||||
void sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx);
|
||||
|
||||
/*
|
||||
* Stop and restart the world, i.e., all threads that interact with the managed heap. For
|
||||
* single-threaded programs this is a nop.
|
||||
*/
|
||||
void sgen_client_stop_world (int generation);
|
||||
void sgen_client_restart_world (int generation, GGTimingInfo *timing);
|
||||
|
||||
/*
|
||||
* Must return FALSE. The bridge is not supported outside of Mono.
|
||||
*/
|
||||
gboolean sgen_client_bridge_need_processing (void);
|
||||
|
||||
/*
|
||||
* None of these should ever be called.
|
||||
*/
|
||||
void sgen_client_bridge_reset_data (void);
|
||||
void sgen_client_bridge_processing_stw_step (void);
|
||||
void sgen_client_bridge_wait_for_processing (void);
|
||||
void sgen_client_bridge_processing_finish (int generation);
|
||||
gboolean sgen_client_bridge_is_bridge_object (GCObject *obj);
|
||||
void sgen_client_bridge_register_finalized_object (GCObject *object);
|
||||
|
||||
/*
|
||||
* No action is necessary.
|
||||
*/
|
||||
void sgen_client_mark_togglerefs (char *start, char *end, ScanCopyContext ctx);
|
||||
void sgen_client_clear_togglerefs (char *start, char *end, ScanCopyContext ctx);
|
||||
|
||||
/*
|
||||
* Called after collections, reporting the amount of time they took. No action is
|
||||
* necessary.
|
||||
*/
|
||||
void sgen_client_log_timing (GGTimingInfo *info, mword last_major_num_sections, mword last_los_memory_usage);
|
||||
|
||||
/*
|
||||
* Called to handle `MONO_GC_PARAMS` and `MONO_GC_DEBUG` options. The `handle` functions
|
||||
* must return TRUE if they have recognized and processed the option, FALSE otherwise.
|
||||
*/
|
||||
gboolean sgen_client_handle_gc_param (const char *opt);
|
||||
void sgen_client_print_gc_params_usage (void);
|
||||
gboolean sgen_client_handle_gc_debug (const char *opt);
|
||||
void sgen_client_print_gc_debug_usage (void);
|
||||
|
||||
/*
|
||||
* Called to obtain an identifier for the current location, such as a method pointer. This
|
||||
* is used for logging the provenances of allocations with the heavy binary protocol.
|
||||
*/
|
||||
gpointer sgen_client_get_provenance (void);
|
||||
|
||||
/*
|
||||
* Called by the debugging infrastructure to describe pointers that have an invalid vtable.
|
||||
* Should usually print to `stdout`.
|
||||
*/
|
||||
void sgen_client_describe_invalid_pointer (GCObject *ptr);
|
||||
|
||||
/*
|
||||
* These client binary protocol functions are called from the respective binary protocol
|
||||
* functions. No action is necessary. We suggest implementing them as inline functions in
|
||||
* the client header file so that no overhead is incurred if they don't actually do
|
||||
* anything.
|
||||
*/
|
||||
|
||||
#define TYPE_INT int
|
||||
#define TYPE_LONGLONG long long
|
||||
#define TYPE_SIZE size_t
|
||||
#define TYPE_POINTER gpointer
|
||||
#define TYPE_BOOL gboolean
|
||||
|
||||
#define BEGIN_PROTOCOL_ENTRY0(method) \
|
||||
void sgen_client_ ## method (void);
|
||||
#define BEGIN_PROTOCOL_ENTRY_HEAVY0(method) \
|
||||
void sgen_client_ ## method (void);
|
||||
#define BEGIN_PROTOCOL_ENTRY1(method,t1,f1) \
|
||||
void sgen_client_ ## method (t1 f1);
|
||||
#define BEGIN_PROTOCOL_ENTRY_HEAVY1(method,t1,f1) \
|
||||
void sgen_client_ ## method (t1 f1);
|
||||
#define BEGIN_PROTOCOL_ENTRY2(method,t1,f1,t2,f2) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2);
|
||||
#define BEGIN_PROTOCOL_ENTRY_HEAVY2(method,t1,f1,t2,f2) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2);
|
||||
#define BEGIN_PROTOCOL_ENTRY3(method,t1,f1,t2,f2,t3,f3) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2, t3 f3);
|
||||
#define BEGIN_PROTOCOL_ENTRY_HEAVY3(method,t1,f1,t2,f2,t3,f3) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2, t3 f3);
|
||||
#define BEGIN_PROTOCOL_ENTRY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2, t3 f3, t4 f4);
|
||||
#define BEGIN_PROTOCOL_ENTRY_HEAVY4(method,t1,f1,t2,f2,t3,f3,t4,f4) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2, t3 f3, t4 f4);
|
||||
#define BEGIN_PROTOCOL_ENTRY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5);
|
||||
#define BEGIN_PROTOCOL_ENTRY_HEAVY5(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5);
|
||||
#define BEGIN_PROTOCOL_ENTRY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5, t6 f6);
|
||||
#define BEGIN_PROTOCOL_ENTRY_HEAVY6(method,t1,f1,t2,f2,t3,f3,t4,f4,t5,f5,t6,f6) \
|
||||
void sgen_client_ ## method (t1 f1, t2 f2, t3 f3, t4 f4, t5 f5, t6 f6);
|
||||
|
||||
#define FLUSH()
|
||||
|
||||
#define DEFAULT_PRINT()
|
||||
#define CUSTOM_PRINT(_)
|
||||
|
||||
#define IS_ALWAYS_MATCH(_)
|
||||
#define MATCH_INDEX(_)
|
||||
#define IS_VTABLE_MATCH(_)
|
||||
|
||||
#define END_PROTOCOL_ENTRY
|
||||
#define END_PROTOCOL_ENTRY_HEAVY
|
||||
|
||||
#include "sgen-protocol-def.h"
|
||||
|
||||
#undef TYPE_INT
|
||||
#undef TYPE_LONGLONG
|
||||
#undef TYPE_SIZE
|
||||
#undef TYPE_POINTER
|
||||
#undef TYPE_BOOL
|
||||
|
||||
#ifdef SGEN_WITHOUT_MONO
|
||||
/*
|
||||
* Get the current thread's thread info. This will only be called on managed threads.
|
||||
*/
|
||||
SgenThreadInfo* mono_thread_info_current (void);
|
||||
|
||||
/*
|
||||
* Get the current thread's small ID. This will be called on managed and worker threads.
|
||||
*/
|
||||
int mono_thread_info_get_small_id (void);
|
||||
#endif
|
||||
219
mono/sgen/sgen-conf.h
Normal file
219
mono/sgen/sgen-conf.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* sgen-conf.h: Tunable parameters and debugging switches.
|
||||
*
|
||||
* Copyright 2001-2003 Ximian, Inc
|
||||
* Copyright 2003-2010 Novell, Inc.
|
||||
* Copyright 2011 Xamarin Inc (http://www.xamarin.com)
|
||||
* Copyright (C) 2012 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef __MONO_SGENCONF_H__
|
||||
#define __MONO_SGENCONF_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/*Basic defines and static tunables */
|
||||
|
||||
#if SIZEOF_VOID_P == 4
|
||||
typedef guint32 mword;
|
||||
#else
|
||||
typedef guint64 mword;
|
||||
#endif
|
||||
|
||||
typedef mword SgenDescriptor;
|
||||
#define SGEN_DESCRIPTOR_NULL 0
|
||||
|
||||
/*
|
||||
* Turning on heavy statistics will turn off the managed allocator and
|
||||
* the managed write barrier.
|
||||
*/
|
||||
// #define HEAVY_STATISTICS
|
||||
|
||||
#ifdef HEAVY_STATISTICS
|
||||
#define HEAVY_STAT(x) x
|
||||
#else
|
||||
#define HEAVY_STAT(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define this to allow the user to change the nursery size by
|
||||
* specifying its value in the MONO_GC_PARAMS environmental
|
||||
* variable. See mono_gc_base_init for details.
|
||||
*/
|
||||
#define USER_CONFIG 1
|
||||
|
||||
/*
|
||||
* The binary protocol enables logging a lot of the GC ativity in a way that is not very
|
||||
* intrusive and produces a compact file that can be searched using a custom tool. This
|
||||
* option enables very fine-grained binary protocol events, which will make the GC a tiny
|
||||
* bit less efficient even if no binary protocol file is generated.
|
||||
*/
|
||||
//#define SGEN_HEAVY_BINARY_PROTOCOL
|
||||
|
||||
/*
|
||||
* This extends the heavy binary protocol to record the provenance of an object
|
||||
* for every allocation.
|
||||
*/
|
||||
//#define SGEN_OBJECT_PROVENANCE
|
||||
|
||||
/*
|
||||
* This enables checks whenever objects are enqueued in gray queues.
|
||||
* Right now the only check done is that we never enqueue nursery
|
||||
* pointers in the concurrent collector.
|
||||
*/
|
||||
//#define SGEN_CHECK_GRAY_OBJECT_ENQUEUE
|
||||
|
||||
/*
|
||||
* This keeps track of where a gray object queue section is and
|
||||
* whether it is where it should be.
|
||||
*/
|
||||
//#define SGEN_CHECK_GRAY_OBJECT_SECTIONS
|
||||
|
||||
/*
|
||||
* Enable this to check every reference update for null references and whether the update is
|
||||
* made in a worker thread. In only a few cases do we potentially update references by
|
||||
* writing nulls, so we assert in all the cases where it's not allowed. The concurrent
|
||||
* collector's worker thread is not allowed to update references at all, so we also assert
|
||||
* that we're not in the worker thread.
|
||||
*/
|
||||
//#define SGEN_CHECK_UPDATE_REFERENCE
|
||||
|
||||
/*
|
||||
* Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
|
||||
* have cross-domain checks in the write barrier.
|
||||
*/
|
||||
//#define XDOMAIN_CHECKS_IN_WBARRIER
|
||||
|
||||
/*
|
||||
* Define this to get number of objects marked information in the
|
||||
* concurrent GC DTrace probes. Has a small performance impact, so
|
||||
* it's disabled by default.
|
||||
*/
|
||||
//#define SGEN_COUNT_NUMBER_OF_MAJOR_OBJECTS_MARKED
|
||||
|
||||
/*
|
||||
* Object layout statistics gather a histogram of reference locations
|
||||
* over all scanned objects. We use this information to improve GC
|
||||
* descriptors to speed up scanning. This does not provide any
|
||||
* troubleshooting assistance (unless you are troubled in highly
|
||||
* unusual ways) and makes scanning slower.
|
||||
*/
|
||||
//#define SGEN_OBJECT_LAYOUT_STATISTICS
|
||||
|
||||
#ifndef SGEN_HEAVY_BINARY_PROTOCOL
|
||||
#ifndef HEAVY_STATISTICS
|
||||
#define MANAGED_ALLOCATION
|
||||
#ifndef XDOMAIN_CHECKS_IN_WBARRIER
|
||||
#define MANAGED_WBARRIER
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Maximum level of debug to enable on this build.
|
||||
* Making this a constant enables us to put logging in a lot of places and
|
||||
* not pay its cost on release builds.
|
||||
*/
|
||||
#define SGEN_MAX_DEBUG_LEVEL 2
|
||||
|
||||
/*
|
||||
* Maximum level of asserts to enable on this build.
|
||||
* FIXME replace all magic numbers with defines.
|
||||
*/
|
||||
#define SGEN_MAX_ASSERT_LEVEL 5
|
||||
|
||||
|
||||
#define GC_BITS_PER_WORD (sizeof (mword) * 8)
|
||||
|
||||
/*Size of the section used by the copying GC. */
|
||||
#define SGEN_SIZEOF_GC_MEM_SECTION ((sizeof (GCMemSection) + 7) & ~7)
|
||||
|
||||
/*
|
||||
* to quickly find the head of an object pinned by a conservative
|
||||
* address we keep track of the objects allocated for each
|
||||
* SGEN_SCAN_START_SIZE memory chunk in the nursery or other memory
|
||||
* sections. Larger values have less memory overhead and bigger
|
||||
* runtime cost. 4-8 KB are reasonable values.
|
||||
*/
|
||||
#define SGEN_SCAN_START_SIZE (4096*2)
|
||||
|
||||
/*
|
||||
* Objects bigger then this go into the large object space. This size has a few
|
||||
* constraints. At least two of them must fit into a major heap block. It must also play
|
||||
* well with the run length GC descriptor, which encodes the object size.
|
||||
*/
|
||||
#define SGEN_MAX_SMALL_OBJ_SIZE 8000
|
||||
|
||||
/*
|
||||
* This is the maximum ammount of memory we're willing to waste in order to speed up allocation.
|
||||
* Wastage comes in thre forms:
|
||||
*
|
||||
* -when building the nursery fragment list, small regions are discarded;
|
||||
* -when allocating memory from a fragment if it ends up below the threshold, we remove it from the fragment list; and
|
||||
* -when allocating a new tlab, we discard the remaining space of the old one
|
||||
*
|
||||
* Increasing this value speeds up allocation but will cause more frequent nursery collections as less space will be used.
|
||||
* Descreasing this value will cause allocation to be slower since we'll have to cycle thru more fragments.
|
||||
* 512 annedoctally keeps wastage under control and doesn't impact allocation performance too much.
|
||||
*/
|
||||
#define SGEN_MAX_NURSERY_WASTE 512
|
||||
|
||||
|
||||
/*
|
||||
* Minimum allowance for nursery allocations, as a multiple of the size of nursery.
|
||||
*
|
||||
* We allow at least this much allocation to happen to the major heap from multiple
|
||||
* minor collections before triggering a major collection.
|
||||
*
|
||||
* Bigger values increases throughput by allowing more garbage to sit in the major heap.
|
||||
* Smaller values leads to better memory effiency but more frequent major collections.
|
||||
*/
|
||||
#define SGEN_DEFAULT_ALLOWANCE_NURSERY_SIZE_RATIO 4.0
|
||||
|
||||
#define SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO 1.0
|
||||
#define SGEN_MAX_ALLOWANCE_NURSERY_SIZE_RATIO 10.0
|
||||
|
||||
/*
|
||||
* Default ratio of memory we want to release in a major collection in relation to the the current heap size.
|
||||
*
|
||||
* A major collection target is to free a given amount of memory. This amount is a ratio of the major heap size.
|
||||
*
|
||||
* Values above 0.5 cause the heap to agressively grow when it's small and waste memory when it's big.
|
||||
* Lower values will produce more reasonable sized heaps when it's small, but will be suboptimal at large
|
||||
* sizes as they will use a small fraction only.
|
||||
*
|
||||
*/
|
||||
#define SGEN_DEFAULT_SAVE_TARGET_RATIO 0.5
|
||||
|
||||
#define SGEN_MIN_SAVE_TARGET_RATIO 0.1
|
||||
#define SGEN_MAX_SAVE_TARGET_RATIO 2.0
|
||||
|
||||
/*
|
||||
* Configurable cementing parameters.
|
||||
*
|
||||
* If there are too many pinned nursery objects with many references
|
||||
* from the major heap, the hash table size must be increased.
|
||||
*
|
||||
* The threshold is the number of references from the major heap to a
|
||||
* pinned nursery object which triggers cementing: if there are more
|
||||
* than that number of references, the pinned object is cemented until
|
||||
* the next major collection.
|
||||
*/
|
||||
#define SGEN_CEMENT_HASH_SHIFT 6
|
||||
#define SGEN_CEMENT_HASH_SIZE (1 << SGEN_CEMENT_HASH_SHIFT)
|
||||
#define SGEN_CEMENT_HASH(hv) (((hv) ^ ((hv) >> SGEN_CEMENT_HASH_SHIFT)) & (SGEN_CEMENT_HASH_SIZE - 1))
|
||||
#define SGEN_CEMENT_THRESHOLD 1000
|
||||
|
||||
#endif
|
||||
86
mono/sgen/sgen-copy-object.h
Normal file
86
mono/sgen/sgen-copy-object.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* sgen-copy-object.h: This is where objects are copied.
|
||||
*
|
||||
* Copyright 2001-2003 Ximian, Inc
|
||||
* Copyright 2003-2010 Novell, Inc.
|
||||
* Copyright (C) 2012 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
extern guint64 stat_copy_object_called_nursery;
|
||||
extern guint64 stat_objects_copied_nursery;
|
||||
|
||||
extern guint64 stat_nursery_copy_object_failed_from_space;
|
||||
extern guint64 stat_nursery_copy_object_failed_forwarded;
|
||||
extern guint64 stat_nursery_copy_object_failed_pinned;
|
||||
|
||||
extern guint64 stat_slots_allocated_in_vain;
|
||||
|
||||
/*
|
||||
* Copies an object and enqueues it if a queue is given.
|
||||
*
|
||||
* This function can be used even if the vtable of obj is not valid
|
||||
* anymore, which is the case in the parallel collector.
|
||||
*/
|
||||
static MONO_ALWAYS_INLINE void
|
||||
par_copy_object_no_checks (char *destination, GCVTable vt, void *obj, mword objsize, SgenGrayQueue *queue)
|
||||
{
|
||||
sgen_client_pre_copy_checks (destination, vt, obj, objsize);
|
||||
binary_protocol_copy (obj, destination, vt, objsize);
|
||||
|
||||
/* FIXME: assumes object layout */
|
||||
memcpy ((char*)destination + sizeof (mword), (char*)obj + sizeof (mword), objsize - sizeof (mword));
|
||||
|
||||
/* adjust array->bounds */
|
||||
SGEN_ASSERT (9, sgen_vtable_get_descriptor (vt), "vtable %p has no gc descriptor", vt);
|
||||
|
||||
sgen_client_update_copied_object (destination, vt, obj, objsize);
|
||||
obj = destination;
|
||||
if (queue) {
|
||||
SGEN_LOG (9, "Enqueuing gray object %p (%s)", obj, sgen_client_vtable_get_name (vt));
|
||||
GRAY_OBJECT_ENQUEUE (queue, obj, sgen_vtable_get_descriptor (vt));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This can return OBJ itself on OOM.
|
||||
*/
|
||||
static MONO_NEVER_INLINE void*
|
||||
copy_object_no_checks (void *obj, SgenGrayQueue *queue)
|
||||
{
|
||||
GCVTable vt = SGEN_LOAD_VTABLE_UNCHECKED (obj);
|
||||
gboolean has_references = SGEN_VTABLE_HAS_REFERENCES (vt);
|
||||
mword objsize = SGEN_ALIGN_UP (sgen_client_par_object_get_size (vt, obj));
|
||||
/* FIXME: Does this not mark the newly allocated object? */
|
||||
void *destination = COLLECTOR_SERIAL_ALLOC_FOR_PROMOTION (vt, obj, objsize, has_references);
|
||||
|
||||
if (G_UNLIKELY (!destination)) {
|
||||
/* FIXME: Is this path ever tested? */
|
||||
collector_pin_object (obj, queue);
|
||||
sgen_set_pinned_from_failed_allocation (objsize);
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (!has_references)
|
||||
queue = NULL;
|
||||
|
||||
par_copy_object_no_checks (destination, vt, obj, objsize, queue);
|
||||
/* FIXME: mark mod union cards if necessary */
|
||||
|
||||
/* set the forwarding pointer */
|
||||
SGEN_FORWARD_OBJECT (obj, destination);
|
||||
|
||||
return destination;
|
||||
}
|
||||
1238
mono/sgen/sgen-debug.c
Normal file
1238
mono/sgen/sgen-debug.c
Normal file
File diff suppressed because it is too large
Load Diff
377
mono/sgen/sgen-descriptor.c
Normal file
377
mono/sgen/sgen-descriptor.c
Normal file
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* sgen-descriptor.c: GC descriptors describe object layout.
|
||||
*
|
||||
* Copyright 2001-2003 Ximian, Inc
|
||||
* Copyright 2003-2010 Novell, Inc.
|
||||
* Copyright 2011 Xamarin Inc (http://www.xamarin.com)
|
||||
* Copyright (C) 2012 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include "config.h"
|
||||
#ifdef HAVE_SGEN_GC
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#ifdef HAVE_SEMAPHORE_H
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#ifdef __MACH__
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
#ifdef __MACH__
|
||||
#define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#include "mono/sgen/sgen-gc.h"
|
||||
#include "mono/sgen/gc-internal-agnostic.h"
|
||||
|
||||
#define MAX_USER_DESCRIPTORS 16
|
||||
|
||||
#define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
|
||||
#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
|
||||
static gsize* complex_descriptors = NULL;
|
||||
static int complex_descriptors_size = 0;
|
||||
static int complex_descriptors_next = 0;
|
||||
static SgenUserRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
|
||||
static int user_descriptors_next = 0;
|
||||
static SgenDescriptor all_ref_root_descrs [32];
|
||||
|
||||
#ifdef HEAVY_STATISTICS
|
||||
static guint64 stat_scanned_count_per_descriptor [DESC_TYPE_MAX];
|
||||
static guint64 stat_copied_count_per_descriptor [DESC_TYPE_MAX];
|
||||
#endif
|
||||
|
||||
static int
|
||||
alloc_complex_descriptor (gsize *bitmap, int numbits)
|
||||
{
|
||||
int nwords, res, i;
|
||||
|
||||
numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
|
||||
nwords = numbits / GC_BITS_PER_WORD + 1;
|
||||
|
||||
sgen_gc_lock ();
|
||||
res = complex_descriptors_next;
|
||||
/* linear search, so we don't have duplicates with domain load/unload
|
||||
* this should not be performance critical or we'd have bigger issues
|
||||
* (the number and size of complex descriptors should be small).
|
||||
*/
|
||||
for (i = 0; i < complex_descriptors_next; ) {
|
||||
if (complex_descriptors [i] == nwords) {
|
||||
int j, found = TRUE;
|
||||
for (j = 0; j < nwords - 1; ++j) {
|
||||
if (complex_descriptors [i + 1 + j] != bitmap [j]) {
|
||||
found = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
sgen_gc_unlock ();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
i += (int)complex_descriptors [i];
|
||||
}
|
||||
if (complex_descriptors_next + nwords > complex_descriptors_size) {
|
||||
int new_size = complex_descriptors_size * 2 + nwords;
|
||||
complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
|
||||
complex_descriptors_size = new_size;
|
||||
}
|
||||
SGEN_LOG (6, "Complex descriptor %d, size: %d (total desc memory: %d)", res, nwords, complex_descriptors_size);
|
||||
complex_descriptors_next += nwords;
|
||||
complex_descriptors [res] = nwords;
|
||||
for (i = 0; i < nwords - 1; ++i) {
|
||||
complex_descriptors [res + 1 + i] = bitmap [i];
|
||||
SGEN_LOG (6, "\tvalue: %p", (void*)complex_descriptors [res + 1 + i]);
|
||||
}
|
||||
sgen_gc_unlock ();
|
||||
return res;
|
||||
}
|
||||
|
||||
gsize*
|
||||
sgen_get_complex_descriptor (SgenDescriptor desc)
|
||||
{
|
||||
return complex_descriptors + (desc >> LOW_TYPE_BITS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Descriptor builders.
|
||||
*/
|
||||
SgenDescriptor
|
||||
mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
|
||||
{
|
||||
int first_set = -1, num_set = 0, last_set = -1, i;
|
||||
SgenDescriptor desc = 0;
|
||||
size_t stored_size = obj_size;
|
||||
|
||||
stored_size += SGEN_ALLOC_ALIGN - 1;
|
||||
stored_size &= ~(SGEN_ALLOC_ALIGN - 1);
|
||||
|
||||
for (i = 0; i < numbits; ++i) {
|
||||
if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
|
||||
if (first_set < 0)
|
||||
first_set = i;
|
||||
last_set = i;
|
||||
num_set++;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_set < 0) {
|
||||
SGEN_LOG (6, "Ptrfree descriptor %p, size: %zd", (void*)desc, stored_size);
|
||||
if (stored_size <= MAX_RUNLEN_OBJECT_SIZE && stored_size <= SGEN_MAX_SMALL_OBJ_SIZE)
|
||||
return DESC_TYPE_SMALL_PTRFREE | stored_size;
|
||||
return DESC_TYPE_COMPLEX_PTRFREE;
|
||||
}
|
||||
|
||||
g_assert (!(stored_size & 0x7));
|
||||
|
||||
SGEN_ASSERT (5, stored_size == SGEN_ALIGN_UP (stored_size), "Size is not aligned");
|
||||
|
||||
/* we know the 2-word header is ptr-free */
|
||||
if (last_set < BITMAP_NUM_BITS + OBJECT_HEADER_WORDS && stored_size <= SGEN_MAX_SMALL_OBJ_SIZE) {
|
||||
desc = DESC_TYPE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
|
||||
SGEN_LOG (6, "Largebitmap descriptor %p, size: %zd, last set: %d", (void*)desc, stored_size, last_set);
|
||||
return desc;
|
||||
}
|
||||
|
||||
if (stored_size <= MAX_RUNLEN_OBJECT_SIZE && stored_size <= SGEN_MAX_SMALL_OBJ_SIZE) {
|
||||
/* check run-length encoding first: one byte offset, one byte number of pointers
|
||||
* on 64 bit archs, we can have 3 runs, just one on 32.
|
||||
* It may be better to use nibbles.
|
||||
*/
|
||||
if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
|
||||
desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
|
||||
SGEN_LOG (6, "Runlen descriptor %p, size: %zd, first set: %d, num set: %d", (void*)desc, stored_size, first_set, num_set);
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
/* it's a complex object ... */
|
||||
desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
|
||||
return desc;
|
||||
}
|
||||
|
||||
/* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
|
||||
SgenDescriptor
|
||||
mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
|
||||
{
|
||||
int first_set = -1, num_set = 0, last_set = -1, i;
|
||||
SgenDescriptor desc = DESC_TYPE_VECTOR | (vector ? VECTOR_KIND_SZARRAY : VECTOR_KIND_ARRAY);
|
||||
for (i = 0; i < numbits; ++i) {
|
||||
if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
|
||||
if (first_set < 0)
|
||||
first_set = i;
|
||||
last_set = i;
|
||||
num_set++;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_set < 0) {
|
||||
if (elem_size <= MAX_ELEMENT_SIZE)
|
||||
return desc | VECTOR_SUBTYPE_PTRFREE | (elem_size << VECTOR_ELSIZE_SHIFT);
|
||||
return DESC_TYPE_COMPLEX_PTRFREE;
|
||||
}
|
||||
|
||||
if (elem_size <= MAX_ELEMENT_SIZE) {
|
||||
desc |= elem_size << VECTOR_ELSIZE_SHIFT;
|
||||
if (!num_set) {
|
||||
return desc | VECTOR_SUBTYPE_PTRFREE;
|
||||
}
|
||||
/* Note: we also handle structs with just ref fields */
|
||||
if (num_set * sizeof (gpointer) == elem_size) {
|
||||
return desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16);
|
||||
}
|
||||
/* FIXME: try run-len first */
|
||||
/* Note: we can't skip the object header here, because it's not present */
|
||||
if (last_set < VECTOR_BITMAP_SIZE) {
|
||||
return desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16);
|
||||
}
|
||||
}
|
||||
/* it's am array of complex structs ... */
|
||||
desc = DESC_TYPE_COMPLEX_ARR;
|
||||
desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
|
||||
return desc;
|
||||
}
|
||||
|
||||
/* Return the bitmap encoded by a descriptor */
|
||||
gsize*
|
||||
mono_gc_get_bitmap_for_descr (SgenDescriptor descr, int *numbits)
|
||||
{
|
||||
SgenDescriptor d = (SgenDescriptor)descr;
|
||||
gsize *bitmap;
|
||||
|
||||
switch (d & DESC_TYPE_MASK) {
|
||||
case DESC_TYPE_RUN_LENGTH: {
|
||||
int first_set = (d >> 16) & 0xff;
|
||||
int num_set = (d >> 24) & 0xff;
|
||||
int i;
|
||||
|
||||
bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
|
||||
|
||||
for (i = first_set; i < first_set + num_set; ++i)
|
||||
bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
|
||||
|
||||
*numbits = first_set + num_set;
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
case DESC_TYPE_BITMAP: {
|
||||
gsize bmap = (d >> LOW_TYPE_BITS) << OBJECT_HEADER_WORDS;
|
||||
|
||||
bitmap = g_new0 (gsize, 1);
|
||||
bitmap [0] = bmap;
|
||||
*numbits = 0;
|
||||
while (bmap) {
|
||||
(*numbits) ++;
|
||||
bmap >>= 1;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
case DESC_TYPE_COMPLEX: {
|
||||
gsize *bitmap_data = sgen_get_complex_descriptor (d);
|
||||
int bwords = (int)(*bitmap_data) - 1;//Max scalar object size is 1Mb, which means up to 32k descriptor words
|
||||
int i;
|
||||
|
||||
bitmap = g_new0 (gsize, bwords);
|
||||
*numbits = bwords * GC_BITS_PER_WORD;
|
||||
|
||||
for (i = 0; i < bwords; ++i) {
|
||||
bitmap [i] = bitmap_data [i + 1];
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
SgenDescriptor
|
||||
mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
|
||||
{
|
||||
if (numbits == 0) {
|
||||
return MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
|
||||
} else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
|
||||
return MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
|
||||
} else {
|
||||
SgenDescriptor complex = alloc_complex_descriptor (bitmap, numbits);
|
||||
return MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
|
||||
}
|
||||
}
|
||||
|
||||
SgenDescriptor
|
||||
mono_gc_make_root_descr_all_refs (int numbits)
|
||||
{
|
||||
gsize *gc_bitmap;
|
||||
SgenDescriptor descr;
|
||||
int num_bytes = numbits / 8;
|
||||
|
||||
if (numbits < 32 && all_ref_root_descrs [numbits])
|
||||
return all_ref_root_descrs [numbits];
|
||||
|
||||
gc_bitmap = g_malloc0 (ALIGN_TO (ALIGN_TO (numbits, 8) + 1, sizeof (gsize)));
|
||||
memset (gc_bitmap, 0xff, num_bytes);
|
||||
if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT))
|
||||
gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
|
||||
else if (numbits && num_bytes % (sizeof (*gc_bitmap)))
|
||||
gc_bitmap[num_bytes / 8] = GUINT64_TO_LE(gc_bitmap [num_bytes / 8]);
|
||||
if (numbits % 8)
|
||||
gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
|
||||
descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
|
||||
g_free (gc_bitmap);
|
||||
|
||||
if (numbits < 32)
|
||||
all_ref_root_descrs [numbits] = descr;
|
||||
|
||||
return descr;
|
||||
}
|
||||
|
||||
SgenDescriptor
|
||||
sgen_make_user_root_descriptor (SgenUserRootMarkFunc marker)
|
||||
{
|
||||
SgenDescriptor descr;
|
||||
|
||||
g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
|
||||
descr = MAKE_ROOT_DESC (ROOT_DESC_USER, (SgenDescriptor)user_descriptors_next);
|
||||
user_descriptors [user_descriptors_next ++] = marker;
|
||||
|
||||
return descr;
|
||||
}
|
||||
|
||||
void*
|
||||
sgen_get_complex_descriptor_bitmap (SgenDescriptor desc)
|
||||
{
|
||||
return complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
|
||||
}
|
||||
|
||||
SgenUserRootMarkFunc
|
||||
sgen_get_user_descriptor_func (SgenDescriptor desc)
|
||||
{
|
||||
return user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
|
||||
}
|
||||
|
||||
#ifdef HEAVY_STATISTICS
|
||||
void
|
||||
sgen_descriptor_count_scanned_object (SgenDescriptor desc)
|
||||
{
|
||||
int type = desc & DESC_TYPE_MASK;
|
||||
SGEN_ASSERT (0, type, "Descriptor type can't be zero");
|
||||
++stat_scanned_count_per_descriptor [type - 1];
|
||||
}
|
||||
|
||||
void
|
||||
sgen_descriptor_count_copied_object (SgenDescriptor desc)
|
||||
{
|
||||
int type = desc & DESC_TYPE_MASK;
|
||||
SGEN_ASSERT (0, type, "Descriptor type can't be zero");
|
||||
++stat_copied_count_per_descriptor [type - 1];
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
sgen_init_descriptors (void)
|
||||
{
|
||||
#ifdef HEAVY_STATISTICS
|
||||
mono_counters_register ("# scanned RUN_LENGTH", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scanned_count_per_descriptor [DESC_TYPE_RUN_LENGTH - 1]);
|
||||
mono_counters_register ("# scanned SMALL_PTRFREE", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scanned_count_per_descriptor [DESC_TYPE_SMALL_PTRFREE - 1]);
|
||||
mono_counters_register ("# scanned COMPLEX", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scanned_count_per_descriptor [DESC_TYPE_COMPLEX - 1]);
|
||||
mono_counters_register ("# scanned VECTOR", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scanned_count_per_descriptor [DESC_TYPE_VECTOR - 1]);
|
||||
mono_counters_register ("# scanned BITMAP", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scanned_count_per_descriptor [DESC_TYPE_BITMAP - 1]);
|
||||
mono_counters_register ("# scanned COMPLEX_ARR", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scanned_count_per_descriptor [DESC_TYPE_COMPLEX_ARR - 1]);
|
||||
mono_counters_register ("# scanned COMPLEX_PTRFREE", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scanned_count_per_descriptor [DESC_TYPE_COMPLEX_PTRFREE - 1]);
|
||||
|
||||
mono_counters_register ("# copied RUN_LENGTH", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copied_count_per_descriptor [DESC_TYPE_RUN_LENGTH - 1]);
|
||||
mono_counters_register ("# copied SMALL_PTRFREE", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copied_count_per_descriptor [DESC_TYPE_SMALL_PTRFREE - 1]);
|
||||
mono_counters_register ("# copied COMPLEX", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copied_count_per_descriptor [DESC_TYPE_COMPLEX - 1]);
|
||||
mono_counters_register ("# copied VECTOR", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copied_count_per_descriptor [DESC_TYPE_VECTOR - 1]);
|
||||
mono_counters_register ("# copied BITMAP", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copied_count_per_descriptor [DESC_TYPE_BITMAP - 1]);
|
||||
mono_counters_register ("# copied COMPLEX_ARR", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copied_count_per_descriptor [DESC_TYPE_COMPLEX_ARR - 1]);
|
||||
mono_counters_register ("# copied COMPLEX_PTRFREE", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copied_count_per_descriptor [DESC_TYPE_COMPLEX_PTRFREE - 1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
330
mono/sgen/sgen-descriptor.h
Normal file
330
mono/sgen/sgen-descriptor.h
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* sgen-descriptor.h: GC descriptors describe object layout.
|
||||
|
||||
* Copyright 2001-2003 Ximian, Inc
|
||||
* Copyright 2003-2010 Novell, Inc.
|
||||
* Copyright 2011 Xamarin Inc (http://www.xamarin.com)
|
||||
*
|
||||
* Copyright (C) 2012 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef __MONO_SGEN_DESCRIPTOR_H__
|
||||
#define __MONO_SGEN_DESCRIPTOR_H__
|
||||
|
||||
#include <mono/sgen/sgen-conf.h>
|
||||
|
||||
/*
|
||||
* ######################################################################
|
||||
* ######## GC descriptors
|
||||
* ######################################################################
|
||||
* Used to quickly get the info the GC needs about an object: size and
|
||||
* where the references are held.
|
||||
*/
|
||||
#define OBJECT_HEADER_WORDS (SGEN_CLIENT_OBJECT_HEADER_SIZE / sizeof(gpointer))
|
||||
#define LOW_TYPE_BITS 3
|
||||
#define DESC_TYPE_MASK ((1 << LOW_TYPE_BITS) - 1)
|
||||
#define MAX_RUNLEN_OBJECT_SIZE 0xFFFF
|
||||
#define VECTOR_INFO_SHIFT 14
|
||||
#define VECTOR_KIND_SHIFT 13
|
||||
#define VECTOR_ELSIZE_SHIFT 3
|
||||
#define VECTOR_BITMAP_SHIFT 16
|
||||
#define VECTOR_BITMAP_SIZE (GC_BITS_PER_WORD - VECTOR_BITMAP_SHIFT)
|
||||
#define BITMAP_NUM_BITS (GC_BITS_PER_WORD - LOW_TYPE_BITS)
|
||||
#define MAX_ELEMENT_SIZE 0x3ff
|
||||
#define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
|
||||
#define VECTOR_SUBTYPE_REFS (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
|
||||
#define VECTOR_SUBTYPE_BITMAP (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
|
||||
|
||||
#define VECTOR_KIND_SZARRAY (DESC_TYPE_V_SZARRAY << VECTOR_KIND_SHIFT)
|
||||
#define VECTOR_KIND_ARRAY (DESC_TYPE_V_ARRAY << VECTOR_KIND_SHIFT)
|
||||
|
||||
/*
|
||||
* Objects are aligned to 8 bytes boundaries.
|
||||
*
|
||||
* A descriptor is a pointer in GCVTable, so 32 or 64 bits of size.
|
||||
* The low 3 bits define the type of the descriptor. The other bits
|
||||
* depend on the type.
|
||||
*
|
||||
* It's important to be able to quickly identify two properties of classes from their
|
||||
* descriptors: whether they are small enough to live in the regular major heap (size <=
|
||||
* SGEN_MAX_SMALL_OBJ_SIZE), and whether they contain references.
|
||||
*
|
||||
* To that end we have three descriptor types that only apply to small classes: RUN_LENGTH,
|
||||
* BITMAP, and SMALL_PTRFREE. We also have the type COMPLEX_PTRFREE, which applies to
|
||||
* classes that are either not small or of unknown size (those being strings and arrays).
|
||||
* The lowest two bits of the SMALL_PTRFREE and COMPLEX_PTRFREE tags are the same, so we can
|
||||
* quickly check for references.
|
||||
*
|
||||
* As a general rule the 13 remaining low bits define the size, either
|
||||
* of the whole object or of the elements in the arrays. While for objects
|
||||
* the size is already in bytes, for arrays we need to shift, because
|
||||
* array elements might be smaller than 8 bytes. In case of arrays, we
|
||||
* use two bits to describe what the additional high bits represents,
|
||||
* so the default behaviour can handle element sizes less than 2048 bytes.
|
||||
* The high 16 bits, if 0 it means the object is pointer-free.
|
||||
* This design should make it easy and fast to skip over ptr-free data.
|
||||
* The first 4 types should cover >95% of the objects.
|
||||
* Note that since the size of objects is limited to 64K, larger objects
|
||||
* will be allocated in the large object heap.
|
||||
* If we want 4-bytes alignment, we need to put vector and small bitmap
|
||||
* inside complex.
|
||||
*
|
||||
* We don't use 0 so that 0 isn't a valid GC descriptor. No deep reason for this other than
|
||||
* to be able to identify a non-inited descriptor for debugging.
|
||||
*/
|
||||
enum {
|
||||
/* Keep in sync with `descriptor_types` in sgen-debug.c! */
|
||||
DESC_TYPE_RUN_LENGTH = 1, /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
|
||||
DESC_TYPE_BITMAP = 2, /* | 29-61 bitmap bits */
|
||||
DESC_TYPE_SMALL_PTRFREE = 3,
|
||||
DESC_TYPE_MAX_SMALL_OBJ = 3,
|
||||
DESC_TYPE_COMPLEX = 4, /* index for bitmap into complex_descriptors */
|
||||
DESC_TYPE_VECTOR = 5, /* 10 bits element size | 1 bit kind | 2 bits desc | element desc */
|
||||
DESC_TYPE_COMPLEX_ARR = 6, /* index for bitmap into complex_descriptors */
|
||||
DESC_TYPE_COMPLEX_PTRFREE = 7, /* Nothing, used to encode large ptr objects and strings. */
|
||||
DESC_TYPE_MAX = 7,
|
||||
|
||||
DESC_TYPE_PTRFREE_MASK = 3,
|
||||
DESC_TYPE_PTRFREE_BITS = 3
|
||||
};
|
||||
|
||||
/* values for array kind */
|
||||
enum {
|
||||
DESC_TYPE_V_SZARRAY = 0, /*vector with no bounds data */
|
||||
DESC_TYPE_V_ARRAY = 1, /* array with bounds data */
|
||||
};
|
||||
|
||||
/* subtypes for arrays and vectors */
|
||||
enum {
|
||||
DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value */
|
||||
DESC_TYPE_V_REFS, /* all the array elements are refs */
|
||||
DESC_TYPE_V_RUN_LEN, /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
|
||||
DESC_TYPE_V_BITMAP /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
|
||||
};
|
||||
|
||||
#define SGEN_DESC_STRING (DESC_TYPE_COMPLEX_PTRFREE | (1 << LOW_TYPE_BITS))
|
||||
|
||||
/* Root bitmap descriptors are simpler: the lower three bits describe the type
|
||||
* and we either have 30/62 bitmap bits or nibble-based run-length,
|
||||
* or a complex descriptor, or a user defined marker function.
|
||||
*/
|
||||
enum {
|
||||
ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
|
||||
ROOT_DESC_BITMAP,
|
||||
ROOT_DESC_RUN_LEN,
|
||||
ROOT_DESC_COMPLEX,
|
||||
ROOT_DESC_USER,
|
||||
ROOT_DESC_TYPE_MASK = 0x7,
|
||||
ROOT_DESC_TYPE_SHIFT = 3,
|
||||
};
|
||||
|
||||
typedef void (*SgenUserMarkFunc) (GCObject **addr, void *gc_data);
|
||||
typedef void (*SgenUserRootMarkFunc) (void *addr, SgenUserMarkFunc mark_func, void *gc_data);
|
||||
|
||||
SgenDescriptor sgen_make_user_root_descriptor (SgenUserRootMarkFunc marker);
|
||||
|
||||
gsize* sgen_get_complex_descriptor (SgenDescriptor desc);
|
||||
void* sgen_get_complex_descriptor_bitmap (SgenDescriptor desc);
|
||||
SgenUserRootMarkFunc sgen_get_user_descriptor_func (SgenDescriptor desc);
|
||||
|
||||
void sgen_init_descriptors (void);
|
||||
|
||||
#ifdef HEAVY_STATISTICS
|
||||
void sgen_descriptor_count_scanned_object (SgenDescriptor desc);
|
||||
void sgen_descriptor_count_copied_object (SgenDescriptor desc);
|
||||
#endif
|
||||
|
||||
static inline gboolean
|
||||
sgen_gc_descr_has_references (SgenDescriptor desc)
|
||||
{
|
||||
/* This covers SMALL_PTRFREE and COMPLEX_PTRFREE */
|
||||
if ((desc & DESC_TYPE_PTRFREE_MASK) == DESC_TYPE_PTRFREE_BITS)
|
||||
return FALSE;
|
||||
|
||||
/*The array is ptr-free*/
|
||||
if ((desc & 0xC007) == (DESC_TYPE_VECTOR | VECTOR_SUBTYPE_PTRFREE))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define SGEN_VTABLE_HAS_REFERENCES(vt) (sgen_gc_descr_has_references (sgen_vtable_get_descriptor ((vt))))
|
||||
#define SGEN_OBJECT_HAS_REFERENCES(o) (SGEN_VTABLE_HAS_REFERENCES (SGEN_LOAD_VTABLE ((o))))
|
||||
|
||||
/* helper macros to scan and traverse objects, macros because we resue them in many functions */
|
||||
#ifdef __GNUC__
|
||||
#define PREFETCH_READ(addr) __builtin_prefetch ((addr), 0, 1)
|
||||
#define PREFETCH_WRITE(addr) __builtin_prefetch ((addr), 1, 1)
|
||||
#else
|
||||
#define PREFETCH_READ(addr)
|
||||
#define PREFETCH_WRITE(addr)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && SIZEOF_VOID_P==4
|
||||
#define GNUC_BUILTIN_CTZ(bmap) __builtin_ctz(bmap)
|
||||
#elif defined(__GNUC__) && SIZEOF_VOID_P==8
|
||||
#define GNUC_BUILTIN_CTZ(bmap) __builtin_ctzl(bmap)
|
||||
#endif
|
||||
|
||||
/* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
|
||||
#define OBJ_RUN_LEN_FOREACH_PTR(desc,obj) do { \
|
||||
if ((desc) & 0xffff0000) { \
|
||||
/* there are pointers */ \
|
||||
void **_objptr_end; \
|
||||
void **_objptr = (void**)(obj); \
|
||||
_objptr += ((desc) >> 16) & 0xff; \
|
||||
_objptr_end = _objptr + (((desc) >> 24) & 0xff); \
|
||||
while (_objptr < _objptr_end) { \
|
||||
HANDLE_PTR ((GCObject**)_objptr, (obj)); \
|
||||
_objptr++; \
|
||||
}; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* a bitmap desc means that there are pointer references or we'd have
|
||||
* choosen run-length, instead: add an assert to check.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
#define OBJ_BITMAP_FOREACH_PTR(desc,obj) do { \
|
||||
/* there are pointers */ \
|
||||
void **_objptr = (void**)(obj); \
|
||||
gsize _bmap = (desc) >> LOW_TYPE_BITS; \
|
||||
_objptr += OBJECT_HEADER_WORDS; \
|
||||
do { \
|
||||
int _index = GNUC_BUILTIN_CTZ (_bmap); \
|
||||
_objptr += _index; \
|
||||
_bmap >>= (_index + 1); \
|
||||
HANDLE_PTR ((GCObject**)_objptr, (obj)); \
|
||||
++_objptr; \
|
||||
} while (_bmap); \
|
||||
} while (0)
|
||||
#else
|
||||
#define OBJ_BITMAP_FOREACH_PTR(desc,obj) do { \
|
||||
/* there are pointers */ \
|
||||
void **_objptr = (void**)(obj); \
|
||||
gsize _bmap = (desc) >> LOW_TYPE_BITS; \
|
||||
_objptr += OBJECT_HEADER_WORDS; \
|
||||
do { \
|
||||
if ((_bmap & 1)) { \
|
||||
HANDLE_PTR ((GCObject**)_objptr, (obj)); \
|
||||
} \
|
||||
_bmap >>= 1; \
|
||||
++_objptr; \
|
||||
} while (_bmap); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do { \
|
||||
/* there are pointers */ \
|
||||
void **_objptr = (void**)(obj); \
|
||||
gsize *bitmap_data = sgen_get_complex_descriptor ((desc)); \
|
||||
gsize bwords = (*bitmap_data) - 1; \
|
||||
void **start_run = _objptr; \
|
||||
bitmap_data++; \
|
||||
while (bwords-- > 0) { \
|
||||
gsize _bmap = *bitmap_data++; \
|
||||
_objptr = start_run; \
|
||||
/*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/ \
|
||||
while (_bmap) { \
|
||||
if ((_bmap & 1)) { \
|
||||
HANDLE_PTR ((GCObject**)_objptr, (obj)); \
|
||||
} \
|
||||
_bmap >>= 1; \
|
||||
++_objptr; \
|
||||
} \
|
||||
start_run += GC_BITS_PER_WORD; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* this one is untested */
|
||||
#define OBJ_COMPLEX_ARR_FOREACH_PTR(desc,obj) do { \
|
||||
/* there are pointers */ \
|
||||
GCVTable vt = SGEN_LOAD_VTABLE (obj); \
|
||||
gsize *mbitmap_data = sgen_get_complex_descriptor ((desc)); \
|
||||
gsize mbwords = (*mbitmap_data++) - 1; \
|
||||
gsize el_size = sgen_client_array_element_size (vt); \
|
||||
char *e_start = sgen_client_array_data_start ((GCObject*)(obj)); \
|
||||
char *e_end = e_start + el_size * sgen_client_array_length ((GCObject*)(obj)); \
|
||||
while (e_start < e_end) { \
|
||||
void **_objptr = (void**)e_start; \
|
||||
gsize *bitmap_data = mbitmap_data; \
|
||||
gsize bwords = mbwords; \
|
||||
while (bwords-- > 0) { \
|
||||
gsize _bmap = *bitmap_data++; \
|
||||
void **start_run = _objptr; \
|
||||
/*g_print ("bitmap: 0x%x\n", _bmap);*/ \
|
||||
while (_bmap) { \
|
||||
if ((_bmap & 1)) { \
|
||||
HANDLE_PTR ((GCObject**)_objptr, (obj)); \
|
||||
} \
|
||||
_bmap >>= 1; \
|
||||
++_objptr; \
|
||||
} \
|
||||
_objptr = start_run + GC_BITS_PER_WORD; \
|
||||
} \
|
||||
e_start += el_size; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define OBJ_VECTOR_FOREACH_PTR(desc,obj) do { \
|
||||
/* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */ \
|
||||
if ((desc) & 0xffffc000) { \
|
||||
int el_size = ((desc) >> 3) & MAX_ELEMENT_SIZE; \
|
||||
/* there are pointers */ \
|
||||
int etype = (desc) & 0xc000; \
|
||||
if (etype == (DESC_TYPE_V_REFS << 14)) { \
|
||||
void **p = (void**)sgen_client_array_data_start ((GCObject*)(obj)); \
|
||||
void **end_refs = (void**)((char*)p + el_size * sgen_client_array_length ((GCObject*)(obj))); \
|
||||
/* Note: this code can handle also arrays of struct with only references in them */ \
|
||||
while (p < end_refs) { \
|
||||
HANDLE_PTR ((GCObject**)p, (obj)); \
|
||||
++p; \
|
||||
} \
|
||||
} else if (etype == DESC_TYPE_V_RUN_LEN << 14) { \
|
||||
int offset = ((desc) >> 16) & 0xff; \
|
||||
int num_refs = ((desc) >> 24) & 0xff; \
|
||||
char *e_start = sgen_client_array_data_start ((GCObject*)(obj)); \
|
||||
char *e_end = e_start + el_size * sgen_client_array_length ((GCObject*)(obj)); \
|
||||
while (e_start < e_end) { \
|
||||
void **p = (void**)e_start; \
|
||||
int i; \
|
||||
p += offset; \
|
||||
for (i = 0; i < num_refs; ++i) { \
|
||||
HANDLE_PTR ((GCObject**)p + i, (obj)); \
|
||||
} \
|
||||
e_start += el_size; \
|
||||
} \
|
||||
} else if (etype == DESC_TYPE_V_BITMAP << 14) { \
|
||||
char *e_start = sgen_client_array_data_start ((GCObject*)(obj)); \
|
||||
char *e_end = e_start + el_size * sgen_client_array_length ((GCObject*)(obj)); \
|
||||
while (e_start < e_end) { \
|
||||
void **p = (void**)e_start; \
|
||||
gsize _bmap = (desc) >> 16; \
|
||||
/* Note: there is no object header here to skip */ \
|
||||
while (_bmap) { \
|
||||
if ((_bmap & 1)) { \
|
||||
HANDLE_PTR ((GCObject**)p, (obj)); \
|
||||
} \
|
||||
_bmap >>= 1; \
|
||||
++p; \
|
||||
} \
|
||||
e_start += el_size; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#endif
|
||||
871
mono/sgen/sgen-fin-weak-hash.c
Normal file
871
mono/sgen/sgen-fin-weak-hash.c
Normal file
File diff suppressed because it is too large
Load Diff
1
mono/sgen/sgen-gc.c.REMOVED.git-id
Normal file
1
mono/sgen/sgen-gc.c.REMOVED.git-id
Normal file
@@ -0,0 +1 @@
|
||||
a24badfaf7c185d8a870932a9ed92ad26f23face
|
||||
1070
mono/sgen/sgen-gc.h
Normal file
1070
mono/sgen/sgen-gc.h
Normal file
File diff suppressed because it is too large
Load Diff
383
mono/sgen/sgen-gray.c
Normal file
383
mono/sgen/sgen-gray.c
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* sgen-gray.c: Gray queue management.
|
||||
*
|
||||
* Copyright 2001-2003 Ximian, Inc
|
||||
* Copyright 2003-2010 Novell, Inc.
|
||||
* Copyright (C) 2012 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include "config.h"
|
||||
#ifdef HAVE_SGEN_GC
|
||||
|
||||
#include "mono/sgen/sgen-gc.h"
|
||||
#include "mono/sgen/sgen-protocol.h"
|
||||
|
||||
#ifdef HEAVY_STATISTICS
|
||||
guint64 stat_gray_queue_section_alloc;
|
||||
guint64 stat_gray_queue_section_free;
|
||||
guint64 stat_gray_queue_enqueue_fast_path;
|
||||
guint64 stat_gray_queue_dequeue_fast_path;
|
||||
guint64 stat_gray_queue_enqueue_slow_path;
|
||||
guint64 stat_gray_queue_dequeue_slow_path;
|
||||
#endif
|
||||
|
||||
#define GRAY_QUEUE_LENGTH_LIMIT 64
|
||||
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
|
||||
#define STATE_TRANSITION(s,o,n) do { \
|
||||
int __old = (o); \
|
||||
if (InterlockedCompareExchange ((volatile int*)&(s)->state, (n), __old) != __old) \
|
||||
g_assert_not_reached (); \
|
||||
} while (0)
|
||||
#define STATE_SET(s,v) (s)->state = (v)
|
||||
#define STATE_ASSERT(s,v) g_assert ((s)->state == (v))
|
||||
#else
|
||||
#define STATE_TRANSITION(s,o,n)
|
||||
#define STATE_SET(s,v)
|
||||
#define STATE_ASSERT(s,v)
|
||||
#endif
|
||||
|
||||
void
|
||||
sgen_gray_object_alloc_queue_section (SgenGrayQueue *queue)
|
||||
{
|
||||
GrayQueueSection *section;
|
||||
|
||||
HEAVY_STAT (stat_gray_queue_section_alloc ++);
|
||||
|
||||
if (queue->alloc_prepare_func)
|
||||
queue->alloc_prepare_func (queue);
|
||||
|
||||
if (queue->free_list) {
|
||||
/* Use the previously allocated queue sections if possible */
|
||||
section = queue->free_list;
|
||||
queue->free_list = section->next;
|
||||
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
|
||||
} else {
|
||||
/* Allocate a new section */
|
||||
section = sgen_alloc_internal (INTERNAL_MEM_GRAY_QUEUE);
|
||||
STATE_SET (section, GRAY_QUEUE_SECTION_STATE_FLOATING);
|
||||
}
|
||||
|
||||
section->size = SGEN_GRAY_QUEUE_SECTION_SIZE;
|
||||
|
||||
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
|
||||
|
||||
/* Link it with the others */
|
||||
section->next = queue->first;
|
||||
queue->first = section;
|
||||
queue->cursor = section->entries - 1;
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_object_free_queue_section (GrayQueueSection *section)
|
||||
{
|
||||
HEAVY_STAT (stat_gray_queue_section_free ++);
|
||||
|
||||
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_FREED);
|
||||
sgen_free_internal (section, INTERNAL_MEM_GRAY_QUEUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following two functions are called in the inner loops of the
|
||||
* collector, so they need to be as fast as possible. We have macros
|
||||
* for them in sgen-gc.h.
|
||||
*/
|
||||
|
||||
void
|
||||
sgen_gray_object_enqueue (SgenGrayQueue *queue, GCObject *obj, SgenDescriptor desc)
|
||||
{
|
||||
GrayQueueEntry entry = SGEN_GRAY_QUEUE_ENTRY (obj, desc);
|
||||
|
||||
HEAVY_STAT (stat_gray_queue_enqueue_slow_path ++);
|
||||
|
||||
SGEN_ASSERT (9, obj, "enqueueing a null object");
|
||||
//sgen_check_objref (obj);
|
||||
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
|
||||
if (queue->enqueue_check_func)
|
||||
queue->enqueue_check_func (obj);
|
||||
#endif
|
||||
|
||||
if (G_UNLIKELY (!queue->first || queue->cursor == GRAY_LAST_CURSOR_POSITION (queue->first))) {
|
||||
if (queue->first) {
|
||||
/* Set the current section size back to default, might have been changed by sgen_gray_object_dequeue_section */
|
||||
queue->first->size = SGEN_GRAY_QUEUE_SECTION_SIZE;
|
||||
}
|
||||
|
||||
sgen_gray_object_alloc_queue_section (queue);
|
||||
}
|
||||
STATE_ASSERT (queue->first, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
|
||||
SGEN_ASSERT (9, queue->cursor <= GRAY_LAST_CURSOR_POSITION (queue->first), "gray queue %p overflow, first %p, cursor %p", queue, queue->first, queue->cursor);
|
||||
*++queue->cursor = entry;
|
||||
|
||||
#ifdef SGEN_HEAVY_BINARY_PROTOCOL
|
||||
binary_protocol_gray_enqueue (queue, queue->cursor, obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
GrayQueueEntry
|
||||
sgen_gray_object_dequeue (SgenGrayQueue *queue)
|
||||
{
|
||||
GrayQueueEntry entry;
|
||||
|
||||
HEAVY_STAT (stat_gray_queue_dequeue_slow_path ++);
|
||||
|
||||
if (sgen_gray_object_queue_is_empty (queue)) {
|
||||
entry.obj = NULL;
|
||||
return entry;
|
||||
}
|
||||
|
||||
STATE_ASSERT (queue->first, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
|
||||
SGEN_ASSERT (9, queue->cursor >= GRAY_FIRST_CURSOR_POSITION (queue->first), "gray queue %p underflow", queue);
|
||||
|
||||
entry = *queue->cursor--;
|
||||
|
||||
#ifdef SGEN_HEAVY_BINARY_PROTOCOL
|
||||
binary_protocol_gray_dequeue (queue, queue->cursor + 1, entry.obj);
|
||||
#endif
|
||||
|
||||
if (G_UNLIKELY (queue->cursor < GRAY_FIRST_CURSOR_POSITION (queue->first))) {
|
||||
GrayQueueSection *section = queue->first;
|
||||
queue->first = section->next;
|
||||
section->next = queue->free_list;
|
||||
|
||||
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FREE_LIST);
|
||||
|
||||
queue->free_list = section;
|
||||
queue->cursor = queue->first ? queue->first->entries + queue->first->size - 1 : NULL;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
GrayQueueSection*
|
||||
sgen_gray_object_dequeue_section (SgenGrayQueue *queue)
|
||||
{
|
||||
GrayQueueSection *section;
|
||||
|
||||
if (!queue->first)
|
||||
return NULL;
|
||||
|
||||
section = queue->first;
|
||||
queue->first = section->next;
|
||||
|
||||
section->next = NULL;
|
||||
section->size = queue->cursor - section->entries + 1;
|
||||
|
||||
queue->cursor = queue->first ? queue->first->entries + queue->first->size - 1 : NULL;
|
||||
|
||||
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FLOATING);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_object_enqueue_section (SgenGrayQueue *queue, GrayQueueSection *section)
|
||||
{
|
||||
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
|
||||
|
||||
if (queue->first)
|
||||
queue->first->size = queue->cursor - queue->first->entries + 1;
|
||||
|
||||
section->next = queue->first;
|
||||
queue->first = section;
|
||||
queue->cursor = queue->first->entries + queue->first->size - 1;
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
|
||||
if (queue->enqueue_check_func) {
|
||||
int i;
|
||||
for (i = 0; i < section->size; ++i)
|
||||
queue->enqueue_check_func (section->entries [i].obj);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_object_queue_trim_free_list (SgenGrayQueue *queue)
|
||||
{
|
||||
GrayQueueSection *section, *next;
|
||||
int i = 0;
|
||||
for (section = queue->free_list; section && i < GRAY_QUEUE_LENGTH_LIMIT - 1; section = section->next) {
|
||||
STATE_ASSERT (section, GRAY_QUEUE_SECTION_STATE_FREE_LIST);
|
||||
i ++;
|
||||
}
|
||||
if (!section)
|
||||
return;
|
||||
while (section->next) {
|
||||
next = section->next;
|
||||
section->next = next->next;
|
||||
STATE_TRANSITION (next, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
|
||||
sgen_gray_object_free_queue_section (next);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_object_queue_init (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func)
|
||||
{
|
||||
g_assert (sgen_gray_object_queue_is_empty (queue));
|
||||
|
||||
queue->alloc_prepare_func = NULL;
|
||||
queue->alloc_prepare_data = NULL;
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
|
||||
queue->enqueue_check_func = enqueue_check_func;
|
||||
#endif
|
||||
|
||||
/* Free the extra sections allocated during the last collection */
|
||||
sgen_gray_object_queue_trim_free_list (queue);
|
||||
}
|
||||
|
||||
static void
|
||||
invalid_prepare_func (SgenGrayQueue *queue)
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_object_queue_init_invalid (SgenGrayQueue *queue)
|
||||
{
|
||||
sgen_gray_object_queue_init (queue, NULL);
|
||||
queue->alloc_prepare_func = invalid_prepare_func;
|
||||
queue->alloc_prepare_data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_queue_set_alloc_prepare (SgenGrayQueue *queue, GrayQueueAllocPrepareFunc alloc_prepare_func, void *data)
|
||||
{
|
||||
SGEN_ASSERT (0, !queue->alloc_prepare_func && !queue->alloc_prepare_data, "Can't set gray queue alloc-prepare twice");
|
||||
queue->alloc_prepare_func = alloc_prepare_func;
|
||||
queue->alloc_prepare_data = data;
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_object_queue_init_with_alloc_prepare (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func,
|
||||
GrayQueueAllocPrepareFunc alloc_prepare_func, void *data)
|
||||
{
|
||||
sgen_gray_object_queue_init (queue, enqueue_check_func);
|
||||
sgen_gray_queue_set_alloc_prepare (queue, alloc_prepare_func, data);
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_object_queue_deinit (SgenGrayQueue *queue)
|
||||
{
|
||||
g_assert (!queue->first);
|
||||
while (queue->free_list) {
|
||||
GrayQueueSection *next = queue->free_list->next;
|
||||
STATE_TRANSITION (queue->free_list, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
|
||||
sgen_gray_object_free_queue_section (queue->free_list);
|
||||
queue->free_list = next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sgen_gray_object_queue_disable_alloc_prepare (SgenGrayQueue *queue)
|
||||
{
|
||||
queue->alloc_prepare_func = NULL;
|
||||
queue->alloc_prepare_data = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
lock_section_queue (SgenSectionGrayQueue *queue)
|
||||
{
|
||||
if (!queue->locked)
|
||||
return;
|
||||
|
||||
mono_mutex_lock (&queue->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
unlock_section_queue (SgenSectionGrayQueue *queue)
|
||||
{
|
||||
if (!queue->locked)
|
||||
return;
|
||||
|
||||
mono_mutex_unlock (&queue->lock);
|
||||
}
|
||||
|
||||
void
|
||||
sgen_section_gray_queue_init (SgenSectionGrayQueue *queue, gboolean locked, GrayQueueEnqueueCheckFunc enqueue_check_func)
|
||||
{
|
||||
g_assert (sgen_section_gray_queue_is_empty (queue));
|
||||
|
||||
queue->locked = locked;
|
||||
if (locked) {
|
||||
mono_mutex_init_recursive (&queue->lock);
|
||||
}
|
||||
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
|
||||
queue->enqueue_check_func = enqueue_check_func;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
sgen_section_gray_queue_is_empty (SgenSectionGrayQueue *queue)
|
||||
{
|
||||
return !queue->first;
|
||||
}
|
||||
|
||||
GrayQueueSection*
|
||||
sgen_section_gray_queue_dequeue (SgenSectionGrayQueue *queue)
|
||||
{
|
||||
GrayQueueSection *section;
|
||||
|
||||
lock_section_queue (queue);
|
||||
|
||||
if (queue->first) {
|
||||
section = queue->first;
|
||||
queue->first = section->next;
|
||||
|
||||
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FLOATING);
|
||||
|
||||
section->next = NULL;
|
||||
} else {
|
||||
section = NULL;
|
||||
}
|
||||
|
||||
unlock_section_queue (queue);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
void
|
||||
sgen_section_gray_queue_enqueue (SgenSectionGrayQueue *queue, GrayQueueSection *section)
|
||||
{
|
||||
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
|
||||
|
||||
lock_section_queue (queue);
|
||||
|
||||
section->next = queue->first;
|
||||
queue->first = section;
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
|
||||
if (queue->enqueue_check_func) {
|
||||
int i;
|
||||
for (i = 0; i < section->size; ++i)
|
||||
queue->enqueue_check_func (section->entries [i].obj);
|
||||
}
|
||||
#endif
|
||||
|
||||
unlock_section_queue (queue);
|
||||
}
|
||||
|
||||
void
|
||||
sgen_init_gray_queues (void)
|
||||
{
|
||||
#ifdef HEAVY_STATISTICS
|
||||
mono_counters_register ("Gray Queue alloc section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_section_alloc);
|
||||
mono_counters_register ("Gray Queue free section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_section_free);
|
||||
mono_counters_register ("Gray Queue enqueue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_enqueue_fast_path);
|
||||
mono_counters_register ("Gray Queue dequeue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_dequeue_fast_path);
|
||||
mono_counters_register ("Gray Queue enqueue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_enqueue_slow_path);
|
||||
mono_counters_register ("Gray Queue dequeue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_dequeue_slow_path);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
219
mono/sgen/sgen-gray.h
Normal file
219
mono/sgen/sgen-gray.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* sgen-gray.h: Gray queue management.
|
||||
*
|
||||
* Copyright 2011 Xamarin Inc (http://www.xamarin.com)
|
||||
* Copyright (C) 2012 Xamarin Inc
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License 2.0 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License 2.0 along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef __MONO_SGEN_GRAY_H__
|
||||
#define __MONO_SGEN_GRAY_H__
|
||||
|
||||
#include "mono/sgen/sgen-protocol.h"
|
||||
|
||||
/*
|
||||
* This gray queue has to be as optimized as possible, because it is in the core of
|
||||
* the mark/copy phase of the garbage collector. The memory access has then to be as
|
||||
* cache friendly as possible. That's why we use a cursor based implementation.
|
||||
*
|
||||
* This simply consist in maintaining a pointer to the current element in the
|
||||
* queue. In addition to using this cursor, we use a simple linked list of arrays,
|
||||
* called sections, so that we have the cache friendliness of arrays without having
|
||||
* the cost of memory reallocation of a dynaic array, not the cost of memory
|
||||
* indirection of a linked list.
|
||||
*
|
||||
* This implementation also allows the dequeuing of a whole section at a time. This is
|
||||
* for example used in the parallel GC because it would be too costly to take one element
|
||||
* at a time. This imply the main constraint that, because we don't carry the cursor
|
||||
* with the section, we still have to store the index of the last element. This is done
|
||||
* through the 'size' field on the section, which default value is it's maximum value
|
||||
* SGEN_GRAY_QUEUE_SECTION_SIZE. This field is updated in multiple cases :
|
||||
* - section allocation : default value
|
||||
* - object push : default value if we fill the current queue first
|
||||
* - section dequeue : position of the cursor in the dequeued section
|
||||
* - section enqueue : position of the cursor in the previously first section in the queue
|
||||
*
|
||||
* The previous implementation was an index based access where we would store the index
|
||||
* of the last element in the section. This was less efficient because we would have
|
||||
* to make 1 memory access for the index value, 1 for the base address of the objects
|
||||
* array and another 1 for the actual value in the array.
|
||||
*/
|
||||
|
||||
/* SGEN_GRAY_QUEUE_HEADER_SIZE is number of machine words */
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
|
||||
#define SGEN_GRAY_QUEUE_HEADER_SIZE 4
|
||||
#else
|
||||
#define SGEN_GRAY_QUEUE_HEADER_SIZE 2
|
||||
#endif
|
||||
|
||||
#define SGEN_GRAY_QUEUE_SECTION_SIZE (128 - SGEN_GRAY_QUEUE_HEADER_SIZE)
|
||||
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
|
||||
typedef enum {
|
||||
GRAY_QUEUE_SECTION_STATE_FLOATING,
|
||||
GRAY_QUEUE_SECTION_STATE_ENQUEUED,
|
||||
GRAY_QUEUE_SECTION_STATE_FREE_LIST,
|
||||
GRAY_QUEUE_SECTION_STATE_FREED
|
||||
} GrayQueueSectionState;
|
||||
#endif
|
||||
|
||||
typedef struct _GrayQueueEntry GrayQueueEntry;
|
||||
struct _GrayQueueEntry {
|
||||
GCObject *obj;
|
||||
SgenDescriptor desc;
|
||||
};
|
||||
|
||||
#define SGEN_GRAY_QUEUE_ENTRY(obj,desc) { (obj), (desc) }
|
||||
|
||||
/*
|
||||
* This is a stack now instead of a queue, so the most recently added items are removed
|
||||
* first, improving cache locality, and keeping the stack size manageable.
|
||||
*/
|
||||
typedef struct _GrayQueueSection GrayQueueSection;
|
||||
struct _GrayQueueSection {
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
|
||||
/*
|
||||
* The dummy is here so that the state doesn't get overwritten
|
||||
* by the internal allocator once the section is freed.
|
||||
*/
|
||||
int dummy;
|
||||
GrayQueueSectionState state;
|
||||
#endif
|
||||
int size;
|
||||
GrayQueueSection *next;
|
||||
GrayQueueEntry entries [SGEN_GRAY_QUEUE_SECTION_SIZE];
|
||||
};
|
||||
|
||||
typedef struct _SgenGrayQueue SgenGrayQueue;
|
||||
|
||||
typedef void (*GrayQueueAllocPrepareFunc) (SgenGrayQueue*);
|
||||
typedef void (*GrayQueueEnqueueCheckFunc) (GCObject*);
|
||||
|
||||
struct _SgenGrayQueue {
|
||||
GrayQueueEntry *cursor;
|
||||
GrayQueueSection *first;
|
||||
GrayQueueSection *free_list;
|
||||
GrayQueueAllocPrepareFunc alloc_prepare_func;
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
|
||||
GrayQueueEnqueueCheckFunc enqueue_check_func;
|
||||
#endif
|
||||
void *alloc_prepare_data;
|
||||
};
|
||||
|
||||
typedef struct _SgenSectionGrayQueue SgenSectionGrayQueue;
|
||||
|
||||
struct _SgenSectionGrayQueue {
|
||||
GrayQueueSection *first;
|
||||
gboolean locked;
|
||||
mono_mutex_t lock;
|
||||
#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
|
||||
GrayQueueEnqueueCheckFunc enqueue_check_func;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define GRAY_LAST_CURSOR_POSITION(s) ((s)->entries + SGEN_GRAY_QUEUE_SECTION_SIZE - 1)
|
||||
#define GRAY_FIRST_CURSOR_POSITION(s) ((s)->entries)
|
||||
|
||||
#ifdef HEAVY_STATISTICS
|
||||
extern guint64 stat_gray_queue_section_alloc;
|
||||
extern guint64 stat_gray_queue_section_free;
|
||||
extern guint64 stat_gray_queue_enqueue_fast_path;
|
||||
extern guint64 stat_gray_queue_dequeue_fast_path;
|
||||
extern guint64 stat_gray_queue_enqueue_slow_path;
|
||||
extern guint64 stat_gray_queue_dequeue_slow_path;
|
||||
#endif
|
||||
|
||||
void sgen_init_gray_queues (void);
|
||||
|
||||
void sgen_gray_object_enqueue (SgenGrayQueue *queue, GCObject *obj, SgenDescriptor desc);
|
||||
GrayQueueEntry sgen_gray_object_dequeue (SgenGrayQueue *queue);
|
||||
GrayQueueSection* sgen_gray_object_dequeue_section (SgenGrayQueue *queue);
|
||||
void sgen_gray_object_enqueue_section (SgenGrayQueue *queue, GrayQueueSection *section);
|
||||
void sgen_gray_object_queue_trim_free_list (SgenGrayQueue *queue);
|
||||
void sgen_gray_object_queue_init (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func);
|
||||
void sgen_gray_object_queue_init_invalid (SgenGrayQueue *queue);
|
||||
void sgen_gray_queue_set_alloc_prepare (SgenGrayQueue *queue, GrayQueueAllocPrepareFunc alloc_prepare_func, void *data);
|
||||
void sgen_gray_object_queue_init_with_alloc_prepare (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func,
|
||||
GrayQueueAllocPrepareFunc func, void *data);
|
||||
void sgen_gray_object_queue_deinit (SgenGrayQueue *queue);
|
||||
void sgen_gray_object_queue_disable_alloc_prepare (SgenGrayQueue *queue);
|
||||
void sgen_gray_object_alloc_queue_section (SgenGrayQueue *queue);
|
||||
void sgen_gray_object_free_queue_section (GrayQueueSection *section);
|
||||
|
||||
void sgen_section_gray_queue_init (SgenSectionGrayQueue *queue, gboolean locked,
|
||||
GrayQueueEnqueueCheckFunc enqueue_check_func);
|
||||
gboolean sgen_section_gray_queue_is_empty (SgenSectionGrayQueue *queue);
|
||||
GrayQueueSection* sgen_section_gray_queue_dequeue (SgenSectionGrayQueue *queue);
|
||||
void sgen_section_gray_queue_enqueue (SgenSectionGrayQueue *queue, GrayQueueSection *section);
|
||||
|
||||
gboolean sgen_gray_object_fill_prefetch (SgenGrayQueue *queue);
|
||||
|
||||
static inline gboolean
|
||||
sgen_gray_object_queue_is_empty (SgenGrayQueue *queue)
|
||||
{
|
||||
return queue->first == NULL;
|
||||
}
|
||||
|
||||
static inline MONO_ALWAYS_INLINE void
|
||||
GRAY_OBJECT_ENQUEUE (SgenGrayQueue *queue, GCObject *obj, SgenDescriptor desc)
|
||||
{
|
||||
#if SGEN_MAX_DEBUG_LEVEL >= 9
|
||||
sgen_gray_object_enqueue (queue, obj, desc);
|
||||
#else
|
||||
if (G_UNLIKELY (!queue->first || queue->cursor == GRAY_LAST_CURSOR_POSITION (queue->first))) {
|
||||
sgen_gray_object_enqueue (queue, obj, desc);
|
||||
} else {
|
||||
GrayQueueEntry entry = SGEN_GRAY_QUEUE_ENTRY (obj, desc);
|
||||
|
||||
HEAVY_STAT (stat_gray_queue_enqueue_fast_path ++);
|
||||
|
||||
*++queue->cursor = entry;
|
||||
#ifdef SGEN_HEAVY_BINARY_PROTOCOL
|
||||
binary_protocol_gray_enqueue (queue, queue->cursor, obj);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline MONO_ALWAYS_INLINE void
|
||||
GRAY_OBJECT_DEQUEUE (SgenGrayQueue *queue, GCObject** obj, SgenDescriptor *desc)
|
||||
{
|
||||
GrayQueueEntry entry;
|
||||
#if SGEN_MAX_DEBUG_LEVEL >= 9
|
||||
entry = sgen_gray_object_dequeue (queue);
|
||||
*obj = entry.obj;
|
||||
*desc = entry.desc;
|
||||
#else
|
||||
if (!queue->first) {
|
||||
HEAVY_STAT (stat_gray_queue_dequeue_fast_path ++);
|
||||
|
||||
*obj = NULL;
|
||||
} else if (G_UNLIKELY (queue->cursor == GRAY_FIRST_CURSOR_POSITION (queue->first))) {
|
||||
entry = sgen_gray_object_dequeue (queue);
|
||||
*obj = entry.obj;
|
||||
*desc = entry.desc;
|
||||
} else {
|
||||
HEAVY_STAT (stat_gray_queue_dequeue_fast_path ++);
|
||||
|
||||
entry = *queue->cursor--;
|
||||
*obj = entry.obj;
|
||||
*desc = entry.desc;
|
||||
#ifdef SGEN_HEAVY_BINARY_PROTOCOL
|
||||
binary_protocol_gray_dequeue (queue, queue->cursor + 1, *obj);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
251
mono/sgen/sgen-hash-table.c
Normal file
251
mono/sgen/sgen-hash-table.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* sgen-hash-table.c
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_SGEN_GC
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <mono/sgen/sgen-gc.h>
|
||||
#include <mono/sgen/sgen-hash-table.h>
|
||||
|
||||
#ifdef HEAVY_STATISTICS
|
||||
static guint64 stat_lookups;
|
||||
static guint64 stat_lookup_iterations;
|
||||
static guint64 stat_lookup_max_iterations;
|
||||
#endif
|
||||
|
||||
static void
|
||||
rehash (SgenHashTable *hash_table)
|
||||
{
|
||||
SgenHashTableEntry **old_hash = hash_table->table;
|
||||
guint old_hash_size = hash_table->size;
|
||||
guint i, hash, new_size;
|
||||
SgenHashTableEntry **new_hash;
|
||||
SgenHashTableEntry *entry, *next;
|
||||
|
||||
if (!old_hash) {
|
||||
sgen_register_fixed_internal_mem_type (hash_table->entry_mem_type,
|
||||
sizeof (SgenHashTableEntry*) + sizeof (gpointer) + hash_table->data_size);
|
||||
new_size = 13;
|
||||
} else {
|
||||
new_size = g_spaced_primes_closest (hash_table->num_entries);
|
||||
}
|
||||
|
||||
new_hash = sgen_alloc_internal_dynamic (new_size * sizeof (SgenHashTableEntry*), hash_table->table_mem_type, TRUE);
|
||||
for (i = 0; i < old_hash_size; ++i) {
|
||||
for (entry = old_hash [i]; entry; entry = next) {
|
||||
hash = hash_table->hash_func (entry->key) % new_size;
|
||||
next = entry->next;
|
||||
entry->next = new_hash [hash];
|
||||
new_hash [hash] = entry;
|
||||
}
|
||||
}
|
||||
sgen_free_internal_dynamic (old_hash, old_hash_size * sizeof (SgenHashTableEntry*), hash_table->table_mem_type);
|
||||
hash_table->table = new_hash;
|
||||
hash_table->size = new_size;
|
||||
}
|
||||
|
||||
static void
|
||||
rehash_if_necessary (SgenHashTable *hash_table)
|
||||
{
|
||||
if (hash_table->num_entries >= hash_table->size * 2)
|
||||
rehash (hash_table);
|
||||
|
||||
SGEN_ASSERT (1, hash_table->size, "rehash guarantees size > 0");
|
||||
}
|
||||
|
||||
static SgenHashTableEntry*
|
||||
lookup (SgenHashTable *hash_table, gpointer key, guint *_hash)
|
||||
{
|
||||
SgenHashTableEntry *entry;
|
||||
guint hash;
|
||||
GEqualFunc equal = hash_table->equal_func;
|
||||
#ifdef HEAVY_STATISTICS
|
||||
guint64 iterations = 0;
|
||||
++stat_lookups;
|
||||
#endif
|
||||
|
||||
if (!hash_table->size)
|
||||
return NULL;
|
||||
|
||||
hash = hash_table->hash_func (key) % hash_table->size;
|
||||
if (_hash)
|
||||
*_hash = hash;
|
||||
|
||||
for (entry = hash_table->table [hash]; entry; entry = entry->next) {
|
||||
#ifdef HEAVY_STATISTICS
|
||||
++stat_lookup_iterations;
|
||||
++iterations;
|
||||
if (iterations > stat_lookup_max_iterations)
|
||||
stat_lookup_max_iterations = iterations;
|
||||
#endif
|
||||
if ((equal && equal (entry->key, key)) || (!equal && entry->key == key))
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpointer
|
||||
sgen_hash_table_lookup (SgenHashTable *hash_table, gpointer key)
|
||||
{
|
||||
SgenHashTableEntry *entry = lookup (hash_table, key, NULL);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
return entry->data;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sgen_hash_table_replace (SgenHashTable *hash_table, gpointer key, gpointer new_value, gpointer old_value)
|
||||
{
|
||||
guint hash;
|
||||
SgenHashTableEntry *entry;
|
||||
|
||||
rehash_if_necessary (hash_table);
|
||||
entry = lookup (hash_table, key, &hash);
|
||||
|
||||
if (entry) {
|
||||
if (old_value)
|
||||
memcpy (old_value, entry->data, hash_table->data_size);
|
||||
memcpy (entry->data, new_value, hash_table->data_size);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
entry = sgen_alloc_internal (hash_table->entry_mem_type);
|
||||
entry->key = key;
|
||||
memcpy (entry->data, new_value, hash_table->data_size);
|
||||
|
||||
entry->next = hash_table->table [hash];
|
||||
hash_table->table [hash] = entry;
|
||||
|
||||
hash_table->num_entries++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sgen_hash_table_set_value (SgenHashTable *hash_table, gpointer key, gpointer new_value, gpointer old_value)
|
||||
{
|
||||
guint hash;
|
||||
SgenHashTableEntry *entry;
|
||||
|
||||
entry = lookup (hash_table, key, &hash);
|
||||
|
||||
if (entry) {
|
||||
if (old_value)
|
||||
memcpy (old_value, entry->data, hash_table->data_size);
|
||||
memcpy (entry->data, new_value, hash_table->data_size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sgen_hash_table_set_key (SgenHashTable *hash_table, gpointer old_key, gpointer new_key)
|
||||
{
|
||||
guint hash;
|
||||
SgenHashTableEntry *entry;
|
||||
|
||||
entry = lookup (hash_table, old_key, &hash);
|
||||
|
||||
if (entry) {
|
||||
entry->key = new_key;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sgen_hash_table_remove (SgenHashTable *hash_table, gpointer key, gpointer data_return)
|
||||
{
|
||||
SgenHashTableEntry *entry, *prev;
|
||||
guint hash;
|
||||
GEqualFunc equal = hash_table->equal_func;
|
||||
|
||||
rehash_if_necessary (hash_table);
|
||||
hash = hash_table->hash_func (key) % hash_table->size;
|
||||
|
||||
prev = NULL;
|
||||
for (entry = hash_table->table [hash]; entry; entry = entry->next) {
|
||||
if ((equal && equal (entry->key, key)) || (!equal && entry->key == key)) {
|
||||
if (prev)
|
||||
prev->next = entry->next;
|
||||
else
|
||||
hash_table->table [hash] = entry->next;
|
||||
|
||||
hash_table->num_entries--;
|
||||
|
||||
if (data_return)
|
||||
memcpy (data_return, entry->data, hash_table->data_size);
|
||||
|
||||
sgen_free_internal (entry, hash_table->entry_mem_type);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
prev = entry;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
sgen_hash_table_clean (SgenHashTable *hash_table)
|
||||
{
|
||||
guint i;
|
||||
|
||||
if (!hash_table->size) {
|
||||
SGEN_ASSERT (1, !hash_table->table, "clean should reset hash_table->table");
|
||||
SGEN_ASSERT (1, !hash_table->num_entries, "clean should reset hash_table->num_entries");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < hash_table->size; ++i) {
|
||||
SgenHashTableEntry *entry = hash_table->table [i];
|
||||
while (entry) {
|
||||
SgenHashTableEntry *next = entry->next;
|
||||
sgen_free_internal (entry, hash_table->entry_mem_type);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
||||
sgen_free_internal_dynamic (hash_table->table, hash_table->size * sizeof (SgenHashTableEntry*), hash_table->table_mem_type);
|
||||
|
||||
hash_table->table = NULL;
|
||||
hash_table->size = 0;
|
||||
hash_table->num_entries = 0;
|
||||
}
|
||||
|
||||
void
|
||||
sgen_init_hash_table (void)
|
||||
{
|
||||
#ifdef HEAVY_STATISTICS
|
||||
mono_counters_register ("Hash table lookups", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_lookups);
|
||||
mono_counters_register ("Hash table lookup iterations", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_lookup_iterations);
|
||||
mono_counters_register ("Hash table lookup max iterations", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_lookup_max_iterations);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
77
mono/sgen/sgen-hash-table.h
Normal file
77
mono/sgen/sgen-hash-table.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef __MONO_SGENHASHTABLE_H__
|
||||
#define __MONO_SGENHASHTABLE_H__
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_SGEN_GC
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/* hash tables */
|
||||
|
||||
typedef struct _SgenHashTableEntry SgenHashTableEntry;
|
||||
struct _SgenHashTableEntry {
|
||||
SgenHashTableEntry *next;
|
||||
gpointer key;
|
||||
char data [MONO_ZERO_LEN_ARRAY]; /* data is pointer-aligned */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int table_mem_type;
|
||||
int entry_mem_type;
|
||||
size_t data_size;
|
||||
GHashFunc hash_func;
|
||||
GEqualFunc equal_func;
|
||||
SgenHashTableEntry **table;
|
||||
guint size;
|
||||
guint num_entries;
|
||||
} SgenHashTable;
|
||||
|
||||
#define SGEN_HASH_TABLE_INIT(table_type,entry_type,data_size,hash_func,equal_func) { (table_type), (entry_type), (data_size), (hash_func), (equal_func), NULL, 0, 0 }
|
||||
#define SGEN_HASH_TABLE_ENTRY_SIZE(data_size) ((data_size) + sizeof (SgenHashTableEntry*) + sizeof (gpointer))
|
||||
|
||||
gpointer sgen_hash_table_lookup (SgenHashTable *table, gpointer key);
|
||||
gboolean sgen_hash_table_replace (SgenHashTable *table, gpointer key, gpointer new_value, gpointer old_value);
|
||||
gboolean sgen_hash_table_set_value (SgenHashTable *table, gpointer key, gpointer new_value, gpointer old_value);
|
||||
gboolean sgen_hash_table_set_key (SgenHashTable *hash_table, gpointer old_key, gpointer new_key);
|
||||
gboolean sgen_hash_table_remove (SgenHashTable *table, gpointer key, gpointer data_return);
|
||||
|
||||
void sgen_hash_table_clean (SgenHashTable *table);
|
||||
|
||||
void sgen_init_hash_table (void);
|
||||
|
||||
#define sgen_hash_table_num_entries(h) ((h)->num_entries)
|
||||
|
||||
#define sgen_hash_table_key_for_value_pointer(v) (((SgenHashTableEntry*)((char*)(v) - G_STRUCT_OFFSET (SgenHashTableEntry, data)))->key)
|
||||
|
||||
#define SGEN_HASH_TABLE_FOREACH(h,k,v) do { \
|
||||
SgenHashTable *__hash_table = (h); \
|
||||
SgenHashTableEntry **__table = __hash_table->table; \
|
||||
guint __i; \
|
||||
for (__i = 0; __i < (h)->size; ++__i) { \
|
||||
SgenHashTableEntry **__iter, **__next; \
|
||||
for (__iter = &__table [__i]; *__iter; __iter = __next) { \
|
||||
SgenHashTableEntry *__entry = *__iter; \
|
||||
__next = &__entry->next; \
|
||||
(k) = __entry->key; \
|
||||
(v) = (gpointer)__entry->data;
|
||||
|
||||
/* The loop must be continue'd after using this! */
|
||||
#define SGEN_HASH_TABLE_FOREACH_REMOVE(free) do { \
|
||||
*__iter = *__next; \
|
||||
__next = __iter; \
|
||||
--__hash_table->num_entries; \
|
||||
if ((free)) \
|
||||
sgen_free_internal (__entry, __hash_table->entry_mem_type); \
|
||||
} while (0)
|
||||
|
||||
#define SGEN_HASH_TABLE_FOREACH_SET_KEY(k) ((__entry)->key = (k))
|
||||
|
||||
#define SGEN_HASH_TABLE_FOREACH_END \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user