static int stat_wbarrier_set_arrayref = 0;
static int stat_wbarrier_arrayref_copy = 0;
static int stat_wbarrier_generic_store = 0;
+static int stat_wbarrier_generic_store_atomic = 0;
static int stat_wbarrier_set_root = 0;
static int stat_wbarrier_value_copy = 0;
static int stat_wbarrier_object_copy = 0;
* ######################################################################
*/
LOCK_DECLARE (gc_mutex);
+gboolean sgen_try_free_some_memory;
#define SCAN_START_SIZE SGEN_SCAN_START_SIZE
return TRUE;
#ifndef DISABLE_REMOTING
- if (mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
+ if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
return TRUE;
#endif
/* The object could be a proxy for an object in the domain
we're deleting. */
#ifndef DISABLE_REMOTING
- if (mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
+ if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
/* The server could already have been zeroed out, so
We must reset the gathered bridges since their original block might be evacuated due to major
fragmentation in the meanwhile and the bridge code should not have to deal with that.
*/
- sgen_bridge_reset_data ();
+ if (sgen_need_bridge_processing ())
+ sgen_bridge_reset_data ();
/*
* Walk the ephemeron tables marking all values with reachable keys. This must be completely done
++ephemeron_rounds;
} while (!done_with_ephemerons);
- sgen_scan_togglerefs (start_addr, end_addr, ctx);
+ sgen_mark_togglerefs (start_addr, end_addr, ctx);
if (sgen_need_bridge_processing ()) {
+ /*Make sure the gray stack is empty before we process bridge objects so we get liveness right*/
+ sgen_drain_gray_stack (-1, ctx);
sgen_collect_bridge_objects (generation, ctx);
if (generation == GENERATION_OLD)
sgen_collect_bridge_objects (GENERATION_NURSERY, ctx);
+
+ /*
+ Do the first bridge step here, as the collector liveness state will become useless after that.
+
+ An important optimization is to only proccess the possibly dead part of the object graph and skip
+ over all live objects as we transitively know everything they point must be alive too.
+
+ The above invariant is completely wrong if we let the gray queue be drained and mark/copy everything.
+
+ This has the unfortunate side effect of making overflow collections perform the first step twice, but
+ given we now have heuristics that perform major GC in anticipation of minor overflows this should not
+ be a big deal.
+ */
+ sgen_bridge_processing_stw_step ();
}
/*
*/
clear_unreachable_ephemerons (ctx);
+ /*
+ * We clear togglerefs only after all possible chances of revival are done.
+ * This is semantically more inline with what users expect and it allows for
+ * user finalizers to correctly interact with TR objects.
+ */
+ sgen_clear_togglerefs (start_addr, end_addr, ctx);
+
TV_GETTIME (btv);
SGEN_LOG (2, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
+ mono_counters_register ("WBarrier generic atomic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_atomic);
mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
continue;
}
sgen_los_pin_object (bigobj->data);
- /* FIXME: only enqueue if object has references */
- GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
+ if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
+ GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
if (G_UNLIKELY (do_pin_stats))
sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
SGEN_LOG (6, "Marked large object %p (%s) size: %lu from roots", bigobj->data, safe_name (bigobj->data), (unsigned long)sgen_los_object_size (bigobj));
static void
wait_for_workers_to_finish (void)
+{
+ while (!sgen_workers_all_done ())
+ g_usleep (200);
+}
+
+static void
+join_workers (void)
{
if (concurrent_collection_in_progress || major_collector.is_parallel) {
gray_queue_redirect (&gray_queue);
TV_GETTIME (btv);
if (concurrent_collection_in_progress || major_collector.is_parallel)
- wait_for_workers_to_finish ();
+ join_workers ();
if (concurrent_collection_in_progress) {
current_object_ops = major_collector.major_concurrent_ops;
major_copy_or_mark_from_roots (NULL, TRUE, scan_mod_union);
- wait_for_workers_to_finish ();
+ join_workers ();
g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
- major_collector.update_cardtable_mod_union ();
- sgen_los_update_cardtable_mod_union ();
-
if (!force_finish && !sgen_workers_all_done ()) {
+ major_collector.update_cardtable_mod_union ();
+ sgen_los_update_cardtable_mod_union ();
+
MONO_GC_CONCURRENT_UPDATE_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
return FALSE;
}
- if (mod_union_consistency_check)
- sgen_check_mod_union_consistency ();
+ /*
+ * The major collector can add global remsets which are processed in the finishing
+ * nursery collection, below. That implies that the workers must have finished
+ * marking before the nursery collection is allowed to run, otherwise we might miss
+ * some remsets.
+ */
+ wait_for_workers_to_finish ();
+
+ major_collector.update_cardtable_mod_union ();
+ sgen_los_update_cardtable_mod_union ();
collect_nursery (&unpin_queue, TRUE);
+ if (mod_union_consistency_check)
+ sgen_check_mod_union_consistency ();
+
current_collection_generation = GENERATION_OLD;
major_finish_collection ("finishing", -1, TRUE);
SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %td", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start);
continue;
}
-
- if (!info->joined_stw) {
- SGEN_LOG (3, "Skipping thread not seen in STW %p, range: %p-%p, size: %td", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start);
+ if (mono_thread_info_run_state (info) != STATE_RUNNING) {
+ SGEN_LOG (3, "Skipping non-running thread %p, range: %p-%p, size: %td (state %d)", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, mono_thread_info_run_state (info));
continue;
}
-
SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, sgen_get_pinned_count ());
- if (!info->thread_is_dying) {
- if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
- UserCopyOrMarkData data = { NULL, queue };
- set_user_copy_or_mark_data (&data);
- gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
- set_user_copy_or_mark_data (NULL);
- } else if (!precise) {
- if (!conservative_stack_mark) {
- fprintf (stderr, "Precise stack mark not supported - disabling.\n");
- conservative_stack_mark = TRUE;
- }
- conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
+ if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
+ UserCopyOrMarkData data = { NULL, queue };
+ set_user_copy_or_mark_data (&data);
+ gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
+ set_user_copy_or_mark_data (NULL);
+ } else if (!precise) {
+ if (!conservative_stack_mark) {
+ fprintf (stderr, "Precise stack mark not supported - disabling.\n");
+ conservative_stack_mark = TRUE;
}
+ conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
}
- if (!info->thread_is_dying && !precise) {
+ if (!precise) {
#ifdef USE_MONO_CTX
conservatively_pin_objects_from ((void**)&info->ctx, (void**)&info->ctx + ARCH_NUM_REGS,
start_nursery, end_nursery, PIN_TYPE_STACK);
#else
- conservatively_pin_objects_from (&info->regs, &info->regs + ARCH_NUM_REGS,
+ conservatively_pin_objects_from ((void**)&info->regs, (void**)&info->regs + ARCH_NUM_REGS,
start_nursery, end_nursery, PIN_TYPE_STACK);
#endif
}
static void*
sgen_thread_register (SgenThreadInfo* info, void *addr)
{
- LOCK_GC;
+ size_t stsize = 0;
+ guint8 *staddr = NULL;
+
#ifndef HAVE_KW_THREAD
info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
sgen_thread_info = info;
#endif
-#if !defined(__MACH__)
+#ifdef SGEN_POSIX_STW
info->stop_count = -1;
info->signal = 0;
#endif
info->skip = 0;
- info->joined_stw = FALSE;
- info->doing_handshake = FALSE;
- info->thread_is_dying = FALSE;
info->stack_start = NULL;
info->stopped_ip = NULL;
info->stopped_domain = NULL;
binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
- /* try to get it with attributes first */
-#if (defined(HAVE_PTHREAD_GETATTR_NP) || defined(HAVE_PTHREAD_ATTR_GET_NP)) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
- {
- size_t size;
- void *sstart;
- pthread_attr_t attr;
-
-#if defined(HAVE_PTHREAD_GETATTR_NP)
- /* Linux */
- pthread_getattr_np (pthread_self (), &attr);
-#elif defined(HAVE_PTHREAD_ATTR_GET_NP)
- /* BSD */
- pthread_attr_init (&attr);
- pthread_attr_get_np (pthread_self (), &attr);
-#else
-#error Cannot determine which API is needed to retrieve pthread attributes.
+ /* On win32, stack_start_limit should be 0, since the stack can grow dynamically */
+#ifndef HOST_WIN32
+ mono_thread_info_get_stack_bounds (&staddr, &stsize);
#endif
-
- pthread_attr_getstack (&attr, &sstart, &size);
- info->stack_start_limit = sstart;
- info->stack_end = (char*)sstart + size;
- pthread_attr_destroy (&attr);
- }
-#elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
- info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
- info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
-#else
- {
- /* FIXME: we assume the stack grows down */
+ if (staddr) {
+ info->stack_start_limit = staddr;
+ info->stack_end = staddr + stsize;
+ } else {
gsize stack_bottom = (gsize)addr;
stack_bottom += 4095;
stack_bottom &= ~4095;
info->stack_end = (char*)stack_bottom;
}
-#endif
#ifdef HAVE_KW_THREAD
stack_end = info->stack_end;
if (gc_callbacks.thread_attach_func)
info->runtime_data = gc_callbacks.thread_attach_func ();
-
- UNLOCK_GC;
return info;
}
static void
-sgen_thread_unregister (SgenThreadInfo *p)
+sgen_thread_detach (SgenThreadInfo *p)
{
/* If a delegate is passed to native code and invoked on a thread we dont
* know about, the jit will register it with mono_jit_thread_attach, but
*/
if (mono_domain_get ())
mono_thread_detach (mono_thread_current ());
+}
- p->thread_is_dying = TRUE;
-
- /*
- There is a race condition between a thread finishing executing and been removed
- from the GC thread set.
- This happens on posix systems when TLS data is been cleaned-up, libpthread will
- set the thread_info slot to NULL before calling the cleanup function. This
- opens a window in which the thread is registered but has a NULL TLS.
-
- The suspend signal handler needs TLS data to know where to store thread state
- data or otherwise it will simply ignore the thread.
-
- This solution works because the thread doing STW will wait until all threads been
- suspended handshake back, so there is no race between the doing_hankshake test
- and the suspend_thread call.
-
- This is not required on systems that do synchronous STW as those can deal with
- the above race at suspend time.
+static void
+sgen_thread_unregister (SgenThreadInfo *p)
+{
+ MonoNativeThreadId tid;
- FIXME: I believe we could avoid this by using mono_thread_info_lookup when
- mono_thread_info_current returns NULL. Or fix mono_thread_info_lookup to do so.
- */
-#if (defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED) || !defined(HAVE_PTHREAD_KILL)
- LOCK_GC;
-#else
- while (!TRYLOCK_GC) {
- SgenThreadInfo *current = mono_thread_info_current ();
- if (current)
- SGEN_ASSERT (0, current == p, "If there's a current thread info, it must be correct.");
- /*
- * If we have a current thread info, the signal
- * handler will eventually suspend us. If not, we
- * need to do it by hand.
- */
- if (current || !sgen_park_current_thread_if_doing_handshake (p))
- g_usleep (50);
- }
- MONO_GC_LOCKED ();
-#endif
+ tid = mono_thread_info_get_tid (p);
+ binary_protocol_thread_unregister ((gpointer)tid);
+ SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
- binary_protocol_thread_unregister ((gpointer)mono_thread_info_get_tid (p));
- SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)mono_thread_info_get_tid (p));
+ mono_threads_add_joinable_thread ((gpointer)tid);
if (gc_callbacks.thread_detach_func) {
gc_callbacks.thread_detach_func (p->runtime_data);
p->runtime_data = NULL;
}
-
- mono_threads_unregister_current_thread (p);
- UNLOCK_GC;
}
void
mono_gc_pthread_exit (void *retval)
{
- mono_thread_info_dettach ();
+ mono_thread_info_detach ();
pthread_exit (retval);
}
HEAVY_STAT (++stat_wbarrier_arrayref_copy);
/*This check can be done without taking a lock since dest_ptr array is pinned*/
if (ptr_in_nursery (dest_ptr) || count <= 0) {
- mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
+ mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
return;
}
sgen_dummy_use (value);
}
+/* Same as mono_gc_wbarrier_generic_store () but performs the store
+ * as an atomic operation with release semantics.
+ */
+void
+mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
+{
+ HEAVY_STAT (++stat_wbarrier_generic_store_atomic);
+
+ SGEN_LOG (8, "Wbarrier atomic store at %p to %p (%s)", ptr, value, value ? safe_name (value) : "null");
+
+ InterlockedWritePointer (ptr, value);
+
+ if (ptr_in_nursery (value))
+ mono_gc_wbarrier_generic_nostore (ptr);
+
+ sgen_dummy_use (value);
+}
+
void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
{
mword *dest = _dest;
if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
size_t element_size = mono_class_value_size (klass, NULL);
size_t size = count * element_size;
- mono_gc_memmove (dest, src, size);
+ mono_gc_memmove_atomic (dest, src, size);
return;
}
if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
size = mono_object_class (obj)->instance_size;
- mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
+ mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
size - sizeof (MonoObject));
return;
}
mono_gc_base_init (void)
{
MonoThreadInfoCallbacks cb;
- char *env;
+ const char *env;
char **opts, **ptr;
char *major_collector_opt = NULL;
char *minor_collector_opt = NULL;
gc_debug_file = stderr;
cb.thread_register = sgen_thread_register;
+ cb.thread_detach = sgen_thread_detach;
cb.thread_unregister = sgen_thread_unregister;
cb.thread_attach = sgen_thread_attach;
cb.mono_method_is_critical = (gpointer)is_critical_method;
#ifndef HOST_WIN32
+ cb.thread_exit = mono_gc_pthread_exit;
cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
#endif
#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
/*
sgen_register_test_bridge_callbacks (g_strdup (opt));
continue;
}
+ if (g_str_has_prefix (opt, "toggleref-test")) {
+ sgen_register_test_toggleref_callback ();
+ continue;
+ }
+
#ifdef USER_CONFIG
if (g_str_has_prefix (opt, "nursery-size=")) {
long val;
res = mono_mb_create_method (mb, sig, 16);
mono_mb_free (mb);
- mono_loader_lock ();
+ LOCK_GC;
if (write_barrier_method) {
/* Already created */
mono_free_method (res);
mono_memory_barrier ();
write_barrier_method = res;
}
- mono_loader_unlock ();
+ UNLOCK_GC;
return write_barrier_method;
}
void
sgen_gc_unlock (void)
{
- UNLOCK_GC;
+ gboolean try_free = sgen_try_free_some_memory;
+ sgen_try_free_some_memory = FALSE;
+ mono_mutex_unlock (&gc_mutex);
+ MONO_GC_UNLOCKED ();
+ if (try_free)
+ mono_thread_hazardous_try_free_some ();
}
void
guint
mono_gc_get_vtable_bits (MonoClass *class)
{
- if (sgen_need_bridge_processing () && sgen_is_bridge_class (class))
+ /* FIXME move this to the bridge code */
+ if (!sgen_need_bridge_processing ())
+ return 0;
+ switch (sgen_bridge_class_kind (class)) {
+ case GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS:
+ case GC_BRIDGE_OPAQUE_BRIDGE_CLASS:
return SGEN_GC_BIT_BRIDGE_OBJECT;
+ }
return 0;
}