Merge pull request #4710 from Unity-Technologies/additional-config-checks
[mono.git] / mono / metadata / sgen-mono.c
1 /**
2  * \file
3  * SGen features specific to Mono.
4  *
5  * Copyright (C) 2014 Xamarin Inc
6  *
7  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8  */
9
10 #include "config.h"
11 #ifdef HAVE_SGEN_GC
12
13 #include "sgen/sgen-gc.h"
14 #include "sgen/sgen-protocol.h"
15 #include "metadata/monitor.h"
16 #include "sgen/sgen-layout-stats.h"
17 #include "sgen/sgen-client.h"
18 #include "sgen/sgen-cardtable.h"
19 #include "sgen/sgen-pinning.h"
20 #include "sgen/sgen-workers.h"
21 #include "metadata/marshal.h"
22 #include "metadata/method-builder.h"
23 #include "metadata/abi-details.h"
24 #include "metadata/mono-gc.h"
25 #include "metadata/runtime.h"
26 #include "metadata/sgen-bridge-internals.h"
27 #include "metadata/gc-internals.h"
28 #include "metadata/handle.h"
29 #include "utils/mono-memory-model.h"
30 #include "utils/mono-logger-internals.h"
31 #include "utils/mono-threads-coop.h"
32 #include "utils/mono-threads.h"
33 #include "metadata/w32handle.h"
34
35 #ifdef HEAVY_STATISTICS
36 static guint64 stat_wbarrier_set_arrayref = 0;
37 static guint64 stat_wbarrier_value_copy = 0;
38 static guint64 stat_wbarrier_object_copy = 0;
39
40 static guint64 los_marked_cards;
41 static guint64 los_array_cards;
42 static guint64 los_array_remsets;
43 #endif
44
45 /* If set, mark stacks conservatively, even if precise marking is possible */
46 static gboolean conservative_stack_mark = FALSE;
47 /* If set, check that there are no references to the domain left at domain unload */
48 gboolean sgen_mono_xdomain_checks = FALSE;
49
50 /* Functions supplied by the runtime to be called by the GC */
51 static MonoGCCallbacks gc_callbacks;
52
53 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
54
55 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
56         a = i,
57
58 enum {
59 #include "mono/cil/opcode.def"
60         CEE_LAST
61 };
62
63 #undef OPDEF
64
65 /*
66  * Write barriers
67  */
68
69 static gboolean
70 ptr_on_stack (void *ptr)
71 {
72         gpointer stack_start = &stack_start;
73         SgenThreadInfo *info = mono_thread_info_current ();
74
75         if (ptr >= stack_start && ptr < (gpointer)info->client_info.stack_end)
76                 return TRUE;
77         return FALSE;
78 }
79
80 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
81 #undef HANDLE_PTR
82 #define HANDLE_PTR(ptr,obj) do {                                        \
83                 gpointer o = *(gpointer*)(ptr);                         \
84                 if ((o)) {                                              \
85                         gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \
86                         binary_protocol_wbarrier (d, o, (gpointer) SGEN_LOAD_VTABLE (o)); \
87                 }                                                       \
88         } while (0)
89
90 static void
91 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
92 {
93 #define SCAN_OBJECT_NOVTABLE
94 #include "sgen/sgen-scan-object.h"
95 }
96 #endif
97
98 void
99 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
100 {
101         HEAVY_STAT (++stat_wbarrier_value_copy);
102         g_assert (klass->valuetype);
103
104         SGEN_LOG (8, "Adding value remset at %p, count %d, descr %p for class %s (%p)", dest, count, (gpointer)klass->gc_descr, klass->name, klass);
105
106         if (sgen_ptr_in_nursery (dest) || ptr_on_stack (dest) || !sgen_gc_descr_has_references ((mword)klass->gc_descr)) {
107                 size_t element_size = mono_class_value_size (klass, NULL);
108                 size_t size = count * element_size;
109                 mono_gc_memmove_atomic (dest, src, size);               
110                 return;
111         }
112
113 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
114         if (binary_protocol_is_heavy_enabled ()) {
115                 size_t element_size = mono_class_value_size (klass, NULL);
116                 int i;
117                 for (i = 0; i < count; ++i) {
118                         scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size,
119                                         (char*)src + i * element_size - sizeof (MonoObject),
120                                         (mword) klass->gc_descr);
121                 }
122         }
123 #endif
124
125         sgen_get_remset ()->wbarrier_value_copy (dest, src, count, mono_class_value_size (klass, NULL));
126 }
127
128 /**
129  * mono_gc_wbarrier_object_copy:
130  *
131  * Write barrier to call when \p obj is the result of a clone or copy of an object.
132  */
133 void
134 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
135 {
136         int size;
137
138         HEAVY_STAT (++stat_wbarrier_object_copy);
139
140         SGEN_ASSERT (6, !ptr_on_stack (obj), "Why is this called for a non-reference type?");
141         if (sgen_ptr_in_nursery (obj) || !SGEN_OBJECT_HAS_REFERENCES (src)) {
142                 size = mono_object_class (obj)->instance_size;
143                 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
144                                 size - sizeof (MonoObject));
145                 return; 
146         }
147
148 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
149         if (binary_protocol_is_heavy_enabled ())
150                 scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr);
151 #endif
152
153         sgen_get_remset ()->wbarrier_object_copy (obj, src);
154 }
155
156 /**
157  * mono_gc_wbarrier_set_arrayref:
158  */
159 void
160 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
161 {
162         HEAVY_STAT (++stat_wbarrier_set_arrayref);
163         if (sgen_ptr_in_nursery (slot_ptr)) {
164                 *(void**)slot_ptr = value;
165                 return;
166         }
167         SGEN_LOG (8, "Adding remset at %p", slot_ptr);
168         if (value)
169                 binary_protocol_wbarrier (slot_ptr, value, value->vtable);
170
171         sgen_get_remset ()->wbarrier_set_field ((GCObject*)arr, slot_ptr, value);
172 }
173
174 /**
175  * mono_gc_wbarrier_set_field:
176  */
177 void
178 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
179 {
180         mono_gc_wbarrier_set_arrayref ((MonoArray*)obj, field_ptr, value);
181 }
182
183 void
184 mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
185 {
186         sgen_wbarrier_range_copy (_dest, _src, size);
187 }
188
189 void*
190 mono_gc_get_range_copy_func (void)
191 {
192         return sgen_get_remset ()->wbarrier_range_copy;
193 }
194
195 int
196 mono_gc_get_suspend_signal (void)
197 {
198         return mono_threads_suspend_get_suspend_signal ();
199 }
200
201 int
202 mono_gc_get_restart_signal (void)
203 {
204         return mono_threads_suspend_get_restart_signal ();
205 }
206
207 static MonoMethod *write_barrier_conc_method;
208 static MonoMethod *write_barrier_noconc_method;
209
210 gboolean
211 sgen_is_critical_method (MonoMethod *method)
212 {
213         return sgen_is_managed_allocator (method);
214 }
215
216 gboolean
217 sgen_has_critical_method (void)
218 {
219         return sgen_has_managed_allocator ();
220 }
221
222 static gboolean
223 ip_in_critical_region (MonoDomain *domain, gpointer ip)
224 {
225         MonoJitInfo *ji;
226         MonoMethod *method;
227
228         /*
229          * We pass false for 'try_aot' so this becomes async safe.
230          * It won't find aot methods whose jit info is not yet loaded,
231          * so we preload their jit info in the JIT.
232          */
233         ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
234         if (!ji)
235                 return FALSE;
236
237         method = mono_jit_info_get_method (ji);
238
239         return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method);
240 }
241
242 gboolean
243 mono_gc_is_critical_method (MonoMethod *method)
244 {
245         return sgen_is_critical_method (method);
246 }
247
248 #ifndef DISABLE_JIT
249
250 static void
251 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels, gboolean is_concurrent)
252 {
253         int shifted_nursery_start = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
254
255         memset (nursery_check_return_labels, 0, sizeof (int) * 2);
256         // if (ptr_in_nursery (ptr)) return;
257         /*
258          * Masking out the bits might be faster, but we would have to use 64 bit
259          * immediates, which might be slower.
260          */
261         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
262         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_START);
263         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
264         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS);
265         mono_mb_emit_byte (mb, CEE_SHR_UN);
266         mono_mb_emit_stloc (mb, shifted_nursery_start);
267
268         mono_mb_emit_ldarg (mb, 0);
269         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
270         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS);
271         mono_mb_emit_byte (mb, CEE_SHR_UN);
272         mono_mb_emit_ldloc (mb, shifted_nursery_start);
273         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
274
275         if (!is_concurrent) {
276                 // if (!ptr_in_nursery (*ptr)) return;
277                 mono_mb_emit_ldarg (mb, 0);
278                 mono_mb_emit_byte (mb, CEE_LDIND_I);
279                 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
280                 mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS);
281                 mono_mb_emit_byte (mb, CEE_SHR_UN);
282                 mono_mb_emit_ldloc (mb, shifted_nursery_start);
283                 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
284         }
285 }
286 #endif
287
288 MonoMethod*
289 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
290 {
291         MonoMethod *res;
292         MonoMethodBuilder *mb;
293         MonoMethodSignature *sig;
294         MonoMethod **write_barrier_method_addr;
295         WrapperInfo *info;
296 #ifdef MANAGED_WBARRIER
297         int i, nursery_check_labels [2];
298 #endif
299
300         // FIXME: Maybe create a separate version for ctors (the branch would be
301         // correctly predicted more times)
302         if (is_concurrent)
303                 write_barrier_method_addr = &write_barrier_conc_method;
304         else
305                 write_barrier_method_addr = &write_barrier_noconc_method;
306
307         if (*write_barrier_method_addr)
308                 return *write_barrier_method_addr;
309
310         /* Create the IL version of mono_gc_barrier_generic_store () */
311         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
312         sig->ret = &mono_defaults.void_class->byval_arg;
313         sig->params [0] = &mono_defaults.int_class->byval_arg;
314
315         if (is_concurrent)
316                 mb = mono_mb_new (mono_defaults.object_class, "wbarrier_conc", MONO_WRAPPER_WRITE_BARRIER);
317         else
318                 mb = mono_mb_new (mono_defaults.object_class, "wbarrier_noconc", MONO_WRAPPER_WRITE_BARRIER);
319
320 #ifndef DISABLE_JIT
321 #ifdef MANAGED_WBARRIER
322         emit_nursery_check (mb, nursery_check_labels, is_concurrent);
323         /*
324         addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
325         *addr = 1;
326
327         sgen_cardtable:
328                 LDC_PTR sgen_cardtable
329
330         address >> CARD_BITS
331                 LDARG_0
332                 LDC_I4 CARD_BITS
333                 SHR_UN
334         if (SGEN_HAVE_OVERLAPPING_CARDS) {
335                 LDC_PTR card_table_mask
336                 AND
337         }
338         AND
339         ldc_i4_1
340         stind_i1
341         */
342         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
343         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_CARD_TABLE);
344         mono_mb_emit_ldarg (mb, 0);
345         mono_mb_emit_icon (mb, CARD_BITS);
346         mono_mb_emit_byte (mb, CEE_SHR_UN);
347         mono_mb_emit_byte (mb, CEE_CONV_I);
348 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
349 #if SIZEOF_VOID_P == 8
350         mono_mb_emit_icon8 (mb, CARD_MASK);
351 #else
352         mono_mb_emit_icon (mb, CARD_MASK);
353 #endif
354         mono_mb_emit_byte (mb, CEE_CONV_I);
355         mono_mb_emit_byte (mb, CEE_AND);
356 #endif
357         mono_mb_emit_byte (mb, CEE_ADD);
358         mono_mb_emit_icon (mb, 1);
359         mono_mb_emit_byte (mb, CEE_STIND_I1);
360
361         // return;
362         for (i = 0; i < 2; ++i) {
363                 if (nursery_check_labels [i])
364                         mono_mb_patch_branch (mb, nursery_check_labels [i]);
365         }
366         mono_mb_emit_byte (mb, CEE_RET);
367 #else
368         mono_mb_emit_ldarg (mb, 0);
369         mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
370         mono_mb_emit_byte (mb, CEE_RET);
371 #endif
372 #endif
373         res = mono_mb_create_method (mb, sig, 16);
374         info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
375         mono_marshal_set_wrapper_info (res, info);
376         mono_mb_free (mb);
377
378         LOCK_GC;
379         if (*write_barrier_method_addr) {
380                 /* Already created */
381                 mono_free_method (res);
382         } else {
383                 /* double-checked locking */
384                 mono_memory_barrier ();
385                 *write_barrier_method_addr = res;
386         }
387         UNLOCK_GC;
388
389         return *write_barrier_method_addr;
390 }
391
392 MonoMethod*
393 mono_gc_get_write_barrier (void)
394 {
395         return mono_gc_get_specific_write_barrier (major_collector.is_concurrent);
396 }
397
398 /*
399  * Dummy filler objects
400  */
401
402 /* Vtable of the objects used to fill out nursery fragments before a collection */
403 static GCVTable array_fill_vtable;
404
405 static GCVTable
406 get_array_fill_vtable (void)
407 {
408         if (!array_fill_vtable) {
409                 static MonoClass klass;
410                 static char _vtable[sizeof(MonoVTable)+8];
411                 MonoVTable* vtable = (MonoVTable*) ALIGN_TO((mword)_vtable, 8);
412                 gsize bmap;
413
414                 MonoDomain *domain = mono_get_root_domain ();
415                 g_assert (domain);
416
417                 klass.element_class = mono_defaults.byte_class;
418                 klass.rank = 1;
419                 klass.instance_size = MONO_SIZEOF_MONO_ARRAY;
420                 klass.sizes.element_size = 1;
421                 klass.name = "array_filler_type";
422
423                 vtable->klass = &klass;
424                 bmap = 0;
425                 vtable->gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1);
426                 vtable->rank = 1;
427
428                 array_fill_vtable = vtable;
429         }
430         return array_fill_vtable;
431 }
432
433 gboolean
434 sgen_client_array_fill_range (char *start, size_t size)
435 {
436         MonoArray *o;
437
438         if (size < MONO_SIZEOF_MONO_ARRAY) {
439                 memset (start, 0, size);
440                 return FALSE;
441         }
442
443         o = (MonoArray*)start;
444         o->obj.vtable = (MonoVTable*)get_array_fill_vtable ();
445         /* Mark this as not a real object */
446         o->obj.synchronisation = (MonoThreadsSync *)GINT_TO_POINTER (-1);
447         o->bounds = NULL;
448         o->max_length = (mono_array_size_t)(size - MONO_SIZEOF_MONO_ARRAY);
449
450         return TRUE;
451 }
452
453 void
454 sgen_client_zero_array_fill_header (void *p, size_t size)
455 {
456         if (size >= MONO_SIZEOF_MONO_ARRAY) {
457                 memset (p, 0, MONO_SIZEOF_MONO_ARRAY);
458         } else {
459                 static guint8 zeros [MONO_SIZEOF_MONO_ARRAY];
460
461                 SGEN_ASSERT (0, !memcmp (p, zeros, size), "TLAB segment must be zeroed out.");
462         }
463 }
464
465 /*
466  * Finalization
467  */
468
469 static MonoGCFinalizerCallbacks fin_callbacks;
470
471 guint
472 mono_gc_get_vtable_bits (MonoClass *klass)
473 {
474         guint res = 0;
475         /* FIXME move this to the bridge code */
476         if (sgen_need_bridge_processing ()) {
477                 switch (sgen_bridge_class_kind (klass)) {
478                 case GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS:
479                 case GC_BRIDGE_OPAQUE_BRIDGE_CLASS:
480                         res = SGEN_GC_BIT_BRIDGE_OBJECT;
481                         break;
482                 case GC_BRIDGE_OPAQUE_CLASS:
483                         res = SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT;
484                         break;
485                 case GC_BRIDGE_TRANSPARENT_CLASS:
486                         break;
487                 }
488         }
489         if (fin_callbacks.is_class_finalization_aware) {
490                 if (fin_callbacks.is_class_finalization_aware (klass))
491                         res |= SGEN_GC_BIT_FINALIZER_AWARE;
492         }
493         return res;
494 }
495
496 static gboolean
497 is_finalization_aware (MonoObject *obj)
498 {
499         MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
500         return (vt->gc_bits & SGEN_GC_BIT_FINALIZER_AWARE) == SGEN_GC_BIT_FINALIZER_AWARE;
501 }
502
503 void
504 sgen_client_object_queued_for_finalization (GCObject *obj)
505 {
506         if (fin_callbacks.object_queued_for_finalization && is_finalization_aware (obj))
507                 fin_callbacks.object_queued_for_finalization (obj);
508
509 #ifdef ENABLE_DTRACE
510         if (G_UNLIKELY (MONO_GC_FINALIZE_ENQUEUE_ENABLED ())) {
511                 int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD;
512                 GCVTable vt = SGEN_LOAD_VTABLE (obj);
513                 MONO_GC_FINALIZE_ENQUEUE ((mword)obj, sgen_safe_object_get_size (obj),
514                                 sgen_client_vtable_get_namespace (vt), sgen_client_vtable_get_name (vt), gen,
515                                 sgen_client_object_has_critical_finalizer (obj));
516         }
517 #endif
518 }
519
520 void
521 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
522 {
523         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
524                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
525
526         fin_callbacks = *callbacks;
527 }
528
529 void
530 sgen_client_run_finalize (MonoObject *obj)
531 {
532         mono_gc_run_finalize (obj, NULL);
533 }
534
535 /**
536  * mono_gc_invoke_finalizers:
537  */
538 int
539 mono_gc_invoke_finalizers (void)
540 {
541         return sgen_gc_invoke_finalizers ();
542 }
543
544 /**
545  * mono_gc_pending_finalizers:
546  */
547 MonoBoolean
548 mono_gc_pending_finalizers (void)
549 {
550         return sgen_have_pending_finalizers ();
551 }
552
553 void
554 sgen_client_finalize_notify (void)
555 {
556         mono_gc_finalize_notify ();
557 }
558
559 void
560 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
561 {
562         sgen_object_register_for_finalization (obj, user_data);
563 }
564
565 static gboolean
566 object_in_domain_predicate (MonoObject *obj, void *user_data)
567 {
568         MonoDomain *domain = (MonoDomain *)user_data;
569         if (mono_object_domain (obj) == domain) {
570                 SGEN_LOG (5, "Unregistering finalizer for object: %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
571                 return TRUE;
572         }
573         return FALSE;
574 }
575
576 /**
577  * mono_gc_finalizers_for_domain:
578  * \param domain the unloading appdomain
579  * \param out_array output array
580  * \param out_size size of output array
581  * Enqueue for finalization all objects that belong to the unloading appdomain \p domain.
582  * \p suspend is used for early termination of the enqueuing process.
583  */
584 void
585 mono_gc_finalize_domain (MonoDomain *domain)
586 {
587         sgen_finalize_if (object_in_domain_predicate, domain);
588 }
589
590 void
591 mono_gc_suspend_finalizers (void)
592 {
593         sgen_set_suspend_finalizers ();
594 }
595
596 /*
597  * Ephemerons
598  */
599
600 typedef struct _EphemeronLinkNode EphemeronLinkNode;
601
602 struct _EphemeronLinkNode {
603         EphemeronLinkNode *next;
604         MonoArray *array;
605 };
606
607 typedef struct {
608        GCObject *key;
609        GCObject *value;
610 } Ephemeron;
611
612 static EphemeronLinkNode *ephemeron_list;
613
614 /* LOCKING: requires that the GC lock is held */
615 static void
616 null_ephemerons_for_domain (MonoDomain *domain)
617 {
618         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
619
620         while (current) {
621                 MonoObject *object = (MonoObject*)current->array;
622
623                 if (object)
624                         SGEN_ASSERT (0, object->vtable, "Can't have objects without vtables.");
625
626                 if (object && object->vtable->domain == domain) {
627                         EphemeronLinkNode *tmp = current;
628
629                         if (prev)
630                                 prev->next = current->next;
631                         else
632                                 ephemeron_list = current->next;
633
634                         current = current->next;
635                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
636                 } else {
637                         prev = current;
638                         current = current->next;
639                 }
640         }
641 }
642
643 /* LOCKING: requires that the GC lock is held */
644 void
645 sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx)
646 {
647         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
648         SgenGrayQueue *queue = ctx.queue;
649         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
650         Ephemeron *cur, *array_end;
651         GCObject *tombstone;
652
653         while (current) {
654                 MonoArray *array = current->array;
655
656                 if (!sgen_is_object_alive_for_current_gen ((GCObject*)array)) {
657                         EphemeronLinkNode *tmp = current;
658
659                         SGEN_LOG (5, "Dead Ephemeron array at %p", array);
660
661                         if (prev)
662                                 prev->next = current->next;
663                         else
664                                 ephemeron_list = current->next;
665
666                         current = current->next;
667                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
668
669                         continue;
670                 }
671
672                 copy_func ((GCObject**)&array, queue);
673                 current->array = array;
674
675                 SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", array);
676
677                 cur = mono_array_addr (array, Ephemeron, 0);
678                 array_end = cur + mono_array_length_fast (array);
679                 tombstone = SGEN_LOAD_VTABLE ((GCObject*)array)->domain->ephemeron_tombstone;
680
681                 for (; cur < array_end; ++cur) {
682                         GCObject *key = cur->key;
683
684                         if (!key || key == tombstone)
685                                 continue;
686
687                         SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
688                                 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
689                                 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
690
691                         if (!sgen_is_object_alive_for_current_gen (key)) {
692                                 cur->key = tombstone;
693                                 cur->value = NULL;
694                                 continue;
695                         }
696                 }
697                 prev = current;
698                 current = current->next;
699         }
700 }
701
702 /*
703 LOCKING: requires that the GC lock is held
704
705 Limitations: We scan all ephemerons on every collection since the current design doesn't allow for a simple nursery/mature split.
706 */
707 gboolean
708 sgen_client_mark_ephemerons (ScanCopyContext ctx)
709 {
710         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
711         SgenGrayQueue *queue = ctx.queue;
712         gboolean nothing_marked = TRUE;
713         EphemeronLinkNode *current = ephemeron_list;
714         Ephemeron *cur, *array_end;
715         GCObject *tombstone;
716
717         for (current = ephemeron_list; current; current = current->next) {
718                 MonoArray *array = current->array;
719                 SGEN_LOG (5, "Ephemeron array at %p", array);
720
721                 /*It has to be alive*/
722                 if (!sgen_is_object_alive_for_current_gen ((GCObject*)array)) {
723                         SGEN_LOG (5, "\tnot reachable");
724                         continue;
725                 }
726
727                 copy_func ((GCObject**)&array, queue);
728
729                 cur = mono_array_addr (array, Ephemeron, 0);
730                 array_end = cur + mono_array_length_fast (array);
731                 tombstone = SGEN_LOAD_VTABLE ((GCObject*)array)->domain->ephemeron_tombstone;
732
733                 for (; cur < array_end; ++cur) {
734                         GCObject *key = cur->key;
735
736                         if (!key || key == tombstone)
737                                 continue;
738
739                         SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
740                                 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
741                                 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
742
743                         if (sgen_is_object_alive_for_current_gen (key)) {
744                                 GCObject *value = cur->value;
745
746                                 copy_func (&cur->key, queue);
747                                 if (value) {
748                                         if (!sgen_is_object_alive_for_current_gen (value))
749                                                 nothing_marked = FALSE;
750                                         copy_func (&cur->value, queue);
751                                 }
752                         }
753                 }
754         }
755
756         SGEN_LOG (5, "Ephemeron run finished. Is it done %d", nothing_marked);
757         return nothing_marked;
758 }
759
760 gboolean
761 mono_gc_ephemeron_array_add (MonoObject *obj)
762 {
763         EphemeronLinkNode *node;
764
765         LOCK_GC;
766
767         node = (EphemeronLinkNode *)sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
768         if (!node) {
769                 UNLOCK_GC;
770                 return FALSE;
771         }
772         node->array = (MonoArray*)obj;
773         node->next = ephemeron_list;
774         ephemeron_list = node;
775
776         SGEN_LOG (5, "Registered ephemeron array %p", obj);
777
778         UNLOCK_GC;
779         return TRUE;
780 }
781
782 /*
783  * Appdomain handling
784  */
785
786 static gboolean
787 need_remove_object_for_domain (GCObject *start, MonoDomain *domain)
788 {
789         if (mono_object_domain (start) == domain) {
790                 SGEN_LOG (4, "Need to cleanup object %p", start);
791                 binary_protocol_cleanup (start, (gpointer)SGEN_LOAD_VTABLE (start), sgen_safe_object_get_size ((GCObject*)start));
792                 return TRUE;
793         }
794         return FALSE;
795 }
796
797 static void
798 process_object_for_domain_clearing (GCObject *start, MonoDomain *domain)
799 {
800         MonoVTable *vt = SGEN_LOAD_VTABLE (start);
801         if (vt->klass == mono_defaults.internal_thread_class)
802                 g_assert (mono_object_domain (start) == mono_get_root_domain ());
803         /* The object could be a proxy for an object in the domain
804            we're deleting. */
805 #ifndef DISABLE_REMOTING
806         if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
807                 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
808
809                 /* The server could already have been zeroed out, so
810                    we need to check for that, too. */
811                 if (server && (!SGEN_LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
812                         SGEN_LOG (4, "Cleaning up remote pointer in %p to object %p", start, server);
813                         ((MonoRealProxy*)start)->unwrapped_server = NULL;
814                 }
815         }
816 #endif
817 }
818
819 static gboolean
820 clear_domain_process_object (GCObject *obj, MonoDomain *domain)
821 {
822         gboolean remove;
823
824         process_object_for_domain_clearing (obj, domain);
825         remove = need_remove_object_for_domain (obj, domain);
826
827         if (remove && obj->synchronisation) {
828                 guint32 dislink = mono_monitor_get_object_monitor_gchandle (obj);
829                 if (dislink)
830                         mono_gchandle_free (dislink);
831         }
832
833         return remove;
834 }
835
836 static void
837 clear_domain_process_minor_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
838 {
839         if (clear_domain_process_object (obj, domain)) {
840                 CANARIFY_SIZE (size);
841                 memset (obj, 0, size);
842         }
843 }
844
845 static void
846 clear_domain_process_major_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
847 {
848         clear_domain_process_object (obj, domain);
849 }
850
851 static void
852 clear_domain_free_major_non_pinned_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
853 {
854         if (need_remove_object_for_domain (obj, domain))
855                 major_collector.free_non_pinned_object (obj, size);
856 }
857
858 static void
859 clear_domain_free_major_pinned_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
860 {
861         if (need_remove_object_for_domain (obj, domain))
862                 major_collector.free_pinned_object (obj, size);
863 }
864
865 /*
866  * When appdomains are unloaded we can easily remove objects that have finalizers,
867  * but all the others could still be present in random places on the heap.
868  * We need a sweep to get rid of them even though it's going to be costly
869  * with big heaps.
870  * The reason we need to remove them is because we access the vtable and class
871  * structures to know the object size and the reference bitmap: once the domain is
872  * unloaded the point to random memory.
873  */
874 void
875 mono_gc_clear_domain (MonoDomain * domain)
876 {
877         LOSObject *bigobj, *prev;
878         int i;
879
880         LOCK_GC;
881
882         binary_protocol_domain_unload_begin (domain);
883
884         sgen_stop_world (0);
885
886         if (sgen_concurrent_collection_in_progress ())
887                 sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE, FALSE);
888         SGEN_ASSERT (0, !sgen_concurrent_collection_in_progress (), "We just ordered a synchronous collection.  Why are we collecting concurrently?");
889
890         major_collector.finish_sweeping ();
891
892         sgen_process_fin_stage_entries ();
893
894         sgen_clear_nursery_fragments ();
895
896         if (sgen_mono_xdomain_checks && domain != mono_get_root_domain ()) {
897                 sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
898                 sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
899                 sgen_check_for_xdomain_refs ();
900         }
901
902         /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
903         to memory returned to the OS.*/
904         null_ephemerons_for_domain (domain);
905         sgen_null_links_for_domain (domain);
906
907         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
908                 sgen_remove_finalizers_if (object_in_domain_predicate, domain, i);
909
910         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
911                         (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE, TRUE);
912
913         /* We need two passes over major and large objects because
914            freeing such objects might give their memory back to the OS
915            (in the case of large objects) or obliterate its vtable
916            (pinned objects with major-copying or pinned and non-pinned
917            objects with major-mark&sweep), but we might need to
918            dereference a pointer from an object to another object if
919            the first object is a proxy. */
920         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
921         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
922                 clear_domain_process_object ((GCObject*)bigobj->data, domain);
923
924         prev = NULL;
925         for (bigobj = los_object_list; bigobj;) {
926                 if (need_remove_object_for_domain ((GCObject*)bigobj->data, domain)) {
927                         LOSObject *to_free = bigobj;
928                         if (prev)
929                                 prev->next = bigobj->next;
930                         else
931                                 los_object_list = bigobj->next;
932                         bigobj = bigobj->next;
933                         SGEN_LOG (4, "Freeing large object %p", bigobj->data);
934                         sgen_los_free_object (to_free);
935                         continue;
936                 }
937                 prev = bigobj;
938                 bigobj = bigobj->next;
939         }
940         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_NON_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
941         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
942
943         if (domain == mono_get_root_domain ()) {
944                 sgen_pin_stats_report ();
945                 sgen_object_layout_dump (stdout);
946         }
947
948         sgen_restart_world (0);
949
950         binary_protocol_domain_unload_end (domain);
951         binary_protocol_flush_buffers (FALSE);
952
953         UNLOCK_GC;
954 }
955
956 /*
957  * Allocation
958  */
959
960 void*
961 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
962 {
963         MonoObject *obj = sgen_alloc_obj (vtable, size);
964
965         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
966                 if (obj)
967                         mono_profiler_allocation (obj);
968         }
969
970         return obj;
971 }
972
973 void*
974 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
975 {
976         MonoObject *obj = sgen_alloc_obj_pinned (vtable, size);
977
978         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
979                 if (obj)
980                         mono_profiler_allocation (obj);
981         }
982
983         return obj;
984 }
985
986 void*
987 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
988 {
989         MonoObject *obj = sgen_alloc_obj_mature (vtable, size);
990
991         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
992                 if (obj)
993                         mono_profiler_allocation (obj);
994         }
995
996         return obj;
997 }
998
999 /**
1000  * mono_gc_alloc_fixed:
1001  */
1002 void*
1003 mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
1004 {
1005         /* FIXME: do a single allocation */
1006         void *res = g_calloc (1, size);
1007         if (!res)
1008                 return NULL;
1009         if (!mono_gc_register_root ((char *)res, size, descr, source, msg)) {
1010                 g_free (res);
1011                 res = NULL;
1012         }
1013         return res;
1014 }
1015
1016 /**
1017  * mono_gc_free_fixed:
1018  */
1019 void
1020 mono_gc_free_fixed (void* addr)
1021 {
1022         mono_gc_deregister_root ((char *)addr);
1023         g_free (addr);
1024 }
1025
1026 /*
1027  * Managed allocator
1028  */
1029
1030 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1031 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1032 static gboolean use_managed_allocator = TRUE;
1033
1034 #ifdef MANAGED_ALLOCATION
1035 // Cache the SgenThreadInfo pointer in a local 'var'.
1036 #define EMIT_TLS_ACCESS_VAR(mb, var) \
1037         do { \
1038                 var = mono_mb_add_local ((mb), &mono_defaults.int_class->byval_arg); \
1039                 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
1040                 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
1041                 mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_THREAD_INFO); \
1042                 mono_mb_emit_stloc ((mb), (var)); \
1043         } while (0)
1044
1045 #define EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR(mb, var) \
1046         do { \
1047                 mono_mb_emit_ldloc ((mb), (var)); \
1048                 mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenClientThreadInfo, in_critical_region)); \
1049                 mono_mb_emit_byte ((mb), CEE_ADD); \
1050         } while (0)
1051
1052 #define EMIT_TLS_ACCESS_NEXT_ADDR(mb, var)      do {    \
1053         mono_mb_emit_ldloc ((mb), (var));               \
1054         mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_next));       \
1055         mono_mb_emit_byte ((mb), CEE_ADD);              \
1056         } while (0)
1057
1058 #define EMIT_TLS_ACCESS_TEMP_END(mb, var)       do {    \
1059         mono_mb_emit_ldloc ((mb), (var));               \
1060         mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_temp_end));   \
1061         mono_mb_emit_byte ((mb), CEE_ADD);              \
1062         mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
1063         } while (0)
1064
1065 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
1066  * for each class. This is currently not easy to do, as it is hard to generate basic 
1067  * blocks + branches, but it is easy with the linear IL codebase.
1068  *
1069  * For this to work we'd need to solve the TLAB race, first.  Now we
1070  * require the allocator to be in a few known methods to make sure
1071  * that they are executed atomically via the restart mechanism.
1072  */
1073 static MonoMethod*
1074 create_allocator (int atype, ManagedAllocatorVariant variant)
1075 {
1076         int p_var, size_var, real_size_var, thread_var G_GNUC_UNUSED;
1077         gboolean slowpath = variant == MANAGED_ALLOCATOR_SLOW_PATH;
1078         guint32 fastpath_branch, max_size_branch, no_oom_branch;
1079         MonoMethodBuilder *mb;
1080         MonoMethod *res;
1081         MonoMethodSignature *csig;
1082         static gboolean registered = FALSE;
1083         int tlab_next_addr_var, new_next_var;
1084         const char *name = NULL;
1085         WrapperInfo *info;
1086         int num_params, i;
1087
1088         if (!registered) {
1089                 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
1090                 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
1091                 mono_register_jit_icall (mono_gc_alloc_string, "mono_gc_alloc_string", mono_create_icall_signature ("object ptr int int32"), FALSE);
1092                 registered = TRUE;
1093         }
1094
1095         if (atype == ATYPE_SMALL) {
1096                 name = slowpath ? "SlowAllocSmall" : "AllocSmall";
1097         } else if (atype == ATYPE_NORMAL) {
1098                 name = slowpath ? "SlowAlloc" : "Alloc";
1099         } else if (atype == ATYPE_VECTOR) {
1100                 name = slowpath ? "SlowAllocVector" : "AllocVector";
1101         } else if (atype == ATYPE_STRING) {
1102                 name = slowpath ? "SlowAllocString" : "AllocString";
1103         } else {
1104                 g_assert_not_reached ();
1105         }
1106
1107         if (atype == ATYPE_NORMAL)
1108                 num_params = 1;
1109         else
1110                 num_params = 2;
1111
1112         csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
1113         if (atype == ATYPE_STRING) {
1114                 csig->ret = &mono_defaults.string_class->byval_arg;
1115                 csig->params [0] = &mono_defaults.int_class->byval_arg;
1116                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
1117         } else {
1118                 csig->ret = &mono_defaults.object_class->byval_arg;
1119                 for (i = 0; i < num_params; i++)
1120                         csig->params [i] = &mono_defaults.int_class->byval_arg;
1121         }
1122
1123         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
1124
1125 #ifndef DISABLE_JIT
1126         if (slowpath) {
1127                 switch (atype) {
1128                 case ATYPE_NORMAL:
1129                 case ATYPE_SMALL:
1130                         mono_mb_emit_ldarg (mb, 0);
1131                         mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1132                         break;
1133                 case ATYPE_VECTOR:
1134                         mono_mb_emit_ldarg (mb, 0);
1135                         mono_mb_emit_ldarg (mb, 1);
1136                         mono_mb_emit_icall (mb, ves_icall_array_new_specific);
1137                         break;
1138                 case ATYPE_STRING:
1139                         mono_mb_emit_ldarg (mb, 1);
1140                         mono_mb_emit_icall (mb, ves_icall_string_alloc);
1141                         break;
1142                 default:
1143                         g_assert_not_reached ();
1144                 }
1145
1146                 goto done;
1147         }
1148
1149         /*
1150          * Tls access might call foreign code or code without jinfo. This can
1151          * only happen if we are outside of the critical region.
1152          */
1153         EMIT_TLS_ACCESS_VAR (mb, thread_var);
1154
1155         size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1156         if (atype == ATYPE_SMALL) {
1157                 /* size_var = size_arg */
1158                 mono_mb_emit_ldarg (mb, 1);
1159                 mono_mb_emit_stloc (mb, size_var);
1160         } else if (atype == ATYPE_NORMAL) {
1161                 /* size = vtable->klass->instance_size; */
1162                 mono_mb_emit_ldarg (mb, 0);
1163                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
1164                 mono_mb_emit_byte (mb, CEE_ADD);
1165                 mono_mb_emit_byte (mb, CEE_LDIND_I);
1166                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, instance_size));
1167                 mono_mb_emit_byte (mb, CEE_ADD);
1168                 /* FIXME: assert instance_size stays a 4 byte integer */
1169                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
1170                 mono_mb_emit_byte (mb, CEE_CONV_I);
1171                 mono_mb_emit_stloc (mb, size_var);
1172         } else if (atype == ATYPE_VECTOR) {
1173                 MonoExceptionClause *clause;
1174                 int pos, pos_leave, pos_error;
1175                 MonoClass *oom_exc_class;
1176                 MonoMethod *ctor;
1177
1178                 /*
1179                  * n > MONO_ARRAY_MAX_INDEX => OutOfMemoryException
1180                  * n < 0                    => OverflowException
1181                  *
1182                  * We can do an unsigned comparison to catch both cases, then in the error
1183                  * case compare signed to distinguish between them.
1184                  */
1185                 mono_mb_emit_ldarg (mb, 1);
1186                 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
1187                 mono_mb_emit_byte (mb, CEE_CONV_U);
1188                 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
1189
1190                 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1191                 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
1192                 mono_mb_emit_ldarg (mb, 1);
1193                 mono_mb_emit_icon (mb, 0);
1194                 pos_error = mono_mb_emit_short_branch (mb, CEE_BLT_S);
1195                 mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
1196                 mono_mb_patch_short_branch (mb, pos_error);
1197                 mono_mb_emit_exception (mb, "OverflowException", NULL);
1198
1199                 mono_mb_patch_short_branch (mb, pos);
1200
1201                 clause = (MonoExceptionClause *)mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
1202                 clause->try_offset = mono_mb_get_label (mb);
1203
1204                 /* vtable->klass->sizes.element_size */
1205                 mono_mb_emit_ldarg (mb, 0);
1206                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
1207                 mono_mb_emit_byte (mb, CEE_ADD);
1208                 mono_mb_emit_byte (mb, CEE_LDIND_I);
1209                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, sizes));
1210                 mono_mb_emit_byte (mb, CEE_ADD);
1211                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
1212                 mono_mb_emit_byte (mb, CEE_CONV_I);
1213
1214                 /* * n */
1215                 mono_mb_emit_ldarg (mb, 1);
1216                 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
1217                 /* + sizeof (MonoArray) */
1218                 mono_mb_emit_icon (mb, MONO_SIZEOF_MONO_ARRAY);
1219                 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
1220                 mono_mb_emit_stloc (mb, size_var);
1221
1222                 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
1223
1224                 /* catch */
1225                 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
1226                 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
1227                 clause->data.catch_class = mono_class_load_from_name (mono_defaults.corlib,
1228                                 "System", "OverflowException");
1229                 clause->handler_offset = mono_mb_get_label (mb);
1230
1231                 oom_exc_class = mono_class_load_from_name (mono_defaults.corlib,
1232                                 "System", "OutOfMemoryException");
1233                 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
1234                 g_assert (ctor);
1235
1236                 mono_mb_emit_byte (mb, CEE_POP);
1237                 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
1238                 mono_mb_emit_byte (mb, CEE_THROW);
1239
1240                 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
1241                 mono_mb_set_clauses (mb, 1, clause);
1242                 mono_mb_patch_branch (mb, pos_leave);
1243                 /* end catch */
1244         } else if (atype == ATYPE_STRING) {
1245                 int pos;
1246
1247                 /*
1248                  * a string allocator method takes the args: (vtable, len)
1249                  *
1250                  * bytes = offsetof (MonoString, chars) + ((len + 1) * 2)
1251                  *
1252                  * condition:
1253                  *
1254                  * bytes <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
1255                  *
1256                  * therefore:
1257                  *
1258                  * offsetof (MonoString, chars) + ((len + 1) * 2) <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
1259                  * len <= (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - offsetof (MonoString, chars)) / 2 - 1
1260                  */
1261                 mono_mb_emit_ldarg (mb, 1);
1262                 mono_mb_emit_icon (mb, (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - MONO_STRUCT_OFFSET (MonoString, chars)) / 2 - 1);
1263                 pos = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1264
1265                 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1266                 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
1267                 mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
1268                 mono_mb_patch_short_branch (mb, pos);
1269
1270                 mono_mb_emit_ldarg (mb, 1);
1271                 mono_mb_emit_icon (mb, 1);
1272                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1273                 //WE manually fold the above + 2 here
1274                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, chars) + 2);
1275                 mono_mb_emit_byte (mb, CEE_ADD);
1276                 mono_mb_emit_stloc (mb, size_var);
1277         } else {
1278                 g_assert_not_reached ();
1279         }
1280
1281 #ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
1282         EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
1283         mono_mb_emit_byte (mb, CEE_LDC_I4_1);
1284         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1285         mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
1286         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE);
1287 #endif
1288
1289         if (nursery_canaries_enabled ()) {
1290                 real_size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1291                 mono_mb_emit_ldloc (mb, size_var);
1292                 mono_mb_emit_stloc(mb, real_size_var);
1293         }
1294         else
1295                 real_size_var = size_var;
1296
1297         /* size += ALLOC_ALIGN - 1; */
1298         mono_mb_emit_ldloc (mb, size_var);
1299         mono_mb_emit_icon (mb, SGEN_ALLOC_ALIGN - 1);
1300         mono_mb_emit_byte (mb, CEE_ADD);
1301         /* size &= ~(ALLOC_ALIGN - 1); */
1302         mono_mb_emit_icon (mb, ~(SGEN_ALLOC_ALIGN - 1));
1303         mono_mb_emit_byte (mb, CEE_AND);
1304         mono_mb_emit_stloc (mb, size_var);
1305
1306         /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
1307         if (atype != ATYPE_SMALL) {
1308                 mono_mb_emit_ldloc (mb, size_var);
1309                 mono_mb_emit_icon (mb, SGEN_MAX_SMALL_OBJ_SIZE);
1310                 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
1311         }
1312
1313         /*
1314          * We need to modify tlab_next, but the JIT only supports reading, so we read
1315          * another tls var holding its address instead.
1316          */
1317
1318         /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
1319         tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1320         EMIT_TLS_ACCESS_NEXT_ADDR (mb, thread_var);
1321         mono_mb_emit_stloc (mb, tlab_next_addr_var);
1322
1323         /* p = (void**)tlab_next; */
1324         p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1325         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
1326         mono_mb_emit_byte (mb, CEE_LDIND_I);
1327         mono_mb_emit_stloc (mb, p_var);
1328         
1329         /* new_next = (char*)p + size; */
1330         new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1331         mono_mb_emit_ldloc (mb, p_var);
1332         mono_mb_emit_ldloc (mb, size_var);
1333         mono_mb_emit_byte (mb, CEE_CONV_I);
1334         mono_mb_emit_byte (mb, CEE_ADD);
1335
1336         if (nursery_canaries_enabled ()) {
1337                         mono_mb_emit_icon (mb, CANARY_SIZE);
1338                         mono_mb_emit_byte (mb, CEE_ADD);
1339         }
1340         mono_mb_emit_stloc (mb, new_next_var);
1341
1342         /* if (G_LIKELY (new_next < tlab_temp_end)) */
1343         mono_mb_emit_ldloc (mb, new_next_var);
1344         EMIT_TLS_ACCESS_TEMP_END (mb, thread_var);
1345         fastpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1346
1347         /* Slowpath */
1348         if (atype != ATYPE_SMALL)
1349                 mono_mb_patch_short_branch (mb, max_size_branch);
1350
1351         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1352         mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
1353         /*
1354          * We are no longer in a critical section. We need to do this before calling
1355          * to unmanaged land in order to avoid stw deadlocks since unmanaged code
1356          * might take locks.
1357          */
1358 #ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
1359         EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
1360         mono_mb_emit_byte (mb, CEE_LDC_I4_0);
1361         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1362         mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
1363         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE);
1364 #endif
1365
1366         /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
1367         mono_mb_emit_ldarg (mb, 0);
1368         mono_mb_emit_ldloc (mb, real_size_var);
1369         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
1370                 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
1371         } else if (atype == ATYPE_VECTOR) {
1372                 mono_mb_emit_ldarg (mb, 1);
1373                 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
1374         } else if (atype == ATYPE_STRING) {
1375                 mono_mb_emit_ldarg (mb, 1);
1376                 mono_mb_emit_icall (mb, mono_gc_alloc_string);
1377         } else {
1378                 g_assert_not_reached ();
1379         }
1380
1381         /* if (ret == NULL) throw OOM; */
1382         mono_mb_emit_byte (mb, CEE_DUP);
1383         no_oom_branch = mono_mb_emit_branch (mb, CEE_BRTRUE);
1384         mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
1385
1386         mono_mb_patch_branch (mb, no_oom_branch);
1387         mono_mb_emit_byte (mb, CEE_RET);
1388
1389         /* Fastpath */
1390         mono_mb_patch_short_branch (mb, fastpath_branch);
1391
1392         /* FIXME: Memory barrier */
1393
1394         /* tlab_next = new_next */
1395         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
1396         mono_mb_emit_ldloc (mb, new_next_var);
1397         mono_mb_emit_byte (mb, CEE_STIND_I);
1398
1399         /* *p = vtable; */
1400         mono_mb_emit_ldloc (mb, p_var);
1401         mono_mb_emit_ldarg (mb, 0);
1402         mono_mb_emit_byte (mb, CEE_STIND_I);
1403
1404         /* mark object end with nursery word */
1405         if (nursery_canaries_enabled ()) {
1406                         mono_mb_emit_ldloc (mb, p_var);
1407                         mono_mb_emit_ldloc (mb, real_size_var);
1408                         mono_mb_emit_byte (mb, MONO_CEE_ADD);
1409                         mono_mb_emit_icon8 (mb, (mword) CANARY_STRING);
1410                         mono_mb_emit_icon (mb, CANARY_SIZE);
1411                         mono_mb_emit_byte (mb, MONO_CEE_PREFIX1);
1412                         mono_mb_emit_byte (mb, CEE_CPBLK);
1413         }
1414
1415         if (atype == ATYPE_VECTOR) {
1416                 /* arr->max_length = max_length; */
1417                 mono_mb_emit_ldloc (mb, p_var);
1418                 mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, max_length));
1419                 mono_mb_emit_ldarg (mb, 1);
1420 #ifdef MONO_BIG_ARRAYS
1421                 mono_mb_emit_byte (mb, CEE_STIND_I);
1422 #else
1423                 mono_mb_emit_byte (mb, CEE_STIND_I4);
1424 #endif
1425         } else  if (atype == ATYPE_STRING) {
1426                 /* need to set length and clear the last char */
1427                 /* s->length = len; */
1428                 mono_mb_emit_ldloc (mb, p_var);
1429                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, length));
1430                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1431                 mono_mb_emit_ldarg (mb, 1);
1432                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1433         }
1434
1435 #ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
1436         EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
1437         mono_mb_emit_byte (mb, CEE_LDC_I4_0);
1438         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1439         mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
1440 #else
1441         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1442         mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
1443 #endif
1444         /*
1445         We must make sure both vtable and max_length are globaly visible before returning to managed land.
1446         */
1447         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);
1448
1449         /* return p */
1450         mono_mb_emit_ldloc (mb, p_var);
1451
1452  done:
1453         mono_mb_emit_byte (mb, CEE_RET);
1454 #endif
1455
1456         info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1457         info->d.alloc.gc_name = "sgen";
1458         info->d.alloc.alloc_type = atype;
1459
1460 #ifndef DISABLE_JIT
1461         mb->init_locals = FALSE;
1462 #endif
1463
1464         res = mono_mb_create (mb, csig, 8, info);
1465         mono_mb_free (mb);
1466
1467
1468         return res;
1469 }
1470 #endif
1471
1472 int
1473 mono_gc_get_aligned_size_for_allocator (int size)
1474 {
1475         return SGEN_ALIGN_UP (size);
1476 }
1477
1478 /*
1479  * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
1480  * The signature of the called method is:
1481  *      object allocate (MonoVTable *vtable)
1482  */
1483 MonoMethod*
1484 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1485 {
1486 #ifdef MANAGED_ALLOCATION
1487         if (collect_before_allocs)
1488                 return NULL;
1489         if (klass->instance_size > tlab_size)
1490                 return NULL;
1491         if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE)
1492                 return NULL;
1493         if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1494                 return NULL;
1495         if (klass->rank)
1496                 return NULL;
1497         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
1498                 return NULL;
1499         if (klass->byval_arg.type == MONO_TYPE_STRING)
1500                 return mono_gc_get_managed_allocator_by_type (ATYPE_STRING, MANAGED_ALLOCATOR_REGULAR);
1501         /* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */
1502         if (known_instance_size)
1503                 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL, MANAGED_ALLOCATOR_REGULAR);
1504         else
1505                 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL, MANAGED_ALLOCATOR_REGULAR);
1506 #else
1507         return NULL;
1508 #endif
1509 }
1510
1511 MonoMethod*
1512 mono_gc_get_managed_array_allocator (MonoClass *klass)
1513 {
1514 #ifdef MANAGED_ALLOCATION
1515         if (klass->rank != 1)
1516                 return NULL;
1517         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
1518                 return NULL;
1519         if (has_per_allocation_action)
1520                 return NULL;
1521         g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass));
1522
1523         return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR, MANAGED_ALLOCATOR_REGULAR);
1524 #else
1525         return NULL;
1526 #endif
1527 }
1528
1529 void
1530 sgen_set_use_managed_allocator (gboolean flag)
1531 {
1532         use_managed_allocator = flag;
1533 }
1534
1535 MonoMethod*
1536 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1537 {
1538 #ifdef MANAGED_ALLOCATION
1539         MonoMethod *res;
1540         MonoMethod **cache;
1541
1542         if (variant == MANAGED_ALLOCATOR_REGULAR && !use_managed_allocator)
1543                 return NULL;
1544
1545         switch (variant) {
1546         case MANAGED_ALLOCATOR_REGULAR: cache = alloc_method_cache; break;
1547         case MANAGED_ALLOCATOR_SLOW_PATH: cache = slowpath_alloc_method_cache; break;
1548         default: g_assert_not_reached (); break;
1549         }
1550
1551         res = cache [atype];
1552         if (res)
1553                 return res;
1554
1555         res = create_allocator (atype, variant);
1556         LOCK_GC;
1557         if (cache [atype]) {
1558                 mono_free_method (res);
1559                 res = cache [atype];
1560         } else {
1561                 mono_memory_barrier ();
1562                 cache [atype] = res;
1563         }
1564         UNLOCK_GC;
1565
1566         return res;
1567 #else
1568         return NULL;
1569 #endif
1570 }
1571
1572 guint32
1573 mono_gc_get_managed_allocator_types (void)
1574 {
1575         return ATYPE_NUM;
1576 }
1577
1578 gboolean
1579 sgen_is_managed_allocator (MonoMethod *method)
1580 {
1581         int i;
1582
1583         for (i = 0; i < ATYPE_NUM; ++i)
1584                 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1585                         return TRUE;
1586         return FALSE;
1587 }
1588
1589 gboolean
1590 sgen_has_managed_allocator (void)
1591 {
1592         int i;
1593
1594         for (i = 0; i < ATYPE_NUM; ++i)
1595                 if (alloc_method_cache [i] || slowpath_alloc_method_cache [i])
1596                         return TRUE;
1597         return FALSE;
1598 }
1599
1600 #define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size))
1601
1602 gboolean
1603 sgen_client_cardtable_scan_object (GCObject *obj, guint8 *cards, ScanCopyContext ctx)
1604 {
1605         MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
1606         MonoClass *klass = vt->klass;
1607
1608         SGEN_ASSERT (0, SGEN_VTABLE_HAS_REFERENCES (vt), "Why would we ever call this on reference-free objects?");
1609
1610         if (vt->rank) {
1611                 MonoArray *arr = (MonoArray*)obj;
1612                 guint8 *card_data, *card_base;
1613                 guint8 *card_data_end;
1614                 char *obj_start = (char *)sgen_card_table_align_pointer (obj);
1615                 mword bounds_size;
1616                 mword obj_size = sgen_mono_array_size (vt, arr, &bounds_size, sgen_vtable_get_descriptor (vt));
1617                 /* We don't want to scan the bounds entries at the end of multidimensional arrays */
1618                 char *obj_end = (char*)obj + obj_size - bounds_size;
1619                 size_t card_count;
1620                 size_t extra_idx = 0;
1621
1622                 mword desc = (mword)klass->element_class->gc_descr;
1623                 int elem_size = mono_array_element_size (klass);
1624
1625 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1626                 guint8 *overflow_scan_end = NULL;
1627 #endif
1628
1629 #ifdef SGEN_OBJECT_LAYOUT_STATISTICS
1630                 if (klass->element_class->valuetype)
1631                         sgen_object_layout_scanned_vtype_array ();
1632                 else
1633                         sgen_object_layout_scanned_ref_array ();
1634 #endif
1635
1636                 if (cards)
1637                         card_data = cards;
1638                 else
1639                         card_data = sgen_card_table_get_card_scan_address ((mword)obj);
1640
1641                 card_base = card_data;
1642                 card_count = sgen_card_table_number_of_cards_in_range ((mword)obj, obj_size);
1643                 card_data_end = card_data + card_count;
1644
1645
1646 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1647                 /*Check for overflow and if so, setup to scan in two steps*/
1648                 if (!cards && card_data_end >= SGEN_SHADOW_CARDTABLE_END) {
1649                         overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END);
1650                         card_data_end = SGEN_SHADOW_CARDTABLE_END;
1651                 }
1652
1653 LOOP_HEAD:
1654 #endif
1655
1656                 card_data = sgen_find_next_card (card_data, card_data_end);
1657                 for (; card_data < card_data_end; card_data = sgen_find_next_card (card_data + 1, card_data_end)) {
1658                         size_t index;
1659                         size_t idx = (card_data - card_base) + extra_idx;
1660                         char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
1661                         char *card_end = start + CARD_SIZE_IN_BYTES;
1662                         char *first_elem, *elem;
1663
1664                         HEAVY_STAT (++los_marked_cards);
1665
1666                         if (!cards)
1667                                 sgen_card_table_prepare_card_for_scanning (card_data);
1668
1669                         card_end = MIN (card_end, obj_end);
1670
1671                         if (start <= (char*)arr->vector)
1672                                 index = 0;
1673                         else
1674                                 index = ARRAY_OBJ_INDEX (start, obj, elem_size);
1675
1676                         elem = first_elem = (char*)mono_array_addr_with_size_fast ((MonoArray*)obj, elem_size, index);
1677                         if (klass->element_class->valuetype) {
1678                                 ScanVTypeFunc scan_vtype_func = ctx.ops->scan_vtype;
1679
1680                                 for (; elem < card_end; elem += elem_size)
1681                                         scan_vtype_func (obj, elem, desc, ctx.queue BINARY_PROTOCOL_ARG (elem_size));
1682                         } else {
1683                                 ScanPtrFieldFunc scan_ptr_field_func = ctx.ops->scan_ptr_field;
1684
1685                                 HEAVY_STAT (++los_array_cards);
1686                                 for (; elem < card_end; elem += SIZEOF_VOID_P)
1687                                         scan_ptr_field_func (obj, (GCObject**)elem, ctx.queue);
1688                         }
1689
1690                         binary_protocol_card_scan (first_elem, elem - first_elem);
1691                 }
1692
1693 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1694                 if (overflow_scan_end) {
1695                         extra_idx = card_data - card_base;
1696                         card_base = card_data = sgen_shadow_cardtable;
1697                         card_data_end = overflow_scan_end;
1698                         overflow_scan_end = NULL;
1699                         goto LOOP_HEAD;
1700                 }
1701 #endif
1702                 return TRUE;
1703         }
1704
1705         return FALSE;
1706 }
1707
1708 /*
1709  * Array and string allocation
1710  */
1711
1712 void*
1713 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
1714 {
1715         MonoArray *arr;
1716         TLAB_ACCESS_INIT;
1717
1718         if (!SGEN_CAN_ALIGN_UP (size))
1719                 return NULL;
1720
1721 #ifndef DISABLE_CRITICAL_REGION
1722         ENTER_CRITICAL_REGION;
1723         arr = (MonoArray*)sgen_try_alloc_obj_nolock (vtable, size);
1724         if (arr) {
1725                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1726                 arr->max_length = (mono_array_size_t)max_length;
1727                 EXIT_CRITICAL_REGION;
1728                 goto done;
1729         }
1730         EXIT_CRITICAL_REGION;
1731 #endif
1732
1733         LOCK_GC;
1734
1735         arr = (MonoArray*)sgen_alloc_obj_nolock (vtable, size);
1736         if (G_UNLIKELY (!arr)) {
1737                 UNLOCK_GC;
1738                 return NULL;
1739         }
1740
1741         arr->max_length = (mono_array_size_t)max_length;
1742
1743         UNLOCK_GC;
1744
1745  done:
1746         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1747                 mono_profiler_allocation (&arr->obj);
1748
1749         SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Vector has incorrect size.");
1750         return arr;
1751 }
1752
1753 void*
1754 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
1755 {
1756         MonoArray *arr;
1757         MonoArrayBounds *bounds;
1758         TLAB_ACCESS_INIT;
1759
1760         if (!SGEN_CAN_ALIGN_UP (size))
1761                 return NULL;
1762
1763 #ifndef DISABLE_CRITICAL_REGION
1764         ENTER_CRITICAL_REGION;
1765         arr = (MonoArray*)sgen_try_alloc_obj_nolock (vtable, size);
1766         if (arr) {
1767                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1768                 arr->max_length = (mono_array_size_t)max_length;
1769
1770                 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
1771                 arr->bounds = bounds;
1772                 EXIT_CRITICAL_REGION;
1773                 goto done;
1774         }
1775         EXIT_CRITICAL_REGION;
1776 #endif
1777
1778         LOCK_GC;
1779
1780         arr = (MonoArray*)sgen_alloc_obj_nolock (vtable, size);
1781         if (G_UNLIKELY (!arr)) {
1782                 UNLOCK_GC;
1783                 return NULL;
1784         }
1785
1786         arr->max_length = (mono_array_size_t)max_length;
1787
1788         bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
1789         arr->bounds = bounds;
1790
1791         UNLOCK_GC;
1792
1793  done:
1794         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1795                 mono_profiler_allocation (&arr->obj);
1796
1797         SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Array has incorrect size.");
1798         return arr;
1799 }
1800
1801 void*
1802 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
1803 {
1804         MonoString *str;
1805         TLAB_ACCESS_INIT;
1806
1807         if (!SGEN_CAN_ALIGN_UP (size))
1808                 return NULL;
1809
1810 #ifndef DISABLE_CRITICAL_REGION
1811         ENTER_CRITICAL_REGION;
1812         str = (MonoString*)sgen_try_alloc_obj_nolock (vtable, size);
1813         if (str) {
1814                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1815                 str->length = len;
1816                 EXIT_CRITICAL_REGION;
1817                 goto done;
1818         }
1819         EXIT_CRITICAL_REGION;
1820 #endif
1821
1822         LOCK_GC;
1823
1824         str = (MonoString*)sgen_alloc_obj_nolock (vtable, size);
1825         if (G_UNLIKELY (!str)) {
1826                 UNLOCK_GC;
1827                 return NULL;
1828         }
1829
1830         str->length = len;
1831
1832         UNLOCK_GC;
1833
1834  done:
1835         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1836                 mono_profiler_allocation (&str->object);
1837
1838         return str;
1839 }
1840
1841 /*
1842  * Strings
1843  */
1844
1845 void
1846 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1847 {
1848         mono_unichar2 *new_end = str->chars + new_length;
1849
1850         /* zero the discarded string. This null-delimits the string and allows
1851          * the space to be reclaimed by SGen. */
1852
1853         if (nursery_canaries_enabled () && sgen_ptr_in_nursery (str)) {
1854                 CHECK_CANARY_FOR_OBJECT ((GCObject*)str, TRUE);
1855                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2) + CANARY_SIZE);
1856                 memcpy (new_end + 1 , CANARY_STRING, CANARY_SIZE);
1857         } else {
1858                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1859         }
1860
1861         str->length = new_length;
1862 }
1863
1864 /*
1865  * Profiling
1866  */
1867
1868 #define GC_ROOT_NUM 32
1869 typedef struct {
1870         int count;              /* must be the first field */
1871         void *objects [GC_ROOT_NUM];
1872         int root_types [GC_ROOT_NUM];
1873         uintptr_t extra_info [GC_ROOT_NUM];
1874 } GCRootReport;
1875
1876 static void
1877 notify_gc_roots (GCRootReport *report)
1878 {
1879         if (!report->count)
1880                 return;
1881         mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
1882         report->count = 0;
1883 }
1884
1885 static void
1886 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
1887 {
1888         if (report->count == GC_ROOT_NUM)
1889                 notify_gc_roots (report);
1890         report->objects [report->count] = object;
1891         report->root_types [report->count] = rtype;
1892         report->extra_info [report->count++] = (uintptr_t)SGEN_LOAD_VTABLE (object)->klass;
1893 }
1894
1895 void
1896 sgen_client_nursery_objects_pinned (void **definitely_pinned, int count)
1897 {
1898         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1899                 GCRootReport report;
1900                 int idx;
1901                 report.count = 0;
1902                 for (idx = 0; idx < count; ++idx)
1903                         add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1904                 notify_gc_roots (&report);
1905         }
1906 }
1907
1908 static void
1909 report_finalizer_roots_from_queue (SgenPointerQueue *queue)
1910 {
1911         GCRootReport report;
1912         size_t i;
1913
1914         report.count = 0;
1915         for (i = 0; i < queue->next_slot; ++i) {
1916                 void *obj = queue->data [i];
1917                 if (!obj)
1918                         continue;
1919                 add_profile_gc_root (&report, obj, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
1920         }
1921         notify_gc_roots (&report);
1922 }
1923
1924 static void
1925 report_finalizer_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
1926 {
1927         report_finalizer_roots_from_queue (fin_ready_queue);
1928         report_finalizer_roots_from_queue (critical_fin_queue);
1929 }
1930
1931 static GCRootReport *root_report;
1932
1933 static void
1934 single_arg_report_root (MonoObject **obj, void *gc_data)
1935 {
1936         if (*obj)
1937                 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
1938 }
1939
1940 static void
1941 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
1942 {
1943         switch (desc & ROOT_DESC_TYPE_MASK) {
1944         case ROOT_DESC_BITMAP:
1945                 desc >>= ROOT_DESC_TYPE_SHIFT;
1946                 while (desc) {
1947                         if ((desc & 1) && *start_root) {
1948                                 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
1949                         }
1950                         desc >>= 1;
1951                         start_root++;
1952                 }
1953                 return;
1954         case ROOT_DESC_COMPLEX: {
1955                 gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
1956                 gsize bwords = (*bitmap_data) - 1;
1957                 void **start_run = start_root;
1958                 bitmap_data++;
1959                 while (bwords-- > 0) {
1960                         gsize bmap = *bitmap_data++;
1961                         void **objptr = start_run;
1962                         while (bmap) {
1963                                 if ((bmap & 1) && *objptr) {
1964                                         add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
1965                                 }
1966                                 bmap >>= 1;
1967                                 ++objptr;
1968                         }
1969                         start_run += GC_BITS_PER_WORD;
1970                 }
1971                 break;
1972         }
1973         case ROOT_DESC_VECTOR: {
1974                 void **p;
1975
1976                 for (p = start_root; p < end_root; p++) {
1977                         if (*p)
1978                                 add_profile_gc_root (report, *p, MONO_PROFILE_GC_ROOT_OTHER, 0);
1979                 }
1980                 break;
1981         }
1982         case ROOT_DESC_USER: {
1983                 MonoGCRootMarkFunc marker = (MonoGCRootMarkFunc)sgen_get_user_descriptor_func (desc);
1984                 root_report = report;
1985                 marker ((MonoObject**)start_root, single_arg_report_root, NULL);
1986                 break;
1987         }
1988         case ROOT_DESC_RUN_LEN:
1989                 g_assert_not_reached ();
1990         default:
1991                 g_assert_not_reached ();
1992         }
1993 }
1994
1995 static void
1996 report_registered_roots_by_type (int root_type)
1997 {
1998         GCRootReport report;
1999         void **start_root;
2000         RootRecord *root;
2001         report.count = 0;
2002         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
2003                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
2004                 precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc);
2005         } SGEN_HASH_TABLE_FOREACH_END;
2006         notify_gc_roots (&report);
2007 }
2008
2009 static void
2010 report_registered_roots (void)
2011 {
2012         report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2013         report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2014 }
2015
2016 void
2017 sgen_client_collecting_minor (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
2018 {
2019         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2020                 report_registered_roots ();
2021         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2022                 report_finalizer_roots (fin_ready_queue, critical_fin_queue);
2023 }
2024
2025 static GCRootReport major_root_report;
2026 static gboolean profile_roots;
2027
2028 void
2029 sgen_client_collecting_major_1 (void)
2030 {
2031         profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
2032         memset (&major_root_report, 0, sizeof (GCRootReport));
2033 }
2034
2035 void
2036 sgen_client_pinned_los_object (GCObject *obj)
2037 {
2038         if (profile_roots)
2039                 add_profile_gc_root (&major_root_report, (char*)obj, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
2040 }
2041
2042 void
2043 sgen_client_collecting_major_2 (void)
2044 {
2045         if (profile_roots)
2046                 notify_gc_roots (&major_root_report);
2047
2048         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2049                 report_registered_roots ();
2050 }
2051
2052 void
2053 sgen_client_collecting_major_3 (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
2054 {
2055         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2056                 report_finalizer_roots (fin_ready_queue, critical_fin_queue);
2057 }
2058
2059 #define MOVED_OBJECTS_NUM 64
2060 static void *moved_objects [MOVED_OBJECTS_NUM];
2061 static int moved_objects_idx = 0;
2062
2063 static SgenPointerQueue moved_objects_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_MOVED_OBJECT);
2064
2065 void
2066 mono_sgen_register_moved_object (void *obj, void *destination)
2067 {
2068         /*
2069          * This function can be called from SGen's worker threads. We want to try
2070          * and avoid exposing those threads to the profiler API, so queue up move
2071          * events and send them later when the main GC thread calls
2072          * mono_sgen_gc_event_moves ().
2073          *
2074          * TODO: Once SGen has multiple worker threads, we need to switch to a
2075          * lock-free data structure for the queue as multiple threads will be
2076          * adding to it at the same time.
2077          */
2078         if (sgen_workers_is_worker_thread (mono_native_thread_id_get ())) {
2079                 sgen_pointer_queue_add (&moved_objects_queue, obj);
2080                 sgen_pointer_queue_add (&moved_objects_queue, destination);
2081         } else {
2082                 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2083                         mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2084                         moved_objects_idx = 0;
2085                 }
2086
2087                 moved_objects [moved_objects_idx++] = obj;
2088                 moved_objects [moved_objects_idx++] = destination;
2089         }
2090 }
2091
2092 void
2093 mono_sgen_gc_event_moves (void)
2094 {
2095         while (!sgen_pointer_queue_is_empty (&moved_objects_queue)) {
2096                 void *dst = sgen_pointer_queue_pop (&moved_objects_queue);
2097                 void *src = sgen_pointer_queue_pop (&moved_objects_queue);
2098
2099                 mono_sgen_register_moved_object (src, dst);
2100         }
2101
2102         if (moved_objects_idx) {
2103                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2104                 moved_objects_idx = 0;
2105         }
2106 }
2107
2108 /*
2109  * Heap walking
2110  */
2111
2112 #define REFS_SIZE 128
2113 typedef struct {
2114         void *data;
2115         MonoGCReferences callback;
2116         int flags;
2117         int count;
2118         int called;
2119         MonoObject *refs [REFS_SIZE];
2120         uintptr_t offsets [REFS_SIZE];
2121 } HeapWalkInfo;
2122
2123 #undef HANDLE_PTR
2124 #define HANDLE_PTR(ptr,obj)     do {    \
2125                 if (*(ptr)) {   \
2126                         if (hwi->count == REFS_SIZE) {  \
2127                                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);    \
2128                                 hwi->count = 0; \
2129                                 hwi->called = 1;        \
2130                         }       \
2131                         hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start;  \
2132                         hwi->refs [hwi->count++] = *(ptr);      \
2133                 }       \
2134         } while (0)
2135
2136 static void
2137 collect_references (HeapWalkInfo *hwi, GCObject *obj, size_t size)
2138 {
2139         char *start = (char*)obj;
2140         mword desc = sgen_obj_get_descriptor (obj);
2141
2142 #include "sgen/sgen-scan-object.h"
2143 }
2144
2145 static void
2146 walk_references (GCObject *start, size_t size, void *data)
2147 {
2148         HeapWalkInfo *hwi = (HeapWalkInfo *)data;
2149         hwi->called = 0;
2150         hwi->count = 0;
2151         collect_references (hwi, start, size);
2152         if (hwi->count || !hwi->called)
2153                 hwi->callback (start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
2154 }
2155
2156 /**
2157  * mono_gc_walk_heap:
2158  * \param flags flags for future use
2159  * \param callback a function pointer called for each object in the heap
2160  * \param data a user data pointer that is passed to callback
2161  * This function can be used to iterate over all the live objects in the heap;
2162  * for each object, \p callback is invoked, providing info about the object's
2163  * location in memory, its class, its size and the objects it references.
2164  * For each referenced object its offset from the object address is
2165  * reported in the offsets array.
2166  * The object references may be buffered, so the callback may be invoked
2167  * multiple times for the same object: in all but the first call, the size
2168  * argument will be zero.
2169  * Note that this function can be only called in the \c MONO_GC_EVENT_PRE_START_WORLD
2170  * profiler event handler.
2171  * \returns a non-zero value if the GC doesn't support heap walking
2172  */
2173 int
2174 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
2175 {
2176         HeapWalkInfo hwi;
2177
2178         hwi.flags = flags;
2179         hwi.callback = callback;
2180         hwi.data = data;
2181
2182         sgen_clear_nursery_fragments ();
2183         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE, TRUE);
2184
2185         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, walk_references, &hwi);
2186         sgen_los_iterate_objects (walk_references, &hwi);
2187
2188         return 0;
2189 }
2190
2191 /*
2192  * Threads
2193  */
2194
2195 void
2196 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
2197 {
2198         gc_callbacks = *callbacks;
2199 }
2200
2201 MonoGCCallbacks *
2202 mono_gc_get_gc_callbacks ()
2203 {
2204         return &gc_callbacks;
2205 }
2206
2207 void
2208 sgen_client_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
2209 {
2210         size_t stsize = 0;
2211         guint8 *staddr = NULL;
2212
2213         mono_tls_set_sgen_thread_info (info);
2214
2215         info->client_info.skip = 0;
2216
2217         info->client_info.stack_start = NULL;
2218
2219 #ifdef SGEN_POSIX_STW
2220         info->client_info.stop_count = -1;
2221         info->client_info.signal = 0;
2222 #endif
2223
2224         mono_thread_info_get_stack_bounds (&staddr, &stsize);
2225         if (staddr) {
2226                 info->client_info.stack_start_limit = staddr;
2227                 info->client_info.stack_end = staddr + stsize;
2228         } else {
2229                 gsize stack_bottom = (gsize)stack_bottom_fallback;
2230                 stack_bottom += 4095;
2231                 stack_bottom &= ~4095;
2232                 info->client_info.stack_end = (char*)stack_bottom;
2233         }
2234
2235         memset (&info->client_info.ctx, 0, sizeof (MonoContext));
2236
2237         if (mono_gc_get_gc_callbacks ()->thread_attach_func)
2238                 info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
2239
2240         binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
2241
2242         SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->client_info.stack_end);
2243
2244         info->client_info.info.handle_stack = mono_handle_stack_alloc ();
2245 }
2246
2247 void
2248 sgen_client_thread_unregister (SgenThreadInfo *p)
2249 {
2250         MonoNativeThreadId tid;
2251
2252         mono_tls_set_sgen_thread_info (NULL);
2253
2254         tid = mono_thread_info_get_tid (p);
2255
2256         if (p->client_info.info.runtime_thread)
2257                 mono_threads_add_joinable_thread ((gpointer)tid);
2258
2259         if (mono_gc_get_gc_callbacks ()->thread_detach_func) {
2260                 mono_gc_get_gc_callbacks ()->thread_detach_func (p->client_info.runtime_data);
2261                 p->client_info.runtime_data = NULL;
2262         }
2263
2264         binary_protocol_thread_unregister ((gpointer)tid);
2265         SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
2266
2267         HandleStack *handles = (HandleStack*) p->client_info.info.handle_stack;
2268         p->client_info.info.handle_stack = NULL;
2269         mono_handle_stack_free (handles);
2270 }
2271
2272 void
2273 mono_gc_set_skip_thread (gboolean skip)
2274 {
2275         SgenThreadInfo *info = mono_thread_info_current ();
2276
2277         LOCK_GC;
2278         info->client_info.gc_disabled = skip;
2279         UNLOCK_GC;
2280
2281         if (skip) {
2282                 /* If we skip scanning a thread with a non-empty handle stack, we may move an
2283                  * object but fail to update the reference in the handle.
2284                  */
2285                 HandleStack *stack = info->client_info.info.handle_stack;
2286                 g_assert (stack == NULL || mono_handle_stack_is_empty (stack));
2287         }
2288 }
2289
2290 static gboolean
2291 thread_in_critical_region (SgenThreadInfo *info)
2292 {
2293         return info->client_info.in_critical_region;
2294 }
2295
2296 static void
2297 sgen_thread_attach (SgenThreadInfo *info)
2298 {
2299         if (mono_gc_get_gc_callbacks ()->thread_attach_func && !info->client_info.runtime_data)
2300                 info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
2301 }
2302
2303 static void
2304 sgen_thread_detach (SgenThreadInfo *p)
2305 {
2306         /* If a delegate is passed to native code and invoked on a thread we dont
2307          * know about, marshal will register it with mono_threads_attach_coop, but
2308          * we have no way of knowing when that thread goes away.  SGen has a TSD
2309          * so we assume that if the domain is still registered, we can detach
2310          * the thread
2311          */
2312         if (mono_thread_internal_current_is_attached ())
2313                 mono_thread_detach_internal (mono_thread_internal_current ());
2314 }
2315
2316 /**
2317  * mono_gc_register_thread:
2318  */
2319 gboolean
2320 mono_gc_register_thread (void *baseptr)
2321 {
2322         return mono_thread_info_attach (baseptr) != NULL;
2323 }
2324
2325 /**
2326  * mono_gc_is_gc_thread:
2327  */
2328 gboolean
2329 mono_gc_is_gc_thread (void)
2330 {
2331         gboolean result;
2332         LOCK_GC;
2333         result = mono_thread_info_current () != NULL;
2334         UNLOCK_GC;
2335         return result;
2336 }
2337
2338 void
2339 sgen_client_thread_register_worker (void)
2340 {
2341         mono_thread_info_register_small_id ();
2342         mono_native_thread_set_name (mono_native_thread_id_get (), "SGen worker");
2343 }
2344
2345 /* Variables holding start/end nursery so it won't have to be passed at every call */
2346 static void *scan_area_arg_start, *scan_area_arg_end;
2347
2348 void
2349 mono_gc_conservatively_scan_area (void *start, void *end)
2350 {
2351         sgen_conservatively_pin_objects_from ((void **)start, (void **)end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
2352 }
2353
2354 void*
2355 mono_gc_scan_object (void *obj, void *gc_data)
2356 {
2357         ScanCopyContext *ctx = (ScanCopyContext *)gc_data;
2358         ctx->ops->copy_or_mark_object ((GCObject**)&obj, ctx->queue);
2359         return obj;
2360 }
2361
2362 typedef struct {
2363         void **start_nursery;
2364         void **end_nursery;
2365 } PinHandleStackInteriorPtrData;
2366
2367 /* Called when we're scanning the handle stack imprecisely and we encounter a pointer into the
2368    middle of an object.
2369  */
2370 static void
2371 pin_handle_stack_interior_ptrs (void **ptr_slot, void *user_data)
2372 {
2373         PinHandleStackInteriorPtrData *ud = (PinHandleStackInteriorPtrData *)user_data;
2374         sgen_conservatively_pin_objects_from (ptr_slot, ptr_slot+1, ud->start_nursery, ud->end_nursery, PIN_TYPE_STACK);
2375 }
2376
2377
2378 /*
2379  * Mark from thread stacks and registers.
2380  */
2381 void
2382 sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx)
2383 {
2384         scan_area_arg_start = start_nursery;
2385         scan_area_arg_end = end_nursery;
2386
2387         FOREACH_THREAD (info) {
2388                 int skip_reason = 0;
2389                 void *aligned_stack_start;
2390
2391                 if (info->client_info.skip) {
2392                         SGEN_LOG (3, "Skipping dead thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start);
2393                         skip_reason = 1;
2394                 } else if (info->client_info.gc_disabled) {
2395                         SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start);
2396                         skip_reason = 2;
2397                 } else if (!mono_thread_info_is_live (info)) {
2398                         SGEN_LOG (3, "Skipping non-running thread %p, range: %p-%p, size: %zd (state %x)", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, info->client_info.info.thread_state);
2399                         skip_reason = 3;
2400                 } else if (!info->client_info.stack_start) {
2401                         SGEN_LOG (3, "Skipping starting or detaching thread %p", info);
2402                         skip_reason = 4;
2403                 }
2404
2405                 binary_protocol_scan_stack ((gpointer)mono_thread_info_get_tid (info), info->client_info.stack_start, info->client_info.stack_end, skip_reason);
2406
2407                 if (skip_reason) {
2408                         if (precise) {
2409                                 /* If we skip a thread with a non-empty handle stack and then it
2410                                  * resumes running we may potentially move an object but fail to
2411                                  * update the reference in the handle.
2412                                  */
2413                                 HandleStack *stack = info->client_info.info.handle_stack;
2414                                 g_assert (stack == NULL || mono_handle_stack_is_empty (stack));
2415                         }
2416                         continue;
2417                 }
2418
2419                 g_assert (info->client_info.stack_start);
2420                 g_assert (info->client_info.stack_end);
2421
2422                 aligned_stack_start = (void*)(mword) ALIGN_TO ((mword)info->client_info.stack_start, SIZEOF_VOID_P);
2423 #ifdef HOST_WIN32
2424                 /* Windows uses a guard page before the committed stack memory pages to detect when the
2425                    stack needs to be grown. If we suspend a thread just after a function prolog has
2426                    decremented the stack pointer to point into the guard page but before the thread has
2427                    been able to read or write to that page, starting the stack scan at aligned_stack_start
2428                    will raise a STATUS_GUARD_PAGE_VIOLATION and the process will crash. This code uses
2429                    VirtualQuery() to determine whether stack_start points into the guard page and then
2430                    updates aligned_stack_start to point at the next non-guard page. */
2431                 MEMORY_BASIC_INFORMATION mem_info;
2432                 SIZE_T result = VirtualQuery(info->client_info.stack_start, &mem_info, sizeof(mem_info));
2433                 g_assert (result != 0);
2434                 if (mem_info.Protect & PAGE_GUARD) {
2435                         aligned_stack_start = ((char*) mem_info.BaseAddress) + mem_info.RegionSize;
2436                 }
2437 #endif
2438
2439                 g_assert (info->client_info.suspend_done);
2440                 SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, sgen_get_pinned_count ());
2441                 if (mono_gc_get_gc_callbacks ()->thread_mark_func && !conservative_stack_mark) {
2442                         mono_gc_get_gc_callbacks ()->thread_mark_func (info->client_info.runtime_data, (guint8 *)aligned_stack_start, (guint8 *)info->client_info.stack_end, precise, &ctx);
2443                 } else if (!precise) {
2444                         if (!conservative_stack_mark) {
2445                                 fprintf (stderr, "Precise stack mark not supported - disabling.\n");
2446                                 conservative_stack_mark = TRUE;
2447                         }
2448                         //FIXME we should eventually use the new stack_mark from coop
2449                         sgen_conservatively_pin_objects_from ((void **)aligned_stack_start, (void **)info->client_info.stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
2450                 }
2451
2452                 if (!precise) {
2453                         sgen_conservatively_pin_objects_from ((void**)&info->client_info.ctx, (void**)(&info->client_info.ctx + 1),
2454                                 start_nursery, end_nursery, PIN_TYPE_STACK);
2455
2456                         {
2457                                 // This is used on Coop GC for platforms where we cannot get the data for individual registers.
2458                                 // We force a spill of all registers into the stack and pass a chunk of data into sgen.
2459                                 //FIXME under coop, for now, what we need to ensure is that we scan any extra memory from info->client_info.stack_end to stack_mark
2460                                 MonoThreadUnwindState *state = &info->client_info.info.thread_saved_state [SELF_SUSPEND_STATE_INDEX];
2461                                 if (state && state->gc_stackdata) {
2462                                         sgen_conservatively_pin_objects_from ((void **)state->gc_stackdata, (void**)((char*)state->gc_stackdata + state->gc_stackdata_size),
2463                                                 start_nursery, end_nursery, PIN_TYPE_STACK);
2464                                 }
2465                         }
2466                 }
2467                 if (info->client_info.info.handle_stack) {
2468                         /*
2469                           Make two passes over the handle stack.  On the imprecise pass, pin all
2470                           objects where the handle points into the interior of the object. On the
2471                           precise pass, copy or mark all the objects that have handles to the
2472                           beginning of the object.
2473                         */
2474                         if (precise)
2475                                 mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, (GcScanFunc)ctx.ops->copy_or_mark_object, ctx.queue, precise);
2476                         else {
2477                                 PinHandleStackInteriorPtrData ud = { .start_nursery = start_nursery,
2478                                                                      .end_nursery = end_nursery,
2479                                 };
2480                                 mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, pin_handle_stack_interior_ptrs, &ud, precise);
2481                         }
2482                 }
2483         } FOREACH_THREAD_END
2484 }
2485
2486 /*
2487  * mono_gc_set_stack_end:
2488  *
2489  *   Set the end of the current threads stack to STACK_END. The stack space between 
2490  * STACK_END and the real end of the threads stack will not be scanned during collections.
2491  */
2492 void
2493 mono_gc_set_stack_end (void *stack_end)
2494 {
2495         SgenThreadInfo *info;
2496
2497         LOCK_GC;
2498         info = mono_thread_info_current ();
2499         if (info) {
2500                 SGEN_ASSERT (0, stack_end < info->client_info.stack_end, "Can only lower stack end");
2501                 info->client_info.stack_end = stack_end;
2502         }
2503         UNLOCK_GC;
2504 }
2505
2506 /*
2507  * Roots
2508  */
2509
2510 int
2511 mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
2512 {
2513         return sgen_register_root (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED, source, msg);
2514 }
2515
2516 int
2517 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
2518 {
2519         return sgen_register_root (start, size, descr, ROOT_TYPE_WBARRIER, source, msg);
2520 }
2521
2522 void
2523 mono_gc_deregister_root (char* addr)
2524 {
2525         sgen_deregister_root (addr);
2526 }
2527
2528 /*
2529  * PThreads
2530  */
2531
2532 #ifndef HOST_WIN32
2533 int
2534 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
2535 {
2536         int res;
2537
2538         mono_threads_join_lock ();
2539         res = pthread_create (new_thread, attr, start_routine, arg);
2540         mono_threads_join_unlock ();
2541
2542         return res;
2543 }
2544 #endif
2545
2546 /*
2547  * Miscellaneous
2548  */
2549
2550 void
2551 sgen_client_total_allocated_heap_changed (size_t allocated_heap)
2552 {
2553         mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
2554 }
2555
2556 gboolean
2557 mono_gc_user_markers_supported (void)
2558 {
2559         return TRUE;
2560 }
2561
2562 gboolean
2563 mono_object_is_alive (MonoObject* o)
2564 {
2565         return TRUE;
2566 }
2567
2568 int
2569 mono_gc_get_generation (MonoObject *obj)
2570 {
2571         if (sgen_ptr_in_nursery (obj))
2572                 return 0;
2573         return 1;
2574 }
2575
2576 const char *
2577 mono_gc_get_gc_name (void)
2578 {
2579         return "sgen";
2580 }
2581
2582 char*
2583 mono_gc_get_description (void)
2584 {
2585 #ifdef HAVE_CONC_GC_AS_DEFAULT
2586         return g_strdup ("sgen (concurrent by default)");
2587 #else
2588         return g_strdup ("sgen");
2589 #endif
2590 }
2591
2592 void
2593 mono_gc_set_desktop_mode (void)
2594 {
2595 }
2596
2597 gboolean
2598 mono_gc_is_moving (void)
2599 {
2600         return TRUE;
2601 }
2602
2603 gboolean
2604 mono_gc_is_disabled (void)
2605 {
2606         return FALSE;
2607 }
2608
2609 #ifdef HOST_WIN32
2610 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
2611 {
2612         return TRUE;
2613 }
2614 #endif
2615
2616 int
2617 mono_gc_max_generation (void)
2618 {
2619         return 1;
2620 }
2621
2622 gboolean
2623 mono_gc_precise_stack_mark_enabled (void)
2624 {
2625         return !conservative_stack_mark;
2626 }
2627
2628 void
2629 mono_gc_collect (int generation)
2630 {
2631         sgen_gc_collect (generation);
2632 }
2633
2634 int
2635 mono_gc_collection_count (int generation)
2636 {
2637         return sgen_gc_collection_count (generation);
2638 }
2639
2640 int64_t
2641 mono_gc_get_used_size (void)
2642 {
2643         return (int64_t)sgen_gc_get_used_size ();
2644 }
2645
2646 int64_t
2647 mono_gc_get_heap_size (void)
2648 {
2649         return (int64_t)sgen_gc_get_total_heap_allocation ();
2650 }
2651
2652 MonoGCDescriptor
2653 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
2654 {
2655         return sgen_make_user_root_descriptor (marker);
2656 }
2657
2658 MonoGCDescriptor
2659 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
2660 {
2661         return SGEN_DESC_STRING;
2662 }
2663
2664 void*
2665 mono_gc_get_nursery (int *shift_bits, size_t *size)
2666 {
2667         *size = sgen_nursery_size;
2668         *shift_bits = DEFAULT_NURSERY_BITS;
2669         return sgen_get_nursery_start ();
2670 }
2671
2672 int
2673 mono_gc_get_los_limit (void)
2674 {
2675         return SGEN_MAX_SMALL_OBJ_SIZE;
2676 }
2677
2678 gpointer
2679 sgen_client_default_metadata (void)
2680 {
2681         return mono_domain_get ();
2682 }
2683
2684 gpointer
2685 sgen_client_metadata_for_object (GCObject *obj)
2686 {
2687         return mono_object_domain (obj);
2688 }
2689
2690 /**
2691  * mono_gchandle_is_in_domain:
2692  * \param gchandle a GCHandle's handle.
2693  * \param domain An application domain.
2694  * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
2695  */
2696 gboolean
2697 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
2698 {
2699         MonoDomain *gchandle_domain = (MonoDomain *)sgen_gchandle_get_metadata (gchandle);
2700         return domain->domain_id == gchandle_domain->domain_id;
2701 }
2702
2703 /**
2704  * mono_gchandle_free_domain:
2705  * \param unloading domain that is unloading
2706  *
2707  * Function used internally to cleanup any GC handle for objects belonging
2708  * to the specified domain during appdomain unload.
2709  */
2710 void
2711 mono_gchandle_free_domain (MonoDomain *unloading)
2712 {
2713 }
2714
2715 static gpointer
2716 null_link_if_in_domain (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
2717 {
2718         MonoDomain *unloading_domain = (MonoDomain *)user;
2719         MonoDomain *obj_domain;
2720         gboolean is_weak = MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type);
2721         if (MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden)) {
2722                 MonoObject *obj = (MonoObject *)MONO_GC_REVEAL_POINTER (hidden, is_weak);
2723                 obj_domain = mono_object_domain (obj);
2724         } else {
2725                 obj_domain = (MonoDomain *)MONO_GC_REVEAL_POINTER (hidden, is_weak);
2726         }
2727         if (unloading_domain->domain_id == obj_domain->domain_id)
2728                 return NULL;
2729         return hidden;
2730 }
2731
2732 void
2733 sgen_null_links_for_domain (MonoDomain *domain)
2734 {
2735         guint type;
2736         for (type = HANDLE_TYPE_MIN; type < HANDLE_TYPE_MAX; ++type)
2737                 sgen_gchandle_iterate ((GCHandleType)type, GENERATION_OLD, null_link_if_in_domain, domain);
2738 }
2739
2740 void
2741 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
2742 {
2743         sgen_gchandle_set_target (gchandle, obj);
2744 }
2745
2746 void
2747 sgen_client_gchandle_created (int handle_type, GCObject *obj, guint32 handle)
2748 {
2749 #ifndef DISABLE_PERFCOUNTERS
2750         mono_perfcounters->gc_num_handles++;
2751 #endif
2752         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handle_type, handle, obj);
2753 }
2754
2755 void
2756 sgen_client_gchandle_destroyed (int handle_type, guint32 handle)
2757 {
2758 #ifndef DISABLE_PERFCOUNTERS
2759         mono_perfcounters->gc_num_handles--;
2760 #endif
2761         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handle_type, handle, NULL);
2762 }
2763
2764 void
2765 sgen_client_ensure_weak_gchandles_accessible (void)
2766 {
2767         /*
2768          * During the second bridge processing step the world is
2769          * running again.  That step processes all weak links once
2770          * more to null those that refer to dead objects.  Before that
2771          * is completed, those links must not be followed, so we
2772          * conservatively wait for bridge processing when any weak
2773          * link is dereferenced.
2774          */
2775         /* FIXME: A GC can occur after this check fails, in which case we
2776          * should wait for bridge processing but would fail to do so.
2777          */
2778         if (G_UNLIKELY (bridge_processing_in_progress))
2779                 mono_gc_wait_for_bridge_processing ();
2780 }
2781
2782 void*
2783 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
2784 {
2785         void *result;
2786         LOCK_INTERRUPTION;
2787         result = func (data);
2788         UNLOCK_INTERRUPTION;
2789         return result;
2790 }
2791
2792 void
2793 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
2794 {
2795         // FIXME:
2796 }
2797
2798 guint8*
2799 mono_gc_get_card_table (int *shift_bits, gpointer *mask)
2800 {
2801         return sgen_get_card_table_configuration (shift_bits, mask);
2802 }
2803
2804 gboolean
2805 mono_gc_card_table_nursery_check (void)
2806 {
2807         return !sgen_get_major_collector ()->is_concurrent;
2808 }
2809
2810 /* Negative value to remove */
2811 void
2812 mono_gc_add_memory_pressure (gint64 value)
2813 {
2814         /* FIXME: Implement at some point? */
2815 }
2816
2817 /*
2818  * Logging
2819  */
2820
2821 void
2822 sgen_client_degraded_allocation (size_t size)
2823 {
2824         static int last_major_gc_warned = -1;
2825         static int num_degraded = 0;
2826
2827         if (last_major_gc_warned < (int)gc_stats.major_gc_count) {
2828                 ++num_degraded;
2829                 if (num_degraded == 1 || num_degraded == 3)
2830                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Degraded allocation.  Consider increasing nursery-size if the warning persists.");
2831                 else if (num_degraded == 10)
2832                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Repeated degraded allocation.  Consider increasing nursery-size.");
2833                 last_major_gc_warned = gc_stats.major_gc_count;
2834         }
2835 }
2836
2837 /*
2838  * Debugging
2839  */
2840
2841 const char*
2842 sgen_client_description_for_internal_mem_type (int type)
2843 {
2844         switch (type) {
2845         case INTERNAL_MEM_EPHEMERON_LINK: return "ephemeron-link";
2846         case INTERNAL_MEM_MOVED_OBJECT: return "moved-object";
2847         default:
2848                 return NULL;
2849         }
2850 }
2851
2852 void
2853 sgen_client_pre_collection_checks (void)
2854 {
2855         if (sgen_mono_xdomain_checks) {
2856                 sgen_clear_nursery_fragments ();
2857                 sgen_check_for_xdomain_refs ();
2858         }
2859 }
2860
2861 gboolean
2862 sgen_client_vtable_is_inited (MonoVTable *vt)
2863 {
2864         return vt->klass->inited;
2865 }
2866
2867 const char*
2868 sgen_client_vtable_get_namespace (MonoVTable *vt)
2869 {
2870         return vt->klass->name_space;
2871 }
2872
2873 const char*
2874 sgen_client_vtable_get_name (MonoVTable *vt)
2875 {
2876         return vt->klass->name;
2877 }
2878
2879 /*
2880  * Initialization
2881  */
2882
2883 void
2884 sgen_client_init (void)
2885 {
2886         int dummy;
2887         MonoThreadInfoCallbacks cb;
2888
2889         cb.thread_register = sgen_thread_register;
2890         cb.thread_detach = sgen_thread_detach;
2891         cb.thread_unregister = sgen_thread_unregister;
2892         cb.thread_attach = sgen_thread_attach;
2893         cb.mono_thread_in_critical_region = thread_in_critical_region;
2894         cb.ip_in_critical_region = ip_in_critical_region;
2895
2896         mono_threads_init (&cb, sizeof (SgenThreadInfo));
2897
2898         ///* Keep this the default for now */
2899         /* Precise marking is broken on all supported targets. Disable until fixed. */
2900         conservative_stack_mark = TRUE;
2901
2902         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
2903
2904         mono_sgen_init_stw ();
2905
2906         mono_tls_init_gc_keys ();
2907
2908         mono_gc_register_thread (&dummy);
2909 }
2910
2911 gboolean
2912 sgen_client_handle_gc_param (const char *opt)
2913 {
2914         if (g_str_has_prefix (opt, "stack-mark=")) {
2915                 opt = strchr (opt, '=') + 1;
2916                 if (!strcmp (opt, "precise")) {
2917                         conservative_stack_mark = FALSE;
2918                 } else if (!strcmp (opt, "conservative")) {
2919                         conservative_stack_mark = TRUE;
2920                 } else {
2921                         sgen_env_var_error (MONO_GC_PARAMS_NAME, conservative_stack_mark ? "Using `conservative`." : "Using `precise`.",
2922                                         "Invalid value `%s` for `stack-mark` option, possible values are: `precise`, `conservative`.", opt);
2923                 }
2924         } else if (g_str_has_prefix (opt, "bridge-implementation=")) {
2925                 opt = strchr (opt, '=') + 1;
2926                 sgen_set_bridge_implementation (opt);
2927         } else if (g_str_has_prefix (opt, "toggleref-test")) {
2928                 /* FIXME: This should probably in MONO_GC_DEBUG */
2929                 sgen_register_test_toggleref_callback ();
2930         } else if (!sgen_bridge_handle_gc_param (opt)) {
2931                 return FALSE;
2932         }
2933         return TRUE;
2934 }
2935
2936 void
2937 sgen_client_print_gc_params_usage (void)
2938 {
2939         fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
2940 }
2941
2942 gboolean
2943 sgen_client_handle_gc_debug (const char *opt)
2944 {
2945         if (!strcmp (opt, "xdomain-checks")) {
2946                 sgen_mono_xdomain_checks = TRUE;
2947         } else if (!strcmp (opt, "do-not-finalize")) {
2948                 mono_do_not_finalize = TRUE;
2949         } else if (g_str_has_prefix (opt, "do-not-finalize=")) {
2950                 opt = strchr (opt, '=') + 1;
2951                 mono_do_not_finalize = TRUE;
2952                 mono_do_not_finalize_class_names = g_strsplit (opt, ",", 0);
2953         } else if (!strcmp (opt, "log-finalizers")) {
2954                 log_finalizers = TRUE;
2955         } else if (!strcmp (opt, "no-managed-allocator")) {
2956                 sgen_set_use_managed_allocator (FALSE);
2957         } else if (!sgen_bridge_handle_gc_debug (opt)) {
2958                 return FALSE;
2959         }
2960         return TRUE;
2961 }
2962
2963 void
2964 sgen_client_print_gc_debug_usage (void)
2965 {
2966         fprintf (stderr, "  xdomain-checks\n");
2967         fprintf (stderr, "  do-not-finalize\n");
2968         fprintf (stderr, "  log-finalizers\n");
2969         fprintf (stderr, "  no-managed-allocator\n");
2970         sgen_bridge_print_gc_debug_usage ();
2971 }
2972
2973
2974 gpointer
2975 sgen_client_get_provenance (void)
2976 {
2977 #ifdef SGEN_OBJECT_PROVENANCE
2978         MonoGCCallbacks *cb = mono_gc_get_gc_callbacks ();
2979         gpointer (*get_provenance_func) (void);
2980         if (!cb)
2981                 return NULL;
2982         get_provenance_func = cb->get_provenance_func;
2983         if (get_provenance_func)
2984                 return get_provenance_func ();
2985         return NULL;
2986 #else
2987         return NULL;
2988 #endif
2989 }
2990
2991 void
2992 sgen_client_describe_invalid_pointer (GCObject *ptr)
2993 {
2994         sgen_bridge_describe_pointer (ptr);
2995 }
2996
2997 static gboolean gc_inited;
2998
2999 /**
3000  * mono_gc_base_init:
3001  */
3002 void
3003 mono_gc_base_init (void)
3004 {
3005         if (gc_inited)
3006                 return;
3007
3008         mono_counters_init ();
3009
3010 #ifndef HOST_WIN32
3011         mono_w32handle_init ();
3012 #endif
3013
3014 #ifdef HEAVY_STATISTICS
3015         mono_counters_register ("los marked cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_marked_cards);
3016         mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_cards);
3017         mono_counters_register ("los array remsets", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_remsets);
3018
3019         mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_arrayref);
3020         mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_value_copy);
3021         mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_object_copy);
3022 #endif
3023
3024         sgen_gc_init ();
3025
3026         gc_inited = TRUE;
3027 }
3028
3029 void
3030 mono_gc_base_cleanup (void)
3031 {
3032         sgen_thread_pool_shutdown (major_collector.get_sweep_pool ());
3033
3034         sgen_workers_shutdown ();
3035
3036         // We should have consumed any outstanding moves.
3037         g_assert (sgen_pointer_queue_is_empty (&moved_objects_queue));
3038 }
3039
3040 gboolean
3041 mono_gc_is_null (void)
3042 {
3043         return FALSE;
3044 }
3045
3046 #endif