[sgen] Make hazard pointer stuff work without Mono, enabling concurrent M&S.
[mono.git] / mono / metadata / sgen-gc.c
1 /*
2  * sgen-gc.c: Simple generational GC.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *  Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10  *
11  * Thread start/stop adapted from Boehm's GC:
12  * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
13  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
14  * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
15  * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
16  * Copyright 2001-2003 Ximian, Inc
17  * Copyright 2003-2010 Novell, Inc.
18  * Copyright 2011 Xamarin, Inc.
19  * Copyright (C) 2012 Xamarin Inc
20  *
21  * This library is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU Library General Public
23  * License 2.0 as published by the Free Software Foundation;
24  *
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28  * Library General Public License for more details.
29  *
30  * You should have received a copy of the GNU Library General Public
31  * License 2.0 along with this library; if not, write to the Free
32  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33  *
34  * Important: allocation provides always zeroed memory, having to do
35  * a memset after allocation is deadly for performance.
36  * Memory usage at startup is currently as follows:
37  * 64 KB pinned space
38  * 64 KB internal space
39  * size of nursery
40  * We should provide a small memory config with half the sizes
41  *
42  * We currently try to make as few mono assumptions as possible:
43  * 1) 2-word header with no GC pointers in it (first vtable, second to store the
44  *    forwarding ptr)
45  * 2) gc descriptor is the second word in the vtable (first word in the class)
46  * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
47  * 4) there is a function to get an object's size and the number of
48  *    elements in an array.
49  * 5) we know the special way bounds are allocated for complex arrays
50  * 6) we know about proxies and how to treat them when domains are unloaded
51  *
52  * Always try to keep stack usage to a minimum: no recursive behaviour
53  * and no large stack allocs.
54  *
55  * General description.
56  * Objects are initially allocated in a nursery using a fast bump-pointer technique.
57  * When the nursery is full we start a nursery collection: this is performed with a
58  * copying GC.
59  * When the old generation is full we start a copying GC of the old generation as well:
60  * this will be changed to mark&sweep with copying when fragmentation becomes to severe
61  * in the future.  Maybe we'll even do both during the same collection like IMMIX.
62  *
63  * The things that complicate this description are:
64  * *) pinned objects: we can't move them so we need to keep track of them
65  * *) no precise info of the thread stacks and registers: we need to be able to
66  *    quickly find the objects that may be referenced conservatively and pin them
67  *    (this makes the first issues more important)
68  * *) large objects are too expensive to be dealt with using copying GC: we handle them
69  *    with mark/sweep during major collections
70  * *) some objects need to not move even if they are small (interned strings, Type handles):
71  *    we use mark/sweep for them, too: they are not allocated in the nursery, but inside
72  *    PinnedChunks regions
73  */
74
75 /*
76  * TODO:
77
78  *) we could have a function pointer in MonoClass to implement
79   customized write barriers for value types
80
81  *) investigate the stuff needed to advance a thread to a GC-safe
82   point (single-stepping, read from unmapped memory etc) and implement it.
83   This would enable us to inline allocations and write barriers, for example,
84   or at least parts of them, like the write barrier checks.
85   We may need this also for handling precise info on stacks, even simple things
86   as having uninitialized data on the stack and having to wait for the prolog
87   to zero it. Not an issue for the last frame that we scan conservatively.
88   We could always not trust the value in the slots anyway.
89
90  *) modify the jit to save info about references in stack locations:
91   this can be done just for locals as a start, so that at least
92   part of the stack is handled precisely.
93
94  *) test/fix endianess issues
95
96  *) Implement a card table as the write barrier instead of remembered
97     sets?  Card tables are not easy to implement with our current
98     memory layout.  We have several different kinds of major heap
99     objects: Small objects in regular blocks, small objects in pinned
100     chunks and LOS objects.  If we just have a pointer we have no way
101     to tell which kind of object it points into, therefore we cannot
102     know where its card table is.  The least we have to do to make
103     this happen is to get rid of write barriers for indirect stores.
104     (See next item)
105
106  *) Get rid of write barriers for indirect stores.  We can do this by
107     telling the GC to wbarrier-register an object once we do an ldloca
108     or ldelema on it, and to unregister it once it's not used anymore
109     (it can only travel downwards on the stack).  The problem with
110     unregistering is that it needs to happen eventually no matter
111     what, even if exceptions are thrown, the thread aborts, etc.
112     Rodrigo suggested that we could do only the registering part and
113     let the collector find out (pessimistically) when it's safe to
114     unregister, namely when the stack pointer of the thread that
115     registered the object is higher than it was when the registering
116     happened.  This might make for a good first implementation to get
117     some data on performance.
118
119  *) Some sort of blacklist support?  Blacklists is a concept from the
120     Boehm GC: if during a conservative scan we find pointers to an
121     area which we might use as heap, we mark that area as unusable, so
122     pointer retention by random pinning pointers is reduced.
123
124  *) experiment with max small object size (very small right now - 2kb,
125     because it's tied to the max freelist size)
126
127   *) add an option to mmap the whole heap in one chunk: it makes for many
128      simplifications in the checks (put the nursery at the top and just use a single
129      check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
130      not flexible (too much of the address space may be used by default or we can't
131      increase the heap as needed) and we'd need a race-free mechanism to return memory
132      back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
133      was written to, munmap is needed, but the following mmap may not find the same segment
134      free...)
135
136  *) memzero the major fragments after restarting the world and optionally a smaller
137     chunk at a time
138
139  *) investigate having fragment zeroing threads
140
141  *) separate locks for finalization and other minor stuff to reduce
142     lock contention
143
144  *) try a different copying order to improve memory locality
145
146  *) a thread abort after a store but before the write barrier will
147     prevent the write barrier from executing
148
149  *) specialized dynamically generated markers/copiers
150
151  *) Dynamically adjust TLAB size to the number of threads.  If we have
152     too many threads that do allocation, we might need smaller TLABs,
153     and we might get better performance with larger TLABs if we only
154     have a handful of threads.  We could sum up the space left in all
155     assigned TLABs and if that's more than some percentage of the
156     nursery size, reduce the TLAB size.
157
158  *) Explore placing unreachable objects on unused nursery memory.
159         Instead of memset'ng a region to zero, place an int[] covering it.
160         A good place to start is add_nursery_frag. The tricky thing here is
161         placing those objects atomically outside of a collection.
162
163  *) Allocation should use asymmetric Dekker synchronization:
164         http://blogs.oracle.com/dave/resource/Asymmetric-Dekker-Synchronization.txt
165         This should help weak consistency archs.
166  */
167 #include "config.h"
168 #ifdef HAVE_SGEN_GC
169
170 #ifdef __MACH__
171 #undef _XOPEN_SOURCE
172 #define _XOPEN_SOURCE
173 #define _DARWIN_C_SOURCE
174 #endif
175
176 #ifdef HAVE_UNISTD_H
177 #include <unistd.h>
178 #endif
179 #ifdef HAVE_PTHREAD_H
180 #include <pthread.h>
181 #endif
182 #ifdef HAVE_PTHREAD_NP_H
183 #include <pthread_np.h>
184 #endif
185 #include <stdio.h>
186 #include <string.h>
187 #include <errno.h>
188 #include <assert.h>
189 #include <stdlib.h>
190
191 #include "mono/metadata/sgen-gc.h"
192 #include "mono/metadata/sgen-cardtable.h"
193 #include "mono/metadata/sgen-protocol.h"
194 #include "mono/metadata/sgen-memory-governor.h"
195 #include "mono/metadata/sgen-hash-table.h"
196 #include "mono/metadata/sgen-cardtable.h"
197 #include "mono/metadata/sgen-pinning.h"
198 #include "mono/metadata/sgen-workers.h"
199 #include "mono/metadata/sgen-client.h"
200 #include "mono/metadata/sgen-pointer-queue.h"
201 #include "mono/metadata/gc-internal-agnostic.h"
202 #include "mono/utils/mono-proclib.h"
203 #include "mono/utils/mono-memory-model.h"
204 #include "mono/utils/hazard-pointer.h"
205
206 #include <mono/utils/memcheck.h>
207
208 #undef pthread_create
209 #undef pthread_join
210 #undef pthread_detach
211
212 /*
213  * ######################################################################
214  * ########  Types and constants used by the GC.
215  * ######################################################################
216  */
217
218 /* 0 means not initialized, 1 is initialized, -1 means in progress */
219 static int gc_initialized = 0;
220 /* If set, check if we need to do something every X allocations */
221 gboolean has_per_allocation_action;
222 /* If set, do a heap check every X allocation */
223 guint32 verify_before_allocs = 0;
224 /* If set, do a minor collection before every X allocation */
225 guint32 collect_before_allocs = 0;
226 /* If set, do a whole heap check before each collection */
227 static gboolean whole_heap_check_before_collection = FALSE;
228 /* If set, do a heap consistency check before each minor collection */
229 static gboolean consistency_check_at_minor_collection = FALSE;
230 /* If set, do a mod union consistency check before each finishing collection pause */
231 static gboolean mod_union_consistency_check = FALSE;
232 /* If set, check whether mark bits are consistent after major collections */
233 static gboolean check_mark_bits_after_major_collection = FALSE;
234 /* If set, check that all nursery objects are pinned/not pinned, depending on context */
235 static gboolean check_nursery_objects_pinned = FALSE;
236 /* If set, do a few checks when the concurrent collector is used */
237 static gboolean do_concurrent_checks = FALSE;
238 /* If set, do a plausibility check on the scan_starts before and after
239    each collection */
240 static gboolean do_scan_starts_check = FALSE;
241
242 /*
243  * If the major collector is concurrent and this is FALSE, we will
244  * never initiate a synchronous major collection, unless requested via
245  * GC.Collect().
246  */
247 static gboolean allow_synchronous_major = TRUE;
248 static gboolean disable_minor_collections = FALSE;
249 static gboolean disable_major_collections = FALSE;
250 static gboolean do_verify_nursery = FALSE;
251 static gboolean do_dump_nursery_content = FALSE;
252 static gboolean enable_nursery_canaries = FALSE;
253
254 #ifdef HEAVY_STATISTICS
255 guint64 stat_objects_alloced_degraded = 0;
256 guint64 stat_bytes_alloced_degraded = 0;
257
258 guint64 stat_copy_object_called_nursery = 0;
259 guint64 stat_objects_copied_nursery = 0;
260 guint64 stat_copy_object_called_major = 0;
261 guint64 stat_objects_copied_major = 0;
262
263 guint64 stat_scan_object_called_nursery = 0;
264 guint64 stat_scan_object_called_major = 0;
265
266 guint64 stat_slots_allocated_in_vain;
267
268 guint64 stat_nursery_copy_object_failed_from_space = 0;
269 guint64 stat_nursery_copy_object_failed_forwarded = 0;
270 guint64 stat_nursery_copy_object_failed_pinned = 0;
271 guint64 stat_nursery_copy_object_failed_to_space = 0;
272
273 static guint64 stat_wbarrier_add_to_global_remset = 0;
274 static guint64 stat_wbarrier_set_field = 0;
275 static guint64 stat_wbarrier_set_arrayref = 0;
276 static guint64 stat_wbarrier_arrayref_copy = 0;
277 static guint64 stat_wbarrier_generic_store = 0;
278 static guint64 stat_wbarrier_generic_store_atomic = 0;
279 static guint64 stat_wbarrier_set_root = 0;
280 static guint64 stat_wbarrier_value_copy = 0;
281 static guint64 stat_wbarrier_object_copy = 0;
282 #endif
283
284 static guint64 stat_pinned_objects = 0;
285
286 static guint64 time_minor_pre_collection_fragment_clear = 0;
287 static guint64 time_minor_pinning = 0;
288 static guint64 time_minor_scan_remsets = 0;
289 static guint64 time_minor_scan_pinned = 0;
290 static guint64 time_minor_scan_roots = 0;
291 static guint64 time_minor_finish_gray_stack = 0;
292 static guint64 time_minor_fragment_creation = 0;
293
294 static guint64 time_major_pre_collection_fragment_clear = 0;
295 static guint64 time_major_pinning = 0;
296 static guint64 time_major_scan_pinned = 0;
297 static guint64 time_major_scan_roots = 0;
298 static guint64 time_major_scan_mod_union = 0;
299 static guint64 time_major_finish_gray_stack = 0;
300 static guint64 time_major_free_bigobjs = 0;
301 static guint64 time_major_los_sweep = 0;
302 static guint64 time_major_sweep = 0;
303 static guint64 time_major_fragment_creation = 0;
304
305 static guint64 time_max = 0;
306
307 static SGEN_TV_DECLARE (time_major_conc_collection_start);
308 static SGEN_TV_DECLARE (time_major_conc_collection_end);
309
310 static SGEN_TV_DECLARE (last_minor_collection_start_tv);
311 static SGEN_TV_DECLARE (last_minor_collection_end_tv);
312
313 int gc_debug_level = 0;
314 FILE* gc_debug_file;
315
316 /*
317 void
318 mono_gc_flush_info (void)
319 {
320         fflush (gc_debug_file);
321 }
322 */
323
324 #define TV_DECLARE SGEN_TV_DECLARE
325 #define TV_GETTIME SGEN_TV_GETTIME
326 #define TV_ELAPSED SGEN_TV_ELAPSED
327
328 SGEN_TV_DECLARE (sgen_init_timestamp);
329
330 NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
331
332 #define object_is_forwarded     SGEN_OBJECT_IS_FORWARDED
333 #define object_is_pinned        SGEN_OBJECT_IS_PINNED
334 #define pin_object              SGEN_PIN_OBJECT
335
336 #define ptr_in_nursery sgen_ptr_in_nursery
337
338 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
339
340 gboolean
341 nursery_canaries_enabled (void)
342 {
343         return enable_nursery_canaries;
344 }
345
346 #define safe_object_get_size    sgen_safe_object_get_size
347
348 /*
349  * ######################################################################
350  * ########  Global data.
351  * ######################################################################
352  */
353 LOCK_DECLARE (gc_mutex);
354 gboolean sgen_try_free_some_memory;
355
356 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
357
358 size_t degraded_mode = 0;
359
360 static mword bytes_pinned_from_failed_allocation = 0;
361
362 GCMemSection *nursery_section = NULL;
363 static volatile mword lowest_heap_address = ~(mword)0;
364 static volatile mword highest_heap_address = 0;
365
366 LOCK_DECLARE (sgen_interruption_mutex);
367
368 int current_collection_generation = -1;
369 volatile gboolean concurrent_collection_in_progress = FALSE;
370
371 /* objects that are ready to be finalized */
372 static SgenPointerQueue fin_ready_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_FINALIZE_READY);
373 static SgenPointerQueue critical_fin_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_FINALIZE_READY);
374
375 /* registered roots: the key to the hash is the root start address */
376 /* 
377  * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
378  */
379 SgenHashTable roots_hash [ROOT_TYPE_NUM] = {
380         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), sgen_aligned_addr_hash, NULL),
381         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), sgen_aligned_addr_hash, NULL),
382         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), sgen_aligned_addr_hash, NULL)
383 };
384 static mword roots_size = 0; /* amount of memory in the root set */
385
386 /* The size of a TLAB */
387 /* The bigger the value, the less often we have to go to the slow path to allocate a new 
388  * one, but the more space is wasted by threads not allocating much memory.
389  * FIXME: Tune this.
390  * FIXME: Make this self-tuning for each thread.
391  */
392 guint32 tlab_size = (1024 * 4);
393
394 #define MAX_SMALL_OBJ_SIZE      SGEN_MAX_SMALL_OBJ_SIZE
395
396 #define ALLOC_ALIGN             SGEN_ALLOC_ALIGN
397
398 #define ALIGN_UP                SGEN_ALIGN_UP
399
400 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
401 MonoNativeThreadId main_gc_thread = NULL;
402 #endif
403
404 /*Object was pinned during the current collection*/
405 static mword objects_pinned;
406
407 /*
408  * ######################################################################
409  * ########  Macros and function declarations.
410  * ######################################################################
411  */
412
413 typedef SgenGrayQueue GrayQueue;
414
415 /* forward declarations */
416 static void scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx);
417
418 static void pin_from_roots (void *start_nursery, void *end_nursery, ScanCopyContext ctx);
419 static void finish_gray_stack (int generation, ScanCopyContext ctx);
420
421
422 SgenMajorCollector major_collector;
423 SgenMinorCollector sgen_minor_collector;
424 /* FIXME: get rid of this */
425 static GrayQueue gray_queue;
426
427 static SgenRememberedSet remset;
428
429 /* The gray queue to use from the main collection thread. */
430 #define WORKERS_DISTRIBUTE_GRAY_QUEUE   (&gray_queue)
431
432 /*
433  * The gray queue a worker job must use.  If we're not parallel or
434  * concurrent, we use the main gray queue.
435  */
436 static SgenGrayQueue*
437 sgen_workers_get_job_gray_queue (WorkerData *worker_data)
438 {
439         return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
440 }
441
442 static void
443 gray_queue_redirect (SgenGrayQueue *queue)
444 {
445         gboolean wake = FALSE;
446
447         for (;;) {
448                 GrayQueueSection *section = sgen_gray_object_dequeue_section (queue);
449                 if (!section)
450                         break;
451                 sgen_section_gray_queue_enqueue (queue->alloc_prepare_data, section);
452                 wake = TRUE;
453         }
454
455         if (wake) {
456                 g_assert (concurrent_collection_in_progress);
457                 sgen_workers_ensure_awake ();
458         }
459 }
460
461 static void
462 gray_queue_enable_redirect (SgenGrayQueue *queue)
463 {
464         if (!concurrent_collection_in_progress)
465                 return;
466
467         sgen_gray_queue_set_alloc_prepare (queue, gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ());
468         gray_queue_redirect (queue);
469 }
470
471 void
472 sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
473 {
474         while (start < end) {
475                 size_t size;
476                 char *obj;
477
478                 if (!*(void**)start) {
479                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
480                         continue;
481                 }
482
483                 if (allow_flags) {
484                         if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
485                                 obj = start;
486                 } else {
487                         obj = start;
488                 }
489
490                 if (!sgen_client_object_is_array_fill ((GCObject*)obj)) {
491                         CHECK_CANARY_FOR_OBJECT (obj);
492                         size = ALIGN_UP (safe_object_get_size ((GCObject*)obj));
493                         callback (obj, size, data);
494                         CANARIFY_SIZE (size);
495                 } else {
496                         size = ALIGN_UP (safe_object_get_size ((GCObject*)obj));
497                 }
498
499                 start += size;
500         }
501 }
502
503 /*
504  * sgen_add_to_global_remset:
505  *
506  *   The global remset contains locations which point into newspace after
507  * a minor collection. This can happen if the objects they point to are pinned.
508  *
509  * LOCKING: If called from a parallel collector, the global remset
510  * lock must be held.  For serial collectors that is not necessary.
511  */
512 void
513 sgen_add_to_global_remset (gpointer ptr, gpointer obj)
514 {
515         SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Target pointer of global remset must be in the nursery");
516
517         HEAVY_STAT (++stat_wbarrier_add_to_global_remset);
518
519         if (!major_collector.is_concurrent) {
520                 SGEN_ASSERT (5, current_collection_generation != -1, "Global remsets can only be added during collections");
521         } else {
522                 if (current_collection_generation == -1)
523                         SGEN_ASSERT (5, sgen_concurrent_collection_in_progress (), "Global remsets outside of collection pauses can only be added by the concurrent collector");
524         }
525
526         if (!object_is_pinned (obj))
527                 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");
528         else if (sgen_cement_lookup_or_register (obj))
529                 return;
530
531         remset.record_pointer (ptr);
532
533         sgen_pin_stats_register_global_remset (obj);
534
535         SGEN_LOG (8, "Adding global remset for %p", ptr);
536         binary_protocol_global_remset (ptr, obj, (gpointer)SGEN_LOAD_VTABLE (obj));
537 }
538
539 /*
540  * sgen_drain_gray_stack:
541  *
542  *   Scan objects in the gray stack until the stack is empty. This should be called
543  * frequently after each object is copied, to achieve better locality and cache
544  * usage.
545  *
546  * max_objs is the maximum number of objects to scan, or -1 to scan until the stack is
547  * empty.
548  */
549 gboolean
550 sgen_drain_gray_stack (int max_objs, ScanCopyContext ctx)
551 {
552         ScanObjectFunc scan_func = ctx.ops->scan_object;
553         GrayQueue *queue = ctx.queue;
554
555         if (current_collection_generation == GENERATION_OLD && major_collector.drain_gray_stack)
556                 return major_collector.drain_gray_stack (ctx);
557
558         do {
559                 int i;
560                 for (i = 0; i != max_objs; ++i) {
561                         char *obj;
562                         mword desc;
563                         GRAY_OBJECT_DEQUEUE (queue, &obj, &desc);
564                         if (!obj)
565                                 return TRUE;
566                         SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
567                         scan_func (obj, desc, queue);
568                 }
569         } while (max_objs < 0);
570         return FALSE;
571 }
572
573 /*
574  * Addresses in the pin queue are already sorted. This function finds
575  * the object header for each address and pins the object. The
576  * addresses must be inside the nursery section.  The (start of the)
577  * address array is overwritten with the addresses of the actually
578  * pinned objects.  Return the number of pinned objects.
579  */
580 static int
581 pin_objects_from_nursery_pin_queue (gboolean do_scan_objects, ScanCopyContext ctx)
582 {
583         GCMemSection *section = nursery_section;
584         void **start =  sgen_pinning_get_entry (section->pin_queue_first_entry);
585         void **end = sgen_pinning_get_entry (section->pin_queue_last_entry);
586         void *start_nursery = section->data;
587         void *end_nursery = section->next_data;
588         void *last = NULL;
589         int count = 0;
590         void *search_start;
591         void *addr;
592         void *pinning_front = start_nursery;
593         size_t idx;
594         void **definitely_pinned = start;
595         ScanObjectFunc scan_func = ctx.ops->scan_object;
596         SgenGrayQueue *queue = ctx.queue;
597
598         sgen_nursery_allocator_prepare_for_pinning ();
599
600         while (start < end) {
601                 void *obj_to_pin = NULL;
602                 size_t obj_to_pin_size = 0;
603                 mword desc;
604
605                 addr = *start;
606
607                 SGEN_ASSERT (0, addr >= start_nursery && addr < end_nursery, "Potential pinning address out of range");
608                 SGEN_ASSERT (0, addr >= last, "Pin queue not sorted");
609
610                 if (addr == last) {
611                         ++start;
612                         continue;
613                 }
614
615                 SGEN_LOG (5, "Considering pinning addr %p", addr);
616                 /* We've already processed everything up to pinning_front. */
617                 if (addr < pinning_front) {
618                         start++;
619                         continue;
620                 }
621
622                 /*
623                  * Find the closest scan start <= addr.  We might search backward in the
624                  * scan_starts array because entries might be NULL.  In the worst case we
625                  * start at start_nursery.
626                  */
627                 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
628                 SGEN_ASSERT (0, idx < section->num_scan_start, "Scan start index out of range");
629                 search_start = (void*)section->scan_starts [idx];
630                 if (!search_start || search_start > addr) {
631                         while (idx) {
632                                 --idx;
633                                 search_start = section->scan_starts [idx];
634                                 if (search_start && search_start <= addr)
635                                         break;
636                         }
637                         if (!search_start || search_start > addr)
638                                 search_start = start_nursery;
639                 }
640
641                 /*
642                  * If the pinning front is closer than the scan start we found, start
643                  * searching at the front.
644                  */
645                 if (search_start < pinning_front)
646                         search_start = pinning_front;
647
648                 /*
649                  * Now addr should be in an object a short distance from search_start.
650                  *
651                  * search_start must point to zeroed mem or point to an object.
652                  */
653                 do {
654                         size_t obj_size, canarified_obj_size;
655
656                         /* Skip zeros. */
657                         if (!*(void**)search_start) {
658                                 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
659                                 /* The loop condition makes sure we don't overrun addr. */
660                                 continue;
661                         }
662
663                         canarified_obj_size = obj_size = ALIGN_UP (safe_object_get_size ((GCObject*)search_start));
664
665                         /*
666                          * Filler arrays are marked by an invalid sync word.  We don't
667                          * consider them for pinning.  They are not delimited by canaries,
668                          * either.
669                          */
670                         if (!sgen_client_object_is_array_fill ((GCObject*)search_start)) {
671                                 CHECK_CANARY_FOR_OBJECT (search_start);
672                                 CANARIFY_SIZE (canarified_obj_size);
673
674                                 if (addr >= search_start && (char*)addr < (char*)search_start + obj_size) {
675                                         /* This is the object we're looking for. */
676                                         obj_to_pin = search_start;
677                                         obj_to_pin_size = canarified_obj_size;
678                                         break;
679                                 }
680                         }
681
682                         /* Skip to the next object */
683                         search_start = (void*)((char*)search_start + canarified_obj_size);
684                 } while (search_start <= addr);
685
686                 /* We've searched past the address we were looking for. */
687                 if (!obj_to_pin) {
688                         pinning_front = search_start;
689                         goto next_pin_queue_entry;
690                 }
691
692                 /*
693                  * We've found an object to pin.  It might still be a dummy array, but we
694                  * can advance the pinning front in any case.
695                  */
696                 pinning_front = (char*)obj_to_pin + obj_to_pin_size;
697
698                 /*
699                  * If this is a dummy array marking the beginning of a nursery
700                  * fragment, we don't pin it.
701                  */
702                 if (sgen_client_object_is_array_fill ((GCObject*)obj_to_pin))
703                         goto next_pin_queue_entry;
704
705                 /*
706                  * Finally - pin the object!
707                  */
708                 desc = sgen_obj_get_descriptor_safe (obj_to_pin);
709                 if (do_scan_objects) {
710                         scan_func (obj_to_pin, desc, queue);
711                 } else {
712                         SGEN_LOG (4, "Pinned object %p, vtable %p (%s), count %d\n",
713                                         obj_to_pin, *(void**)obj_to_pin, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj_to_pin)), count);
714                         binary_protocol_pin (obj_to_pin,
715                                         (gpointer)LOAD_VTABLE (obj_to_pin),
716                                         safe_object_get_size (obj_to_pin));
717
718                         pin_object (obj_to_pin);
719                         GRAY_OBJECT_ENQUEUE (queue, obj_to_pin, desc);
720                         sgen_pin_stats_register_object (obj_to_pin, obj_to_pin_size);
721                         definitely_pinned [count] = obj_to_pin;
722                         count++;
723                 }
724
725         next_pin_queue_entry:
726                 last = addr;
727                 ++start;
728         }
729         sgen_client_nursery_objects_pinned (definitely_pinned, count);
730         stat_pinned_objects += count;
731         return count;
732 }
733
734 static void
735 pin_objects_in_nursery (gboolean do_scan_objects, ScanCopyContext ctx)
736 {
737         size_t reduced_to;
738
739         if (nursery_section->pin_queue_first_entry == nursery_section->pin_queue_last_entry)
740                 return;
741
742         reduced_to = pin_objects_from_nursery_pin_queue (do_scan_objects, ctx);
743         nursery_section->pin_queue_last_entry = nursery_section->pin_queue_first_entry + reduced_to;
744 }
745
746 /*
747  * This function is only ever called (via `collector_pin_object()` in `sgen-copy-object.h`)
748  * when we can't promote an object because we're out of memory.
749  */
750 void
751 sgen_pin_object (void *object, GrayQueue *queue)
752 {
753         /*
754          * All pinned objects are assumed to have been staged, so we need to stage as well.
755          * Also, the count of staged objects shows that "late pinning" happened.
756          */
757         sgen_pin_stage_ptr (object);
758
759         SGEN_PIN_OBJECT (object);
760         binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
761
762         ++objects_pinned;
763         sgen_pin_stats_register_object (object, safe_object_get_size (object));
764
765         GRAY_OBJECT_ENQUEUE (queue, object, sgen_obj_get_descriptor_safe (object));
766 }
767
768 /* Sort the addresses in array in increasing order.
769  * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
770  */
771 void
772 sgen_sort_addresses (void **array, size_t size)
773 {
774         size_t i;
775         void *tmp;
776
777         for (i = 1; i < size; ++i) {
778                 size_t child = i;
779                 while (child > 0) {
780                         size_t parent = (child - 1) / 2;
781
782                         if (array [parent] >= array [child])
783                                 break;
784
785                         tmp = array [parent];
786                         array [parent] = array [child];
787                         array [child] = tmp;
788
789                         child = parent;
790                 }
791         }
792
793         for (i = size - 1; i > 0; --i) {
794                 size_t end, root;
795                 tmp = array [i];
796                 array [i] = array [0];
797                 array [0] = tmp;
798
799                 end = i - 1;
800                 root = 0;
801
802                 while (root * 2 + 1 <= end) {
803                         size_t child = root * 2 + 1;
804
805                         if (child < end && array [child] < array [child + 1])
806                                 ++child;
807                         if (array [root] >= array [child])
808                                 break;
809
810                         tmp = array [root];
811                         array [root] = array [child];
812                         array [child] = tmp;
813
814                         root = child;
815                 }
816         }
817 }
818
819 /* 
820  * Scan the memory between start and end and queue values which could be pointers
821  * to the area between start_nursery and end_nursery for later consideration.
822  * Typically used for thread stacks.
823  */
824 void
825 sgen_conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
826 {
827         int count = 0;
828
829 #ifdef VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE
830         VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE (start, (char*)end - (char*)start);
831 #endif
832
833         while (start < end) {
834                 if (*start >= start_nursery && *start < end_nursery) {
835                         /*
836                          * *start can point to the middle of an object
837                          * note: should we handle pointing at the end of an object?
838                          * pinning in C# code disallows pointing at the end of an object
839                          * but there is some small chance that an optimizing C compiler
840                          * may keep the only reference to an object by pointing
841                          * at the end of it. We ignore this small chance for now.
842                          * Pointers to the end of an object are indistinguishable
843                          * from pointers to the start of the next object in memory
844                          * so if we allow that we'd need to pin two objects...
845                          * We queue the pointer in an array, the
846                          * array will then be sorted and uniqued. This way
847                          * we can coalesce several pinning pointers and it should
848                          * be faster since we'd do a memory scan with increasing
849                          * addresses. Note: we can align the address to the allocation
850                          * alignment, so the unique process is more effective.
851                          */
852                         mword addr = (mword)*start;
853                         addr &= ~(ALLOC_ALIGN - 1);
854                         if (addr >= (mword)start_nursery && addr < (mword)end_nursery) {
855                                 SGEN_LOG (6, "Pinning address %p from %p", (void*)addr, start);
856                                 sgen_pin_stage_ptr ((void*)addr);
857                                 binary_protocol_pin_stage (start, (void*)addr);
858                                 count++;
859                         }
860
861                         /*
862                          * FIXME: It seems we're registering objects from all over the heap
863                          * (at least from the nursery and the LOS), but we're only
864                          * registering pinned addresses in the nursery.  What's up with
865                          * that?
866                          *
867                          * Also, why wouldn't we register addresses once the pinning queue
868                          * is sorted and uniqued?
869                          */
870                         if (ptr_in_nursery ((void*)addr))
871                                 sgen_pin_stats_register_address ((char*)addr, pin_type);
872                 }
873                 start++;
874         }
875         if (count)
876                 SGEN_LOG (7, "found %d potential pinned heap pointers", count);
877 }
878
879 /*
880  * The first thing we do in a collection is to identify pinned objects.
881  * This function considers all the areas of memory that need to be
882  * conservatively scanned.
883  */
884 static void
885 pin_from_roots (void *start_nursery, void *end_nursery, ScanCopyContext ctx)
886 {
887         void **start_root;
888         RootRecord *root;
889         SGEN_LOG (2, "Scanning pinned roots (%d bytes, %d/%d entries)", (int)roots_size, roots_hash [ROOT_TYPE_NORMAL].num_entries, roots_hash [ROOT_TYPE_PINNED].num_entries);
890         /* objects pinned from the API are inside these roots */
891         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], start_root, root) {
892                 SGEN_LOG (6, "Pinned roots %p-%p", start_root, root->end_root);
893                 sgen_conservatively_pin_objects_from (start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
894         } SGEN_HASH_TABLE_FOREACH_END;
895         /* now deal with the thread stacks
896          * in the future we should be able to conservatively scan only:
897          * *) the cpu registers
898          * *) the unmanaged stack frames
899          * *) the _last_ managed stack frame
900          * *) pointers slots in managed frames
901          */
902         sgen_client_scan_thread_data (start_nursery, end_nursery, FALSE, ctx);
903 }
904
905 static void
906 unpin_objects_from_queue (SgenGrayQueue *queue)
907 {
908         for (;;) {
909                 char *addr;
910                 mword desc;
911                 GRAY_OBJECT_DEQUEUE (queue, &addr, &desc);
912                 if (!addr)
913                         break;
914                 g_assert (SGEN_OBJECT_IS_PINNED (addr));
915                 SGEN_UNPIN_OBJECT (addr);
916         }
917 }
918
919 static void
920 single_arg_user_copy_or_mark (void **obj, void *gc_data)
921 {
922         ScanCopyContext *ctx = gc_data;
923         ctx->ops->copy_or_mark_object (obj, ctx->queue);
924 }
925
926 /*
927  * The memory area from start_root to end_root contains pointers to objects.
928  * Their position is precisely described by @desc (this means that the pointer
929  * can be either NULL or the pointer to the start of an object).
930  * This functions copies them to to_space updates them.
931  *
932  * This function is not thread-safe!
933  */
934 static void
935 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc, ScanCopyContext ctx)
936 {
937         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
938         SgenGrayQueue *queue = ctx.queue;
939
940         switch (desc & ROOT_DESC_TYPE_MASK) {
941         case ROOT_DESC_BITMAP:
942                 desc >>= ROOT_DESC_TYPE_SHIFT;
943                 while (desc) {
944                         if ((desc & 1) && *start_root) {
945                                 copy_func (start_root, queue);
946                                 SGEN_LOG (9, "Overwrote root at %p with %p", start_root, *start_root);
947                         }
948                         desc >>= 1;
949                         start_root++;
950                 }
951                 return;
952         case ROOT_DESC_COMPLEX: {
953                 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
954                 gsize bwords = (*bitmap_data) - 1;
955                 void **start_run = start_root;
956                 bitmap_data++;
957                 while (bwords-- > 0) {
958                         gsize bmap = *bitmap_data++;
959                         void **objptr = start_run;
960                         while (bmap) {
961                                 if ((bmap & 1) && *objptr) {
962                                         copy_func (objptr, queue);
963                                         SGEN_LOG (9, "Overwrote root at %p with %p", objptr, *objptr);
964                                 }
965                                 bmap >>= 1;
966                                 ++objptr;
967                         }
968                         start_run += GC_BITS_PER_WORD;
969                 }
970                 break;
971         }
972         case ROOT_DESC_USER: {
973                 SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
974                 marker (start_root, single_arg_user_copy_or_mark, &ctx);
975                 break;
976         }
977         case ROOT_DESC_RUN_LEN:
978                 g_assert_not_reached ();
979         default:
980                 g_assert_not_reached ();
981         }
982 }
983
984 static void
985 reset_heap_boundaries (void)
986 {
987         lowest_heap_address = ~(mword)0;
988         highest_heap_address = 0;
989 }
990
991 void
992 sgen_update_heap_boundaries (mword low, mword high)
993 {
994         mword old;
995
996         do {
997                 old = lowest_heap_address;
998                 if (low >= old)
999                         break;
1000         } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
1001
1002         do {
1003                 old = highest_heap_address;
1004                 if (high <= old)
1005                         break;
1006         } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
1007 }
1008
1009 /*
1010  * Allocate and setup the data structures needed to be able to allocate objects
1011  * in the nursery. The nursery is stored in nursery_section.
1012  */
1013 static void
1014 alloc_nursery (void)
1015 {
1016         GCMemSection *section;
1017         char *data;
1018         size_t scan_starts;
1019         size_t alloc_size;
1020
1021         if (nursery_section)
1022                 return;
1023         SGEN_LOG (2, "Allocating nursery size: %zu", (size_t)sgen_nursery_size);
1024         /* later we will alloc a larger area for the nursery but only activate
1025          * what we need. The rest will be used as expansion if we have too many pinned
1026          * objects in the existing nursery.
1027          */
1028         /* FIXME: handle OOM */
1029         section = sgen_alloc_internal (INTERNAL_MEM_SECTION);
1030
1031         alloc_size = sgen_nursery_size;
1032
1033         /* If there isn't enough space even for the nursery we should simply abort. */
1034         g_assert (sgen_memgov_try_alloc_space (alloc_size, SPACE_NURSERY));
1035
1036         data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
1037         sgen_update_heap_boundaries ((mword)data, (mword)(data + sgen_nursery_size));
1038         SGEN_LOG (4, "Expanding nursery size (%p-%p): %lu, total: %lu", data, data + alloc_size, (unsigned long)sgen_nursery_size, (unsigned long)sgen_gc_get_total_heap_allocation ());
1039         section->data = section->next_data = data;
1040         section->size = alloc_size;
1041         section->end_data = data + sgen_nursery_size;
1042         scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
1043         section->scan_starts = sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS, TRUE);
1044         section->num_scan_start = scan_starts;
1045
1046         nursery_section = section;
1047
1048         sgen_nursery_allocator_set_nursery_bounds (data, data + sgen_nursery_size);
1049 }
1050
1051 FILE *
1052 mono_gc_get_logfile (void)
1053 {
1054         return gc_debug_file;
1055 }
1056
1057 static void
1058 scan_finalizer_entries (SgenPointerQueue *fin_queue, ScanCopyContext ctx)
1059 {
1060         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
1061         SgenGrayQueue *queue = ctx.queue;
1062         size_t i;
1063
1064         for (i = 0; i < fin_queue->next_slot; ++i) {
1065                 void *obj = fin_queue->data [i];
1066                 if (!obj)
1067                         continue;
1068                 SGEN_LOG (5, "Scan of fin ready object: %p (%s)\n", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
1069                 copy_func (&fin_queue->data [i], queue);
1070         }
1071 }
1072
1073 static const char*
1074 generation_name (int generation)
1075 {
1076         switch (generation) {
1077         case GENERATION_NURSERY: return "nursery";
1078         case GENERATION_OLD: return "old";
1079         default: g_assert_not_reached ();
1080         }
1081 }
1082
1083 const char*
1084 sgen_generation_name (int generation)
1085 {
1086         return generation_name (generation);
1087 }
1088
1089 static void
1090 finish_gray_stack (int generation, ScanCopyContext ctx)
1091 {
1092         TV_DECLARE (atv);
1093         TV_DECLARE (btv);
1094         int done_with_ephemerons, ephemeron_rounds = 0;
1095         char *start_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_start () : NULL;
1096         char *end_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_end () : (char*)-1;
1097         SgenGrayQueue *queue = ctx.queue;
1098
1099         /*
1100          * We copied all the reachable objects. Now it's the time to copy
1101          * the objects that were not referenced by the roots, but by the copied objects.
1102          * we built a stack of objects pointed to by gray_start: they are
1103          * additional roots and we may add more items as we go.
1104          * We loop until gray_start == gray_objects which means no more objects have
1105          * been added. Note this is iterative: no recursion is involved.
1106          * We need to walk the LO list as well in search of marked big objects
1107          * (use a flag since this is needed only on major collections). We need to loop
1108          * here as well, so keep a counter of marked LO (increasing it in copy_object).
1109          *   To achieve better cache locality and cache usage, we drain the gray stack 
1110          * frequently, after each object is copied, and just finish the work here.
1111          */
1112         sgen_drain_gray_stack (-1, ctx);
1113         TV_GETTIME (atv);
1114         SGEN_LOG (2, "%s generation done", generation_name (generation));
1115
1116         /*
1117         Reset bridge data, we might have lingering data from a previous collection if this is a major
1118         collection trigged by minor overflow.
1119
1120         We must reset the gathered bridges since their original block might be evacuated due to major
1121         fragmentation in the meanwhile and the bridge code should not have to deal with that.
1122         */
1123         if (sgen_client_bridge_need_processing ())
1124                 sgen_client_bridge_reset_data ();
1125
1126         /*
1127          * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
1128          * before processing finalizable objects and non-tracking weak links to avoid finalizing/clearing
1129          * objects that are in fact reachable.
1130          */
1131         done_with_ephemerons = 0;
1132         do {
1133                 done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
1134                 sgen_drain_gray_stack (-1, ctx);
1135                 ++ephemeron_rounds;
1136         } while (!done_with_ephemerons);
1137
1138         sgen_client_mark_togglerefs (start_addr, end_addr, ctx);
1139
1140         if (sgen_client_bridge_need_processing ()) {
1141                 /*Make sure the gray stack is empty before we process bridge objects so we get liveness right*/
1142                 sgen_drain_gray_stack (-1, ctx);
1143                 sgen_collect_bridge_objects (generation, ctx);
1144                 if (generation == GENERATION_OLD)
1145                         sgen_collect_bridge_objects (GENERATION_NURSERY, ctx);
1146
1147                 /*
1148                 Do the first bridge step here, as the collector liveness state will become useless after that.
1149
1150                 An important optimization is to only proccess the possibly dead part of the object graph and skip
1151                 over all live objects as we transitively know everything they point must be alive too.
1152
1153                 The above invariant is completely wrong if we let the gray queue be drained and mark/copy everything.
1154
1155                 This has the unfortunate side effect of making overflow collections perform the first step twice, but
1156                 given we now have heuristics that perform major GC in anticipation of minor overflows this should not
1157                 be a big deal.
1158                 */
1159                 sgen_client_bridge_processing_stw_step ();
1160         }
1161
1162         /*
1163         Make sure we drain the gray stack before processing disappearing links and finalizers.
1164         If we don't make sure it is empty we might wrongly see a live object as dead.
1165         */
1166         sgen_drain_gray_stack (-1, ctx);
1167
1168         /*
1169         We must clear weak links that don't track resurrection before processing object ready for
1170         finalization so they can be cleared before that.
1171         */
1172         sgen_null_link_in_range (generation, TRUE, ctx);
1173         if (generation == GENERATION_OLD)
1174                 sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx);
1175
1176
1177         /* walk the finalization queue and move also the objects that need to be
1178          * finalized: use the finalized objects as new roots so the objects they depend
1179          * on are also not reclaimed. As with the roots above, only objects in the nursery
1180          * are marked/copied.
1181          */
1182         sgen_finalize_in_range (generation, ctx);
1183         if (generation == GENERATION_OLD)
1184                 sgen_finalize_in_range (GENERATION_NURSERY, ctx);
1185         /* drain the new stack that might have been created */
1186         SGEN_LOG (6, "Precise scan of gray area post fin");
1187         sgen_drain_gray_stack (-1, ctx);
1188
1189         /*
1190          * This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
1191          */
1192         done_with_ephemerons = 0;
1193         do {
1194                 done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
1195                 sgen_drain_gray_stack (-1, ctx);
1196                 ++ephemeron_rounds;
1197         } while (!done_with_ephemerons);
1198
1199         sgen_client_clear_unreachable_ephemerons (ctx);
1200
1201         /*
1202          * We clear togglerefs only after all possible chances of revival are done. 
1203          * This is semantically more inline with what users expect and it allows for
1204          * user finalizers to correctly interact with TR objects.
1205         */
1206         sgen_client_clear_togglerefs (start_addr, end_addr, ctx);
1207
1208         TV_GETTIME (btv);
1209         SGEN_LOG (2, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
1210
1211         /*
1212          * handle disappearing links
1213          * Note we do this after checking the finalization queue because if an object
1214          * survives (at least long enough to be finalized) we don't clear the link.
1215          * This also deals with a possible issue with the monitor reclamation: with the Boehm
1216          * GC a finalized object my lose the monitor because it is cleared before the finalizer is
1217          * called.
1218          */
1219         g_assert (sgen_gray_object_queue_is_empty (queue));
1220         for (;;) {
1221                 sgen_null_link_in_range (generation, FALSE, ctx);
1222                 if (generation == GENERATION_OLD)
1223                         sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx);
1224                 if (sgen_gray_object_queue_is_empty (queue))
1225                         break;
1226                 sgen_drain_gray_stack (-1, ctx);
1227         }
1228
1229         g_assert (sgen_gray_object_queue_is_empty (queue));
1230
1231         sgen_gray_object_queue_trim_free_list (queue);
1232 }
1233
1234 void
1235 sgen_check_section_scan_starts (GCMemSection *section)
1236 {
1237         size_t i;
1238         for (i = 0; i < section->num_scan_start; ++i) {
1239                 if (section->scan_starts [i]) {
1240                         mword size = safe_object_get_size ((GCObject*) section->scan_starts [i]);
1241                         SGEN_ASSERT (0, size >= SGEN_CLIENT_MINIMUM_OBJECT_SIZE && size <= MAX_SMALL_OBJ_SIZE, "Weird object size at scan starts.");
1242                 }
1243         }
1244 }
1245
1246 static void
1247 check_scan_starts (void)
1248 {
1249         if (!do_scan_starts_check)
1250                 return;
1251         sgen_check_section_scan_starts (nursery_section);
1252         major_collector.check_scan_starts ();
1253 }
1254
1255 static void
1256 scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx)
1257 {
1258         void **start_root;
1259         RootRecord *root;
1260         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1261                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
1262                 precisely_scan_objects_from (start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, ctx);
1263         } SGEN_HASH_TABLE_FOREACH_END;
1264 }
1265
1266 static void
1267 init_stats (void)
1268 {
1269         static gboolean inited = FALSE;
1270
1271         if (inited)
1272                 return;
1273
1274         mono_counters_register ("Collection max time",  MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC, &time_max);
1275
1276         mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pre_collection_fragment_clear);
1277         mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pinning);
1278         mono_counters_register ("Minor scan remembered set", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_remsets);
1279         mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_pinned);
1280         mono_counters_register ("Minor scan roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_roots);
1281         mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_fragment_creation);
1282
1283         mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_pre_collection_fragment_clear);
1284         mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_pinning);
1285         mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_pinned);
1286         mono_counters_register ("Major scan roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_roots);
1287         mono_counters_register ("Major scan mod union", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_mod_union);
1288         mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_finish_gray_stack);
1289         mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_free_bigobjs);
1290         mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_los_sweep);
1291         mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_sweep);
1292         mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_fragment_creation);
1293
1294         mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_pinned_objects);
1295
1296 #ifdef HEAVY_STATISTICS
1297         mono_counters_register ("WBarrier remember pointer", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_add_to_global_remset);
1298         mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_field);
1299         mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_arrayref);
1300         mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_arrayref_copy);
1301         mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_generic_store);
1302         mono_counters_register ("WBarrier generic atomic store called", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_generic_store_atomic);
1303         mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_root);
1304         mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_value_copy);
1305         mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_object_copy);
1306
1307         mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_alloced_degraded);
1308         mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_bytes_alloced_degraded);
1309
1310         mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copy_object_called_nursery);
1311         mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_copied_nursery);
1312         mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copy_object_called_major);
1313         mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_copied_major);
1314
1315         mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scan_object_called_nursery);
1316         mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scan_object_called_major);
1317
1318         mono_counters_register ("Slots allocated in vain", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_slots_allocated_in_vain);
1319
1320         mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_from_space);
1321         mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_forwarded);
1322         mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_pinned);
1323         mono_counters_register ("# nursery copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_to_space);
1324
1325         sgen_nursery_allocator_init_heavy_stats ();
1326 #endif
1327
1328         inited = TRUE;
1329 }
1330
1331
1332 static void
1333 reset_pinned_from_failed_allocation (void)
1334 {
1335         bytes_pinned_from_failed_allocation = 0;
1336 }
1337
1338 void
1339 sgen_set_pinned_from_failed_allocation (mword objsize)
1340 {
1341         bytes_pinned_from_failed_allocation += objsize;
1342 }
1343
1344 gboolean
1345 sgen_collection_is_concurrent (void)
1346 {
1347         switch (current_collection_generation) {
1348         case GENERATION_NURSERY:
1349                 return FALSE;
1350         case GENERATION_OLD:
1351                 return concurrent_collection_in_progress;
1352         default:
1353                 g_error ("Invalid current generation %d", current_collection_generation);
1354         }
1355         return FALSE;
1356 }
1357
1358 gboolean
1359 sgen_concurrent_collection_in_progress (void)
1360 {
1361         return concurrent_collection_in_progress;
1362 }
1363
1364 typedef struct {
1365         SgenThreadPoolJob job;
1366         SgenObjectOperations *ops;
1367 } ScanJob;
1368
1369 static void
1370 job_remembered_set_scan (void *worker_data_untyped, SgenThreadPoolJob *job)
1371 {
1372         WorkerData *worker_data = worker_data_untyped;
1373         ScanJob *job_data = (ScanJob*)job;
1374         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1375         remset.scan_remsets (ctx);
1376 }
1377
1378 typedef struct {
1379         SgenThreadPoolJob job;
1380         SgenObjectOperations *ops;
1381         char *heap_start;
1382         char *heap_end;
1383         int root_type;
1384 } ScanFromRegisteredRootsJob;
1385
1386 static void
1387 job_scan_from_registered_roots (void *worker_data_untyped, SgenThreadPoolJob *job)
1388 {
1389         WorkerData *worker_data = worker_data_untyped;
1390         ScanFromRegisteredRootsJob *job_data = (ScanFromRegisteredRootsJob*)job;
1391         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1392
1393         scan_from_registered_roots (job_data->heap_start, job_data->heap_end, job_data->root_type, ctx);
1394 }
1395
1396 typedef struct {
1397         SgenThreadPoolJob job;
1398         SgenObjectOperations *ops;
1399         char *heap_start;
1400         char *heap_end;
1401 } ScanThreadDataJob;
1402
1403 static void
1404 job_scan_thread_data (void *worker_data_untyped, SgenThreadPoolJob *job)
1405 {
1406         WorkerData *worker_data = worker_data_untyped;
1407         ScanThreadDataJob *job_data = (ScanThreadDataJob*)job;
1408         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1409
1410         sgen_client_scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE, ctx);
1411 }
1412
1413 typedef struct {
1414         SgenThreadPoolJob job;
1415         SgenObjectOperations *ops;
1416         SgenPointerQueue *queue;
1417 } ScanFinalizerEntriesJob;
1418
1419 static void
1420 job_scan_finalizer_entries (void *worker_data_untyped, SgenThreadPoolJob *job)
1421 {
1422         WorkerData *worker_data = worker_data_untyped;
1423         ScanFinalizerEntriesJob *job_data = (ScanFinalizerEntriesJob*)job;
1424         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1425
1426         scan_finalizer_entries (job_data->queue, ctx);
1427 }
1428
1429 static void
1430 job_scan_major_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
1431 {
1432         WorkerData *worker_data = worker_data_untyped;
1433         ScanJob *job_data = (ScanJob*)job;
1434         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1435
1436         g_assert (concurrent_collection_in_progress);
1437         major_collector.scan_card_table (TRUE, ctx);
1438 }
1439
1440 static void
1441 job_scan_los_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
1442 {
1443         WorkerData *worker_data = worker_data_untyped;
1444         ScanJob *job_data = (ScanJob*)job;
1445         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1446
1447         g_assert (concurrent_collection_in_progress);
1448         sgen_los_scan_card_table (TRUE, ctx);
1449 }
1450
1451 static void
1452 init_gray_queue (void)
1453 {
1454         if (sgen_collection_is_concurrent ())
1455                 sgen_workers_init_distribute_gray_queue ();
1456         sgen_gray_object_queue_init (&gray_queue, NULL);
1457 }
1458
1459 static void
1460 enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperations *ops)
1461 {
1462         ScanFromRegisteredRootsJob *scrrj;
1463         ScanThreadDataJob *stdj;
1464         ScanFinalizerEntriesJob *sfej;
1465
1466         /* registered roots, this includes static fields */
1467
1468         scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots normal", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
1469         scrrj->ops = ops;
1470         scrrj->heap_start = heap_start;
1471         scrrj->heap_end = heap_end;
1472         scrrj->root_type = ROOT_TYPE_NORMAL;
1473         sgen_workers_enqueue_job (&scrrj->job);
1474
1475         scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots wbarrier", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
1476         scrrj->ops = ops;
1477         scrrj->heap_start = heap_start;
1478         scrrj->heap_end = heap_end;
1479         scrrj->root_type = ROOT_TYPE_WBARRIER;
1480         sgen_workers_enqueue_job (&scrrj->job);
1481
1482         /* Threads */
1483
1484         stdj = (ScanThreadDataJob*)sgen_thread_pool_job_alloc ("scan thread data", job_scan_thread_data, sizeof (ScanThreadDataJob));
1485         stdj->heap_start = heap_start;
1486         stdj->heap_end = heap_end;
1487         sgen_workers_enqueue_job (&stdj->job);
1488
1489         /* Scan the list of objects ready for finalization. */
1490
1491         sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
1492         sfej->queue = &fin_ready_queue;
1493         sfej->ops = ops;
1494         sgen_workers_enqueue_job (&sfej->job);
1495
1496         sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan critical finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
1497         sfej->queue = &critical_fin_queue;
1498         sfej->ops = ops;
1499         sgen_workers_enqueue_job (&sfej->job);
1500 }
1501
1502 /*
1503  * Perform a nursery collection.
1504  *
1505  * Return whether any objects were late-pinned due to being out of memory.
1506  */
1507 static gboolean
1508 collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
1509 {
1510         gboolean needs_major;
1511         size_t max_garbage_amount;
1512         char *nursery_next;
1513         mword fragment_total;
1514         ScanJob *sj;
1515         SgenObjectOperations *object_ops = &sgen_minor_collector.serial_ops;
1516         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, &gray_queue);
1517         TV_DECLARE (atv);
1518         TV_DECLARE (btv);
1519
1520         if (disable_minor_collections)
1521                 return TRUE;
1522
1523         TV_GETTIME (last_minor_collection_start_tv);
1524         atv = last_minor_collection_start_tv;
1525
1526         binary_protocol_collection_begin (gc_stats.minor_gc_count, GENERATION_NURSERY);
1527
1528         if (do_verify_nursery || do_dump_nursery_content)
1529                 sgen_debug_verify_nursery (do_dump_nursery_content);
1530
1531         current_collection_generation = GENERATION_NURSERY;
1532
1533         SGEN_ASSERT (0, !sgen_collection_is_concurrent (), "Why is the nursery collection concurrent?");
1534
1535         reset_pinned_from_failed_allocation ();
1536
1537         check_scan_starts ();
1538
1539         sgen_nursery_alloc_prepare_for_minor ();
1540
1541         degraded_mode = 0;
1542         objects_pinned = 0;
1543         nursery_next = sgen_nursery_alloc_get_upper_alloc_bound ();
1544         /* FIXME: optimize later to use the higher address where an object can be present */
1545         nursery_next = MAX (nursery_next, sgen_get_nursery_end ());
1546
1547         SGEN_LOG (1, "Start nursery collection %d %p-%p, size: %d", gc_stats.minor_gc_count, sgen_get_nursery_start (), nursery_next, (int)(nursery_next - sgen_get_nursery_start ()));
1548         max_garbage_amount = nursery_next - sgen_get_nursery_start ();
1549         g_assert (nursery_section->size >= max_garbage_amount);
1550
1551         /* world must be stopped already */
1552         TV_GETTIME (btv);
1553         time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
1554
1555         sgen_client_pre_collection_checks ();
1556
1557         nursery_section->next_data = nursery_next;
1558
1559         major_collector.start_nursery_collection ();
1560
1561         sgen_memgov_minor_collection_start ();
1562
1563         init_gray_queue ();
1564
1565         gc_stats.minor_gc_count ++;
1566
1567         if (whole_heap_check_before_collection) {
1568                 sgen_clear_nursery_fragments ();
1569                 sgen_check_whole_heap (finish_up_concurrent_mark);
1570         }
1571         if (consistency_check_at_minor_collection)
1572                 sgen_check_consistency ();
1573
1574         sgen_process_fin_stage_entries ();
1575         sgen_process_dislink_stage_entries ();
1576
1577         /* pin from pinned handles */
1578         sgen_init_pinning ();
1579         sgen_client_binary_protocol_mark_start (GENERATION_NURSERY);
1580         pin_from_roots (sgen_get_nursery_start (), nursery_next, ctx);
1581         /* pin cemented objects */
1582         sgen_pin_cemented_objects ();
1583         /* identify pinned objects */
1584         sgen_optimize_pin_queue ();
1585         sgen_pinning_setup_section (nursery_section);
1586
1587         pin_objects_in_nursery (FALSE, ctx);
1588         sgen_pinning_trim_queue_to_section (nursery_section);
1589
1590         TV_GETTIME (atv);
1591         time_minor_pinning += TV_ELAPSED (btv, atv);
1592         SGEN_LOG (2, "Finding pinned pointers: %zd in %d usecs", sgen_get_pinned_count (), TV_ELAPSED (btv, atv));
1593         SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
1594
1595         /*
1596          * FIXME: When we finish a concurrent collection we do a nursery collection first,
1597          * as part of which we scan the card table.  Then, later, we scan the mod union
1598          * cardtable.  We should only have to do one.
1599          */
1600         sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan remset", job_remembered_set_scan, sizeof (ScanJob));
1601         sj->ops = object_ops;
1602         sgen_workers_enqueue_job (&sj->job);
1603
1604         /* we don't have complete write barrier yet, so we scan all the old generation sections */
1605         TV_GETTIME (btv);
1606         time_minor_scan_remsets += TV_ELAPSED (atv, btv);
1607         SGEN_LOG (2, "Old generation scan: %d usecs", TV_ELAPSED (atv, btv));
1608
1609         sgen_drain_gray_stack (-1, ctx);
1610
1611         /* FIXME: Why do we do this at this specific, seemingly random, point? */
1612         sgen_client_collecting_minor (&fin_ready_queue, &critical_fin_queue);
1613
1614         TV_GETTIME (atv);
1615         time_minor_scan_pinned += TV_ELAPSED (btv, atv);
1616
1617         enqueue_scan_from_roots_jobs (sgen_get_nursery_start (), nursery_next, object_ops);
1618
1619         TV_GETTIME (btv);
1620         time_minor_scan_roots += TV_ELAPSED (atv, btv);
1621
1622         finish_gray_stack (GENERATION_NURSERY, ctx);
1623
1624         TV_GETTIME (atv);
1625         time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
1626         sgen_client_binary_protocol_mark_end (GENERATION_NURSERY);
1627
1628         if (objects_pinned) {
1629                 sgen_optimize_pin_queue ();
1630                 sgen_pinning_setup_section (nursery_section);
1631         }
1632
1633         /* walk the pin_queue, build up the fragment list of free memory, unmark
1634          * pinned objects as we go, memzero() the empty fragments so they are ready for the
1635          * next allocations.
1636          */
1637         sgen_client_binary_protocol_reclaim_start (GENERATION_NURSERY);
1638         fragment_total = sgen_build_nursery_fragments (nursery_section, unpin_queue);
1639         if (!fragment_total)
1640                 degraded_mode = 1;
1641
1642         /* Clear TLABs for all threads */
1643         sgen_clear_tlabs ();
1644
1645         sgen_client_binary_protocol_reclaim_end (GENERATION_NURSERY);
1646         TV_GETTIME (btv);
1647         time_minor_fragment_creation += TV_ELAPSED (atv, btv);
1648         SGEN_LOG (2, "Fragment creation: %d usecs, %lu bytes available", TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
1649
1650         if (consistency_check_at_minor_collection)
1651                 sgen_check_major_refs ();
1652
1653         major_collector.finish_nursery_collection ();
1654
1655         TV_GETTIME (last_minor_collection_end_tv);
1656         gc_stats.minor_gc_time += TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
1657
1658         sgen_debug_dump_heap ("minor", gc_stats.minor_gc_count - 1, NULL);
1659
1660         /* prepare the pin queue for the next collection */
1661         sgen_finish_pinning ();
1662         if (sgen_have_pending_finalizers ()) {
1663                 SGEN_LOG (4, "Finalizer-thread wakeup");
1664                 sgen_client_finalize_notify ();
1665         }
1666         sgen_pin_stats_reset ();
1667         /* clear cemented hash */
1668         sgen_cement_clear_below_threshold ();
1669
1670         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
1671
1672         remset.finish_minor_collection ();
1673
1674         check_scan_starts ();
1675
1676         binary_protocol_flush_buffers (FALSE);
1677
1678         sgen_memgov_minor_collection_end ();
1679
1680         /*objects are late pinned because of lack of memory, so a major is a good call*/
1681         needs_major = objects_pinned > 0;
1682         current_collection_generation = -1;
1683         objects_pinned = 0;
1684
1685         binary_protocol_collection_end (gc_stats.minor_gc_count - 1, GENERATION_NURSERY, 0, 0);
1686
1687         if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
1688                 sgen_check_nursery_objects_pinned (unpin_queue != NULL);
1689
1690         return needs_major;
1691 }
1692
1693 static void
1694 scan_nursery_objects_callback (char *obj, size_t size, ScanCopyContext *ctx)
1695 {
1696         /*
1697          * This is called on all objects in the nursery, including pinned ones, so we need
1698          * to use sgen_obj_get_descriptor_safe(), which masks out the vtable tag bits.
1699          */
1700         ctx->ops->scan_object (obj, sgen_obj_get_descriptor_safe (obj), ctx->queue);
1701 }
1702
1703 static void
1704 scan_nursery_objects (ScanCopyContext ctx)
1705 {
1706         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1707                         (IterateObjectCallbackFunc)scan_nursery_objects_callback, (void*)&ctx, FALSE);
1708 }
1709
1710 typedef enum {
1711         COPY_OR_MARK_FROM_ROOTS_SERIAL,
1712         COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT,
1713         COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT
1714 } CopyOrMarkFromRootsMode;
1715
1716 static void
1717 major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMode mode, gboolean scan_whole_nursery, SgenObjectOperations *object_ops)
1718 {
1719         LOSObject *bigobj;
1720         TV_DECLARE (atv);
1721         TV_DECLARE (btv);
1722         /* FIXME: only use these values for the precise scan
1723          * note that to_space pointers should be excluded anyway...
1724          */
1725         char *heap_start = NULL;
1726         char *heap_end = (char*)-1;
1727         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, WORKERS_DISTRIBUTE_GRAY_QUEUE);
1728         gboolean concurrent = mode != COPY_OR_MARK_FROM_ROOTS_SERIAL;
1729
1730         SGEN_ASSERT (0, !!concurrent == !!concurrent_collection_in_progress, "We've been called with the wrong mode.");
1731
1732         if (scan_whole_nursery)
1733                 SGEN_ASSERT (0, mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, "Scanning whole nursery only makes sense when we're finishing a concurrent collection.");
1734
1735         if (concurrent) {
1736                 /*This cleans up unused fragments */
1737                 sgen_nursery_allocator_prepare_for_pinning ();
1738
1739                 if (do_concurrent_checks)
1740                         sgen_debug_check_nursery_is_clean ();
1741         } else {
1742                 /* The concurrent collector doesn't touch the nursery. */
1743                 sgen_nursery_alloc_prepare_for_major ();
1744         }
1745
1746         init_gray_queue ();
1747
1748         TV_GETTIME (atv);
1749
1750         /* Pinning depends on this */
1751         sgen_clear_nursery_fragments ();
1752
1753         if (whole_heap_check_before_collection)
1754                 sgen_check_whole_heap (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT);
1755
1756         TV_GETTIME (btv);
1757         time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
1758
1759         if (!sgen_collection_is_concurrent ())
1760                 nursery_section->next_data = sgen_get_nursery_end ();
1761         /* we should also coalesce scanning from sections close to each other
1762          * and deal with pointers outside of the sections later.
1763          */
1764
1765         objects_pinned = 0;
1766
1767         sgen_client_pre_collection_checks ();
1768
1769         if (!concurrent) {
1770                 /* Remsets are not useful for a major collection */
1771                 remset.clear_cards ();
1772         }
1773
1774         sgen_process_fin_stage_entries ();
1775         sgen_process_dislink_stage_entries ();
1776
1777         TV_GETTIME (atv);
1778         sgen_init_pinning ();
1779         SGEN_LOG (6, "Collecting pinned addresses");
1780         pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, ctx);
1781
1782         if (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
1783                 if (major_collector.is_concurrent) {
1784                         /*
1785                          * The concurrent major collector cannot evict
1786                          * yet, so we need to pin cemented objects to
1787                          * not break some asserts.
1788                          *
1789                          * FIXME: We could evict now!
1790                          */
1791                         sgen_pin_cemented_objects ();
1792                 }
1793         }
1794
1795         sgen_optimize_pin_queue ();
1796
1797         sgen_client_collecting_major_1 ();
1798
1799         /*
1800          * pin_queue now contains all candidate pointers, sorted and
1801          * uniqued.  We must do two passes now to figure out which
1802          * objects are pinned.
1803          *
1804          * The first is to find within the pin_queue the area for each
1805          * section.  This requires that the pin_queue be sorted.  We
1806          * also process the LOS objects and pinned chunks here.
1807          *
1808          * The second, destructive, pass is to reduce the section
1809          * areas to pointers to the actually pinned objects.
1810          */
1811         SGEN_LOG (6, "Pinning from sections");
1812         /* first pass for the sections */
1813         sgen_find_section_pin_queue_start_end (nursery_section);
1814         /* identify possible pointers to the insize of large objects */
1815         SGEN_LOG (6, "Pinning from large objects");
1816         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
1817                 size_t dummy;
1818                 if (sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + sgen_los_object_size (bigobj), &dummy, &dummy)) {
1819                         binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (((GCObject*)(bigobj->data))));
1820
1821                         if (sgen_los_object_is_pinned (bigobj->data)) {
1822                                 SGEN_ASSERT (0, mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, "LOS objects can only be pinned here after concurrent marking.");
1823                                 continue;
1824                         }
1825                         sgen_los_pin_object (bigobj->data);
1826                         if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
1827                                 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data, sgen_obj_get_descriptor (bigobj->data));
1828                         sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((GCObject*) bigobj->data));
1829                         SGEN_LOG (6, "Marked large object %p (%s) size: %lu from roots", bigobj->data,
1830                                         sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (bigobj->data)),
1831                                         (unsigned long)sgen_los_object_size (bigobj));
1832
1833                         sgen_client_pinned_los_object (bigobj->data);
1834                 }
1835         }
1836         /* second pass for the sections */
1837
1838         /*
1839          * Concurrent mark never follows references into the nursery.  In the start and
1840          * finish pauses we must scan live nursery objects, though.
1841          *
1842          * In the finish pause we do this conservatively by scanning all nursery objects.
1843          * Previously we would only scan pinned objects here.  We assumed that all objects
1844          * that were pinned during the nursery collection immediately preceding this finish
1845          * mark would be pinned again here.  Due to the way we get the stack end for the GC
1846          * thread, however, that's not necessarily the case: we scan part of the stack used
1847          * by the GC itself, which changes constantly, so pinning isn't entirely
1848          * deterministic.
1849          *
1850          * The split nursery also complicates things because non-pinned objects can survive
1851          * in the nursery.  That's why we need to do a full scan of the nursery for it, too.
1852          *
1853          * In the future we shouldn't do a preceding nursery collection at all and instead
1854          * do the finish pause with promotion from the nursery.
1855          *
1856          * A further complication arises when we have late-pinned objects from the preceding
1857          * nursery collection.  Those are the result of being out of memory when trying to
1858          * evacuate objects.  They won't be found from the roots, so we just scan the whole
1859          * nursery.
1860          *
1861          * Non-concurrent mark evacuates from the nursery, so it's
1862          * sufficient to just scan pinned nursery objects.
1863          */
1864         if (scan_whole_nursery || mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT || (concurrent && sgen_minor_collector.is_split)) {
1865                 scan_nursery_objects (ctx);
1866         } else {
1867                 pin_objects_in_nursery (concurrent, ctx);
1868                 if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
1869                         sgen_check_nursery_objects_pinned (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
1870         }
1871
1872         major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
1873         if (old_next_pin_slot)
1874                 *old_next_pin_slot = sgen_get_pinned_count ();
1875
1876         TV_GETTIME (btv);
1877         time_major_pinning += TV_ELAPSED (atv, btv);
1878         SGEN_LOG (2, "Finding pinned pointers: %zd in %d usecs", sgen_get_pinned_count (), TV_ELAPSED (atv, btv));
1879         SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
1880
1881         major_collector.init_to_space ();
1882
1883         /*
1884          * The concurrent collector doesn't move objects, neither on
1885          * the major heap nor in the nursery, so we can mark even
1886          * before pinning has finished.  For the non-concurrent
1887          * collector we start the workers after pinning.
1888          */
1889         if (mode != COPY_OR_MARK_FROM_ROOTS_SERIAL) {
1890                 SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
1891                 sgen_workers_start_all_workers (object_ops);
1892                 gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
1893         }
1894
1895 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
1896         main_gc_thread = mono_native_thread_self ();
1897 #endif
1898
1899         sgen_client_collecting_major_2 ();
1900
1901         TV_GETTIME (atv);
1902         time_major_scan_pinned += TV_ELAPSED (btv, atv);
1903
1904         sgen_client_collecting_major_3 (&fin_ready_queue, &critical_fin_queue);
1905
1906         /*
1907          * FIXME: is this the right context?  It doesn't seem to contain a copy function
1908          * unless we're concurrent.
1909          */
1910         enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops);
1911
1912         TV_GETTIME (btv);
1913         time_major_scan_roots += TV_ELAPSED (atv, btv);
1914
1915         if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
1916                 ScanJob *sj;
1917
1918                 /* Mod union card table */
1919                 sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan mod union cardtable", job_scan_major_mod_union_card_table, sizeof (ScanJob));
1920                 sj->ops = object_ops;
1921                 sgen_workers_enqueue_job (&sj->job);
1922
1923                 sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan LOS mod union cardtable", job_scan_los_mod_union_card_table, sizeof (ScanJob));
1924                 sj->ops = object_ops;
1925                 sgen_workers_enqueue_job (&sj->job);
1926
1927                 TV_GETTIME (atv);
1928                 time_major_scan_mod_union += TV_ELAPSED (btv, atv);
1929         }
1930 }
1931
1932 static void
1933 major_finish_copy_or_mark (void)
1934 {
1935         if (!concurrent_collection_in_progress)
1936                 return;
1937
1938         /*
1939          * Prepare the pin queue for the next collection.  Since pinning runs on the worker
1940          * threads we must wait for the jobs to finish before we can reset it.
1941          */
1942         sgen_workers_wait_for_jobs_finished ();
1943         sgen_finish_pinning ();
1944
1945         sgen_pin_stats_reset ();
1946
1947         if (do_concurrent_checks)
1948                 sgen_debug_check_nursery_is_clean ();
1949 }
1950
1951 static void
1952 major_start_collection (gboolean concurrent, size_t *old_next_pin_slot)
1953 {
1954         SgenObjectOperations *object_ops;
1955
1956         binary_protocol_collection_begin (gc_stats.major_gc_count, GENERATION_OLD);
1957
1958         current_collection_generation = GENERATION_OLD;
1959
1960         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
1961
1962         sgen_cement_reset ();
1963
1964         if (concurrent) {
1965                 g_assert (major_collector.is_concurrent);
1966                 concurrent_collection_in_progress = TRUE;
1967
1968                 object_ops = &major_collector.major_ops_concurrent_start;
1969         } else {
1970                 object_ops = &major_collector.major_ops_serial;
1971         }
1972
1973         reset_pinned_from_failed_allocation ();
1974
1975         sgen_memgov_major_collection_start ();
1976
1977         //count_ref_nonref_objs ();
1978         //consistency_check ();
1979
1980         check_scan_starts ();
1981
1982         degraded_mode = 0;
1983         SGEN_LOG (1, "Start major collection %d", gc_stats.major_gc_count);
1984         gc_stats.major_gc_count ++;
1985
1986         if (major_collector.start_major_collection)
1987                 major_collector.start_major_collection ();
1988
1989         major_copy_or_mark_from_roots (old_next_pin_slot, concurrent ? COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT : COPY_OR_MARK_FROM_ROOTS_SERIAL, FALSE, object_ops);
1990         major_finish_copy_or_mark ();
1991 }
1992
1993 static void
1994 major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean forced, gboolean scan_whole_nursery)
1995 {
1996         ScannedObjectCounts counts;
1997         SgenObjectOperations *object_ops;
1998         TV_DECLARE (atv);
1999         TV_DECLARE (btv);
2000
2001         TV_GETTIME (btv);
2002
2003         if (concurrent_collection_in_progress) {
2004                 object_ops = &major_collector.major_ops_concurrent_finish;
2005
2006                 major_copy_or_mark_from_roots (NULL, COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, scan_whole_nursery, object_ops);
2007
2008                 major_finish_copy_or_mark ();
2009
2010                 sgen_workers_join ();
2011
2012                 SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty after workers have finished working?");
2013
2014 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2015                 main_gc_thread = NULL;
2016 #endif
2017
2018                 if (do_concurrent_checks)
2019                         sgen_debug_check_nursery_is_clean ();
2020         } else {
2021                 SGEN_ASSERT (0, !scan_whole_nursery, "scan_whole_nursery only applies to concurrent collections");
2022                 object_ops = &major_collector.major_ops_serial;
2023         }
2024
2025         /*
2026          * The workers have stopped so we need to finish gray queue
2027          * work that might result from finalization in the main GC
2028          * thread.  Redirection must therefore be turned off.
2029          */
2030         sgen_gray_object_queue_disable_alloc_prepare (&gray_queue);
2031         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
2032
2033         /* all the objects in the heap */
2034         finish_gray_stack (GENERATION_OLD, CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, &gray_queue));
2035         TV_GETTIME (atv);
2036         time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
2037
2038         SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after joining");
2039
2040         if (objects_pinned) {
2041                 g_assert (!concurrent_collection_in_progress);
2042
2043                 /*
2044                  * This is slow, but we just OOM'd.
2045                  *
2046                  * See comment at `sgen_pin_queue_clear_discarded_entries` for how the pin
2047                  * queue is laid out at this point.
2048                  */
2049                 sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
2050                 /*
2051                  * We need to reestablish all pinned nursery objects in the pin queue
2052                  * because they're needed for fragment creation.  Unpinning happens by
2053                  * walking the whole queue, so it's not necessary to reestablish where major
2054                  * heap block pins are - all we care is that they're still in there
2055                  * somewhere.
2056                  */
2057                 sgen_optimize_pin_queue ();
2058                 sgen_find_section_pin_queue_start_end (nursery_section);
2059                 objects_pinned = 0;
2060         }
2061
2062         reset_heap_boundaries ();
2063         sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
2064
2065         if (!concurrent_collection_in_progress) {
2066                 /* walk the pin_queue, build up the fragment list of free memory, unmark
2067                  * pinned objects as we go, memzero() the empty fragments so they are ready for the
2068                  * next allocations.
2069                  */
2070                 if (!sgen_build_nursery_fragments (nursery_section, NULL))
2071                         degraded_mode = 1;
2072
2073                 /* prepare the pin queue for the next collection */
2074                 sgen_finish_pinning ();
2075
2076                 /* Clear TLABs for all threads */
2077                 sgen_clear_tlabs ();
2078
2079                 sgen_pin_stats_reset ();
2080         }
2081
2082         sgen_cement_clear_below_threshold ();
2083
2084         if (check_mark_bits_after_major_collection)
2085                 sgen_check_heap_marked (concurrent_collection_in_progress);
2086
2087         TV_GETTIME (btv);
2088         time_major_fragment_creation += TV_ELAPSED (atv, btv);
2089
2090         binary_protocol_sweep_begin (GENERATION_OLD, !major_collector.sweeps_lazily);
2091
2092         TV_GETTIME (atv);
2093         time_major_free_bigobjs += TV_ELAPSED (btv, atv);
2094
2095         sgen_los_sweep ();
2096
2097         TV_GETTIME (btv);
2098         time_major_los_sweep += TV_ELAPSED (atv, btv);
2099
2100         major_collector.sweep ();
2101
2102         binary_protocol_sweep_end (GENERATION_OLD, !major_collector.sweeps_lazily);
2103
2104         TV_GETTIME (atv);
2105         time_major_sweep += TV_ELAPSED (btv, atv);
2106
2107         sgen_debug_dump_heap ("major", gc_stats.major_gc_count - 1, reason);
2108
2109         if (sgen_have_pending_finalizers ()) {
2110                 SGEN_LOG (4, "Finalizer-thread wakeup");
2111                 sgen_client_finalize_notify ();
2112         }
2113
2114         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2115
2116         sgen_memgov_major_collection_end (forced);
2117         current_collection_generation = -1;
2118
2119         memset (&counts, 0, sizeof (ScannedObjectCounts));
2120         major_collector.finish_major_collection (&counts);
2121
2122         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
2123
2124         SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after major collection has finished");
2125         if (concurrent_collection_in_progress)
2126                 concurrent_collection_in_progress = FALSE;
2127
2128         check_scan_starts ();
2129
2130         binary_protocol_flush_buffers (FALSE);
2131
2132         //consistency_check ();
2133
2134         binary_protocol_collection_end (gc_stats.major_gc_count - 1, GENERATION_OLD, counts.num_scanned_objects, counts.num_unique_scanned_objects);
2135 }
2136
2137 static gboolean
2138 major_do_collection (const char *reason, gboolean forced)
2139 {
2140         TV_DECLARE (time_start);
2141         TV_DECLARE (time_end);
2142         size_t old_next_pin_slot;
2143
2144         if (disable_major_collections)
2145                 return FALSE;
2146
2147         if (major_collector.get_and_reset_num_major_objects_marked) {
2148                 long long num_marked = major_collector.get_and_reset_num_major_objects_marked ();
2149                 g_assert (!num_marked);
2150         }
2151
2152         /* world must be stopped already */
2153         TV_GETTIME (time_start);
2154
2155         major_start_collection (FALSE, &old_next_pin_slot);
2156         major_finish_collection (reason, old_next_pin_slot, forced, FALSE);
2157
2158         TV_GETTIME (time_end);
2159         gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
2160
2161         /* FIXME: also report this to the user, preferably in gc-end. */
2162         if (major_collector.get_and_reset_num_major_objects_marked)
2163                 major_collector.get_and_reset_num_major_objects_marked ();
2164
2165         return bytes_pinned_from_failed_allocation > 0;
2166 }
2167
2168 static void
2169 major_start_concurrent_collection (const char *reason)
2170 {
2171         TV_DECLARE (time_start);
2172         TV_DECLARE (time_end);
2173         long long num_objects_marked;
2174
2175         if (disable_major_collections)
2176                 return;
2177
2178         TV_GETTIME (time_start);
2179         SGEN_TV_GETTIME (time_major_conc_collection_start);
2180
2181         num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
2182         g_assert (num_objects_marked == 0);
2183
2184         binary_protocol_concurrent_start ();
2185
2186         // FIXME: store reason and pass it when finishing
2187         major_start_collection (TRUE, NULL);
2188
2189         gray_queue_redirect (&gray_queue);
2190
2191         num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
2192
2193         TV_GETTIME (time_end);
2194         gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
2195
2196         current_collection_generation = -1;
2197 }
2198
2199 /*
2200  * Returns whether the major collection has finished.
2201  */
2202 static gboolean
2203 major_should_finish_concurrent_collection (void)
2204 {
2205         SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty before we have started doing anything?");
2206         return sgen_workers_all_done ();
2207 }
2208
2209 static void
2210 major_update_concurrent_collection (void)
2211 {
2212         TV_DECLARE (total_start);
2213         TV_DECLARE (total_end);
2214
2215         TV_GETTIME (total_start);
2216
2217         binary_protocol_concurrent_update ();
2218
2219         major_collector.update_cardtable_mod_union ();
2220         sgen_los_update_cardtable_mod_union ();
2221
2222         TV_GETTIME (total_end);
2223         gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end);
2224 }
2225
2226 static void
2227 major_finish_concurrent_collection (gboolean forced)
2228 {
2229         TV_DECLARE (total_start);
2230         TV_DECLARE (total_end);
2231         gboolean late_pinned;
2232         SgenGrayQueue unpin_queue;
2233         memset (&unpin_queue, 0, sizeof (unpin_queue));
2234
2235         TV_GETTIME (total_start);
2236
2237         binary_protocol_concurrent_finish ();
2238
2239         /*
2240          * The major collector can add global remsets which are processed in the finishing
2241          * nursery collection, below.  That implies that the workers must have finished
2242          * marking before the nursery collection is allowed to run, otherwise we might miss
2243          * some remsets.
2244          */
2245         sgen_workers_wait ();
2246
2247         SGEN_TV_GETTIME (time_major_conc_collection_end);
2248         gc_stats.major_gc_time_concurrent += SGEN_TV_ELAPSED (time_major_conc_collection_start, time_major_conc_collection_end);
2249
2250         major_collector.update_cardtable_mod_union ();
2251         sgen_los_update_cardtable_mod_union ();
2252
2253         late_pinned = collect_nursery (&unpin_queue, TRUE);
2254
2255         if (mod_union_consistency_check)
2256                 sgen_check_mod_union_consistency ();
2257
2258         current_collection_generation = GENERATION_OLD;
2259         major_finish_collection ("finishing", -1, forced, late_pinned);
2260
2261         if (whole_heap_check_before_collection)
2262                 sgen_check_whole_heap (FALSE);
2263
2264         unpin_objects_from_queue (&unpin_queue);
2265         sgen_gray_object_queue_deinit (&unpin_queue);
2266
2267         TV_GETTIME (total_end);
2268         gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end) - TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
2269
2270         current_collection_generation = -1;
2271 }
2272
2273 /*
2274  * Ensure an allocation request for @size will succeed by freeing enough memory.
2275  *
2276  * LOCKING: The GC lock MUST be held.
2277  */
2278 void
2279 sgen_ensure_free_space (size_t size)
2280 {
2281         int generation_to_collect = -1;
2282         const char *reason = NULL;
2283
2284         if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
2285                 if (sgen_need_major_collection (size)) {
2286                         reason = "LOS overflow";
2287                         generation_to_collect = GENERATION_OLD;
2288                 }
2289         } else {
2290                 if (degraded_mode) {
2291                         if (sgen_need_major_collection (size)) {
2292                                 reason = "Degraded mode overflow";
2293                                 generation_to_collect = GENERATION_OLD;
2294                         }
2295                 } else if (sgen_need_major_collection (size)) {
2296                         reason = "Minor allowance";
2297                         generation_to_collect = GENERATION_OLD;
2298                 } else {
2299                         generation_to_collect = GENERATION_NURSERY;
2300                         reason = "Nursery full";                        
2301                 }
2302         }
2303
2304         if (generation_to_collect == -1) {
2305                 if (concurrent_collection_in_progress && sgen_workers_all_done ()) {
2306                         generation_to_collect = GENERATION_OLD;
2307                         reason = "Finish concurrent collection";
2308                 }
2309         }
2310
2311         if (generation_to_collect == -1)
2312                 return;
2313         sgen_perform_collection (size, generation_to_collect, reason, FALSE);
2314 }
2315
2316 /*
2317  * LOCKING: Assumes the GC lock is held.
2318  */
2319 void
2320 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish)
2321 {
2322         TV_DECLARE (gc_start);
2323         TV_DECLARE (gc_end);
2324         TV_DECLARE (gc_total_start);
2325         TV_DECLARE (gc_total_end);
2326         GGTimingInfo infos [2];
2327         int overflow_generation_to_collect = -1;
2328         int oldest_generation_collected = generation_to_collect;
2329         const char *overflow_reason = NULL;
2330
2331         binary_protocol_collection_requested (generation_to_collect, requested_size, wait_to_finish ? 1 : 0);
2332
2333         SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD, "What generation is this?");
2334
2335         TV_GETTIME (gc_start);
2336
2337         sgen_stop_world (generation_to_collect);
2338
2339         TV_GETTIME (gc_total_start);
2340
2341         if (concurrent_collection_in_progress) {
2342                 /*
2343                  * We update the concurrent collection.  If it finished, we're done.  If
2344                  * not, and we've been asked to do a nursery collection, we do that.
2345                  */
2346                 gboolean finish = major_should_finish_concurrent_collection () || (wait_to_finish && generation_to_collect == GENERATION_OLD);
2347
2348                 if (finish) {
2349                         major_finish_concurrent_collection (wait_to_finish);
2350                         oldest_generation_collected = GENERATION_OLD;
2351                 } else {
2352                         sgen_workers_signal_start_nursery_collection_and_wait ();
2353
2354                         major_update_concurrent_collection ();
2355                         if (generation_to_collect == GENERATION_NURSERY)
2356                                 collect_nursery (NULL, FALSE);
2357
2358                         sgen_workers_signal_finish_nursery_collection ();
2359                 }
2360
2361                 goto done;
2362         }
2363
2364         /*
2365          * If we've been asked to do a major collection, and the major collector wants to
2366          * run synchronously (to evacuate), we set the flag to do that.
2367          */
2368         if (generation_to_collect == GENERATION_OLD &&
2369                         allow_synchronous_major &&
2370                         major_collector.want_synchronous_collection &&
2371                         *major_collector.want_synchronous_collection) {
2372                 wait_to_finish = TRUE;
2373         }
2374
2375         SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
2376
2377         /*
2378          * There's no concurrent collection in progress.  Collect the generation we're asked
2379          * to collect.  If the major collector is concurrent and we're not forced to wait,
2380          * start a concurrent collection.
2381          */
2382         // FIXME: extract overflow reason
2383         if (generation_to_collect == GENERATION_NURSERY) {
2384                 if (collect_nursery (NULL, FALSE)) {
2385                         overflow_generation_to_collect = GENERATION_OLD;
2386                         overflow_reason = "Minor overflow";
2387                 }
2388         } else {
2389                 if (major_collector.is_concurrent && !wait_to_finish) {
2390                         collect_nursery (NULL, FALSE);
2391                         major_start_concurrent_collection (reason);
2392                         // FIXME: set infos[0] properly
2393                         goto done;
2394                 }
2395
2396                 if (major_do_collection (reason, wait_to_finish)) {
2397                         overflow_generation_to_collect = GENERATION_NURSERY;
2398                         overflow_reason = "Excessive pinning";
2399                 }
2400         }
2401
2402         TV_GETTIME (gc_end);
2403
2404         memset (infos, 0, sizeof (infos));
2405         infos [0].generation = generation_to_collect;
2406         infos [0].reason = reason;
2407         infos [0].is_overflow = FALSE;
2408         infos [1].generation = -1;
2409         infos [0].total_time = SGEN_TV_ELAPSED (gc_start, gc_end);
2410
2411         SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
2412
2413         if (overflow_generation_to_collect != -1) {
2414                 /*
2415                  * We need to do an overflow collection, either because we ran out of memory
2416                  * or the nursery is fully pinned.
2417                  */
2418
2419                 infos [1].generation = overflow_generation_to_collect;
2420                 infos [1].reason = overflow_reason;
2421                 infos [1].is_overflow = TRUE;
2422                 gc_start = gc_end;
2423
2424                 if (overflow_generation_to_collect == GENERATION_NURSERY)
2425                         collect_nursery (NULL, FALSE);
2426                 else
2427                         major_do_collection (overflow_reason, wait_to_finish);
2428
2429                 TV_GETTIME (gc_end);
2430                 infos [1].total_time = SGEN_TV_ELAPSED (gc_start, gc_end);
2431
2432                 oldest_generation_collected = MAX (oldest_generation_collected, overflow_generation_to_collect);
2433         }
2434
2435         SGEN_LOG (2, "Heap size: %lu, LOS size: %lu", (unsigned long)sgen_gc_get_total_heap_allocation (), (unsigned long)los_memory_usage);
2436
2437         /* this also sets the proper pointers for the next allocation */
2438         if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
2439                 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2440                 SGEN_LOG (1, "nursery collection didn't find enough room for %zd alloc (%zd pinned)", requested_size, sgen_get_pinned_count ());
2441                 sgen_dump_pin_queue ();
2442                 degraded_mode = 1;
2443         }
2444
2445  done:
2446         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2447
2448         TV_GETTIME (gc_total_end);
2449         time_max = MAX (time_max, TV_ELAPSED (gc_total_start, gc_total_end));
2450
2451         sgen_restart_world (oldest_generation_collected, infos);
2452 }
2453
2454 /*
2455  * ######################################################################
2456  * ########  Memory allocation from the OS
2457  * ######################################################################
2458  * This section of code deals with getting memory from the OS and
2459  * allocating memory for GC-internal data structures.
2460  * Internal memory can be handled with a freelist for small objects.
2461  */
2462
2463 /*
2464  * Debug reporting.
2465  */
2466 G_GNUC_UNUSED static void
2467 report_internal_mem_usage (void)
2468 {
2469         printf ("Internal memory usage:\n");
2470         sgen_report_internal_mem_usage ();
2471         printf ("Pinned memory usage:\n");
2472         major_collector.report_pinned_memory_usage ();
2473 }
2474
2475 /*
2476  * ######################################################################
2477  * ########  Finalization support
2478  * ######################################################################
2479  */
2480
2481 /*
2482  * If the object has been forwarded it means it's still referenced from a root. 
2483  * If it is pinned it's still alive as well.
2484  * A LOS object is only alive if we have pinned it.
2485  * Return TRUE if @obj is ready to be finalized.
2486  */
2487 static inline gboolean
2488 sgen_is_object_alive (void *object)
2489 {
2490         if (ptr_in_nursery (object))
2491                 return sgen_nursery_is_object_alive (object);
2492
2493         return sgen_major_is_object_alive (object);
2494 }
2495
2496 /*
2497  * This function returns true if @object is either alive and belongs to the
2498  * current collection - major collections are full heap, so old gen objects
2499  * are never alive during a minor collection.
2500  */
2501 static inline int
2502 sgen_is_object_alive_and_on_current_collection (char *object)
2503 {
2504         if (ptr_in_nursery (object))
2505                 return sgen_nursery_is_object_alive (object);
2506
2507         if (current_collection_generation == GENERATION_NURSERY)
2508                 return FALSE;
2509
2510         return sgen_major_is_object_alive (object);
2511 }
2512
2513
2514 gboolean
2515 sgen_gc_is_object_ready_for_finalization (void *object)
2516 {
2517         return !sgen_is_object_alive (object);
2518 }
2519
2520 void
2521 sgen_queue_finalization_entry (GCObject *obj)
2522 {
2523         gboolean critical = sgen_client_object_has_critical_finalizer (obj);
2524
2525         sgen_pointer_queue_add (critical ? &critical_fin_queue : &fin_ready_queue, obj);
2526
2527         sgen_client_object_queued_for_finalization (obj);
2528 }
2529
2530 gboolean
2531 sgen_object_is_live (void *obj)
2532 {
2533         return sgen_is_object_alive_and_on_current_collection (obj);
2534 }
2535
2536 /*
2537  * `System.GC.WaitForPendingFinalizers` first checks `sgen_have_pending_finalizers()` to
2538  * determine whether it can exit quickly.  The latter must therefore only return FALSE if
2539  * all finalizers have really finished running.
2540  *
2541  * `sgen_gc_invoke_finalizers()` first dequeues a finalizable object, and then finalizes it.
2542  * This means that just checking whether the queues are empty leaves the possibility that an
2543  * object might have been dequeued but not yet finalized.  That's why we need the additional
2544  * flag `pending_unqueued_finalizer`.
2545  */
2546
2547 static volatile gboolean pending_unqueued_finalizer = FALSE;
2548
2549 int
2550 sgen_gc_invoke_finalizers (void)
2551 {
2552         int count = 0;
2553
2554         g_assert (!pending_unqueued_finalizer);
2555
2556         /* FIXME: batch to reduce lock contention */
2557         while (sgen_have_pending_finalizers ()) {
2558                 void *obj;
2559
2560                 LOCK_GC;
2561
2562                 /*
2563                  * We need to set `pending_unqueued_finalizer` before dequeing the
2564                  * finalizable object.
2565                  */
2566                 if (!sgen_pointer_queue_is_empty (&fin_ready_queue)) {
2567                         pending_unqueued_finalizer = TRUE;
2568                         mono_memory_write_barrier ();
2569                         obj = sgen_pointer_queue_pop (&fin_ready_queue);
2570                 } else if (!sgen_pointer_queue_is_empty (&critical_fin_queue)) {
2571                         pending_unqueued_finalizer = TRUE;
2572                         mono_memory_write_barrier ();
2573                         obj = sgen_pointer_queue_pop (&critical_fin_queue);
2574                 } else {
2575                         obj = NULL;
2576                 }
2577
2578                 if (obj)
2579                         SGEN_LOG (7, "Finalizing object %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
2580
2581                 UNLOCK_GC;
2582
2583                 if (!obj)
2584                         break;
2585
2586                 count++;
2587                 /* the object is on the stack so it is pinned */
2588                 /*g_print ("Calling finalizer for object: %p (%s)\n", obj, sgen_client_object_safe_name (obj));*/
2589                 sgen_client_run_finalize (obj);
2590         }
2591
2592         if (pending_unqueued_finalizer) {
2593                 mono_memory_write_barrier ();
2594                 pending_unqueued_finalizer = FALSE;
2595         }
2596
2597         return count;
2598 }
2599
2600 gboolean
2601 sgen_have_pending_finalizers (void)
2602 {
2603         return pending_unqueued_finalizer || !sgen_pointer_queue_is_empty (&fin_ready_queue) || !sgen_pointer_queue_is_empty (&critical_fin_queue);
2604 }
2605
2606 /*
2607  * ######################################################################
2608  * ########  registered roots support
2609  * ######################################################################
2610  */
2611
2612 /*
2613  * We do not coalesce roots.
2614  */
2615 int
2616 sgen_register_root (char *start, size_t size, void *descr, int root_type)
2617 {
2618         RootRecord new_root;
2619         int i;
2620         LOCK_GC;
2621         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
2622                 RootRecord *root = sgen_hash_table_lookup (&roots_hash [i], start);
2623                 /* we allow changing the size and the descriptor (for thread statics etc) */
2624                 if (root) {
2625                         size_t old_size = root->end_root - start;
2626                         root->end_root = start + size;
2627                         g_assert (((root->root_desc != 0) && (descr != NULL)) ||
2628                                           ((root->root_desc == 0) && (descr == NULL)));
2629                         root->root_desc = (mword)descr;
2630                         roots_size += size;
2631                         roots_size -= old_size;
2632                         UNLOCK_GC;
2633                         return TRUE;
2634                 }
2635         }
2636
2637         new_root.end_root = start + size;
2638         new_root.root_desc = (mword)descr;
2639
2640         sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
2641         roots_size += size;
2642
2643         SGEN_LOG (3, "Added root for range: %p-%p, descr: %p  (%d/%d bytes)", start, new_root.end_root, descr, (int)size, (int)roots_size);
2644
2645         UNLOCK_GC;
2646         return TRUE;
2647 }
2648
2649 void
2650 sgen_deregister_root (char* addr)
2651 {
2652         int root_type;
2653         RootRecord root;
2654
2655         LOCK_GC;
2656         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
2657                 if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
2658                         roots_size -= (root.end_root - addr);
2659         }
2660         UNLOCK_GC;
2661 }
2662
2663 /*
2664  * ######################################################################
2665  * ########  Thread handling (stop/start code)
2666  * ######################################################################
2667  */
2668
2669 int
2670 sgen_get_current_collection_generation (void)
2671 {
2672         return current_collection_generation;
2673 }
2674
2675 void*
2676 sgen_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
2677 {
2678 #ifndef HAVE_KW_THREAD
2679         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
2680 #endif
2681
2682         sgen_init_tlab_info (info);
2683
2684         sgen_client_thread_register (info, stack_bottom_fallback);
2685
2686         return info;
2687 }
2688
2689 void
2690 sgen_thread_unregister (SgenThreadInfo *p)
2691 {
2692         sgen_client_thread_unregister (p);
2693 }
2694
2695
2696 /*
2697  * ######################################################################
2698  * ########  Write barriers
2699  * ######################################################################
2700  */
2701
2702 /*
2703  * Note: the write barriers first do the needed GC work and then do the actual store:
2704  * this way the value is visible to the conservative GC scan after the write barrier
2705  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
2706  * the conservative scan, otherwise by the remembered set scan.
2707  */
2708 void
2709 mono_gc_wbarrier_set_field (GCObject *obj, gpointer field_ptr, GCObject* value)
2710 {
2711         HEAVY_STAT (++stat_wbarrier_set_field);
2712         if (ptr_in_nursery (field_ptr)) {
2713                 *(void**)field_ptr = value;
2714                 return;
2715         }
2716         SGEN_LOG (8, "Adding remset at %p", field_ptr);
2717         if (value)
2718                 binary_protocol_wbarrier (field_ptr, value, value->vtable);
2719
2720         remset.wbarrier_set_field (obj, field_ptr, value);
2721 }
2722
2723 void
2724 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
2725 {
2726         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
2727         /*This check can be done without taking a lock since dest_ptr array is pinned*/
2728         if (ptr_in_nursery (dest_ptr) || count <= 0) {
2729                 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
2730                 return;
2731         }
2732
2733 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
2734         if (binary_protocol_is_heavy_enabled ()) {
2735                 int i;
2736                 for (i = 0; i < count; ++i) {
2737                         gpointer dest = (gpointer*)dest_ptr + i;
2738                         gpointer obj = *((gpointer*)src_ptr + i);
2739                         if (obj)
2740                                 binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
2741                 }
2742         }
2743 #endif
2744
2745         remset.wbarrier_arrayref_copy (dest_ptr, src_ptr, count);
2746 }
2747
2748 void
2749 mono_gc_wbarrier_generic_nostore (gpointer ptr)
2750 {
2751         gpointer obj;
2752
2753         HEAVY_STAT (++stat_wbarrier_generic_store);
2754
2755         sgen_client_wbarrier_generic_nostore_check (ptr);
2756
2757         obj = *(gpointer*)ptr;
2758         if (obj)
2759                 binary_protocol_wbarrier (ptr, obj, (gpointer)LOAD_VTABLE (obj));
2760
2761         /*
2762          * We need to record old->old pointer locations for the
2763          * concurrent collector.
2764          */
2765         if (!ptr_in_nursery (obj) && !concurrent_collection_in_progress) {
2766                 SGEN_LOG (8, "Skipping remset at %p", ptr);
2767                 return;
2768         }
2769
2770         SGEN_LOG (8, "Adding remset at %p", ptr);
2771
2772         remset.wbarrier_generic_nostore (ptr);
2773 }
2774
2775 void
2776 mono_gc_wbarrier_generic_store (gpointer ptr, GCObject* value)
2777 {
2778         SGEN_LOG (8, "Wbarrier store at %p to %p (%s)", ptr, value, value ? sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (value)) : "null");
2779         SGEN_UPDATE_REFERENCE_ALLOW_NULL (ptr, value);
2780         if (ptr_in_nursery (value))
2781                 mono_gc_wbarrier_generic_nostore (ptr);
2782         sgen_dummy_use (value);
2783 }
2784
2785 /* Same as mono_gc_wbarrier_generic_store () but performs the store
2786  * as an atomic operation with release semantics.
2787  */
2788 void
2789 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, GCObject *value)
2790 {
2791         HEAVY_STAT (++stat_wbarrier_generic_store_atomic);
2792
2793         SGEN_LOG (8, "Wbarrier atomic store at %p to %p (%s)", ptr, value, value ? sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (value)) : "null");
2794
2795         InterlockedWritePointer (ptr, value);
2796
2797         if (ptr_in_nursery (value))
2798                 mono_gc_wbarrier_generic_nostore (ptr);
2799
2800         sgen_dummy_use (value);
2801 }
2802
2803 void
2804 sgen_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
2805 {
2806         GCObject **dest = _dest;
2807         GCObject **src = _src;
2808
2809         while (size) {
2810                 if (bitmap & 0x1)
2811                         mono_gc_wbarrier_generic_store (dest, *src);
2812                 else
2813                         SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest, *src);
2814                 ++src;
2815                 ++dest;
2816                 size -= SIZEOF_VOID_P;
2817                 bitmap >>= 1;
2818         }
2819 }
2820
2821 /*
2822  * ######################################################################
2823  * ########  Other mono public interface functions.
2824  * ######################################################################
2825  */
2826
2827 void
2828 sgen_gc_collect (int generation)
2829 {
2830         LOCK_GC;
2831         if (generation > 1)
2832                 generation = 1;
2833         sgen_perform_collection (0, generation, "user request", TRUE);
2834         UNLOCK_GC;
2835 }
2836
2837 int
2838 sgen_gc_collection_count (int generation)
2839 {
2840         if (generation == 0)
2841                 return gc_stats.minor_gc_count;
2842         return gc_stats.major_gc_count;
2843 }
2844
2845 size_t
2846 sgen_gc_get_used_size (void)
2847 {
2848         gint64 tot = 0;
2849         LOCK_GC;
2850         tot = los_memory_usage;
2851         tot += nursery_section->next_data - nursery_section->data;
2852         tot += major_collector.get_used_size ();
2853         /* FIXME: account for pinned objects */
2854         UNLOCK_GC;
2855         return tot;
2856 }
2857
2858 GCObject*
2859 sgen_weak_link_get (void **link_addr)
2860 {
2861         void * volatile *link_addr_volatile;
2862         void *ptr;
2863         GCObject *obj;
2864  retry:
2865         link_addr_volatile = link_addr;
2866         ptr = (void*)*link_addr_volatile;
2867         /*
2868          * At this point we have a hidden pointer.  If the GC runs
2869          * here, it will not recognize the hidden pointer as a
2870          * reference, and if the object behind it is not referenced
2871          * elsewhere, it will be freed.  Once the world is restarted
2872          * we reveal the pointer, giving us a pointer to a freed
2873          * object.  To make sure we don't return it, we load the
2874          * hidden pointer again.  If it's still the same, we can be
2875          * sure the object reference is valid.
2876          */
2877         if (ptr)
2878                 obj = (GCObject*) REVEAL_POINTER (ptr);
2879         else
2880                 return NULL;
2881
2882         mono_memory_barrier ();
2883
2884         /*
2885          * During the second bridge processing step the world is
2886          * running again.  That step processes all weak links once
2887          * more to null those that refer to dead objects.  Before that
2888          * is completed, those links must not be followed, so we
2889          * conservatively wait for bridge processing when any weak
2890          * link is dereferenced.
2891          */
2892         sgen_client_bridge_wait_for_processing ();
2893
2894         if ((void*)*link_addr_volatile != ptr)
2895                 goto retry;
2896
2897         return obj;
2898 }
2899
2900 gboolean
2901 sgen_set_allow_synchronous_major (gboolean flag)
2902 {
2903         if (!major_collector.is_concurrent)
2904                 return flag;
2905
2906         allow_synchronous_major = flag;
2907         return TRUE;
2908 }
2909
2910 void
2911 sgen_env_var_error (const char *env_var, const char *fallback, const char *description_format, ...)
2912 {
2913         va_list ap;
2914
2915         va_start (ap, description_format);
2916
2917         fprintf (stderr, "Warning: In environment variable `%s': ", env_var);
2918         vfprintf (stderr, description_format, ap);
2919         if (fallback)
2920                 fprintf (stderr, " - %s", fallback);
2921         fprintf (stderr, "\n");
2922
2923         va_end (ap);
2924 }
2925
2926 static gboolean
2927 parse_double_in_interval (const char *env_var, const char *opt_name, const char *opt, double min, double max, double *result)
2928 {
2929         char *endptr;
2930         double val = strtod (opt, &endptr);
2931         if (endptr == opt) {
2932                 sgen_env_var_error (env_var, "Using default value.", "`%s` must be a number.", opt_name);
2933                 return FALSE;
2934         }
2935         else if (val < min || val > max) {
2936                 sgen_env_var_error (env_var, "Using default value.", "`%s` must be between %.2f - %.2f.", opt_name, min, max);
2937                 return FALSE;
2938         }
2939         *result = val;
2940         return TRUE;
2941 }
2942
2943 void
2944 sgen_gc_init (void)
2945 {
2946         const char *env;
2947         char **opts, **ptr;
2948         char *major_collector_opt = NULL;
2949         char *minor_collector_opt = NULL;
2950         size_t max_heap = 0;
2951         size_t soft_limit = 0;
2952         int result;
2953         int dummy;
2954         gboolean debug_print_allowance = FALSE;
2955         double allowance_ratio = 0, save_target = 0;
2956         gboolean cement_enabled = TRUE;
2957
2958         do {
2959                 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
2960                 switch (result) {
2961                 case 1:
2962                         /* already inited */
2963                         return;
2964                 case -1:
2965                         /* being inited by another thread */
2966                         g_usleep (1000);
2967                         break;
2968                 case 0:
2969                         /* we will init it */
2970                         break;
2971                 default:
2972                         g_assert_not_reached ();
2973                 }
2974         } while (result != 0);
2975
2976         SGEN_TV_GETTIME (sgen_init_timestamp);
2977
2978 #ifdef SGEN_WITHOUT_MONO
2979         mono_thread_smr_init ();
2980 #endif
2981
2982         LOCK_INIT (gc_mutex);
2983
2984         gc_debug_file = stderr;
2985
2986         LOCK_INIT (sgen_interruption_mutex);
2987
2988         if ((env = g_getenv (MONO_GC_PARAMS_NAME))) {
2989                 opts = g_strsplit (env, ",", -1);
2990                 for (ptr = opts; *ptr; ++ptr) {
2991                         char *opt = *ptr;
2992                         if (g_str_has_prefix (opt, "major=")) {
2993                                 opt = strchr (opt, '=') + 1;
2994                                 major_collector_opt = g_strdup (opt);
2995                         } else if (g_str_has_prefix (opt, "minor=")) {
2996                                 opt = strchr (opt, '=') + 1;
2997                                 minor_collector_opt = g_strdup (opt);
2998                         }
2999                 }
3000         } else {
3001                 opts = NULL;
3002         }
3003
3004         init_stats ();
3005         sgen_init_internal_allocator ();
3006         sgen_init_nursery_allocator ();
3007         sgen_init_fin_weak_hash ();
3008         sgen_init_hash_table ();
3009         sgen_init_descriptors ();
3010         sgen_init_gray_queues ();
3011         sgen_init_allocator ();
3012
3013         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
3014         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
3015
3016         sgen_client_init ();
3017
3018         /* FIXME: Move this to mono code! */
3019         mono_gc_register_thread (&dummy);
3020
3021         if (!minor_collector_opt) {
3022                 sgen_simple_nursery_init (&sgen_minor_collector);
3023         } else {
3024                 if (!strcmp (minor_collector_opt, "simple")) {
3025                 use_simple_nursery:
3026                         sgen_simple_nursery_init (&sgen_minor_collector);
3027                 } else if (!strcmp (minor_collector_opt, "split")) {
3028                         sgen_split_nursery_init (&sgen_minor_collector);
3029                 } else {
3030                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `simple` instead.", "Unknown minor collector `%s'.", minor_collector_opt);
3031                         goto use_simple_nursery;
3032                 }
3033         }
3034
3035         if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
3036         use_marksweep_major:
3037                 sgen_marksweep_init (&major_collector);
3038         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) {
3039                 sgen_marksweep_conc_init (&major_collector);
3040         } else {
3041                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `marksweep` instead.", "Unknown major collector `%s'.", major_collector_opt);
3042                 goto use_marksweep_major;
3043         }
3044
3045         sgen_nursery_size = DEFAULT_NURSERY_SIZE;
3046
3047         if (major_collector.is_concurrent)
3048                 cement_enabled = FALSE;
3049
3050         if (opts) {
3051                 gboolean usage_printed = FALSE;
3052
3053                 for (ptr = opts; *ptr; ++ptr) {
3054                         char *opt = *ptr;
3055                         if (!strcmp (opt, ""))
3056                                 continue;
3057                         if (g_str_has_prefix (opt, "major="))
3058                                 continue;
3059                         if (g_str_has_prefix (opt, "minor="))
3060                                 continue;
3061                         if (g_str_has_prefix (opt, "max-heap-size=")) {
3062                                 size_t page_size = mono_pagesize ();
3063                                 size_t max_heap_candidate = 0;
3064                                 opt = strchr (opt, '=') + 1;
3065                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap_candidate)) {
3066                                         max_heap = (max_heap_candidate + page_size - 1) & ~(size_t)(page_size - 1);
3067                                         if (max_heap != max_heap_candidate)
3068                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Rounding up.", "`max-heap-size` size must be a multiple of %d.", page_size);
3069                                 } else {
3070                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`max-heap-size` must be an integer.");
3071                                 }
3072                                 continue;
3073                         }
3074                         if (g_str_has_prefix (opt, "soft-heap-limit=")) {
3075                                 opt = strchr (opt, '=') + 1;
3076                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
3077                                         if (soft_limit <= 0) {
3078                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be positive.");
3079                                                 soft_limit = 0;
3080                                         }
3081                                 } else {
3082                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be an integer.");
3083                                 }
3084                                 continue;
3085                         }
3086
3087 #ifdef USER_CONFIG
3088                         if (g_str_has_prefix (opt, "nursery-size=")) {
3089                                 size_t val;
3090                                 opt = strchr (opt, '=') + 1;
3091                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
3092                                         if ((val & (val - 1))) {
3093                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be a power of two.");
3094                                                 continue;
3095                                         }
3096
3097                                         if (val < SGEN_MAX_NURSERY_WASTE) {
3098                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.",
3099                                                                 "`nursery-size` must be at least %d bytes.", SGEN_MAX_NURSERY_WASTE);
3100                                                 continue;
3101                                         }
3102
3103                                         sgen_nursery_size = val;
3104                                         sgen_nursery_bits = 0;
3105                                         while (ONE_P << (++ sgen_nursery_bits) != sgen_nursery_size)
3106                                                 ;
3107                                 } else {
3108                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be an integer.");
3109                                         continue;
3110                                 }
3111                                 continue;
3112                         }
3113 #endif
3114                         if (g_str_has_prefix (opt, "save-target-ratio=")) {
3115                                 double val;
3116                                 opt = strchr (opt, '=') + 1;
3117                                 if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "save-target-ratio", opt,
3118                                                 SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO, &val)) {
3119                                         save_target = val;
3120                                 }
3121                                 continue;
3122                         }
3123                         if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
3124                                 double val;
3125                                 opt = strchr (opt, '=') + 1;
3126                                 if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "default-allowance-ratio", opt,
3127                                                 SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, &val)) {
3128                                         allowance_ratio = val;
3129                                 }
3130                                 continue;
3131                         }
3132                         if (g_str_has_prefix (opt, "allow-synchronous-major=")) {
3133                                 if (!major_collector.is_concurrent) {
3134                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`allow-synchronous-major` is only valid for the concurrent major collector.");
3135                                         continue;
3136                                 }
3137
3138                                 opt = strchr (opt, '=') + 1;
3139
3140                                 if (!strcmp (opt, "yes")) {
3141                                         allow_synchronous_major = TRUE;
3142                                 } else if (!strcmp (opt, "no")) {
3143                                         allow_synchronous_major = FALSE;
3144                                 } else {
3145                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`allow-synchronous-major` must be either `yes' or `no'.");
3146                                         continue;
3147                                 }
3148                         }
3149
3150                         if (!strcmp (opt, "cementing")) {
3151                                 cement_enabled = TRUE;
3152                                 continue;
3153                         }
3154                         if (!strcmp (opt, "no-cementing")) {
3155                                 cement_enabled = FALSE;
3156                                 continue;
3157                         }
3158
3159                         if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
3160                                 continue;
3161
3162                         if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
3163                                 continue;
3164
3165                         if (sgen_client_handle_gc_param (opt))
3166                                 continue;
3167
3168                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Unknown option `%s`.", opt);
3169
3170                         if (usage_printed)
3171                                 continue;
3172
3173                         fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME);
3174                         fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
3175                         fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
3176                         fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
3177                         fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par')\n");
3178                         fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
3179                         fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
3180                         fprintf (stderr, "  [no-]cementing\n");
3181                         if (major_collector.is_concurrent)
3182                                 fprintf (stderr, "  allow-synchronous-major=FLAG (where FLAG is `yes' or `no')\n");
3183                         if (major_collector.print_gc_param_usage)
3184                                 major_collector.print_gc_param_usage ();
3185                         if (sgen_minor_collector.print_gc_param_usage)
3186                                 sgen_minor_collector.print_gc_param_usage ();
3187                         sgen_client_print_gc_params_usage ();
3188                         fprintf (stderr, " Experimental options:\n");
3189                         fprintf (stderr, "  save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
3190                         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);
3191                         fprintf (stderr, "\n");
3192
3193                         usage_printed = TRUE;
3194                 }
3195                 g_strfreev (opts);
3196         }
3197
3198         if (major_collector_opt)
3199                 g_free (major_collector_opt);
3200
3201         if (minor_collector_opt)
3202                 g_free (minor_collector_opt);
3203
3204         alloc_nursery ();
3205
3206         if (major_collector.is_concurrent && cement_enabled) {
3207                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`cementing` is not supported on concurrent major collectors.");
3208                 cement_enabled = FALSE;
3209         }
3210
3211         sgen_cement_init (cement_enabled);
3212
3213         if ((env = g_getenv (MONO_GC_DEBUG_NAME))) {
3214                 gboolean usage_printed = FALSE;
3215
3216                 opts = g_strsplit (env, ",", -1);
3217                 for (ptr = opts; ptr && *ptr; ptr ++) {
3218                         char *opt = *ptr;
3219                         if (!strcmp (opt, ""))
3220                                 continue;
3221                         if (opt [0] >= '0' && opt [0] <= '9') {
3222                                 gc_debug_level = atoi (opt);
3223                                 opt++;
3224                                 if (opt [0] == ':')
3225                                         opt++;
3226                                 if (opt [0]) {
3227                                         char *rf = g_strdup_printf ("%s.%d", opt, mono_process_current_pid ());
3228                                         gc_debug_file = fopen (rf, "wb");
3229                                         if (!gc_debug_file)
3230                                                 gc_debug_file = stderr;
3231                                         g_free (rf);
3232                                 }
3233                         } else if (!strcmp (opt, "print-allowance")) {
3234                                 debug_print_allowance = TRUE;
3235                         } else if (!strcmp (opt, "print-pinning")) {
3236                                 sgen_pin_stats_enable ();
3237                         } else if (!strcmp (opt, "verify-before-allocs")) {
3238                                 verify_before_allocs = 1;
3239                                 has_per_allocation_action = TRUE;
3240                         } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
3241                                 char *arg = strchr (opt, '=') + 1;
3242                                 verify_before_allocs = atoi (arg);
3243                                 has_per_allocation_action = TRUE;
3244                         } else if (!strcmp (opt, "collect-before-allocs")) {
3245                                 collect_before_allocs = 1;
3246                                 has_per_allocation_action = TRUE;
3247                         } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
3248                                 char *arg = strchr (opt, '=') + 1;
3249                                 has_per_allocation_action = TRUE;
3250                                 collect_before_allocs = atoi (arg);
3251                         } else if (!strcmp (opt, "verify-before-collections")) {
3252                                 whole_heap_check_before_collection = TRUE;
3253                         } else if (!strcmp (opt, "check-at-minor-collections")) {
3254                                 consistency_check_at_minor_collection = TRUE;
3255                                 nursery_clear_policy = CLEAR_AT_GC;
3256                         } else if (!strcmp (opt, "mod-union-consistency-check")) {
3257                                 if (!major_collector.is_concurrent) {
3258                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`mod-union-consistency-check` only works with concurrent major collector.");
3259                                         continue;
3260                                 }
3261                                 mod_union_consistency_check = TRUE;
3262                         } else if (!strcmp (opt, "check-mark-bits")) {
3263                                 check_mark_bits_after_major_collection = TRUE;
3264                         } else if (!strcmp (opt, "check-nursery-pinned")) {
3265                                 check_nursery_objects_pinned = TRUE;
3266                         } else if (!strcmp (opt, "clear-at-gc")) {
3267                                 nursery_clear_policy = CLEAR_AT_GC;
3268                         } else if (!strcmp (opt, "clear-nursery-at-gc")) {
3269                                 nursery_clear_policy = CLEAR_AT_GC;
3270                         } else if (!strcmp (opt, "clear-at-tlab-creation")) {
3271                                 nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
3272                         } else if (!strcmp (opt, "debug-clear-at-tlab-creation")) {
3273                                 nursery_clear_policy = CLEAR_AT_TLAB_CREATION_DEBUG;
3274                         } else if (!strcmp (opt, "check-scan-starts")) {
3275                                 do_scan_starts_check = TRUE;
3276                         } else if (!strcmp (opt, "verify-nursery-at-minor-gc")) {
3277                                 do_verify_nursery = TRUE;
3278                         } else if (!strcmp (opt, "check-concurrent")) {
3279                                 if (!major_collector.is_concurrent) {
3280                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`check-concurrent` only works with concurrent major collectors.");
3281                                         continue;
3282                                 }
3283                                 do_concurrent_checks = TRUE;
3284                         } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
3285                                 do_dump_nursery_content = TRUE;
3286                         } else if (!strcmp (opt, "disable-minor")) {
3287                                 disable_minor_collections = TRUE;
3288                         } else if (!strcmp (opt, "disable-major")) {
3289                                 disable_major_collections = TRUE;
3290                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
3291                                 char *filename = strchr (opt, '=') + 1;
3292                                 nursery_clear_policy = CLEAR_AT_GC;
3293                                 sgen_debug_enable_heap_dump (filename);
3294                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
3295                                 char *filename = strchr (opt, '=') + 1;
3296                                 char *colon = strrchr (filename, ':');
3297                                 size_t limit = -1;
3298                                 if (colon) {
3299                                         if (!mono_gc_parse_environment_string_extract_number (colon + 1, &limit)) {
3300                                                 sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring limit.", "Binary protocol file size limit must be an integer.");
3301                                                 limit = -1;
3302                                         }
3303                                         *colon = '\0';
3304                                 }
3305                                 binary_protocol_init (filename, (long long)limit);
3306                         } else if (!strcmp (opt, "nursery-canaries")) {
3307                                 do_verify_nursery = TRUE;
3308                                 enable_nursery_canaries = TRUE;
3309                         } else if (!sgen_client_handle_gc_debug (opt)) {
3310                                 sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "Unknown option `%s`.", opt);
3311
3312                                 if (usage_printed)
3313                                         continue;
3314
3315                                 fprintf (stderr, "\n%s must be of the format [<l>[:<filename>]|<option>]+ where <l> is a debug level 0-9.\n", MONO_GC_DEBUG_NAME);
3316                                 fprintf (stderr, "Valid <option>s are:\n");
3317                                 fprintf (stderr, "  collect-before-allocs[=<n>]\n");
3318                                 fprintf (stderr, "  verify-before-allocs[=<n>]\n");
3319                                 fprintf (stderr, "  check-at-minor-collections\n");
3320                                 fprintf (stderr, "  check-mark-bits\n");
3321                                 fprintf (stderr, "  check-nursery-pinned\n");
3322                                 fprintf (stderr, "  verify-before-collections\n");
3323                                 fprintf (stderr, "  verify-nursery-at-minor-gc\n");
3324                                 fprintf (stderr, "  dump-nursery-at-minor-gc\n");
3325                                 fprintf (stderr, "  disable-minor\n");
3326                                 fprintf (stderr, "  disable-major\n");
3327                                 fprintf (stderr, "  check-concurrent\n");
3328                                 fprintf (stderr, "  clear-[nursery-]at-gc\n");
3329                                 fprintf (stderr, "  clear-at-tlab-creation\n");
3330                                 fprintf (stderr, "  debug-clear-at-tlab-creation\n");
3331                                 fprintf (stderr, "  check-scan-starts\n");
3332                                 fprintf (stderr, "  print-allowance\n");
3333                                 fprintf (stderr, "  print-pinning\n");
3334                                 fprintf (stderr, "  heap-dump=<filename>\n");
3335                                 fprintf (stderr, "  binary-protocol=<filename>[:<file-size-limit>]\n");
3336                                 fprintf (stderr, "  nursery-canaries\n");
3337                                 sgen_client_print_gc_debug_usage ();
3338                                 fprintf (stderr, "\n");
3339
3340                                 usage_printed = TRUE;
3341                         }
3342                 }
3343                 g_strfreev (opts);
3344         }
3345
3346         if (check_mark_bits_after_major_collection)
3347                 nursery_clear_policy = CLEAR_AT_GC;
3348
3349         if (major_collector.post_param_init)
3350                 major_collector.post_param_init (&major_collector);
3351
3352         if (major_collector.needs_thread_pool)
3353                 sgen_workers_init (1);
3354
3355         sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
3356
3357         memset (&remset, 0, sizeof (remset));
3358
3359         sgen_card_table_init (&remset);
3360
3361         gc_initialized = 1;
3362 }
3363
3364 NurseryClearPolicy
3365 sgen_get_nursery_clear_policy (void)
3366 {
3367         return nursery_clear_policy;
3368 }
3369
3370 void
3371 sgen_gc_lock (void)
3372 {
3373         LOCK_GC;
3374 }
3375
3376 void
3377 sgen_gc_unlock (void)
3378 {
3379         gboolean try_free = sgen_try_free_some_memory;
3380         sgen_try_free_some_memory = FALSE;
3381         mono_mutex_unlock (&gc_mutex);
3382         if (try_free)
3383                 mono_thread_hazardous_try_free_some ();
3384 }
3385
3386 void
3387 sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
3388 {
3389         major_collector.iterate_live_block_ranges (callback);
3390 }
3391
3392 SgenMajorCollector*
3393 sgen_get_major_collector (void)
3394 {
3395         return &major_collector;
3396 }
3397
3398 SgenRememberedSet*
3399 sgen_get_remset (void)
3400 {
3401         return &remset;
3402 }
3403
3404 static void
3405 count_cards (long long *major_total, long long *major_marked, long long *los_total, long long *los_marked)
3406 {
3407         sgen_get_major_collector ()->count_cards (major_total, major_marked);
3408         sgen_los_count_cards (los_total, los_marked);
3409 }
3410
3411 static gboolean world_is_stopped = FALSE;
3412
3413 /* LOCKING: assumes the GC lock is held */
3414 void
3415 sgen_stop_world (int generation)
3416 {
3417         long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
3418
3419         SGEN_ASSERT (0, !world_is_stopped, "Why are we stopping a stopped world?");
3420
3421         binary_protocol_world_stopping (generation, sgen_timestamp ());
3422
3423         sgen_client_stop_world (generation);
3424
3425         world_is_stopped = TRUE;
3426
3427         if (binary_protocol_is_heavy_enabled ())
3428                 count_cards (&major_total, &major_marked, &los_total, &los_marked);
3429         binary_protocol_world_stopped (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
3430 }
3431
3432 /* LOCKING: assumes the GC lock is held */
3433 void
3434 sgen_restart_world (int generation, GGTimingInfo *timing)
3435 {
3436         SGEN_ASSERT (0, world_is_stopped, "Why are we restarting a running world?");
3437
3438         if (binary_protocol_is_enabled ()) {
3439                 long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
3440                 if (binary_protocol_is_heavy_enabled ())
3441                         count_cards (&major_total, &major_marked, &los_total, &los_marked);
3442                 binary_protocol_world_restarting (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
3443         }
3444
3445         sgen_client_restart_world (generation, timing);
3446
3447         world_is_stopped = FALSE;
3448
3449         binary_protocol_world_restarted (generation, sgen_timestamp ());
3450
3451         sgen_try_free_some_memory = TRUE;
3452
3453         if (sgen_client_bridge_need_processing ())
3454                 sgen_client_bridge_processing_finish (generation);
3455
3456         sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);
3457 }
3458
3459 gboolean
3460 sgen_is_world_stopped (void)
3461 {
3462         return world_is_stopped;
3463 }
3464
3465 void
3466 sgen_check_whole_heap_stw (void)
3467 {
3468         sgen_stop_world (0);
3469         sgen_clear_nursery_fragments ();
3470         sgen_check_whole_heap (FALSE);
3471         sgen_restart_world (0, NULL);
3472 }
3473
3474 gint64
3475 sgen_timestamp (void)
3476 {
3477         SGEN_TV_DECLARE (timestamp);
3478         SGEN_TV_GETTIME (timestamp);
3479         return SGEN_TV_ELAPSED (sgen_init_timestamp, timestamp);
3480 }
3481
3482 #endif /* HAVE_SGEN_GC */