/* If set, do a plausibility check on the scan_starts before and after
each collection */
static gboolean do_scan_starts_check = FALSE;
+static gboolean disable_minor_collections = FALSE;
+static gboolean disable_major_collections = FALSE;
#ifdef HEAVY_STATISTICS
static long long stat_objects_alloced = 0;
*/
#define DEFAULT_REMSET_SIZE 1024
static RememberedSet* alloc_remset (int size, gpointer id);
+static RememberedSet* alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id);
#define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
#define object_is_pinned SGEN_OBJECT_IS_PINNED
#define pin_object SGEN_PIN_OBJECT
#define unpin_object SGEN_UNPIN_OBJECT
-#define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_real_end))
+#define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_end))
#define LOAD_VTABLE SGEN_LOAD_VTABLE
/*
* The current allocation cursors
* We allocate objects in the nursery.
- * The nursery is the area between nursery_start and nursery_real_end.
+ * The nursery is the area between nursery_start and nursery_end.
* Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
* from nursery fragments.
* tlab_next is the pointer to the space inside the TLAB where the next object will
* MAX(nursery_last_pinned_end, nursery_frag_real_end)
*/
static char *nursery_start = NULL;
+static char *nursery_next = NULL;
+static char *nursery_frag_real_end = NULL;
+static char *nursery_end = NULL;
+static char *nursery_last_pinned_end = NULL;
#ifdef HAVE_KW_THREAD
#define TLAB_ACCESS_INIT
static __thread char *stack_end;
static __thread long *store_remset_buffer_index_addr;
#endif
-static char *nursery_next = NULL;
-static char *nursery_frag_real_end = NULL;
-static char *nursery_real_end = NULL;
-static char *nursery_last_pinned_end = NULL;
/* The size of a TLAB */
/* The bigger the value, the less often we have to go to the slow path to allocate a new
/* forward declarations */
static int stop_world (int generation);
static int restart_world (int generation);
-static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
+static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
+static void scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
static void null_links_for_domain (MonoDomain *domain, int generation);
-static gboolean search_fragment_for_size (size_t size);
-static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
+static gboolean alloc_fragment_for_size (size_t size);
+static int alloc_fragment_for_size_range (size_t desired_size, size_t minimum_size);
static void clear_nursery_fragments (char *next);
-static void pin_from_roots (void *start_nursery, void *end_nursery);
+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, GrayQueue *queue);
static void optimize_pin_queue (int start_slot);
static void clear_remsets (void);
* lock must be held. For serial collectors that is not necessary.
*/
void
-mono_sgen_add_to_global_remset (gpointer ptr)
+mono_sgen_add_to_global_remset (SgenInternalAllocator *alc, gpointer ptr)
{
RememberedSet *rs;
- gboolean lock;
+ gboolean lock = major_collector.is_parallel;
if (use_cardtable) {
sgen_card_table_mark_address ((mword)ptr);
g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
- lock = (current_collection_generation == GENERATION_OLD && major_collector.is_parallel);
if (lock)
LOCK_GLOBAL_REMSET;
*(global_remset->store_next++) = (mword)ptr;
goto done;
}
- rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
+ rs = alloc_global_remset (alc, global_remset->end_set - global_remset->data, NULL);
rs->next = global_remset;
global_remset = rs;
*(global_remset->store_next++) = (mword)ptr;
* conservatively scanned.
*/
static void
-pin_from_roots (void *start_nursery, void *end_nursery)
+pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
{
RootRecord *root;
int i;
* *) the _last_ managed stack frame
* *) pointers slots in managed frames
*/
- scan_thread_data (start_nursery, end_nursery, FALSE);
+ scan_thread_data (start_nursery, end_nursery, FALSE, queue);
evacuate_pin_staging_area ();
}
-static CopyOrMarkObjectFunc user_copy_or_mark_func;
-static GrayQueue *user_copy_or_mark_queue;
+typedef struct {
+ CopyOrMarkObjectFunc func;
+ GrayQueue *queue;
+} UserCopyOrMarkData;
+
+static pthread_key_t user_copy_or_mark_key;
+
+static void
+init_user_copy_or_mark_key (void)
+{
+ pthread_key_create (&user_copy_or_mark_key, NULL);
+}
+
+static void
+set_user_copy_or_mark_data (UserCopyOrMarkData *data)
+{
+ static pthread_once_t init_control = PTHREAD_ONCE_INIT;
+ pthread_once (&init_control, init_user_copy_or_mark_key);
+
+ pthread_setspecific (user_copy_or_mark_key, data);
+}
static void
single_arg_user_copy_or_mark (void **obj)
{
- user_copy_or_mark_func (obj, user_copy_or_mark_queue);
+ UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
+
+ data->func (obj, data->queue);
}
/*
break;
}
case ROOT_DESC_USER: {
+ UserCopyOrMarkData data = { copy_func, queue };
MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
- user_copy_or_mark_func = copy_func;
- user_copy_or_mark_queue = queue;
+ set_user_copy_or_mark_data (&data);
marker (start_root, single_arg_user_copy_or_mark);
- user_copy_or_mark_func = NULL;
- user_copy_or_mark_queue = NULL;
+ set_user_copy_or_mark_data (NULL);
break;
}
case ROOT_DESC_RUN_LEN:
frag->next = NULL;
return frag;
}
+
+static void
+add_fragment (char *start, char *end)
+{
+ Fragment *fragment;
+
+ fragment = alloc_fragment ();
+ fragment->fragment_start = start;
+ fragment->fragment_limit = start;
+ fragment->fragment_end = end;
+ fragment->next = nursery_fragments;
+ nursery_fragments = fragment;
+}
/* size must be a power of 2 */
void*
GCMemSection *section;
char *data;
int scan_starts;
- Fragment *frag;
int alloc_size;
if (nursery_section)
data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
#endif
nursery_start = data;
- nursery_real_end = nursery_start + nursery_size;
- mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
- nursery_next = nursery_start;
+ nursery_end = nursery_start + nursery_size;
+ mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %lu, total: %lu\n", data, data + alloc_size, (unsigned long)nursery_size, (unsigned long)total_alloc));
section->data = section->next_data = data;
section->size = alloc_size;
- section->end_data = nursery_real_end;
+ section->end_data = nursery_end;
scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
section->num_scan_start = scan_starts;
nursery_section = section;
/* Setup the single first large fragment */
- frag = alloc_fragment ();
- frag->fragment_start = nursery_start;
- frag->fragment_limit = nursery_start;
- frag->fragment_end = nursery_real_end;
- nursery_frag_real_end = nursery_real_end;
- /* FIXME: frag here is lost */
+ add_fragment (nursery_start, nursery_end);
}
void*
static void
add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
{
- Fragment *fragment;
DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
binary_protocol_empty (frag_start, frag_size);
/* Not worth dealing with smaller fragments: need to tune */
if (nursery_clear_policy == CLEAR_AT_GC)
memset (frag_start, 0, frag_size);
- fragment = alloc_fragment ();
- fragment->fragment_start = frag_start;
- fragment->fragment_limit = frag_start;
- fragment->fragment_end = frag_end;
- fragment->next = nursery_fragments;
- nursery_fragments = fragment;
+ add_fragment (frag_start, frag_end);
fragment_total += frag_size;
} else {
/* Clear unused fragments, pinning depends on this */
fin_ready = num_ready_finalizers;
finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
if (generation == GENERATION_OLD)
- finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
+ finalize_in_range (copy_func, nursery_start, nursery_end, GENERATION_NURSERY, queue);
if (fin_ready != num_ready_finalizers) {
++num_loops;
frag_start = (char*)start [i] + frag_size;
}
nursery_last_pinned_end = frag_start;
- frag_end = nursery_real_end;
+ frag_end = nursery_end;
frag_size = frag_end - frag_start;
if (frag_size)
add_nursery_frag (frag_size, frag_start, frag_end);
return need_major_collection (space_needed);
}
+static GrayQueue*
+job_gray_queue (WorkerData *worker_data)
+{
+ return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
+}
+
+typedef struct
+{
+ char *heap_start;
+ char *heap_end;
+} ScanFromRemsetsJobData;
+
+static void
+job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
+{
+ ScanFromRemsetsJobData *job_data = job_data_untyped;
+
+ scan_from_remsets (job_data->heap_start, job_data->heap_end, job_gray_queue (worker_data));
+}
+
+typedef struct
+{
+ CopyOrMarkObjectFunc func;
+ char *heap_start;
+ char *heap_end;
+ int root_type;
+} ScanFromRegisteredRootsJobData;
+
+static void
+job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
+{
+ ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
+
+ scan_from_registered_roots (job_data->func,
+ job_data->heap_start, job_data->heap_end,
+ job_data->root_type,
+ job_gray_queue (worker_data));
+}
+
+typedef struct
+{
+ char *heap_start;
+ char *heap_end;
+} ScanThreadDataJobData;
+
+static void
+job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
+{
+ ScanThreadDataJobData *job_data = job_data_untyped;
+
+ scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
+ job_gray_queue (worker_data));
+}
+
/*
* Collect objects in the nursery. Returns whether to trigger a major
* collection.
gboolean needs_major;
size_t max_garbage_amount;
char *orig_nursery_next;
+ ScanFromRemsetsJobData sfrjd;
+ ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
+ ScanThreadDataJobData stdjd;
TV_DECLARE (all_atv);
TV_DECLARE (all_btv);
TV_DECLARE (atv);
TV_DECLARE (btv);
+ if (disable_minor_collections)
+ return TRUE;
+
mono_perfcounters->gc_collections0++;
current_collection_generation = GENERATION_NURSERY;
orig_nursery_next = nursery_next;
nursery_next = MAX (nursery_next, nursery_last_pinned_end);
/* FIXME: optimize later to use the higher address where an object can be present */
- nursery_next = MAX (nursery_next, nursery_real_end);
+ nursery_next = MAX (nursery_next, nursery_end);
DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", num_minor_gcs, nursery_start, nursery_next, (int)(nursery_next - nursery_start)));
max_garbage_amount = nursery_next - nursery_start;
try_calculate_minor_collection_allowance (FALSE);
gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
+ workers_init_distribute_gray_queue ();
num_minor_gcs++;
mono_stats.minor_gc_count ++;
/* pin from pinned handles */
init_pinning ();
mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
- pin_from_roots (nursery_start, nursery_next);
+ pin_from_roots (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
/* identify pinned objects */
optimize_pin_queue (0);
- next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, &gray_queue);
+ next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
nursery_section->pin_queue_start = pin_queue;
nursery_section->pin_queue_num_entries = next_pin_slot;
TV_GETTIME (atv);
if (consistency_check_at_minor_collection)
check_consistency ();
- /*
- * walk all the roots and copy the young objects to the old generation,
- * starting from to_space
+ workers_start_all_workers ();
+
+ /*
+ * Walk all the roots and copy the young objects to the old
+ * generation, starting from to_space.
+ *
+ * The global remsets must be processed before the workers start
+ * marking because they might add global remsets.
*/
+ scan_from_global_remsets (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+
+ workers_start_marking ();
+
+ sfrjd.heap_start = nursery_start;
+ sfrjd.heap_end = nursery_next;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_remsets, &sfrjd);
- scan_from_remsets (nursery_start, nursery_next, &gray_queue);
/* we don't have complete write barrier yet, so we scan all the old generation sections */
TV_GETTIME (btv);
time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
if (use_cardtable) {
atv = btv;
card_tables_collect_stats (TRUE);
- scan_from_card_tables (nursery_start, nursery_next, &gray_queue);
+ scan_from_card_tables (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
TV_GETTIME (btv);
time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
}
- drain_gray_stack (&gray_queue, -1);
+ if (!major_collector.is_parallel)
+ drain_gray_stack (&gray_queue, -1);
if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
report_registered_roots ();
report_finalizer_roots ();
TV_GETTIME (atv);
time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
+
/* registered roots, this includes static fields */
- scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, &gray_queue);
- scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &gray_queue);
+ scrrjd_normal.func = major_collector.copy_object;
+ scrrjd_normal.heap_start = nursery_start;
+ scrrjd_normal.heap_end = nursery_next;
+ scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
+
+ scrrjd_wbarrier.func = major_collector.copy_object;
+ scrrjd_wbarrier.heap_start = nursery_start;
+ scrrjd_wbarrier.heap_end = nursery_next;
+ scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
+
TV_GETTIME (btv);
time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
+
/* thread data */
- scan_thread_data (nursery_start, nursery_next, TRUE);
+ stdjd.heap_start = nursery_start;
+ stdjd.heap_end = nursery_next;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
+
TV_GETTIME (atv);
time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
btv = atv;
+ if (major_collector.is_parallel) {
+ while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
+ workers_distribute_gray_queue_sections ();
+ usleep (1000);
+ }
+ }
+ workers_join ();
+
+ if (major_collector.is_parallel)
+ g_assert (gray_object_queue_is_empty (&gray_queue));
+
finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
TV_GETTIME (atv);
time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
+ /*
+ * The (single-threaded) finalization code might have done
+ * some copying/marking so we can only reset the GC thread's
+ * worker data here instead of earlier when we joined the
+ * workers.
+ */
+ if (major_collector.reset_worker_data)
+ major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
+
if (objects_pinned) {
evacuate_pin_staging_area ();
optimize_pin_queue (0);
return needs_major;
}
+typedef struct
+{
+ FinalizeEntry *list;
+} ScanFinalizerEntriesJobData;
+
+static void
+job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
+{
+ ScanFinalizerEntriesJobData *job_data = job_data_untyped;
+
+ scan_finalizer_entries (major_collector.copy_or_mark_object,
+ job_data->list,
+ job_gray_queue (worker_data));
+}
+
static void
major_do_collection (const char *reason)
{
char *heap_start = NULL;
char *heap_end = (char*)-1;
int old_next_pin_slot;
+ ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
+ ScanThreadDataJobData stdjd;
+ ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
mono_perfcounters->gc_collections1++;
TV_GETTIME (btv);
time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
- nursery_section->next_data = nursery_real_end;
+ nursery_section->next_data = nursery_end;
/* we should also coalesce scanning from sections close to each other
* and deal with pointers outside of the sections later.
*/
TV_GETTIME (atv);
init_pinning ();
DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
- pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
+ pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
optimize_pin_queue (0);
/*
#endif
workers_start_all_workers ();
+ workers_start_marking ();
if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
report_registered_roots ();
time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
/* registered roots, this includes static fields */
- scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
- scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ scrrjd_normal.func = major_collector.copy_or_mark_object;
+ scrrjd_normal.heap_start = heap_start;
+ scrrjd_normal.heap_end = heap_end;
+ scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
+
+ scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
+ scrrjd_wbarrier.heap_start = heap_start;
+ scrrjd_wbarrier.heap_end = heap_end;
+ scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
+
TV_GETTIME (btv);
time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
/* Threads */
- /* FIXME: This is the wrong place for this, because it does
- pinning */
- scan_thread_data (heap_start, heap_end, TRUE);
+ stdjd.heap_start = heap_start;
+ stdjd.heap_end = heap_end;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
+
TV_GETTIME (atv);
time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
report_finalizer_roots ();
+
/* scan the list of objects ready for finalization */
- scan_finalizer_entries (major_collector.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
- scan_finalizer_entries (major_collector.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ sfejd_fin_ready.list = fin_ready_list;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_fin_ready);
+
+ sfejd_critical_fin.list = critical_fin_list;
+ workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_critical_fin);
+
TV_GETTIME (atv);
time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
}
reset_heap_boundaries ();
- mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
+ mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
/* sweep the big objects list */
prevbo = NULL;
static void
major_collection (const char *reason)
{
- if (g_getenv ("MONO_GC_NO_MAJOR")) {
+ if (disable_major_collections) {
collect_nursery (0);
return;
}
DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
restart_world (0);
/* this also sets the proper pointers for the next allocation */
- if (!search_fragment_for_size (size)) {
+ if (!alloc_fragment_for_size (size)) {
int i;
/* TypeBuilder and MonoMethod are killing mcs with fragmentation */
DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
fragment_freelist = frag;
}
-/* check if we have a suitable fragment in nursery_fragments to be able to allocate
- * an object of size @size
- * Return FALSE if not found (which means we need a collection)
+/*
+ * Allocate a new nursery fragment able to hold an object of size @size.
+ * nursery_next and nursery_frag_real_end are set to the boundaries of the fragment.
+ * Return TRUE if found, FALSE otherwise.
*/
static gboolean
-search_fragment_for_size (size_t size)
+alloc_fragment_for_size (size_t size)
{
Fragment *frag, *prev;
DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
}
/*
- * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
+ * Same as alloc_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
* This improves nursery usage.
*/
static int
-search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
+alloc_fragment_for_size_range (size_t desired_size, size_t minimum_size)
{
Fragment *frag, *prev, *min_prev;
DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, desired size: %zd minimum size %zd\n", nursery_frag_real_end, desired_size, minimum_size));
collect_nursery (0);
restart_world (0);
mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
- if (!degraded_mode && !search_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
+ if (!degraded_mode && !alloc_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
// FIXME:
g_assert_not_reached ();
}
if (size > tlab_size) {
/* Allocate directly from the nursery */
if (nursery_next + size >= nursery_frag_real_end) {
- if (!search_fragment_for_size (size)) {
+ if (!alloc_fragment_for_size (size)) {
minor_collect_or_expand_inner (size);
if (degraded_mode) {
p = alloc_degraded (vtable, size);
if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
alloc_size = available_in_nursery;
} else {
- alloc_size = search_fragment_for_size_range (tlab_size, size);
+ alloc_size = alloc_fragment_for_size_range (tlab_size, size);
if (!alloc_size) {
alloc_size = tlab_size;
minor_collect_or_expand_inner (tlab_size);
res = alloc_degraded (vtable, size);
*res = vtable;
UNLOCK_GC;
+ if (G_UNLIKELY (vtable->klass->has_finalize))
+ mono_object_register_finalizer ((MonoObject*)res);
+
return res;
}
if (was_promoted) {
if (ptr_in_nursery (key)) {/*key was not promoted*/
DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
- mono_sgen_add_to_global_remset (&cur->key);
+ mono_sgen_add_to_global_remset (queue->allocator, &cur->key);
}
if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
- mono_sgen_add_to_global_remset (&cur->value);
+ mono_sgen_add_to_global_remset (queue->allocator, &cur->value);
}
}
}
static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
#endif
+SgenThreadInfo*
+mono_sgen_thread_info_current (void)
+{
+#ifdef HAVE_KW_THREAD
+ return thread_info;
+#else
+ return pthread_getspecific (thread_info_key);
+#endif
+}
SgenThreadInfo*
mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
update_current_thread_stack (void *start)
{
int stack_guard = 0;
-#ifdef USE_MONO_CTX
- void *ptr = NULL;
-#else
+#ifndef USE_MONO_CTX
void *ptr = cur_thread_regs;
#endif
- SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+ SgenThreadInfo *info = mono_sgen_thread_info_current ();
info->stack_start = align_pointer (&stack_guard);
g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
suspend_handler (int sig, siginfo_t *siginfo, void *context)
{
SgenThreadInfo *info;
- pthread_t id;
int stop_count;
int old_errno = errno;
#ifdef USE_MONO_CTX
#endif
gpointer stack_start;
- id = pthread_self ();
- info = mono_sgen_thread_info_lookup (id);
+ info = mono_sgen_thread_info_current ();
info->stopped_domain = mono_domain_get ();
info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
stop_count = global_stop_count;
SgenThreadInfo *info;
int old_errno = errno;
- info = mono_sgen_thread_info_lookup (pthread_self ());
+ info = mono_sgen_thread_info_current ();
info->signal = restart_signal_num;
DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
update_current_thread_stack (&count);
global_stop_count++;
- DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()), (gpointer)ARCH_GET_THREAD ()));
+ DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, mono_sgen_thread_info_current (), (gpointer)ARCH_GET_THREAD ()));
TV_GETTIME (stop_world_time);
count = mono_sgen_thread_handshake (suspend_signal_num);
count -= restart_threads_until_none_in_managed_allocator ();
void*
mono_gc_scan_object (void *obj)
{
+ UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
+
if (current_collection_generation == GENERATION_NURSERY)
- major_collector.copy_object (&obj, &gray_queue);
+ major_collector.copy_object (&obj, data->queue);
else
- major_collector.copy_or_mark_object (&obj, &gray_queue);
+ major_collector.copy_or_mark_object (&obj, data->queue);
return obj;
}
* Mark from thread stacks and registers.
*/
static void
-scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
+scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
{
SgenThreadInfo *info;
continue;
}
DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
- if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
+ 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);
- else if (!precise)
+ set_user_copy_or_mark_data (NULL);
+ } else if (!precise) {
conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
+ }
#ifdef USE_MONO_CTX
if (!precise)
ptr_on_stack (void *ptr)
{
gpointer stack_start = &stack_start;
- SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+ SgenThreadInfo *info = mono_sgen_thread_info_current ();
if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
return TRUE;
* becomes part of the global remset, which can grow very large.
*/
DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
- mono_sgen_add_to_global_remset (ptr);
+ mono_sgen_add_to_global_remset (queue->allocator, ptr);
}
} else {
DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
major_collector.copy_object (ptr, queue);
DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
if (!global && *ptr >= start_nursery && *ptr < end_nursery)
- mono_sgen_add_to_global_remset (ptr);
+ mono_sgen_add_to_global_remset (queue->allocator, ptr);
++ptr;
}
return p + 2;
}
static void
-scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
+scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
{
- int i;
- SgenThreadInfo *info;
RememberedSet *remset;
- GenericStoreRememberedSet *store_remset;
mword *p, *next_p, *store_pos;
-#ifdef HEAVY_STATISTICS
- remset_stats ();
-#endif
-
/* the global one */
for (remset = global_remset; remset; remset = remset->next) {
DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
/* Truncate the remset */
remset->store_next = store_pos;
}
+}
+
+static void
+scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
+{
+ int i;
+ SgenThreadInfo *info;
+ RememberedSet *remset;
+ GenericStoreRememberedSet *store_remset;
+ mword *p;
+
+#ifdef HEAVY_STATISTICS
+ remset_stats ();
+#endif
/* the generic store ones */
store_remset = generic_store_remsets;
remset->next = NULL;
if (remset != global_remset) {
DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
- mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
+ mono_sgen_free_internal_dynamic_delayed (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET,
+ mono_sgen_get_unmanaged_allocator ());
}
}
/* the generic store ones */
store_remset_buffer_index_addr = &store_remset_buffer_index;
#endif
+#if defined(__MACH__)
+ info->mach_port = mach_thread_self ();
+#endif
+
/* try to get it with attributes first */
#if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
{
prev->next = p->next;
}
+#if defined(__MACH__)
+ mach_port_deallocate (current_task (), p->mach_port);
+#endif
+
if (gc_callbacks.thread_detach_func) {
gc_callbacks.thread_detach_func (p->runtime_data);
p->runtime_data = NULL;
LOCK_GC;
init_stats ();
- info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+ info = mono_sgen_thread_info_current ();
if (info == NULL) {
info = gc_register_current_thread (baseptr);
} else {
SgenThreadInfo *info;
LOCK_GC;
- info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+ info = mono_sgen_thread_info_current ();
if (info) {
g_assert (stack_end < info->stack_end);
info->stack_end = stack_end;
return res;
}
+static RememberedSet*
+alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id)
+{
+ RememberedSet* res = mono_sgen_alloc_internal_full (alc, sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
+ res->store_next = res->data;
+ res->end_set = res->data + size;
+ res->next = NULL;
+ DEBUG (4, fprintf (gc_debug_file, "Allocated global remset size %d at %p for %p\n", size, res->data, id));
+ return res;
+}
+
/*
* Note: the write barriers first do the needed GC work and then do the actual store:
* this way the value is visible to the conservative GC scan after the write barrier
rs->next = REMEMBERED_SET;
REMEMBERED_SET = rs;
#ifdef HAVE_KW_THREAD
- mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+ mono_sgen_thread_info_current ()->remset = rs;
#endif
*(rs->store_next++) = (mword)field_ptr;
*(void**)field_ptr = value;
rs->next = REMEMBERED_SET;
REMEMBERED_SET = rs;
#ifdef HAVE_KW_THREAD
- mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+ mono_sgen_thread_info_current ()->remset = rs;
#endif
*(rs->store_next++) = (mword)slot_ptr;
*(void**)slot_ptr = value;
rs->next = REMEMBERED_SET;
REMEMBERED_SET = rs;
#ifdef HAVE_KW_THREAD
- mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+ mono_sgen_thread_info_current ()->remset = rs;
#endif
*(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
*(rs->store_next++) = count;
rs->next = REMEMBERED_SET;
REMEMBERED_SET = rs;
#ifdef HAVE_KW_THREAD
- mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+ mono_sgen_thread_info_current ()->remset = rs;
#endif
*(rs->store_next++) = (mword)dest | REMSET_VTYPE;
*(rs->store_next++) = (mword)klass->gc_descr;
rs->next = REMEMBERED_SET;
REMEMBERED_SET = rs;
#ifdef HAVE_KW_THREAD
- mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+ mono_sgen_thread_info_current ()->remset = rs;
#endif
*(rs->store_next++) = (mword)obj | REMSET_OBJECT;
UNLOCK_GC;
{
gboolean result;
LOCK_GC;
- result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
+ result = mono_sgen_thread_info_current () != NULL;
UNLOCK_GC;
return result;
}
nursery_clear_policy = CLEAR_AT_GC;
} else if (!strcmp (opt, "check-scan-starts")) {
do_scan_starts_check = TRUE;
+ } else if (!strcmp (opt, "disable-minor")) {
+ disable_minor_collections = TRUE;
+ } else if (!strcmp (opt, "disable-major")) {
+ disable_major_collections = TRUE;
} else if (g_str_has_prefix (opt, "heap-dump=")) {
char *filename = strchr (opt, '=') + 1;
nursery_clear_policy = CLEAR_AT_GC;
} 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]|<option>]+ where l is a debug level 0-9.\n");
- fprintf (stderr, "Valid options are: collect-before-allocs[=<n>], check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
+ fprintf (stderr, "Valid options are:\n");
+ fprintf (stderr, " collect-before-allocs[=<n>]\n");
+ fprintf (stderr, " check-at-minor-collections\n");
+ fprintf (stderr, " disable-minor\n");
+ fprintf (stderr, " disable-major\n");
+ fprintf (stderr, " xdomain-checks\n");
+ fprintf (stderr, " clear-at-gc\n");
exit (1);
}
}
mono_mb_emit_ptr (mb, (gpointer) nursery_start);
label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
- // if (ptr >= nursery_real_end)) goto continue;
+ // if (ptr >= nursery_end)) goto continue;
mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
+ mono_mb_emit_ptr (mb, (gpointer) nursery_end);
label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
// Otherwise return
// if (*ptr >= nursery_end) return;
mono_mb_emit_ldloc (mb, dereferenced_var);
- mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
+ mono_mb_emit_ptr (mb, (gpointer) nursery_end);
label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
#endif