X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fsgen-gc.c;h=affe3b06729e1ffef9ebd0dc3813694b64d4708a;hb=17d1c098bf3041da16bee8cf93069efc9e88ef14;hp=26ed63c2cc698c5f72f5fa7355c61f1e63388be8;hpb=d4446a314502ffef044464347495f38b9342cc75;p=mono.git diff --git a/mono/metadata/sgen-gc.c b/mono/metadata/sgen-gc.c index 26ed63c2cc6..affe3b06729 100644 --- a/mono/metadata/sgen-gc.c +++ b/mono/metadata/sgen-gc.c @@ -179,6 +179,9 @@ #ifdef HAVE_PTHREAD_H #include #endif +#ifdef HAVE_PTHREAD_NP_H +#include +#endif #ifdef HAVE_SEMAPHORE_H #include #endif @@ -195,7 +198,6 @@ #include "metadata/object-internals.h" #include "metadata/threads.h" #include "metadata/sgen-cardtable.h" -#include "metadata/sgen-ssb.h" #include "metadata/sgen-protocol.h" #include "metadata/sgen-archdep.h" #include "metadata/sgen-bridge.h" @@ -212,6 +214,7 @@ #include "metadata/sgen-cardtable.h" #include "metadata/sgen-pinning.h" #include "metadata/sgen-workers.h" +#include "metadata/sgen-layout-stats.h" #include "utils/mono-mmap.h" #include "utils/mono-time.h" #include "utils/mono-semaphore.h" @@ -260,6 +263,8 @@ guint32 collect_before_allocs = 0; static gboolean whole_heap_check_before_collection = FALSE; /* If set, do a heap consistency check before each minor collection */ static gboolean consistency_check_at_minor_collection = FALSE; +/* If set, do a mod union consistency check before each finishing collection pause */ +static gboolean mod_union_consistency_check = FALSE; /* If set, check whether mark bits are consistent after major collections */ static gboolean check_mark_bits_after_major_collection = FALSE; /* If set, check that all nursery objects are pinned/not pinned, depending on context */ @@ -275,6 +280,12 @@ static gboolean conservative_stack_mark = FALSE; /* If set, do a plausibility check on the scan_starts before and after each collection */ static gboolean do_scan_starts_check = FALSE; +/* + * If the major collector is concurrent and this is FALSE, we will + * never initiate a synchronous major collection, unless requested via + * GC.Collect(). + */ +static gboolean allow_synchronous_major = TRUE; static gboolean nursery_collection_is_parallel = FALSE; static gboolean disable_minor_collections = FALSE; static gboolean disable_major_collections = FALSE; @@ -301,6 +312,7 @@ long long stat_nursery_copy_object_failed_forwarded = 0; long long stat_nursery_copy_object_failed_pinned = 0; long long stat_nursery_copy_object_failed_to_space = 0; +static int stat_wbarrier_add_to_global_remset = 0; static int stat_wbarrier_set_field = 0; static int stat_wbarrier_set_arrayref = 0; static int stat_wbarrier_arrayref_copy = 0; @@ -388,9 +400,6 @@ sgen_safe_name (void* obj) * ###################################################################### */ LOCK_DECLARE (gc_mutex); -static int gc_disabled = 0; - -static gboolean use_cardtable; #define SCAN_START_SIZE SGEN_SCAN_START_SIZE @@ -478,10 +487,7 @@ MonoNativeTlsKey thread_info_key; #ifdef HAVE_KW_THREAD __thread SgenThreadInfo *sgen_thread_info; -__thread gpointer *store_remset_buffer; -__thread long store_remset_buffer_index; __thread char *stack_end; -__thread long *store_remset_buffer_index_addr; #endif /* The size of a TLAB */ @@ -542,7 +548,7 @@ static void report_registered_roots (void); static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue); static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, ScanCopyContext ctx); -static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue); +static void finish_gray_stack (int generation, GrayQueue *queue); void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise); @@ -550,14 +556,15 @@ void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise); static void init_stats (void); static int mark_ephemerons_in_range (ScanCopyContext ctx); -static void clear_unreachable_ephemerons (gboolean concurrent_cementing, ScanCopyContext ctx); +static void clear_unreachable_ephemerons (ScanCopyContext ctx); static void null_ephemerons_for_domain (MonoDomain *domain); +static gboolean major_update_or_finish_concurrent_collection (gboolean force_finish); + SgenObjectOperations current_object_ops; SgenMajorCollector major_collector; SgenMinorCollector sgen_minor_collector; static GrayQueue gray_queue; -static GrayQueue remember_major_objects_gray_queue; static SgenRemeberedSet remset; @@ -574,33 +581,6 @@ sgen_workers_get_job_gray_queue (WorkerData *worker_data) return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE; } -static gboolean have_non_collection_major_object_remembers = FALSE; - -gboolean -sgen_remember_major_object_for_concurrent_mark (char *obj) -{ - if (!major_collector.is_concurrent) - return FALSE; - - g_assert (current_collection_generation == GENERATION_NURSERY || current_collection_generation == -1); - - if (!concurrent_collection_in_progress) - return FALSE; - - GRAY_OBJECT_ENQUEUE (&remember_major_objects_gray_queue, obj); - - if (current_collection_generation != GENERATION_NURSERY) { - /* - * This happens when the mutator allocates large or - * pinned objects or when allocating in degraded - * mode. - */ - have_non_collection_major_object_remembers = TRUE; - } - - return TRUE; -} - static void gray_queue_redirect (SgenGrayQueue *queue) { @@ -627,13 +607,6 @@ gray_queue_redirect (SgenGrayQueue *queue) } } -static void -redirect_major_object_remembers (void) -{ - gray_queue_redirect (&remember_major_objects_gray_queue); - have_non_collection_major_object_remembers = FALSE; -} - static gboolean is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain) { @@ -645,9 +618,12 @@ is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain) return TRUE; if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext)) return TRUE; + +#ifndef DISABLE_REMOTING if (mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) && offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server)) return TRUE; +#endif /* Thread.cached_culture_info */ if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") && !strcmp (ref->vtable->klass->name, "CultureInfo") && @@ -918,6 +894,7 @@ process_object_for_domain_clearing (char *start, MonoDomain *domain) g_assert (mono_object_domain (start) == mono_get_root_domain ()); /* 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)) { MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server; @@ -928,6 +905,7 @@ process_object_for_domain_clearing (char *start, MonoDomain *domain) ((MonoRealProxy*)start)->unwrapped_server = NULL; } } +#endif } static MonoDomain *check_domain = NULL; @@ -1070,6 +1048,14 @@ mono_gc_clear_domain (MonoDomain * domain) LOCK_GC; + binary_protocol_domain_unload_begin (domain); + + sgen_stop_world (0); + + if (concurrent_collection_in_progress) + sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE); + g_assert (!concurrent_collection_in_progress); + sgen_process_fin_stage_entries (); sgen_process_dislink_stage_entries (); @@ -1124,11 +1110,16 @@ mono_gc_clear_domain (MonoDomain * domain) major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain); major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain); - if (G_UNLIKELY (do_pin_stats)) { - if (domain == mono_get_root_domain ()) + if (domain == mono_get_root_domain ()) { + if (G_UNLIKELY (do_pin_stats)) sgen_pin_stats_print_class_stats (); + sgen_object_layout_dump (stdout); } + sgen_restart_world (0, NULL); + + binary_protocol_domain_unload_end (domain); + UNLOCK_GC; } @@ -1142,36 +1133,34 @@ mono_gc_clear_domain (MonoDomain * domain) * lock must be held. For serial collectors that is not necessary. */ void -sgen_add_to_global_remset (gpointer ptr, gpointer obj, gboolean concurrent_cementing) +sgen_add_to_global_remset (gpointer ptr, gpointer obj) { SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Target pointer of global remset must be in the nursery"); + HEAVY_STAT (++stat_wbarrier_add_to_global_remset); + if (!major_collector.is_concurrent) { - SGEN_ASSERT (5, !concurrent_cementing, "Concurrent cementing must only happen with the concurrent collector"); SGEN_ASSERT (5, current_collection_generation != -1, "Global remsets can only be added during collections"); } else { if (current_collection_generation == -1) - SGEN_ASSERT (5, concurrent_cementing, "Global remsets outside of collection pauses can only be added by the concurrent collector"); - if (concurrent_cementing) - SGEN_ASSERT (5, concurrent_collection_in_progress, "Concurrent collection must be in process in order to add global remsets"); + SGEN_ASSERT (5, sgen_concurrent_collection_in_progress (), "Global remsets outside of collection pauses can only be added by the concurrent collector"); } if (!object_is_pinned (obj)) - SGEN_ASSERT (5, sgen_minor_collector.is_split, "Non-pinned objects can only remain in nursery if it is a split nursery"); - else if (sgen_cement_lookup_or_register (obj, concurrent_cementing)) + SGEN_ASSERT (5, sgen_minor_collector.is_split || sgen_concurrent_collection_in_progress (), "Non-pinned objects can only remain in nursery if it is a split nursery"); + else if (sgen_cement_lookup_or_register (obj)) return; remset.record_pointer (ptr); -#ifdef ENABLE_DTRACE if (G_UNLIKELY (do_pin_stats)) sgen_pin_stats_register_global_remset (obj); SGEN_LOG (8, "Adding global remset for %p", ptr); binary_protocol_global_remset (ptr, obj, (gpointer)SGEN_LOAD_VTABLE (obj)); - HEAVY_STAT (++stat_global_remsets_added); +#ifdef ENABLE_DTRACE if (G_UNLIKELY (MONO_GC_GLOBAL_REMSET_ADD_ENABLED ())) { MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj); MONO_GC_GLOBAL_REMSET_ADD ((mword)ptr, (mword)obj, sgen_safe_object_get_size (obj), @@ -1892,7 +1881,7 @@ sgen_get_current_object_ops (void){ static void -finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue) +finish_gray_stack (int generation, GrayQueue *queue) { TV_DECLARE (atv); TV_DECLARE (btv); @@ -1900,6 +1889,8 @@ finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue * CopyOrMarkObjectFunc copy_func = current_object_ops.copy_or_mark_object; ScanObjectFunc scan_func = current_object_ops.scan_object; ScanCopyContext ctx = { scan_func, copy_func, queue }; + char *start_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_start () : NULL; + char *end_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_end () : (char*)-1; /* * We copied all the reachable objects. Now it's the time to copy @@ -1940,8 +1931,6 @@ finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue * } while (!done_with_ephemerons); sgen_scan_togglerefs (start_addr, end_addr, ctx); - if (generation == GENERATION_OLD) - sgen_scan_togglerefs (sgen_get_nursery_start (), sgen_get_nursery_end (), ctx); if (sgen_need_bridge_processing ()) { sgen_collect_bridge_objects (generation, ctx); @@ -1990,7 +1979,7 @@ finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue * * Clear ephemeron pairs with unreachable keys. * We pass the copy func so we can figure out if an array was promoted or not. */ - clear_unreachable_ephemerons (generation == GENERATION_OLD && major_collector.is_concurrent, ctx); + clear_unreachable_ephemerons (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); @@ -2223,6 +2212,7 @@ init_stats (void) mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects); #ifdef HEAVY_STATISTICS + mono_counters_register ("WBarrier remember pointer", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_add_to_global_remset); mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field); 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); @@ -2289,7 +2279,7 @@ sgen_collection_is_concurrent (void) case GENERATION_NURSERY: return FALSE; case GENERATION_OLD: - return major_collector.is_concurrent; + return concurrent_collection_in_progress; default: g_error ("Invalid current generation %d", current_collection_generation); } @@ -2476,13 +2466,6 @@ init_gray_queue (void) } else { sgen_gray_object_queue_init (&gray_queue, NULL); } - - if (major_collector.is_concurrent) { - sgen_gray_object_queue_init_with_alloc_prepare (&remember_major_objects_gray_queue, NULL, - gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ()); - } else { - sgen_gray_object_queue_init_invalid (&remember_major_objects_gray_queue); - } } static void @@ -2570,9 +2553,6 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark) stat_minor_gcs++; gc_stats.minor_gc_count ++; - if (remset.prepare_for_minor_collection) - remset.prepare_for_minor_collection (); - MONO_GC_CHECKPOINT_1 (GENERATION_NURSERY); sgen_process_fin_stage_entries (); @@ -2610,14 +2590,6 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark) sgen_check_consistency (); sgen_workers_start_all_workers (); - - /* - * Perform the sequential part of remembered set scanning. - * This usually involves scanning global information that might later be produced by evacuation. - */ - if (remset.begin_scan_remsets) - remset.begin_scan_remsets (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE); - sgen_workers_start_marking (); frssjd = sgen_alloc_internal_dynamic (sizeof (FinishRememberedSetScanJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE); @@ -2698,7 +2670,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark) MONO_GC_CHECKPOINT_8 (GENERATION_NURSERY); - finish_gray_stack (sgen_get_nursery_start (), nursery_next, GENERATION_NURSERY, &gray_queue); + finish_gray_stack (GENERATION_NURSERY, &gray_queue); TV_GETTIME (atv); time_minor_finish_gray_stack += TV_ELAPSED (btv, atv); mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0); @@ -2760,8 +2732,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark) g_assert (sgen_gray_object_queue_is_empty (&gray_queue)); - if (remset.finish_minor_collection) - remset.finish_minor_collection (); + remset.finish_minor_collection (); check_scan_starts (); @@ -2814,7 +2785,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur ScanFinalizerEntriesJobData *sfejd_fin_ready, *sfejd_critical_fin; ScanCopyContext ctx; - if (major_collector.is_concurrent) { + if (concurrent_collection_in_progress) { /*This cleans up unused fragments */ sgen_nursery_allocator_prepare_for_pinning (); @@ -2835,9 +2806,6 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur if (whole_heap_check_before_collection) sgen_check_whole_heap (finish_up_concurrent_mark); - if (!major_collector.is_concurrent) - sgen_cement_reset (); - TV_GETTIME (btv); time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv); @@ -2855,7 +2823,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur check_for_xdomain_refs (); } - if (!major_collector.is_concurrent) { + if (!concurrent_collection_in_progress) { /* Remsets are not useful for a major collection */ remset.prepare_for_major_collection (); } @@ -2867,6 +2835,23 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur sgen_init_pinning (); SGEN_LOG (6, "Collecting pinned addresses"); pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE); + + if (!concurrent_collection_in_progress || finish_up_concurrent_mark) { + if (major_collector.is_concurrent) { + /* + * The concurrent major collector cannot evict + * yet, so we need to pin cemented objects to + * not break some asserts. + * + * FIXME: We could evict now! + */ + sgen_cement_iterate (pin_stage_object_callback, NULL); + } + + if (!concurrent_collection_in_progress) + sgen_cement_reset (); + } + sgen_optimize_pin_queue (0); /* @@ -2875,7 +2860,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur * before pinning has finished. For the non-concurrent * collector we start the workers after pinning. */ - if (major_collector.is_concurrent) { + if (concurrent_collection_in_progress) { sgen_workers_start_all_workers (); sgen_workers_start_marking (); } @@ -2932,13 +2917,27 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur ctx.copy_func = NULL; ctx.queue = WORKERS_DISTRIBUTE_GRAY_QUEUE; - if (major_collector.is_concurrent && sgen_minor_collector.is_split) { - /* - * With the split nursery, not all remaining nursery - * objects are pinned: those in to-space are not. We - * need to scan all nursery objects, though, so we - * have to do it by iterating over the whole nursery. - */ + /* + * Concurrent mark never follows references into the nursery. + * In the start and finish pauses we must scan live nursery + * objects, though. We could simply scan all nursery objects, + * but that would be conservative. The easiest way is to do a + * nursery collection, which copies all live nursery objects + * (except pinned ones, with the simple nursery) to the major + * heap. Scanning the mod union table later will then scan + * those promoted objects, provided they're reachable. Pinned + * objects in the nursery - which we can trivially find in the + * pinning queue - are treated as roots in the mark pauses. + * + * The split nursery complicates the latter part because + * non-pinned objects can survive in the nursery. That's why + * we need to do a full front-to-back scan of the nursery, + * marking all objects. + * + * Non-concurrent mark evacuates from the nursery, so it's + * sufficient to just scan pinned nursery objects. + */ + if (concurrent_collection_in_progress && sgen_minor_collector.is_split) { scan_nursery_objects (ctx); } else { sgen_pin_objects_in_section (nursery_section, ctx); @@ -2961,7 +2960,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur main_gc_thread = mono_native_thread_self (); #endif - if (!major_collector.is_concurrent) { + if (!concurrent_collection_in_progress && major_collector.is_parallel) { sgen_workers_start_all_workers (); sgen_workers_start_marking (); } @@ -3030,7 +3029,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur TV_GETTIME (btv); time_major_scan_big_objects += TV_ELAPSED (atv, btv); - if (major_collector.is_concurrent) { + if (concurrent_collection_in_progress) { /* prepare the pin queue for the next collection */ sgen_finish_pinning (); @@ -3042,7 +3041,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur } static void -major_start_collection (int *old_next_pin_slot) +major_start_collection (gboolean concurrent, int *old_next_pin_slot) { MONO_GC_BEGIN (GENERATION_OLD); binary_protocol_collection_begin (stat_major_gcs, GENERATION_OLD); @@ -3054,13 +3053,16 @@ major_start_collection (int *old_next_pin_slot) g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ())); - if (major_collector.is_concurrent) { + if (concurrent) { + g_assert (major_collector.is_concurrent); concurrent_collection_in_progress = TRUE; sgen_cement_concurrent_start (); - } - current_object_ops = major_collector.major_ops; + current_object_ops = major_collector.major_concurrent_ops; + } else { + current_object_ops = major_collector.major_ops; + } reset_pinned_from_failed_allocation (); @@ -3085,9 +3087,7 @@ major_start_collection (int *old_next_pin_slot) static void wait_for_workers_to_finish (void) { - g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue)); - - if (major_collector.is_parallel || major_collector.is_concurrent) { + if (concurrent_collection_in_progress || major_collector.is_parallel) { gray_queue_redirect (&gray_queue); sgen_workers_join (); } @@ -3105,17 +3105,15 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca LOSObject *bigobj, *prevbo; TV_DECLARE (atv); TV_DECLARE (btv); - char *heap_start = NULL; - char *heap_end = (char*)-1; TV_GETTIME (btv); - if (major_collector.is_concurrent || major_collector.is_parallel) + if (concurrent_collection_in_progress || major_collector.is_parallel) wait_for_workers_to_finish (); - current_object_ops = major_collector.major_ops; + if (concurrent_collection_in_progress) { + current_object_ops = major_collector.major_concurrent_ops; - if (major_collector.is_concurrent) { major_copy_or_mark_from_roots (NULL, TRUE, scan_mod_union); wait_for_workers_to_finish (); @@ -3123,6 +3121,8 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca if (do_concurrent_checks) check_nursery_is_clean (); + } else { + current_object_ops = major_collector.major_ops; } /* @@ -3134,7 +3134,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ())); /* all the objects in the heap */ - finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue); + finish_gray_stack (GENERATION_OLD, &gray_queue); TV_GETTIME (atv); time_major_finish_gray_stack += TV_ELAPSED (btv, atv); @@ -3147,7 +3147,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca sgen_workers_reset_data (); if (objects_pinned) { - g_assert (!major_collector.is_concurrent); + g_assert (!concurrent_collection_in_progress); /*This is slow, but we just OOM'd*/ sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot); @@ -3202,7 +3202,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca TV_GETTIME (btv); time_major_sweep += TV_ELAPSED (atv, btv); - if (!major_collector.is_concurrent) { + if (!concurrent_collection_in_progress) { /* walk the pin_queue, build up the fragment list of free memory, unmark * pinned objects as we go, memzero() the empty fragments so they are ready for the * next allocations. @@ -3219,7 +3219,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca sgen_pin_stats_reset (); } - if (major_collector.is_concurrent) + if (concurrent_collection_in_progress) sgen_cement_concurrent_finish (); sgen_cement_clear_below_threshold (); @@ -3243,7 +3243,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ())); - if (major_collector.is_concurrent) + if (concurrent_collection_in_progress) concurrent_collection_in_progress = FALSE; check_scan_starts (); @@ -3271,7 +3271,7 @@ major_do_collection (const char *reason) /* world must be stopped already */ TV_GETTIME (all_atv); - major_start_collection (&old_next_pin_slot); + major_start_collection (FALSE, &old_next_pin_slot); major_finish_collection (reason, old_next_pin_slot, FALSE); TV_GETTIME (all_btv); @@ -3296,7 +3296,7 @@ major_start_concurrent_collection (const char *reason) MONO_GC_CONCURRENT_START_BEGIN (GENERATION_OLD); // FIXME: store reason and pass it when finishing - major_start_collection (NULL); + major_start_collection (TRUE, NULL); gray_queue_redirect (&gray_queue); sgen_workers_wait_for_jobs (); @@ -3316,8 +3316,6 @@ major_update_or_finish_concurrent_collection (gboolean force_finish) MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ()); g_assert (sgen_gray_object_queue_is_empty (&gray_queue)); - if (!have_non_collection_major_object_remembers) - g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue)); major_collector.update_cardtable_mod_union (); sgen_los_update_cardtable_mod_union (); @@ -3327,8 +3325,10 @@ major_update_or_finish_concurrent_collection (gboolean force_finish) return FALSE; } + if (mod_union_consistency_check) + sgen_check_mod_union_consistency (); + collect_nursery (&unpin_queue, TRUE); - redirect_major_object_remembers (); current_collection_generation = GENERATION_OLD; major_finish_collection ("finishing", -1, TRUE); @@ -3390,6 +3390,9 @@ sgen_ensure_free_space (size_t size) sgen_perform_collection (size, generation_to_collect, reason, FALSE); } +/* + * LOCKING: Assumes the GC lock is held. + */ void sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish) { @@ -3400,14 +3403,11 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const const char *overflow_reason = NULL; MONO_GC_REQUESTED (generation_to_collect, requested_size, wait_to_finish ? 1 : 0); + if (wait_to_finish) + binary_protocol_collection_force (generation_to_collect); g_assert (generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD); - if (have_non_collection_major_object_remembers) { - g_assert (concurrent_collection_in_progress); - redirect_major_object_remembers (); - } - memset (infos, 0, sizeof (infos)); mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect); @@ -3426,6 +3426,13 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const } if (generation_to_collect == GENERATION_OLD) goto done; + } else { + if (generation_to_collect == GENERATION_OLD && + allow_synchronous_major && + major_collector.want_synchronous_collection && + *major_collector.want_synchronous_collection) { + wait_to_finish = TRUE; + } } //FIXME extract overflow reason @@ -3434,23 +3441,11 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const overflow_generation_to_collect = GENERATION_OLD; overflow_reason = "Minor overflow"; } - if (concurrent_collection_in_progress) { - redirect_major_object_remembers (); - sgen_workers_wake_up_all (); - } } else { - SgenGrayQueue unpin_queue; - SgenGrayQueue *unpin_queue_ptr; - memset (&unpin_queue, 0, sizeof (unpin_queue)); - - if (major_collector.is_concurrent && wait_to_finish) - unpin_queue_ptr = &unpin_queue; - else - unpin_queue_ptr = NULL; - if (major_collector.is_concurrent) { g_assert (!concurrent_collection_in_progress); - collect_nursery (unpin_queue_ptr, FALSE); + if (!wait_to_finish) + collect_nursery (NULL, FALSE); } if (major_collector.is_concurrent && !wait_to_finish) { @@ -3463,11 +3458,6 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const overflow_reason = "Excessive pinning"; } } - - if (unpin_queue_ptr) { - unpin_objects_from_queue (unpin_queue_ptr); - sgen_gray_object_queue_deinit (unpin_queue_ptr); - } } TV_GETTIME (gc_end); @@ -3507,7 +3497,6 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const done: g_assert (sgen_gray_object_queue_is_empty (&gray_queue)); - g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue)); sgen_restart_world (oldest_generation_collected, infos); @@ -3687,7 +3676,7 @@ null_ephemerons_for_domain (MonoDomain *domain) /* LOCKING: requires that the GC lock is held */ static void -clear_unreachable_ephemerons (gboolean concurrent_cementing, ScanCopyContext ctx) +clear_unreachable_ephemerons (ScanCopyContext ctx) { CopyOrMarkObjectFunc copy_func = ctx.copy_func; GrayQueue *queue = ctx.queue; @@ -3952,13 +3941,6 @@ mono_gc_deregister_root (char* addr) unsigned int sgen_global_stop_count = 0; -void -sgen_fill_thread_info_for_suspend (SgenThreadInfo *info) -{ - if (remset.fill_thread_info_for_suspend) - remset.fill_thread_info_for_suspend (info); -} - int sgen_get_current_collection_generation (void) { @@ -4062,10 +4044,6 @@ ptr_on_stack (void *ptr) static void* sgen_thread_register (SgenThreadInfo* info, void *addr) { -#ifndef HAVE_KW_THREAD - SgenThreadInfo *__thread_info__ = info; -#endif - LOCK_GC; #ifndef HAVE_KW_THREAD info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL; @@ -4085,8 +4063,6 @@ sgen_thread_register (SgenThreadInfo* info, void *addr) info->doing_handshake = FALSE; info->thread_is_dying = FALSE; info->stack_start = NULL; - info->store_remset_buffer_addr = &STORE_REMSET_BUFFER; - info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX; info->stopped_ip = NULL; info->stopped_domain = NULL; #ifdef USE_MONO_CTX @@ -4099,22 +4075,29 @@ sgen_thread_register (SgenThreadInfo* info, void *addr) binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info)); -#ifdef HAVE_KW_THREAD - store_remset_buffer_index_addr = &store_remset_buffer_index; + /* 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. #endif - /* try to get it with attributes first */ -#if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) - { - size_t size; - void *sstart; - pthread_attr_t attr; - pthread_getattr_np (pthread_self (), &attr); - pthread_attr_getstack (&attr, &sstart, &size); - info->stack_start_limit = sstart; - info->stack_end = (char*)sstart + size; - pthread_attr_destroy (&attr); - } + 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 ()); @@ -4132,9 +4115,6 @@ sgen_thread_register (SgenThreadInfo* info, void *addr) stack_end = info->stack_end; #endif - if (remset.register_thread) - remset.register_thread (info); - SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->stack_end); if (gc_callbacks.thread_attach_func) @@ -4144,13 +4124,6 @@ sgen_thread_register (SgenThreadInfo* info, void *addr) return info; } -static void -sgen_wbarrier_cleanup_thread (SgenThreadInfo *p) -{ - if (remset.cleanup_thread) - remset.cleanup_thread (p); -} - static void sgen_thread_unregister (SgenThreadInfo *p) { @@ -4202,7 +4175,6 @@ sgen_thread_unregister (SgenThreadInfo *p) gc_callbacks.thread_detach_func (p->runtime_data); p->runtime_data = NULL; } - sgen_wbarrier_cleanup_thread (p); mono_threads_unregister_current_thread (p); UNLOCK_GC; @@ -4270,6 +4242,7 @@ mono_gc_pthread_detach (pthread_t thread) void mono_gc_pthread_exit (void *retval) { + mono_thread_info_dettach (); pthread_exit (retval); } @@ -4651,22 +4624,6 @@ mono_gc_get_used_size (void) return tot; } -void -mono_gc_disable (void) -{ - LOCK_GC; - gc_disabled++; - UNLOCK_GC; -} - -void -mono_gc_enable (void) -{ - LOCK_GC; - gc_disabled--; - UNLOCK_GC; -} - int mono_gc_get_los_limit (void) { @@ -4713,15 +4670,29 @@ mono_gc_weak_link_remove (void **link_addr, gboolean track) MonoObject* mono_gc_weak_link_get (void **link_addr) { + void * volatile *link_addr_volatile; + void *ptr; + MonoObject *obj; + retry: + link_addr_volatile = link_addr; + ptr = (void*)*link_addr_volatile; /* - * We must only load *link_addr once because it might change - * under our feet, and REVEAL_POINTER (NULL) results in an - * invalid reference. + * At this point we have a hidden pointer. If the GC runs + * here, it will not recognize the hidden pointer as a + * reference, and if the object behind it is not referenced + * elsewhere, it will be freed. Once the world is restarted + * we reveal the pointer, giving us a pointer to a freed + * object. To make sure we don't return it, we load the + * hidden pointer again. If it's still the same, we can be + * sure the object reference is valid. */ - void *ptr = *link_addr; - if (!ptr) + if (ptr) + obj = (MonoObject*) REVEAL_POINTER (ptr); + else return NULL; + mono_memory_barrier (); + /* * During the second bridge processing step the world is * running again. That step processes all weak links once @@ -4733,7 +4704,10 @@ mono_gc_weak_link_get (void **link_addr) if (G_UNLIKELY (bridge_processing_in_progress)) mono_gc_wait_for_bridge_processing (); - return (MonoObject*) REVEAL_POINTER (ptr); + if ((void*)*link_addr_volatile != ptr) + goto retry; + + return obj; } gboolean @@ -4758,6 +4732,16 @@ mono_gc_ephemeron_array_add (MonoObject *obj) return TRUE; } +gboolean +mono_gc_set_allow_synchronous_major (gboolean flag) +{ + if (!major_collector.is_concurrent) + return flag; + + allow_synchronous_major = flag; + return TRUE; +} + void* mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data) { @@ -4783,7 +4767,40 @@ is_critical_method (MonoMethod *method) { return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method); } - + +void +sgen_env_var_error (const char *env_var, const char *fallback, const char *description_format, ...) +{ + va_list ap; + + va_start (ap, description_format); + + fprintf (stderr, "Warning: In environment variable `%s': ", env_var); + vfprintf (stderr, description_format, ap); + if (fallback) + fprintf (stderr, " - %s", fallback); + fprintf (stderr, "\n"); + + va_end (ap); +} + +static gboolean +parse_double_in_interval (const char *env_var, const char *opt_name, const char *opt, double min, double max, double *result) +{ + char *endptr; + double val = strtod (opt, &endptr); + if (endptr == opt) { + sgen_env_var_error (env_var, "Using default value.", "`%s` must be a number.", opt_name); + return FALSE; + } + else if (val < min || val > max) { + sgen_env_var_error (env_var, "Using default value.", "`%s` must be between %.2f - %.2f.", opt_name, min, max); + return FALSE; + } + *result = val; + return TRUE; +} + void mono_gc_base_init (void) { @@ -4840,7 +4857,7 @@ mono_gc_base_init (void) init_user_copy_or_mark_key (); - if ((env = getenv ("MONO_GC_PARAMS"))) { + if ((env = getenv (MONO_GC_PARAMS_NAME))) { opts = g_strsplit (env, ",", -1); for (ptr = opts; *ptr; ++ptr) { char *opt = *ptr; @@ -4859,12 +4876,11 @@ mono_gc_base_init (void) init_stats (); sgen_init_internal_allocator (); sgen_init_nursery_allocator (); + sgen_init_fin_weak_hash (); sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION); sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry)); sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection)); - g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE); - sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet)); sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode)); #ifndef HAVE_KW_THREAD @@ -4884,17 +4900,19 @@ mono_gc_base_init (void) sgen_simple_nursery_init (&sgen_minor_collector); } else { if (!strcmp (minor_collector_opt, "simple")) { + use_simple_nursery: sgen_simple_nursery_init (&sgen_minor_collector); } else if (!strcmp (minor_collector_opt, "split")) { sgen_split_nursery_init (&sgen_minor_collector); have_split_nursery = TRUE; } else { - fprintf (stderr, "Unknown minor collector `%s'.\n", minor_collector_opt); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `simple` instead.", "Unknown minor collector `%s'.", minor_collector_opt); + goto use_simple_nursery; } } if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) { + use_marksweep_major: sgen_marksweep_init (&major_collector); } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) { sgen_marksweep_fixed_init (&major_collector); @@ -4905,15 +4923,14 @@ mono_gc_base_init (void) } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) { sgen_marksweep_conc_init (&major_collector); } else { - fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `marksweep` instead.", "Unknown major collector `%s'.", major_collector_opt); + goto use_marksweep_major; } -#ifdef SGEN_HAVE_CARDTABLE - use_cardtable = major_collector.supports_cardtable; -#else - use_cardtable = FALSE; -#endif + if (have_split_nursery && major_collector.is_parallel) { + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Disabling split minor collector.", "`minor=split` is not supported with the parallel collector yet."); + have_split_nursery = FALSE; + } num_workers = mono_cpu_count (); g_assert (num_workers > 0); @@ -4927,44 +4944,25 @@ mono_gc_base_init (void) sgen_nursery_size = DEFAULT_NURSERY_SIZE; if (opts) { + gboolean usage_printed = FALSE; + for (ptr = opts; *ptr; ++ptr) { char *opt = *ptr; + if (!strcmp (opt, "")) + continue; if (g_str_has_prefix (opt, "major=")) continue; if (g_str_has_prefix (opt, "minor=")) continue; - if (g_str_has_prefix (opt, "wbarrier=")) { - opt = strchr (opt, '=') + 1; - if (strcmp (opt, "remset") == 0) { - if (major_collector.is_concurrent) { - fprintf (stderr, "The concurrent collector does not support the SSB write barrier.\n"); - exit (1); - } - use_cardtable = FALSE; - } else if (strcmp (opt, "cardtable") == 0) { - if (!use_cardtable) { - if (major_collector.supports_cardtable) - fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n"); - else - fprintf (stderr, "The major collector does not support the cardtable write barrier.\n"); - exit (1); - } - } else { - fprintf (stderr, "wbarrier must either be `remset' or `cardtable'."); - exit (1); - } - continue; - } if (g_str_has_prefix (opt, "max-heap-size=")) { + glong max_heap_candidate = 0; opt = strchr (opt, '=') + 1; - if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) { - if ((max_heap & (mono_pagesize () - 1))) { - fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ()); - exit (1); - } + if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap_candidate)) { + max_heap = (max_heap_candidate + mono_pagesize () - 1) & ~(glong)(mono_pagesize () - 1); + if (max_heap != max_heap_candidate) + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Rounding up.", "`max-heap-size` size must be a multiple of %d.", mono_pagesize ()); } else { - fprintf (stderr, "max-heap-size must be an integer.\n"); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`max-heap-size` must be an integer."); } continue; } @@ -4972,12 +4970,11 @@ mono_gc_base_init (void) opt = strchr (opt, '=') + 1; if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) { if (soft_limit <= 0) { - fprintf (stderr, "soft-heap-limit must be positive.\n"); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be positive."); + soft_limit = 0; } } else { - fprintf (stderr, "soft-heap-limit must be an integer.\n"); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be an integer."); } continue; } @@ -4985,18 +4982,18 @@ mono_gc_base_init (void) long val; char *endptr; if (!major_collector.is_parallel) { - fprintf (stderr, "The workers= option can only be used for parallel collectors."); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "The `workers` option can only be used for parallel collectors."); + continue; } opt = strchr (opt, '=') + 1; val = strtol (opt, &endptr, 10); if (!*opt || *endptr) { - fprintf (stderr, "Cannot parse the workers= option value."); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Cannot parse the `workers` option value."); + continue; } if (val <= 0 || val > 16) { - fprintf (stderr, "The number of workers must be in the range 1 to 16."); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "The number of `workers` must be in the range 1 to 16."); + continue; } num_workers = (int)val; continue; @@ -5008,8 +5005,8 @@ mono_gc_base_init (void) } else if (!strcmp (opt, "conservative")) { conservative_stack_mark = TRUE; } else { - fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt); - exit (1); + 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); } continue; } @@ -5023,58 +5020,67 @@ mono_gc_base_init (void) long val; opt = strchr (opt, '=') + 1; if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) { - sgen_nursery_size = val; #ifdef SGEN_ALIGN_NURSERY if ((val & (val - 1))) { - fprintf (stderr, "The nursery size must be a power of two.\n"); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be a power of two."); + continue; } if (val < SGEN_MAX_NURSERY_WASTE) { - fprintf (stderr, "The nursery size must be at least %d bytes.\n", SGEN_MAX_NURSERY_WASTE); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", + "`nursery-size` must be at least %d bytes.\n", SGEN_MAX_NURSERY_WASTE); + continue; } + sgen_nursery_size = val; sgen_nursery_bits = 0; while (1 << (++ sgen_nursery_bits) != sgen_nursery_size) ; +#else + sgen_nursery_size = val; #endif } else { - fprintf (stderr, "nursery-size must be an integer.\n"); - exit (1); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be an integer."); + continue; } continue; } #endif if (g_str_has_prefix (opt, "save-target-ratio=")) { - char *endptr; + double val; opt = strchr (opt, '=') + 1; - save_target = strtod (opt, &endptr); - if (endptr == opt) { - fprintf (stderr, "save-target-ratio must be a number."); - exit (1); - } - if (save_target < SGEN_MIN_SAVE_TARGET_RATIO || save_target > SGEN_MAX_SAVE_TARGET_RATIO) { - fprintf (stderr, "save-target-ratio must be between %.2f - %.2f.", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO); - exit (1); + if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "save-target-ratio", opt, + SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO, &val)) { + save_target = val; } continue; } if (g_str_has_prefix (opt, "default-allowance-ratio=")) { - char *endptr; + double val; opt = strchr (opt, '=') + 1; - - allowance_ratio = strtod (opt, &endptr); - if (endptr == opt) { - fprintf (stderr, "save-target-ratio must be a number."); - exit (1); - } - if (allowance_ratio < SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO || allowance_ratio > SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO) { - fprintf (stderr, "default-allowance-ratio must be between %.2f - %.2f.", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO); - exit (1); + if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "default-allowance-ratio", opt, + SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, &val)) { + allowance_ratio = val; } continue; } + if (g_str_has_prefix (opt, "allow-synchronous-major=")) { + if (!major_collector.is_concurrent) { + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`allow-synchronous-major` is only valid for the concurrent major collector."); + continue; + } + + opt = strchr (opt, '=') + 1; + + if (!strcmp (opt, "yes")) { + allow_synchronous_major = TRUE; + } else if (!strcmp (opt, "no")) { + allow_synchronous_major = FALSE; + } else { + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`allow-synchronous-major` must be either `yes' or `no'."); + continue; + } + } if (!strcmp (opt, "cementing")) { cement_enabled = TRUE; @@ -5091,15 +5097,22 @@ mono_gc_base_init (void) if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt)) continue; - fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n"); + sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Unknown option `%s`.", opt); + + if (usage_printed) + continue; + + fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME); fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n"); fprintf (stderr, " soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n"); fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n"); - fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par', 'marksweep-fixed' or 'marksweep-fixed-par')\n"); + fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par', 'marksweep-fixed' or 'marksweep-fixed-par')\n"); fprintf (stderr, " minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n"); fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n"); fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n"); fprintf (stderr, " [no-]cementing\n"); + if (major_collector.is_concurrent) + fprintf (stderr, " allow-synchronous-major=FLAG (where FLAG is `yes' or `no')\n"); if (major_collector.print_gc_param_usage) major_collector.print_gc_param_usage (); if (sgen_minor_collector.print_gc_param_usage) @@ -5107,7 +5120,9 @@ mono_gc_base_init (void) fprintf (stderr, " Experimental options:\n"); fprintf (stderr, " save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO); fprintf (stderr, " default-allowance-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MAX_ALLOWANCE_NURSERY_SIZE_RATIO); - exit (1); + fprintf (stderr, "\n"); + + usage_printed = TRUE; } g_strfreev (opts); } @@ -5127,10 +5142,14 @@ mono_gc_base_init (void) sgen_cement_init (cement_enabled); - if ((env = getenv ("MONO_GC_DEBUG"))) { + if ((env = getenv (MONO_GC_DEBUG_NAME))) { + gboolean usage_printed = FALSE; + opts = g_strsplit (env, ",", -1); for (ptr = opts; ptr && *ptr; ptr ++) { char *opt = *ptr; + if (!strcmp (opt, "")) + continue; if (opt [0] >= '0' && opt [0] <= '9') { gc_debug_level = atoi (opt); opt++; @@ -5170,6 +5189,12 @@ mono_gc_base_init (void) } else if (!strcmp (opt, "check-at-minor-collections")) { consistency_check_at_minor_collection = TRUE; nursery_clear_policy = CLEAR_AT_GC; + } else if (!strcmp (opt, "mod-union-consistency-check")) { + if (!major_collector.is_concurrent) { + sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`mod-union-consistency-check` only works with concurrent major collector."); + continue; + } + mod_union_consistency_check = TRUE; } else if (!strcmp (opt, "check-mark-bits")) { check_mark_bits_after_major_collection = TRUE; } else if (!strcmp (opt, "check-nursery-pinned")) { @@ -5186,8 +5211,8 @@ mono_gc_base_init (void) do_verify_nursery = TRUE; } else if (!strcmp (opt, "check-concurrent")) { if (!major_collector.is_concurrent) { - fprintf (stderr, "Error: check-concurrent only world with concurrent major collectors.\n"); - exit (1); + sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`check-concurrent` only works with concurrent major collectors."); + continue; } do_concurrent_checks = TRUE; } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) { @@ -5210,13 +5235,15 @@ mono_gc_base_init (void) } else if (g_str_has_prefix (opt, "binary-protocol=")) { char *filename = strchr (opt, '=') + 1; binary_protocol_init (filename); - if (use_cardtable) - fprintf (stderr, "Warning: Cardtable write barriers will not be binary-protocolled.\n"); #endif } else { - fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env); - fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|