Imported Upstream version 4.2.0.179

Former-commit-id: 0a113cb3a6feb7873f632839b1307cc6033cd595
This commit is contained in:
Xamarin Public Jenkins
2015-08-26 07:17:56 -04:00
committed by Jo Shields
parent 183bba2c9a
commit 6992685b86
7507 changed files with 90259 additions and 657307 deletions

76
mono/sgen/Makefile.am Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

209
mono/sgen/sgen-archdep.h Normal file
View 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

File diff suppressed because it is too large Load Diff

144
mono/sgen/sgen-cardtable.h Normal file
View 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
View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

377
mono/sgen/sgen-descriptor.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
a24badfaf7c185d8a870932a9ed92ad26f23face

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
View 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
View 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
View 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

View 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