#include "config.h"
#ifdef HAVE_SGEN_GC
-#include "metadata/sgen-gc.h"
-#include "metadata/sgen-protocol.h"
+#include "sgen/sgen-gc.h"
+#include "sgen/sgen-protocol.h"
#include "metadata/monitor.h"
-#include "metadata/sgen-layout-stats.h"
-#include "metadata/sgen-client.h"
-#include "metadata/sgen-cardtable.h"
+#include "sgen/sgen-layout-stats.h"
+#include "sgen/sgen-client.h"
+#include "sgen/sgen-cardtable.h"
+#include "sgen/sgen-pinning.h"
#include "metadata/marshal.h"
#include "metadata/method-builder.h"
#include "metadata/abi-details.h"
#include "metadata/mono-gc.h"
#include "metadata/runtime.h"
+#include "metadata/sgen-bridge-internal.h"
+#include "metadata/gc-internal.h"
#include "utils/mono-memory-model.h"
+#include "utils/mono-logger-internal.h"
+#ifdef HEAVY_STATISTICS
+static guint64 stat_wbarrier_set_arrayref = 0;
+static guint64 stat_wbarrier_value_copy = 0;
+static guint64 stat_wbarrier_object_copy = 0;
+
+static guint64 los_marked_cards;
+static guint64 los_array_cards;
+static guint64 los_array_remsets;
+#endif
+
+/* If set, mark stacks conservatively, even if precise marking is possible */
+static gboolean conservative_stack_mark = FALSE;
/* If set, check that there are no references to the domain left at domain unload */
gboolean sgen_mono_xdomain_checks = FALSE;
+/* Functions supplied by the runtime to be called by the GC */
+static MonoGCCallbacks gc_callbacks;
+
+#ifdef HAVE_KW_THREAD
+__thread SgenThreadInfo *sgen_thread_info;
+#else
+MonoNativeTlsKey thread_info_key;
+#endif
+
#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
#define OPDEF(a,b,c,d,e,f,g,h,i,j) \
gpointer stack_start = &stack_start;
SgenThreadInfo *info = mono_thread_info_current ();
- if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
+ if (ptr >= stack_start && ptr < (gpointer)info->client_info.stack_end)
return TRUE;
return FALSE;
}
scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
{
#define SCAN_OBJECT_NOVTABLE
-#include "sgen-scan-object.h"
+#include "sgen/sgen-scan-object.h"
}
#endif
HEAVY_STAT (++stat_wbarrier_object_copy);
- if (sgen_ptr_in_nursery (obj) || ptr_on_stack (obj)) {
+ if (sgen_ptr_in_nursery (obj) || ptr_on_stack (obj) || !SGEN_OBJECT_HAS_REFERENCES (src)) {
size = mono_object_class (obj)->instance_size;
mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
size - sizeof (MonoObject));
sgen_get_remset ()->wbarrier_set_field ((GCObject*)arr, slot_ptr, value);
}
+void
+mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
+{
+ mono_gc_wbarrier_set_arrayref ((MonoArray*)obj, field_ptr, value);
+}
+
+void
+mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
+{
+ sgen_wbarrier_value_copy_bitmap (_dest, _src, size, bitmap);
+}
+
static MonoMethod *write_barrier_conc_method;
static MonoMethod *write_barrier_noconc_method;
MonoMethod **write_barrier_method_addr;
#ifdef MANAGED_WBARRIER
int i, nursery_check_labels [2];
-
-#ifdef HAVE_KW_THREAD
- int stack_end_offset = -1;
-
- MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
- g_assert (stack_end_offset != -1);
-#endif
#endif
// FIXME: Maybe create a separate version for ctors (the branch would be
/* Vtable of the objects used to fill out nursery fragments before a collection */
static GCVTable *array_fill_vtable;
-GCVTable*
-sgen_client_get_array_fill_vtable (void)
+static GCVTable*
+get_array_fill_vtable (void)
{
if (!array_fill_vtable) {
static MonoClass klass;
}
o = (MonoArray*)start;
- o->obj.vtable = (MonoVTable*)sgen_client_get_array_fill_vtable ();
+ o->obj.vtable = (MonoVTable*)get_array_fill_vtable ();
/* Mark this as not a real object */
o->obj.synchronisation = GINT_TO_POINTER (-1);
o->bounds = NULL;
fin_callbacks = *callbacks;
}
+void
+sgen_client_run_finalize (MonoObject *obj)
+{
+ mono_gc_run_finalize (obj, NULL);
+}
+
+int
+mono_gc_invoke_finalizers (void)
+{
+ return sgen_gc_invoke_finalizers ();
+}
+
+gboolean
+mono_gc_pending_finalizers (void)
+{
+ return sgen_have_pending_finalizers ();
+}
+
+void
+sgen_client_finalize_notify (void)
+{
+ mono_gc_finalize_notify ();
+}
+
+void
+mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
+{
+ sgen_object_register_for_finalization (obj, user_data);
+}
+
+static gboolean
+object_in_domain_predicate (MonoObject *obj, void *user_data)
+{
+ MonoDomain *domain = user_data;
+ if (mono_object_domain (obj) == domain) {
+ SGEN_LOG (5, "Unregistering finalizer for object: %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * mono_gc_finalizers_for_domain:
+ * @domain: the unloading appdomain
+ * @out_array: output array
+ * @out_size: size of output array
+ *
+ * Store inside @out_array up to @out_size objects that belong to the unloading
+ * appdomain @domain. Returns the number of stored items. Can be called repeteadly
+ * until it returns 0.
+ * The items are removed from the finalizer data structure, so the caller is supposed
+ * to finalize them.
+ * @out_array should be on the stack to allow the GC to know the objects are still alive.
+ */
+int
+mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
+{
+ return sgen_gather_finalizers_if (object_in_domain_predicate, domain, out_array, out_size);
+}
+
/*
* Ephemerons
*/
if (!key || key == tombstone)
continue;
- SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
+ SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
if (!key || key == tombstone)
continue;
- SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
+ SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
/* Could be called from sgen_thread_unregister () with a NULL info */
if (domain) {
g_assert (info);
- info->stopped_domain = domain;
+ info->client_info.stopped_domain = domain;
}
}
null_ephemerons_for_domain (domain);
for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
- sgen_null_links_for_domain (domain, i);
+ sgen_null_links_if (object_in_domain_predicate, domain, i);
for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
- sgen_remove_finalizers_for_domain (domain, i);
+ sgen_remove_finalizers_if (object_in_domain_predicate, domain, i);
sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
(IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
UNLOCK_GC;
}
+/*
+ * Allocation
+ */
+
+static gboolean alloc_events = FALSE;
+
+void
+mono_gc_enable_alloc_events (void)
+{
+ alloc_events = TRUE;
+}
+
+void*
+mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
+{
+ MonoObject *obj = sgen_alloc_obj (vtable, size);
+
+ if (G_UNLIKELY (alloc_events))
+ mono_profiler_allocation (obj);
+
+ return obj;
+}
+
+void*
+mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
+{
+ MonoObject *obj = sgen_alloc_obj_pinned (vtable, size);
+
+ if (G_UNLIKELY (alloc_events))
+ mono_profiler_allocation (obj);
+
+ return obj;
+}
+
+void*
+mono_gc_alloc_mature (MonoVTable *vtable)
+{
+ MonoObject *obj = sgen_alloc_obj_mature (vtable, vtable->klass->instance_size);
+
+ if (obj && G_UNLIKELY (obj->vtable->klass->has_finalize))
+ mono_object_register_finalizer (obj);
+
+ if (G_UNLIKELY (alloc_events))
+ mono_profiler_allocation (obj);
+
+ return obj;
+}
+
+void*
+mono_gc_alloc_fixed (size_t size, void *descr)
+{
+ /* FIXME: do a single allocation */
+ void *res = calloc (1, size);
+ if (!res)
+ return NULL;
+ if (!mono_gc_register_root (res, size, descr)) {
+ free (res);
+ res = NULL;
+ }
+ return res;
+}
+
+void
+mono_gc_free_fixed (void* addr)
+{
+ mono_gc_deregister_root (addr);
+ free (addr);
+}
+
/*
* Managed allocator
*/
static MonoMethod* alloc_method_cache [ATYPE_NUM];
+static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
static gboolean use_managed_allocator = TRUE;
#ifdef MANAGED_ALLOCATION
* that they are executed atomically via the restart mechanism.
*/
static MonoMethod*
-create_allocator (int atype)
+create_allocator (int atype, gboolean slowpath)
{
int p_var, size_var;
guint32 slowpath_branch, max_size_branch;
MonoMethodSignature *csig;
static gboolean registered = FALSE;
int tlab_next_addr_var, new_next_var;
- int num_params, i;
const char *name = NULL;
AllocatorWrapperInfo *info;
+ int num_params, i;
if (!registered) {
mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
}
if (atype == ATYPE_SMALL) {
- num_params = 2;
- name = "AllocSmall";
+ name = slowpath ? "SlowAllocSmall" : "AllocSmall";
} else if (atype == ATYPE_NORMAL) {
- num_params = 1;
- name = "Alloc";
+ name = slowpath ? "SlowAlloc" : "Alloc";
} else if (atype == ATYPE_VECTOR) {
- num_params = 2;
- name = "AllocVector";
+ name = slowpath ? "SlowAllocVector" : "AllocVector";
} else if (atype == ATYPE_STRING) {
- num_params = 2;
- name = "AllocString";
+ name = slowpath ? "SlowAllocString" : "AllocString";
} else {
g_assert_not_reached ();
}
+ if (atype == ATYPE_NORMAL)
+ num_params = 1;
+ else
+ num_params = 2;
+
csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
if (atype == ATYPE_STRING) {
csig->ret = &mono_defaults.string_class->byval_arg;
csig->params [1] = &mono_defaults.int32_class->byval_arg;
} else {
csig->ret = &mono_defaults.object_class->byval_arg;
- for (i = 0; i < num_params; ++i)
+ for (i = 0; i < num_params; i++)
csig->params [i] = &mono_defaults.int_class->byval_arg;
}
mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
#ifndef DISABLE_JIT
+ if (slowpath) {
+ switch (atype) {
+ case ATYPE_NORMAL:
+ case ATYPE_SMALL:
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_icall (mb, mono_object_new_specific);
+ break;
+ case ATYPE_VECTOR:
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_icall (mb, mono_array_new_specific);
+ break;
+ case ATYPE_STRING:
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_icall (mb, mono_string_alloc);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ goto done;
+ }
+
size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
if (atype == ATYPE_SMALL) {
/* size_var = size_arg */
/* return p */
mono_mb_emit_ldloc (mb, p_var);
+
+ done:
mono_mb_emit_byte (mb, CEE_RET);
#endif
return NULL;
if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE)
return NULL;
- if (klass->has_finalize || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
+ if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
return NULL;
if (klass->rank)
return NULL;
if (klass->byval_arg.type == MONO_TYPE_STRING)
- return mono_gc_get_managed_allocator_by_type (ATYPE_STRING);
+ return mono_gc_get_managed_allocator_by_type (ATYPE_STRING, FALSE);
/* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */
if (known_instance_size)
- return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
+ return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL, FALSE);
else
- return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
+ return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL, FALSE);
#else
return NULL;
#endif
return NULL;
g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass));
- return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
+ return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR, FALSE);
#else
return NULL;
#endif
}
MonoMethod*
-mono_gc_get_managed_allocator_by_type (int atype)
+mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
{
#ifdef MANAGED_ALLOCATION
MonoMethod *res;
+ MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
if (!use_managed_allocator)
return NULL;
if (!mono_runtime_has_tls_get ())
return NULL;
- res = alloc_method_cache [atype];
+ res = cache [atype];
if (res)
return res;
- res = create_allocator (atype);
+ res = create_allocator (atype, slowpath);
LOCK_GC;
- if (alloc_method_cache [atype]) {
+ if (cache [atype]) {
mono_free_method (res);
- res = alloc_method_cache [atype];
+ res = cache [atype];
} else {
mono_memory_barrier ();
- alloc_method_cache [atype] = res;
+ cache [atype] = res;
}
UNLOCK_GC;
int i;
for (i = 0; i < ATYPE_NUM; ++i)
- if (method == alloc_method_cache [i])
+ if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
return TRUE;
return FALSE;
}
int i;
for (i = 0; i < ATYPE_NUM; ++i)
- if (alloc_method_cache [i])
+ if (alloc_method_cache [i] || slowpath_alloc_method_cache [i])
return TRUE;
return FALSE;
}
UNLOCK_GC;
done:
+ if (G_UNLIKELY (alloc_events))
+ mono_profiler_allocation (&arr->obj);
+
SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size ((GCVTable*)vtable, (GCObject*)arr)), "Vector has incorrect size.");
return arr;
}
UNLOCK_GC;
done:
+ if (G_UNLIKELY (alloc_events))
+ mono_profiler_allocation (&arr->obj);
+
SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size ((GCVTable*)vtable, (GCObject*)arr)), "Array has incorrect size.");
return arr;
}
/*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
str->length = len;
EXIT_CRITICAL_REGION;
- return str;
+ goto done;
}
EXIT_CRITICAL_REGION;
#endif
UNLOCK_GC;
+ done:
+ if (G_UNLIKELY (alloc_events))
+ mono_profiler_allocation (&str->object);
+
return str;
}
{
mword desc = sgen_obj_get_descriptor (start);
-#include "sgen-scan-object.h"
+#include "sgen/sgen-scan-object.h"
}
static void
* Threads
*/
+void
+mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
+{
+ gc_callbacks = *callbacks;
+}
+
+MonoGCCallbacks *
+mono_gc_get_gc_callbacks ()
+{
+ return &gc_callbacks;
+}
+
+void
+sgen_client_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
+{
+ size_t stsize = 0;
+ guint8 *staddr = NULL;
+
+#ifndef HAVE_KW_THREAD
+ g_assert (!mono_native_tls_get_value (thread_info_key));
+ mono_native_tls_set_value (thread_info_key, info);
+#else
+ sgen_thread_info = info;
+#endif
+
+ info->client_info.skip = 0;
+ info->client_info.stopped_ip = NULL;
+ info->client_info.stopped_domain = NULL;
+
+ info->client_info.stack_start = NULL;
+
+#ifdef SGEN_POSIX_STW
+ info->client_info.stop_count = -1;
+ info->client_info.signal = 0;
+#endif
+
+ /* On win32, stack_start_limit should be 0, since the stack can grow dynamically */
+ mono_thread_info_get_stack_bounds (&staddr, &stsize);
+ if (staddr) {
+#ifndef HOST_WIN32
+ info->client_info.stack_start_limit = staddr;
+#endif
+ info->client_info.stack_end = staddr + stsize;
+ } else {
+ gsize stack_bottom = (gsize)stack_bottom_fallback;
+ stack_bottom += 4095;
+ stack_bottom &= ~4095;
+ info->client_info.stack_end = (char*)stack_bottom;
+ }
+
+#ifdef USE_MONO_CTX
+ memset (&info->client_info.ctx, 0, sizeof (MonoContext));
+#else
+ memset (&info->client_info.regs, 0, sizeof (info->client_info.regs));
+#endif
+
+ if (mono_gc_get_gc_callbacks ()->thread_attach_func)
+ info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
+
+ binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
+
+ SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->client_info.stack_end);
+}
+
+void
+sgen_client_thread_unregister (SgenThreadInfo *p)
+{
+ MonoNativeThreadId tid;
+
+#ifndef HAVE_KW_THREAD
+ mono_native_tls_set_value (thread_info_key, NULL);
+#else
+ sgen_thread_info = NULL;
+#endif
+
+ tid = mono_thread_info_get_tid (p);
+
+ if (p->client_info.info.runtime_thread)
+ mono_threads_add_joinable_thread ((gpointer)tid);
+
+ if (mono_gc_get_gc_callbacks ()->thread_detach_func) {
+ mono_gc_get_gc_callbacks ()->thread_detach_func (p->client_info.runtime_data);
+ p->client_info.runtime_data = NULL;
+ }
+
+ binary_protocol_thread_unregister ((gpointer)tid);
+ SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
+}
+
+void
+mono_gc_set_skip_thread (gboolean skip)
+{
+ SgenThreadInfo *info = mono_thread_info_current ();
+
+ LOCK_GC;
+ info->client_info.gc_disabled = skip;
+ UNLOCK_GC;
+}
+
static gboolean
is_critical_method (MonoMethod *method)
{
static gboolean
thread_in_critical_region (SgenThreadInfo *info)
{
- return info->in_critical_region;
+ return info->client_info.in_critical_region;
+}
+
+static void
+sgen_thread_attach (SgenThreadInfo *info)
+{
+ if (mono_gc_get_gc_callbacks ()->thread_attach_func && !info->client_info.runtime_data)
+ info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
}
static void
mono_thread_detach_internal (mono_thread_internal_current ());
}
+gboolean
+mono_gc_register_thread (void *baseptr)
+{
+ return mono_thread_info_attach (baseptr) != NULL;
+}
+
+gboolean
+mono_gc_is_gc_thread (void)
+{
+ gboolean result;
+ LOCK_GC;
+ result = mono_thread_info_current () != NULL;
+ UNLOCK_GC;
+ return result;
+}
+
+void
+sgen_client_thread_register_worker (void)
+{
+ mono_thread_info_register_small_id ();
+}
+
+/* Variables holding start/end nursery so it won't have to be passed at every call */
+static void *scan_area_arg_start, *scan_area_arg_end;
+
+void
+mono_gc_conservatively_scan_area (void *start, void *end)
+{
+ sgen_conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
+}
+
+void*
+mono_gc_scan_object (void *obj, void *gc_data)
+{
+ ScanCopyContext *ctx = gc_data;
+ ctx->ops->copy_or_mark_object (&obj, ctx->queue);
+ return obj;
+}
+
+/*
+ * Mark from thread stacks and registers.
+ */
+void
+sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx)
+{
+ SgenThreadInfo *info;
+
+ scan_area_arg_start = start_nursery;
+ scan_area_arg_end = end_nursery;
+
+ FOREACH_THREAD (info) {
+ int skip_reason = 0;
+ if (info->client_info.skip) {
+ SGEN_LOG (3, "Skipping dead thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start);
+ skip_reason = 1;
+ } else if (info->client_info.gc_disabled) {
+ SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start);
+ skip_reason = 2;
+ } else if (!mono_thread_info_is_live (info)) {
+ SGEN_LOG (3, "Skipping non-running thread %p, range: %p-%p, size: %zd (state %x)", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, info->client_info.info.thread_state);
+ skip_reason = 3;
+ }
+
+ binary_protocol_scan_stack ((gpointer)mono_thread_info_get_tid (info), info->client_info.stack_start, info->client_info.stack_end, skip_reason);
+
+ if (skip_reason)
+ continue;
+
+ g_assert (info->client_info.suspend_done);
+ SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, sgen_get_pinned_count ());
+ if (mono_gc_get_gc_callbacks ()->thread_mark_func && !conservative_stack_mark) {
+ mono_gc_get_gc_callbacks ()->thread_mark_func (info->client_info.runtime_data, info->client_info.stack_start, info->client_info.stack_end, precise, &ctx);
+ } else if (!precise) {
+ if (!conservative_stack_mark) {
+ fprintf (stderr, "Precise stack mark not supported - disabling.\n");
+ conservative_stack_mark = TRUE;
+ }
+ sgen_conservatively_pin_objects_from (info->client_info.stack_start, info->client_info.stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
+ }
+
+ if (!precise) {
+#ifdef USE_MONO_CTX
+ sgen_conservatively_pin_objects_from ((void**)&info->client_info.ctx, (void**)&info->client_info.ctx + ARCH_NUM_REGS,
+ start_nursery, end_nursery, PIN_TYPE_STACK);
+#else
+ sgen_conservatively_pin_objects_from ((void**)&info->client_info.regs, (void**)&info->client_info.regs + ARCH_NUM_REGS,
+ start_nursery, end_nursery, PIN_TYPE_STACK);
+#endif
+ }
+ } END_FOREACH_THREAD
+}
+
+/*
+ * mono_gc_set_stack_end:
+ *
+ * Set the end of the current threads stack to STACK_END. The stack space between
+ * STACK_END and the real end of the threads stack will not be scanned during collections.
+ */
+void
+mono_gc_set_stack_end (void *stack_end)
+{
+ SgenThreadInfo *info;
+
+ LOCK_GC;
+ info = mono_thread_info_current ();
+ if (info) {
+ SGEN_ASSERT (0, stack_end < info->client_info.stack_end, "Can only lower stack end");
+ info->client_info.stack_end = stack_end;
+ }
+ UNLOCK_GC;
+}
+
+/*
+ * Roots
+ */
+
+int
+mono_gc_register_root (char *start, size_t size, void *descr)
+{
+ return sgen_register_root (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
+}
+
+int
+mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
+{
+ return sgen_register_root (start, size, descr, ROOT_TYPE_WBARRIER);
+}
+
+void
+mono_gc_deregister_root (char* addr)
+{
+ sgen_deregister_root (addr);
+}
+
+/*
+ * PThreads
+ */
+
+#ifndef HOST_WIN32
+int
+mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
+{
+ return pthread_create (new_thread, attr, start_routine, arg);
+}
+#endif
+
/*
* Miscellaneous
*/
+void
+sgen_client_total_allocated_heap_changed (size_t allocated_heap)
+{
+ mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
+}
+
gboolean
mono_gc_user_markers_supported (void)
{
return 1;
}
+gboolean
+mono_gc_precise_stack_mark_enabled (void)
+{
+ return !conservative_stack_mark;
+}
+
+void
+mono_gc_collect (int generation)
+{
+ sgen_gc_collect (generation);
+}
+
+int
+mono_gc_collection_count (int generation)
+{
+ return sgen_gc_collection_count (generation);
+}
+
+int64_t
+mono_gc_get_used_size (void)
+{
+ return (int64_t)sgen_gc_get_used_size ();
+}
+
+int64_t
+mono_gc_get_heap_size (void)
+{
+ return (int64_t)sgen_gc_get_total_heap_allocation ();
+}
+
+void*
+mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
+{
+ return sgen_make_user_root_descriptor (marker);
+}
+
+void*
+mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
+{
+ return (void*)SGEN_DESC_STRING;
+}
+
+void*
+mono_gc_get_nursery (int *shift_bits, size_t *size)
+{
+ *size = sgen_nursery_size;
+ *shift_bits = DEFAULT_NURSERY_BITS;
+ return sgen_get_nursery_start ();
+}
+
+int
+mono_gc_get_los_limit (void)
+{
+ return SGEN_MAX_SMALL_OBJ_SIZE;
+}
+
+void
+mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
+{
+ sgen_register_disappearing_link (obj, link_addr, track, FALSE);
+}
+
+void
+mono_gc_weak_link_remove (void **link_addr, gboolean track)
+{
+ sgen_register_disappearing_link (NULL, link_addr, track, FALSE);
+}
+
+MonoObject*
+mono_gc_weak_link_get (void **link_addr)
+{
+ return sgen_weak_link_get (link_addr);
+}
+
+gboolean
+mono_gc_set_allow_synchronous_major (gboolean flag)
+{
+ return sgen_set_allow_synchronous_major (flag);
+}
+
+void*
+mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
+{
+ void *result;
+ LOCK_INTERRUPTION;
+ result = func (data);
+ UNLOCK_INTERRUPTION;
+ return result;
+}
+
+void
+mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
+{
+ // FIXME:
+}
+
+void
+sgen_client_out_of_memory (size_t size)
+{
+ mono_gc_out_of_memory (size);
+}
+
+guint8*
+mono_gc_get_card_table (int *shift_bits, gpointer *mask)
+{
+ return sgen_get_card_table_configuration (shift_bits, mask);
+}
+
+gboolean
+mono_gc_card_table_nursery_check (void)
+{
+ return !sgen_get_major_collector ()->is_concurrent;
+}
+
+/* Negative value to remove */
+void
+mono_gc_add_memory_pressure (gint64 value)
+{
+ /* FIXME: Implement at some point? */
+}
+
+/*
+ * Logging
+ */
+
+void
+sgen_client_degraded_allocation (size_t size)
+{
+ static int last_major_gc_warned = -1;
+ static int num_degraded = 0;
+
+ if (last_major_gc_warned < gc_stats.major_gc_count) {
+ ++num_degraded;
+ if (num_degraded == 1 || num_degraded == 3)
+ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Degraded allocation. Consider increasing nursery-size if the warning persists.");
+ else if (num_degraded == 10)
+ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Repeated degraded allocation. Consider increasing nursery-size.");
+ last_major_gc_warned = gc_stats.major_gc_count;
+ }
+}
+
+void
+sgen_client_log_timing (GGTimingInfo *info, mword last_major_num_sections, mword last_los_memory_usage)
+{
+ SgenMajorCollector *major_collector = sgen_get_major_collector ();
+ mword num_major_sections = major_collector->get_num_major_sections ();
+ char full_timing_buff [1024];
+ full_timing_buff [0] = '\0';
+
+ if (!info->is_overflow)
+ sprintf (full_timing_buff, "total %.2fms, bridge %.2fms", info->stw_time / 10000.0f, (int)info->bridge_time / 10000.0f);
+ if (info->generation == GENERATION_OLD)
+ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR%s: (%s) pause %.2fms, %s major %dK/%dK los %dK/%dK",
+ info->is_overflow ? "_OVERFLOW" : "",
+ info->reason ? info->reason : "",
+ (int)info->total_time / 10000.0f,
+ full_timing_buff,
+ major_collector->section_size * num_major_sections / 1024,
+ major_collector->section_size * last_major_num_sections / 1024,
+ los_memory_usage / 1024,
+ last_los_memory_usage / 1024);
+ else
+ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR%s: (%s) pause %.2fms, %s promoted %dK major %dK los %dK",
+ info->is_overflow ? "_OVERFLOW" : "",
+ info->reason ? info->reason : "",
+ (int)info->total_time / 10000.0f,
+ full_timing_buff,
+ (num_major_sections - last_major_num_sections) * major_collector->section_size / 1024,
+ major_collector->section_size * num_major_sections / 1024,
+ los_memory_usage / 1024);
+}
+
/*
* Debugging
*/
}
}
-const char*
-sgen_client_object_safe_name (GCObject *obj)
+gboolean
+sgen_client_vtable_is_inited (GCVTable *gc_vtable)
{
- MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
- return vt->klass->name;
+ MonoVTable *vt = (MonoVTable*)gc_vtable;
+ return vt->klass->inited;
}
const char*
void
sgen_client_init (void)
{
+ int dummy;
MonoThreadInfoCallbacks cb;
cb.thread_register = sgen_thread_register;
cb.thread_attach = sgen_thread_attach;
cb.mono_method_is_critical = (gpointer)is_critical_method;
cb.mono_thread_in_critical_region = thread_in_critical_region;
-#ifndef HOST_WIN32
- cb.thread_exit = mono_gc_pthread_exit;
- cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
-#endif
mono_threads_init (&cb, sizeof (SgenThreadInfo));
+ ///* Keep this the default for now */
+ /* Precise marking is broken on all supported targets. Disable until fixed. */
+ conservative_stack_mark = TRUE;
+
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
mono_sgen_init_stw ();
+
+#ifndef HAVE_KW_THREAD
+ mono_native_tls_alloc (&thread_info_key, NULL);
+#if defined(__APPLE__) || defined (HOST_WIN32)
+ /*
+ * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
+ * where the two are the same.
+ */
+ mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, thread_info_key);
+#endif
+#else
+ {
+ int tls_offset = -1;
+ MONO_THREAD_VAR_OFFSET (sgen_thread_info, tls_offset);
+ mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, tls_offset);
+ }
+#endif
+
+ /*
+ * This needs to happen before any internal allocations because
+ * it inits the small id which is required for hazard pointer
+ * operations.
+ */
+ sgen_os_init ();
+
+ mono_gc_register_thread (&dummy);
+}
+
+gboolean
+sgen_client_handle_gc_param (const char *opt)
+{
+ if (g_str_has_prefix (opt, "stack-mark=")) {
+ opt = strchr (opt, '=') + 1;
+ if (!strcmp (opt, "precise")) {
+ conservative_stack_mark = FALSE;
+ } else if (!strcmp (opt, "conservative")) {
+ conservative_stack_mark = TRUE;
+ } else {
+ sgen_env_var_error (MONO_GC_PARAMS_NAME, conservative_stack_mark ? "Using `conservative`." : "Using `precise`.",
+ "Invalid value `%s` for `stack-mark` option, possible values are: `precise`, `conservative`.", opt);
+ }
+ } else if (g_str_has_prefix (opt, "bridge-implementation=")) {
+ opt = strchr (opt, '=') + 1;
+ sgen_set_bridge_implementation (opt);
+ } else if (g_str_has_prefix (opt, "toggleref-test")) {
+ /* FIXME: This should probably in MONO_GC_DEBUG */
+ sgen_register_test_toggleref_callback ();
+ } else {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void
+sgen_client_print_gc_params_usage (void)
+{
+ fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
}
gboolean
{
if (!strcmp (opt, "xdomain-checks")) {
sgen_mono_xdomain_checks = TRUE;
- } else {
+ } else if (!strcmp (opt, "do-not-finalize")) {
+ do_not_finalize = TRUE;
+ } else if (!strcmp (opt, "log-finalizers")) {
+ log_finalizers = TRUE;
+ } else if (!strcmp (opt, "no-managed-allocator")) {
+ sgen_set_use_managed_allocator (FALSE);
+ } else if (!sgen_bridge_handle_gc_debug (opt)) {
return FALSE;
}
return TRUE;
sgen_client_print_gc_debug_usage (void)
{
fprintf (stderr, " xdomain-checks\n");
+ fprintf (stderr, " do-not-finalize\n");
+ fprintf (stderr, " log-finalizers\n");
+ fprintf (stderr, " no-managed-allocator\n");
+ sgen_bridge_print_gc_debug_usage ();
+}
+
+
+gpointer
+sgen_client_get_provenance (void)
+{
+#ifdef SGEN_OBJECT_PROVENANCE
+ MonoGCCallbacks *cb = mono_gc_get_gc_callbacks ();
+ gpointer (*get_provenance_func) (void);
+ if (!cb)
+ return NULL;
+ get_provenance_func = cb->get_provenance_func;
+ if (get_provenance_func)
+ return get_provenance_func ();
+ return NULL;
+#else
+ return NULL;
+#endif
+}
+
+void
+sgen_client_describe_invalid_pointer (GCObject *ptr)
+{
+ sgen_bridge_describe_pointer (ptr);
+}
+
+void
+mono_gc_base_init (void)
+{
+ mono_counters_init ();
+
+#ifdef HEAVY_STATISTICS
+ mono_counters_register ("los marked cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_marked_cards);
+ mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_cards);
+ mono_counters_register ("los array remsets", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_remsets);
+
+ mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_arrayref);
+ mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_value_copy);
+ mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_object_copy);
+#endif
+
+ sgen_gc_init ();
+
+ if (nursery_canaries_enabled ())
+ sgen_set_use_managed_allocator (FALSE);
+}
+
+void
+mono_gc_base_cleanup (void)
+{
+}
+
+gboolean
+mono_gc_is_null (void)
+{
+ return FALSE;
}
#endif