* frequently after each object is copied, to achieve better locality and cache
* usage.
*
- * max_objs is the maximum number of objects to scan, or -1 to scan until the stack is
- * empty.
*/
gboolean
-sgen_drain_gray_stack (int max_objs, ScanCopyContext ctx)
+sgen_drain_gray_stack (ScanCopyContext ctx)
{
ScanObjectFunc scan_func = ctx.ops->scan_object;
GrayQueue *queue = ctx.queue;
- if (current_collection_generation == GENERATION_OLD && major_collector.drain_gray_stack)
- return major_collector.drain_gray_stack (ctx);
+ if (ctx.ops->drain_gray_stack)
+ return ctx.ops->drain_gray_stack (queue);
- do {
- int i;
- for (i = 0; i != max_objs; ++i) {
- GCObject *obj;
- SgenDescriptor desc;
- GRAY_OBJECT_DEQUEUE (queue, &obj, &desc);
- if (!obj)
- return TRUE;
- SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
- scan_func (obj, desc, queue);
- }
- } while (max_objs < 0);
+ for (;;) {
+ GCObject *obj;
+ SgenDescriptor desc;
+ GRAY_OBJECT_DEQUEUE (queue, &obj, &desc);
+ if (!obj)
+ return TRUE;
+ SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
+ scan_func (obj, desc, queue);
+ }
return FALSE;
}
{
int count = 0;
+ SGEN_ASSERT (0, ((mword)start & (SIZEOF_VOID_P - 1)) == 0, "Why are we scanning for references in unaligned memory ?");
+
#if defined(VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE) && !defined(_WIN64)
VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE (start, (char*)end - (char*)start);
#endif
* To achieve better cache locality and cache usage, we drain the gray stack
* frequently, after each object is copied, and just finish the work here.
*/
- sgen_drain_gray_stack (-1, ctx);
+ sgen_drain_gray_stack (ctx);
TV_GETTIME (atv);
SGEN_LOG (2, "%s generation done", generation_name (generation));
done_with_ephemerons = 0;
do {
done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
- sgen_drain_gray_stack (-1, ctx);
+ sgen_drain_gray_stack (ctx);
++ephemeron_rounds;
} while (!done_with_ephemerons);
if (sgen_client_bridge_need_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_drain_gray_stack (ctx);
sgen_collect_bridge_objects (generation, ctx);
if (generation == GENERATION_OLD)
sgen_collect_bridge_objects (GENERATION_NURSERY, ctx);
Make sure we drain the gray stack before processing disappearing links and finalizers.
If we don't make sure it is empty we might wrongly see a live object as dead.
*/
- sgen_drain_gray_stack (-1, ctx);
+ sgen_drain_gray_stack (ctx);
/*
We must clear weak links that don't track resurrection before processing object ready for
finalization so they can be cleared before that.
*/
- sgen_null_link_in_range (generation, TRUE, ctx);
+ sgen_null_link_in_range (generation, ctx, FALSE);
if (generation == GENERATION_OLD)
- sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx);
+ sgen_null_link_in_range (GENERATION_NURSERY, ctx, FALSE);
/* walk the finalization queue and move also the objects that need to be
sgen_finalize_in_range (GENERATION_NURSERY, ctx);
/* drain the new stack that might have been created */
SGEN_LOG (6, "Precise scan of gray area post fin");
- sgen_drain_gray_stack (-1, ctx);
+ sgen_drain_gray_stack (ctx);
/*
* This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
done_with_ephemerons = 0;
do {
done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
- sgen_drain_gray_stack (-1, ctx);
+ sgen_drain_gray_stack (ctx);
++ephemeron_rounds;
} while (!done_with_ephemerons);
*/
g_assert (sgen_gray_object_queue_is_empty (queue));
for (;;) {
- sgen_null_link_in_range (generation, FALSE, ctx);
+ sgen_null_link_in_range (generation, ctx, TRUE);
if (generation == GENERATION_OLD)
- sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx);
+ sgen_null_link_in_range (GENERATION_NURSERY, ctx, TRUE);
if (sgen_gray_object_queue_is_empty (queue))
break;
- sgen_drain_gray_stack (-1, ctx);
+ sgen_drain_gray_stack (ctx);
}
g_assert (sgen_gray_object_queue_is_empty (queue));
}
static void
-enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperations *ops)
+enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperations *ops, gboolean enqueue)
{
ScanFromRegisteredRootsJob *scrrj;
ScanThreadDataJob *stdj;
scrrj->heap_start = heap_start;
scrrj->heap_end = heap_end;
scrrj->root_type = ROOT_TYPE_NORMAL;
- sgen_workers_enqueue_job (&scrrj->job);
+ sgen_workers_enqueue_job (&scrrj->job, enqueue);
scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots wbarrier", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
scrrj->ops = ops;
scrrj->heap_start = heap_start;
scrrj->heap_end = heap_end;
scrrj->root_type = ROOT_TYPE_WBARRIER;
- sgen_workers_enqueue_job (&scrrj->job);
+ sgen_workers_enqueue_job (&scrrj->job, enqueue);
/* Threads */
stdj = (ScanThreadDataJob*)sgen_thread_pool_job_alloc ("scan thread data", job_scan_thread_data, sizeof (ScanThreadDataJob));
stdj->heap_start = heap_start;
stdj->heap_end = heap_end;
- sgen_workers_enqueue_job (&stdj->job);
+ sgen_workers_enqueue_job (&stdj->job, enqueue);
/* Scan the list of objects ready for finalization. */
sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
sfej->queue = &fin_ready_queue;
sfej->ops = ops;
- sgen_workers_enqueue_job (&sfej->job);
+ sgen_workers_enqueue_job (&sfej->job, enqueue);
sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan critical finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
sfej->queue = &critical_fin_queue;
sfej->ops = ops;
- sgen_workers_enqueue_job (&sfej->job);
+ sgen_workers_enqueue_job (&sfej->job, enqueue);
}
/*
sgen_check_consistency ();
sgen_process_fin_stage_entries ();
- sgen_process_dislink_stage_entries ();
/* pin from pinned handles */
sgen_init_pinning ();
*/
sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan remset", job_remembered_set_scan, sizeof (ScanJob));
sj->ops = object_ops;
- sgen_workers_enqueue_job (&sj->job);
+ sgen_workers_enqueue_job (&sj->job, FALSE);
/* we don't have complete write barrier yet, so we scan all the old generation sections */
TV_GETTIME (btv);
sgen_pin_stats_print_class_stats ();
- sgen_drain_gray_stack (-1, ctx);
+ sgen_drain_gray_stack (ctx);
/* FIXME: Why do we do this at this specific, seemingly random, point? */
sgen_client_collecting_minor (&fin_ready_queue, &critical_fin_queue);
TV_GETTIME (atv);
time_minor_scan_pinned += TV_ELAPSED (btv, atv);
- enqueue_scan_from_roots_jobs (sgen_get_nursery_start (), nursery_next, object_ops);
+ enqueue_scan_from_roots_jobs (sgen_get_nursery_start (), nursery_next, object_ops, FALSE);
TV_GETTIME (btv);
time_minor_scan_roots += TV_ELAPSED (atv, btv);
}
sgen_process_fin_stage_entries ();
- sgen_process_dislink_stage_entries ();
TV_GETTIME (atv);
sgen_init_pinning ();
* before pinning has finished. For the non-concurrent
* collector we start the workers after pinning.
*/
- if (mode != COPY_OR_MARK_FROM_ROOTS_SERIAL) {
+ if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
sgen_workers_start_all_workers (object_ops);
gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
* FIXME: is this the right context? It doesn't seem to contain a copy function
* unless we're concurrent.
*/
- enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops);
+ enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops, mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
TV_GETTIME (btv);
time_major_scan_roots += TV_ELAPSED (atv, btv);
/* Mod union card table */
sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan mod union cardtable", job_scan_major_mod_union_card_table, sizeof (ScanJob));
sj->ops = object_ops;
- sgen_workers_enqueue_job (&sj->job);
+ sgen_workers_enqueue_job (&sj->job, FALSE);
sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan LOS mod union cardtable", job_scan_los_mod_union_card_table, sizeof (ScanJob));
sj->ops = object_ops;
- sgen_workers_enqueue_job (&sj->job);
+ sgen_workers_enqueue_job (&sj->job, FALSE);
TV_GETTIME (atv);
time_major_scan_mod_union += TV_ELAPSED (btv, atv);
static void
major_finish_copy_or_mark (CopyOrMarkFromRootsMode mode)
{
- switch (mode) {
- case COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT:
+ if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
/*
* Prepare the pin queue for the next collection. Since pinning runs on the worker
* threads we must wait for the jobs to finish before we can reset it.
if (do_concurrent_checks)
sgen_debug_check_nursery_is_clean ();
- break;
- case COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT:
- sgen_workers_wait_for_jobs_finished ();
- break;
- case COPY_OR_MARK_FROM_ROOTS_SERIAL:
- break;
- default:
- g_assert_not_reached ();
}
}
major_finish_copy_or_mark (COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT);
- sgen_workers_join ();
-
- SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty after workers have finished working?");
-
#ifdef SGEN_DEBUG_INTERNAL_ALLOC
main_gc_thread = NULL;
#endif
generation_to_collect = GENERATION_OLD;
}
} else if (sgen_need_major_collection (size)) {
- reason = "Minor allowance";
+ reason = concurrent_collection_in_progress ? "Forced finish concurrent collection" : "Minor allowance";
generation_to_collect = GENERATION_OLD;
} else {
generation_to_collect = GENERATION_NURSERY;
if (concurrent_collection_in_progress) {
/*
- * We update the concurrent collection. If it finished, we're done. If
- * not, and we've been asked to do a nursery collection, we do that.
+ * If the concurrent worker is finished or we are asked to do a major collection
+ * then we finish the concurrent collection.
*/
- gboolean finish = major_should_finish_concurrent_collection () || (wait_to_finish && generation_to_collect == GENERATION_OLD);
+ gboolean finish = major_should_finish_concurrent_collection () || generation_to_collect == GENERATION_OLD;
if (finish) {
major_finish_concurrent_collection (wait_to_finish);
oldest_generation_collected = GENERATION_OLD;
} else {
- sgen_workers_signal_start_nursery_collection_and_wait ();
-
+ SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY, "Why aren't we finishing the concurrent collection?");
major_update_concurrent_collection ();
- if (generation_to_collect == GENERATION_NURSERY)
- collect_nursery (NULL, FALSE);
-
- sgen_workers_signal_finish_nursery_collection ();
+ collect_nursery (NULL, FALSE);
}
goto done;
return tot;
}
-GCObject*
-sgen_weak_link_get (void **link_addr)
-{
- void * volatile *link_addr_volatile;
- void *ptr;
- GCObject *obj;
- retry:
- link_addr_volatile = link_addr;
- ptr = (void*)*link_addr_volatile;
- /*
- * 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.
- */
- if (ptr)
- obj = (GCObject*) 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
- * more to null those that refer to dead objects. Before that
- * is completed, those links must not be followed, so we
- * conservatively wait for bridge processing when any weak
- * link is dereferenced.
- */
- sgen_client_bridge_wait_for_processing ();
-
- if ((void*)*link_addr_volatile != ptr)
- goto retry;
-
- return obj;
-}
-
gboolean
sgen_set_allow_synchronous_major (gboolean flag)
{
sgen_init_descriptors ();
sgen_init_gray_queues ();
sgen_init_allocator ();
+ sgen_init_gchandles ();
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
} else if (g_str_has_prefix (opt, "binary-protocol=")) {
char *filename = strchr (opt, '=') + 1;
char *colon = strrchr (filename, ':');
- size_t limit = -1;
+ size_t limit = 0;
if (colon) {
if (!mono_gc_parse_environment_string_extract_number (colon + 1, &limit)) {
sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring limit.", "Binary protocol file size limit must be an integer.");
sgen_card_table_init (&remset);
+ sgen_register_root (NULL, 0, sgen_make_user_root_descriptor (sgen_mark_normal_gc_handles), ROOT_TYPE_NORMAL, MONO_ROOT_SOURCE_GC_HANDLE, "normal gc handles");
+
gc_initialized = 1;
}