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