Build libmonoruntime under none desktop Windows API family.
[mono.git] / mono / metadata / boehm-gc.c
1 /*
2  * boehm-gc.c: GC implementation using either the installed or included Boehm GC.
3  *
4  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
5  * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
6  * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
7  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8  */
9
10 #include "config.h"
11
12 #include <string.h>
13
14 #define GC_I_HIDE_POINTERS
15 #include <mono/metadata/gc-internals.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/metadata/class-internals.h>
19 #include <mono/metadata/method-builder.h>
20 #include <mono/metadata/opcodes.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/marshal.h>
24 #include <mono/metadata/runtime.h>
25 #include <mono/metadata/handle.h>
26 #include <mono/metadata/sgen-toggleref.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-logger-internals.h>
29 #include <mono/utils/mono-memory-model.h>
30 #include <mono/utils/mono-time.h>
31 #include <mono/utils/mono-threads.h>
32 #include <mono/utils/dtrace.h>
33 #include <mono/utils/gc_wrapper.h>
34 #include <mono/utils/mono-os-mutex.h>
35 #include <mono/utils/mono-counters.h>
36
37 #if HAVE_BOEHM_GC
38
39 #undef TRUE
40 #undef FALSE
41 #define THREAD_LOCAL_ALLOC 1
42 #include "private/pthread_support.h"
43
44 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
45 void *pthread_get_stackaddr_np(pthread_t);
46 #endif
47
48 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
49 /*Boehm max heap cannot be smaller than 16MB*/
50 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
51 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
52
53 static gboolean gc_initialized = FALSE;
54 static mono_mutex_t mono_gc_lock;
55
56 static void*
57 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
58 static void
59 boehm_thread_unregister (MonoThreadInfo *p);
60 static void
61 boehm_thread_detach (MonoThreadInfo *p);
62 static void
63 register_test_toggleref_callback (void);
64
65 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
66 static MonoGCFinalizerCallbacks fin_callbacks;
67
68 /* GC Handles */
69
70 static mono_mutex_t handle_section;
71 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
72 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
73
74 typedef struct {
75         guint32  *bitmap;
76         gpointer *entries;
77         guint32   size;
78         guint8    type;
79         guint     slot_hint : 24; /* starting slot for search in bitmap */
80         /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
81         /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
82         guint16  *domain_ids;
83 } HandleData;
84
85 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
86
87 /* weak and weak-track arrays will be allocated in malloc memory 
88  */
89 static HandleData gc_handles [] = {
90         EMPTY_HANDLE_DATA (HANDLE_WEAK),
91         EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
92         EMPTY_HANDLE_DATA (HANDLE_NORMAL),
93         EMPTY_HANDLE_DATA (HANDLE_PINNED)
94 };
95
96 static void
97 mono_gc_warning (char *msg, GC_word arg)
98 {
99         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
100 }
101
102 static void on_gc_notification (GC_EventType event);
103 static void on_gc_heap_resize (size_t new_size);
104
105 void
106 mono_gc_base_init (void)
107 {
108         MonoThreadInfoCallbacks cb;
109         const char *env;
110         int dummy;
111
112         if (gc_initialized)
113                 return;
114
115         mono_counters_init ();
116
117 #ifndef HOST_WIN32
118         mono_w32handle_init ();
119 #endif
120
121         /*
122          * Handle the case when we are called from a thread different from the main thread,
123          * confusing libgc.
124          * FIXME: Move this to libgc where it belongs.
125          *
126          * we used to do this only when running on valgrind,
127          * but it happens also in other setups.
128          */
129 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
130         {
131                 size_t size;
132                 void *sstart;
133                 pthread_attr_t attr;
134                 pthread_getattr_np (pthread_self (), &attr);
135                 pthread_attr_getstack (&attr, &sstart, &size);
136                 pthread_attr_destroy (&attr); 
137                 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
138 #ifdef __ia64__
139                 /*
140                  * The calculation above doesn't seem to work on ia64, also we need to set
141                  * GC_register_stackbottom as well, but don't know how.
142                  */
143 #else
144                 /* apparently with some linuxthreads implementations sstart can be NULL,
145                  * fallback to the more imprecise method (bug# 78096).
146                  */
147                 if (sstart) {
148                         GC_stackbottom = (char*)sstart + size;
149                 } else {
150                         int dummy;
151                         gsize stack_bottom = (gsize)&dummy;
152                         stack_bottom += 4095;
153                         stack_bottom &= ~4095;
154                         GC_stackbottom = (char*)stack_bottom;
155                 }
156 #endif
157         }
158 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
159                 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
160 #elif defined(__OpenBSD__)
161 #  include <pthread_np.h>
162         {
163                 stack_t ss;
164                 int rslt;
165
166                 rslt = pthread_stackseg_np(pthread_self(), &ss);
167                 g_assert (rslt == 0);
168
169                 GC_stackbottom = (char*)ss.ss_sp;
170         }
171 #elif defined(__native_client__)
172         /* Do nothing, GC_stackbottom is set correctly in libgc */
173 #else
174         {
175                 int dummy;
176                 gsize stack_bottom = (gsize)&dummy;
177                 stack_bottom += 4095;
178                 stack_bottom &= ~4095;
179                 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
180                 GC_stackbottom = (char*)stack_bottom;
181         }
182 #endif
183
184 #if !defined(PLATFORM_ANDROID)
185         /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
186         GC_no_dls = TRUE;
187 #endif
188         {
189                 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
190                         char **opts = g_strsplit (env, ",", -1);
191                         for (char **ptr = opts; ptr && *ptr; ptr ++) {
192                                 char *opt = *ptr;
193                                 if (!strcmp (opt, "do-not-finalize")) {
194                                         mono_do_not_finalize = 1;
195                                 } else if (!strcmp (opt, "log-finalizers")) {
196                                         log_finalizers = 1;
197                                 }
198                         }
199                 }
200         }
201
202         GC_init ();
203
204         GC_set_warn_proc (mono_gc_warning);
205         GC_finalize_on_demand = 1;
206         GC_finalizer_notifier = mono_gc_finalize_notify;
207
208         GC_init_gcj_malloc (5, NULL);
209         GC_allow_register_threads ();
210
211         if ((env = g_getenv ("MONO_GC_PARAMS"))) {
212                 char **ptr, **opts = g_strsplit (env, ",", -1);
213                 for (ptr = opts; *ptr; ++ptr) {
214                         char *opt = *ptr;
215                         if (g_str_has_prefix (opt, "max-heap-size=")) {
216                                 size_t max_heap;
217
218                                 opt = strchr (opt, '=') + 1;
219                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
220                                         if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
221                                                 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
222                                                 exit (1);
223                                         }
224                                         GC_set_max_heap_size (max_heap);
225                                 } else {
226                                         fprintf (stderr, "max-heap-size must be an integer.\n");
227                                         exit (1);
228                                 }
229                                 continue;
230                         } else if (g_str_has_prefix (opt, "toggleref-test")) {
231                                 register_test_toggleref_callback ();
232                                 continue;
233                         } else {
234                                 /* Could be a parameter for sgen */
235                                 /*
236                                 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
237                                 fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
238                                 exit (1);
239                                 */
240                         }
241                 }
242                 g_strfreev (opts);
243         }
244
245         memset (&cb, 0, sizeof (cb));
246         cb.thread_register = boehm_thread_register;
247         cb.thread_unregister = boehm_thread_unregister;
248         cb.thread_detach = boehm_thread_detach;
249         cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
250
251         mono_threads_init (&cb, sizeof (MonoThreadInfo));
252         mono_os_mutex_init (&mono_gc_lock);
253         mono_os_mutex_init_recursive (&handle_section);
254
255         mono_thread_info_attach (&dummy);
256
257         GC_set_on_collection_event (on_gc_notification);
258         GC_on_heap_resize = on_gc_heap_resize;
259
260         MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
261         MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
262
263         gc_initialized = TRUE;
264 }
265
266 void
267 mono_gc_base_cleanup (void)
268 {
269         GC_finalizer_notifier = NULL;
270 }
271
272 /**
273  * mono_gc_collect:
274  * @generation: GC generation identifier
275  *
276  * Perform a garbage collection for the given generation, higher numbers
277  * mean usually older objects. Collecting a high-numbered generation
278  * implies collecting also the lower-numbered generations.
279  * The maximum value for @generation can be retrieved with a call to
280  * mono_gc_max_generation(), so this function is usually called as:
281  *
282  *      mono_gc_collect (mono_gc_max_generation ());
283  */
284 void
285 mono_gc_collect (int generation)
286 {
287 #ifndef DISABLE_PERFCOUNTERS
288         mono_perfcounters->gc_induced++;
289 #endif
290         GC_gcollect ();
291 }
292
293 /**
294  * mono_gc_max_generation:
295  *
296  * Get the maximum generation number used by the current garbage
297  * collector. The value will be 0 for the Boehm collector, 1 or more
298  * for the generational collectors.
299  *
300  * Returns: the maximum generation number.
301  */
302 int
303 mono_gc_max_generation (void)
304 {
305         return 0;
306 }
307
308 /**
309  * mono_gc_get_generation:
310  * @object: a managed object
311  *
312  * Get the garbage collector's generation that @object belongs to.
313  * Use this has a hint only.
314  *
315  * Returns: a garbage collector generation number
316  */
317 int
318 mono_gc_get_generation  (MonoObject *object)
319 {
320         return 0;
321 }
322
323 /**
324  * mono_gc_collection_count:
325  * @generation: a GC generation number
326  *
327  * Get how many times a garbage collection has been performed
328  * for the given @generation number.
329  *
330  * Returns: the number of garbage collections
331  */
332 int
333 mono_gc_collection_count (int generation)
334 {
335         return GC_gc_no;
336 }
337
338 /**
339  * mono_gc_add_memory_pressure:
340  * @value: amount of bytes
341  *
342  * Adjust the garbage collector's view of how many bytes of memory
343  * are indirectly referenced by managed objects (for example unmanaged
344  * memory holding image or other binary data).
345  * This is a hint only to the garbage collector algorithm.
346  * Note that negative amounts of @value will decrease the memory
347  * pressure.
348  */
349 void
350 mono_gc_add_memory_pressure (gint64 value)
351 {
352 }
353
354 /**
355  * mono_gc_get_used_size:
356  *
357  * Get the approximate amount of memory used by managed objects.
358  *
359  * Returns: the amount of memory used in bytes
360  */
361 int64_t
362 mono_gc_get_used_size (void)
363 {
364         return GC_get_heap_size () - GC_get_free_bytes ();
365 }
366
367 /**
368  * mono_gc_get_heap_size:
369  *
370  * Get the amount of memory used by the garbage collector.
371  *
372  * Returns: the size of the heap in bytes
373  */
374 int64_t
375 mono_gc_get_heap_size (void)
376 {
377         return GC_get_heap_size ();
378 }
379
380 gboolean
381 mono_gc_is_gc_thread (void)
382 {
383         return GC_thread_is_registered ();
384 }
385
386 gboolean
387 mono_gc_register_thread (void *baseptr)
388 {
389         return mono_thread_info_attach (baseptr) != NULL;
390 }
391
392 static void*
393 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
394 {
395         struct GC_stack_base sb;
396         int res;
397
398         /* TODO: use GC_get_stack_base instead of baseptr. */
399         sb.mem_base = baseptr;
400         res = GC_register_my_thread (&sb);
401         if (res == GC_UNIMPLEMENTED)
402             return NULL; /* Cannot happen with GC v7+. */
403
404         info->handle_stack = mono_handle_stack_alloc ();
405
406         return info;
407 }
408
409 static void
410 boehm_thread_unregister (MonoThreadInfo *p)
411 {
412         MonoNativeThreadId tid;
413
414         tid = mono_thread_info_get_tid (p);
415
416         if (p->runtime_thread)
417                 mono_threads_add_joinable_thread ((gpointer)tid);
418 }
419
420 static void
421 boehm_thread_detach (MonoThreadInfo *p)
422 {
423         if (mono_thread_internal_current_is_attached ())
424                 mono_thread_detach_internal (mono_thread_internal_current ());
425 }
426
427 gboolean
428 mono_object_is_alive (MonoObject* o)
429 {
430         return GC_is_marked ((ptr_t)o);
431 }
432
433 int
434 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
435 {
436         return 1;
437 }
438
439 static gint64 gc_start_time;
440
441 static void
442 on_gc_notification (GC_EventType event)
443 {
444         MonoGCEvent e = (MonoGCEvent)event;
445
446         switch (e) {
447         case MONO_GC_EVENT_PRE_STOP_WORLD:
448                 MONO_GC_WORLD_STOP_BEGIN ();
449                 break;
450
451         case MONO_GC_EVENT_POST_STOP_WORLD:
452                 MONO_GC_WORLD_STOP_END ();
453                 break;
454
455         case MONO_GC_EVENT_PRE_START_WORLD:
456                 MONO_GC_WORLD_RESTART_BEGIN (1);
457                 break;
458
459         case MONO_GC_EVENT_POST_START_WORLD:
460                 MONO_GC_WORLD_RESTART_END (1);
461                 break;
462
463         case MONO_GC_EVENT_START:
464                 MONO_GC_BEGIN (1);
465 #ifndef DISABLE_PERFCOUNTERS
466                 if (mono_perfcounters)
467                         mono_perfcounters->gc_collections0++;
468 #endif
469                 gc_stats.major_gc_count ++;
470                 gc_start_time = mono_100ns_ticks ();
471                 break;
472
473         case MONO_GC_EVENT_END:
474                 MONO_GC_END (1);
475 #if defined(ENABLE_DTRACE) && defined(__sun__)
476                 /* This works around a dtrace -G problem on Solaris.
477                    Limit its actual use to when the probe is enabled. */
478                 if (MONO_GC_END_ENABLED ())
479                         sleep(0);
480 #endif
481
482 #ifndef DISABLE_PERFCOUNTERS
483                 if (mono_perfcounters) {
484                         guint64 heap_size = GC_get_heap_size ();
485                         guint64 used_size = heap_size - GC_get_free_bytes ();
486                         mono_perfcounters->gc_total_bytes = used_size;
487                         mono_perfcounters->gc_committed_bytes = heap_size;
488                         mono_perfcounters->gc_reserved_bytes = heap_size;
489                         mono_perfcounters->gc_gen0size = heap_size;
490                 }
491 #endif
492                 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
493                 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
494                 break;
495         default:
496                 break;
497         }
498
499         mono_profiler_gc_event (e, 0);
500
501         switch (e) {
502         case MONO_GC_EVENT_PRE_STOP_WORLD:
503                 mono_thread_info_suspend_lock ();
504                 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
505                 break;
506         case MONO_GC_EVENT_POST_START_WORLD:
507                 mono_thread_info_suspend_unlock ();
508                 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
509                 break;
510         default:
511                 break;
512         }
513 }
514
515  
516 static void
517 on_gc_heap_resize (size_t new_size)
518 {
519         guint64 heap_size = GC_get_heap_size ();
520 #ifndef DISABLE_PERFCOUNTERS
521         if (mono_perfcounters) {
522                 mono_perfcounters->gc_committed_bytes = heap_size;
523                 mono_perfcounters->gc_reserved_bytes = heap_size;
524                 mono_perfcounters->gc_gen0size = heap_size;
525         }
526 #endif
527         mono_profiler_gc_heap_resize (new_size);
528 }
529
530 int
531 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
532 {
533         /* for some strange reason, they want one extra byte on the end */
534         GC_add_roots (start, start + size + 1);
535
536         return TRUE;
537 }
538
539 void
540 mono_gc_deregister_root (char* addr)
541 {
542 #ifndef HOST_WIN32
543         /* FIXME: libgc doesn't define this work win32 for some reason */
544         /* FIXME: No size info */
545         GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
546 #endif
547 }
548
549 static void
550 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
551 {
552         /* libgc requires that we use HIDE_POINTER... */
553         *link_addr = (void*)HIDE_POINTER (obj);
554         if (track)
555                 GC_REGISTER_LONG_LINK (link_addr, obj);
556         else
557                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
558 }
559
560 static void
561 mono_gc_weak_link_remove (void **link_addr, gboolean track)
562 {
563         if (track)
564                 GC_unregister_long_link (link_addr);
565         else
566                 GC_unregister_disappearing_link (link_addr);
567         *link_addr = NULL;
568 }
569
570 static gpointer
571 reveal_link (gpointer link_addr)
572 {
573         void **link_a = (void **)link_addr;
574         return REVEAL_POINTER (*link_a);
575 }
576
577 static MonoObject *
578 mono_gc_weak_link_get (void **link_addr)
579 {
580         MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
581         if (obj == (MonoObject *) -1)
582                 return NULL;
583         return obj;
584 }
585
586 void*
587 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
588 {
589         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
590 }
591
592 void*
593 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
594 {
595         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
596 }
597
598 void*
599 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
600 {
601         /* libgc has no usable support for arrays... */
602         return GC_NO_DESCRIPTOR;
603 }
604
605 void*
606 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
607 {
608         /* It seems there are issues when the bitmap doesn't fit: play it safe */
609         if (numbits >= 30)
610                 return GC_NO_DESCRIPTOR;
611         else
612                 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
613 }
614
615 void*
616 mono_gc_make_root_descr_all_refs (int numbits)
617 {
618         return NULL;
619 }
620
621 void*
622 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
623 {
624         /* To help track down typed allocation bugs */
625         /*
626         static int count;
627         count ++;
628         if (count == atoi (g_getenv ("COUNT2")))
629                 printf ("HIT!\n");
630         if (count > atoi (g_getenv ("COUNT2")))
631                 return GC_MALLOC (size);
632         */
633
634         if (descr)
635                 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
636         else
637                 return GC_MALLOC (size);
638 }
639
640 void
641 mono_gc_free_fixed (void* addr)
642 {
643 }
644
645 void *
646 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
647 {
648         MonoObject *obj;
649
650         if (!vtable->klass->has_references) {
651                 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
652                 if (G_UNLIKELY (!obj))
653                         return NULL;
654
655                 obj->vtable = vtable;
656                 obj->synchronisation = NULL;
657
658                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
659         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
660                 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
661                 if (G_UNLIKELY (!obj))
662                         return NULL;
663         } else {
664                 obj = (MonoObject *)GC_MALLOC (size);
665                 if (G_UNLIKELY (!obj))
666                         return NULL;
667
668                 obj->vtable = vtable;
669         }
670
671         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
672                 mono_profiler_allocation (obj);
673
674         return obj;
675 }
676
677 void *
678 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
679 {
680         MonoArray *obj;
681
682         if (!vtable->klass->has_references) {
683                 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
684                 if (G_UNLIKELY (!obj))
685                         return NULL;
686
687                 obj->obj.vtable = vtable;
688                 obj->obj.synchronisation = NULL;
689
690                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
691         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
692                 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
693                 if (G_UNLIKELY (!obj))
694                         return NULL;
695         } else {
696                 obj = (MonoArray *)GC_MALLOC (size);
697                 if (G_UNLIKELY (!obj))
698                         return NULL;
699
700                 obj->obj.vtable = vtable;
701         }
702
703         obj->max_length = max_length;
704
705         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
706                 mono_profiler_allocation (&obj->obj);
707
708         return obj;
709 }
710
711 void *
712 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
713 {
714         MonoArray *obj;
715
716         if (!vtable->klass->has_references) {
717                 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
718                 if (G_UNLIKELY (!obj))
719                         return NULL;
720
721                 obj->obj.vtable = vtable;
722                 obj->obj.synchronisation = NULL;
723
724                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
725         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
726                 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
727                 if (G_UNLIKELY (!obj))
728                         return NULL;
729         } else {
730                 obj = (MonoArray *)GC_MALLOC (size);
731                 if (G_UNLIKELY (!obj))
732                         return NULL;
733
734                 obj->obj.vtable = vtable;
735         }
736
737         obj->max_length = max_length;
738
739         if (bounds_size)
740                 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
741
742         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
743                 mono_profiler_allocation (&obj->obj);
744
745         return obj;
746 }
747
748 void *
749 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
750 {
751         MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
752         if (G_UNLIKELY (!obj))
753                 return NULL;
754
755         obj->object.vtable = vtable;
756         obj->object.synchronisation = NULL;
757         obj->length = len;
758         obj->chars [len] = 0;
759
760         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
761                 mono_profiler_allocation (&obj->object);
762
763         return obj;
764 }
765
766 void*
767 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
768 {
769         return mono_gc_alloc_obj (vtable, size);
770 }
771
772 void*
773 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
774 {
775         return mono_gc_alloc_obj (vtable, size);
776 }
777
778 int
779 mono_gc_invoke_finalizers (void)
780 {
781         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
782          * the 'mem_freed' variable is not initialized when there are no
783          * objects to finalize, which leads to strange behavior later on.
784          * The check is necessary to work around that bug.
785          */
786         if (GC_should_invoke_finalizers ())
787                 return GC_invoke_finalizers ();
788         return 0;
789 }
790
791 MonoBoolean
792 mono_gc_pending_finalizers (void)
793 {
794         return GC_should_invoke_finalizers ();
795 }
796
797 void
798 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
799 {
800         *(void**)field_ptr = value;
801 }
802
803 void
804 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
805 {
806         *(void**)slot_ptr = value;
807 }
808
809 void
810 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
811 {
812         mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
813 }
814
815 void
816 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
817 {
818         *(void**)ptr = value;
819 }
820
821 void
822 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
823 {
824         InterlockedWritePointer ((volatile gpointer *)ptr, value);
825 }
826
827 void
828 mono_gc_wbarrier_generic_nostore (gpointer ptr)
829 {
830 }
831
832 void
833 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
834 {
835         mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
836 }
837
838 void
839 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
840 {
841         /* do not copy the sync state */
842         mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
843                         mono_object_class (obj)->instance_size - sizeof (MonoObject));
844 }
845
846 void
847 mono_gc_clear_domain (MonoDomain *domain)
848 {
849 }
850
851 void
852 mono_gc_suspend_finalizers (void)
853 {
854 }
855
856 int
857 mono_gc_get_suspend_signal (void)
858 {
859         return GC_get_suspend_signal ();
860 }
861
862 int
863 mono_gc_get_restart_signal (void)
864 {
865         return GC_get_thr_restart_signal ();
866 }
867
868 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
869 extern __thread MONO_TLS_FAST void* GC_thread_tls;
870 #include "metadata-internals.h"
871
872 static int
873 shift_amount (int v)
874 {
875         int i = 0;
876         while (!(v & (1 << i)))
877                 i++;
878         return i;
879 }
880
881 enum {
882         ATYPE_FREEPTR,
883         ATYPE_FREEPTR_FOR_BOX,
884         ATYPE_NORMAL,
885         ATYPE_GCJ,
886         ATYPE_STRING,
887         ATYPE_NUM
888 };
889
890 static MonoMethod*
891 create_allocator (int atype, int tls_key, gboolean slowpath)
892 {
893         int index_var, bytes_var, my_fl_var, my_entry_var;
894         guint32 no_freelist_branch, not_small_enough_branch = 0;
895         guint32 size_overflow_branch = 0;
896         MonoMethodBuilder *mb;
897         MonoMethod *res;
898         MonoMethodSignature *csig;
899         const char *name = NULL;
900         WrapperInfo *info;
901
902         if (atype == ATYPE_FREEPTR) {
903                 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
904         } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
905                 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
906         } else if (atype == ATYPE_NORMAL) {
907                 name = slowpath ? "SlowAlloc" : "Alloc";
908         } else if (atype == ATYPE_GCJ) {
909                 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
910         } else if (atype == ATYPE_STRING) {
911                 name = slowpath ? "SlowAllocString" : "AllocString";
912         } else {
913                 g_assert_not_reached ();
914         }
915
916         csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
917
918         if (atype == ATYPE_STRING) {
919                 csig->ret = &mono_defaults.string_class->byval_arg;
920                 csig->params [0] = &mono_defaults.int_class->byval_arg;
921                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
922         } else {
923                 csig->ret = &mono_defaults.object_class->byval_arg;
924                 csig->params [0] = &mono_defaults.int_class->byval_arg;
925                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
926         }
927
928         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
929
930         if (slowpath)
931                 goto always_slowpath;
932
933         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
934         if (atype == ATYPE_STRING) {
935                 /* a string alloator method takes the args: (vtable, len) */
936                 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
937                 mono_mb_emit_ldarg (mb, 1);
938                 mono_mb_emit_icon (mb, 1);
939                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
940                 mono_mb_emit_icon (mb, 1);
941                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
942                 // sizeof (MonoString) might include padding
943                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
944                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
945                 mono_mb_emit_stloc (mb, bytes_var);
946         } else {
947                 mono_mb_emit_ldarg (mb, 1);
948                 mono_mb_emit_stloc (mb, bytes_var);
949         }
950
951         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
952         if (atype == ATYPE_STRING) {
953                 /* check for size */
954                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
955                 mono_mb_emit_ldloc (mb, bytes_var);
956                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
957                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
958                 /* check for overflow */
959                 mono_mb_emit_ldloc (mb, bytes_var);
960                 mono_mb_emit_icon (mb, sizeof (MonoString));
961                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
962         }
963
964         /* int index = INDEX_FROM_BYTES(bytes); */
965         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
966         
967         mono_mb_emit_ldloc (mb, bytes_var);
968         mono_mb_emit_icon (mb, GRANULARITY - 1);
969         mono_mb_emit_byte (mb, MONO_CEE_ADD);
970         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
971         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
972         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
973         mono_mb_emit_byte (mb, MONO_CEE_SHL);
974         /* index var is already adjusted into bytes */
975         mono_mb_emit_stloc (mb, index_var);
976
977         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
978         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
979         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
980         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
981         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
982         mono_mb_emit_i4 (mb, tls_key);
983         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
984                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
985                                         + G_STRUCT_OFFSET (struct thread_local_freelists,
986                                                            ptrfree_freelists));
987         else if (atype == ATYPE_NORMAL)
988                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
989                                         + G_STRUCT_OFFSET (struct thread_local_freelists,
990                                                            normal_freelists));
991         else if (atype == ATYPE_GCJ)
992                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
993                                         + G_STRUCT_OFFSET (struct thread_local_freelists,
994                                                            gcj_freelists));
995         else
996                 g_assert_not_reached ();
997         mono_mb_emit_byte (mb, MONO_CEE_ADD);
998         mono_mb_emit_ldloc (mb, index_var);
999         mono_mb_emit_byte (mb, MONO_CEE_ADD);
1000         mono_mb_emit_stloc (mb, my_fl_var);
1001
1002         /* my_entry = *my_fl; */
1003         mono_mb_emit_ldloc (mb, my_fl_var);
1004         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1005         mono_mb_emit_stloc (mb, my_entry_var);
1006
1007         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1008         mono_mb_emit_ldloc (mb, my_entry_var);
1009         mono_mb_emit_icon (mb, HBLKSIZE);
1010         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1011
1012         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1013         mono_mb_emit_ldloc (mb, my_fl_var);
1014         mono_mb_emit_ldloc (mb, my_entry_var);
1015         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1016         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1017
1018         /* set the vtable and clear the words in the object */
1019         mono_mb_emit_ldloc (mb, my_entry_var);
1020         mono_mb_emit_ldarg (mb, 0);
1021         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1022
1023         if (atype == ATYPE_FREEPTR) {
1024                 int start_var, end_var, start_loop;
1025                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1026                  */
1027                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1028                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1029                 mono_mb_emit_ldloc (mb, my_entry_var);
1030                 mono_mb_emit_ldloc (mb, bytes_var);
1031                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1032                 mono_mb_emit_stloc (mb, end_var);
1033                 mono_mb_emit_ldloc (mb, my_entry_var);
1034                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1035                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1036                 mono_mb_emit_stloc (mb, start_var);
1037                 /*
1038                  * do {
1039                  *      *start++ = NULL;
1040                  * } while (start < end);
1041                  */
1042                 start_loop = mono_mb_get_label (mb);
1043                 mono_mb_emit_ldloc (mb, start_var);
1044                 mono_mb_emit_icon (mb, 0);
1045                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1046                 mono_mb_emit_ldloc (mb, start_var);
1047                 mono_mb_emit_icon (mb, sizeof (gpointer));
1048                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1049                 mono_mb_emit_stloc (mb, start_var);
1050
1051                 mono_mb_emit_ldloc (mb, start_var);
1052                 mono_mb_emit_ldloc (mb, end_var);
1053                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1054                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1055         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1056                 /* need to clear just the sync pointer */
1057                 mono_mb_emit_ldloc (mb, my_entry_var);
1058                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1059                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1060                 mono_mb_emit_icon (mb, 0);
1061                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1062         }
1063
1064         if (atype == ATYPE_STRING) {
1065                 /* need to set length and clear the last char */
1066                 /* s->length = len; */
1067                 mono_mb_emit_ldloc (mb, my_entry_var);
1068                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1069                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1070                 mono_mb_emit_ldarg (mb, 1);
1071                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1072                 /* s->chars [len] = 0; */
1073                 mono_mb_emit_ldloc (mb, my_entry_var);
1074                 mono_mb_emit_ldloc (mb, bytes_var);
1075                 mono_mb_emit_icon (mb, 2);
1076                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1077                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1078                 mono_mb_emit_icon (mb, 0);
1079                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1080         }
1081
1082         /* return my_entry; */
1083         mono_mb_emit_ldloc (mb, my_entry_var);
1084         mono_mb_emit_byte (mb, MONO_CEE_RET);
1085         
1086         mono_mb_patch_short_branch (mb, no_freelist_branch);
1087         if (not_small_enough_branch > 0)
1088                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1089         if (size_overflow_branch > 0)
1090                 mono_mb_patch_short_branch (mb, size_overflow_branch);
1091
1092         /* the slow path: we just call back into the runtime */
1093  always_slowpath:
1094         if (atype == ATYPE_STRING) {
1095                 mono_mb_emit_ldarg (mb, 1);
1096                 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1097         } else {
1098                 mono_mb_emit_ldarg (mb, 0);
1099                 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1100         }
1101
1102         mono_mb_emit_byte (mb, MONO_CEE_RET);
1103
1104         info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1105         info->d.alloc.gc_name = "boehm";
1106         info->d.alloc.alloc_type = atype;
1107         mb->init_locals = FALSE;
1108
1109         res = mono_mb_create (mb, csig, 8, info);
1110         mono_mb_free (mb);
1111
1112         return res;
1113 }
1114
1115 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1116 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1117
1118 static G_GNUC_UNUSED gboolean
1119 mono_gc_is_critical_method (MonoMethod *method)
1120 {
1121         int i;
1122
1123         for (i = 0; i < ATYPE_NUM; ++i)
1124                 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1125                         return TRUE;
1126
1127         return FALSE;
1128 }
1129
1130 /*
1131  * If possible, generate a managed method that can quickly allocate objects in class
1132  * @klass. The method will typically have an thread-local inline allocation sequence.
1133  * The signature of the called method is:
1134  *      object allocate (MonoVTable *vtable)
1135  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1136  * keep in sync.
1137  * The thread local alloc logic is taken from libgc/pthread_support.c.
1138  */
1139
1140 MonoMethod*
1141 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1142 {
1143         int offset = -1;
1144         int atype;
1145         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1146
1147         /*g_print ("thread tls: %d\n", offset);*/
1148         if (offset == -1)
1149                 return NULL;
1150         if (!SMALL_ENOUGH (klass->instance_size))
1151                 return NULL;
1152         if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1153                 return NULL;
1154         if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1155                 return NULL;
1156         if (klass->rank)
1157                 return NULL;
1158         if (mono_class_is_open_constructed_type (&klass->byval_arg))
1159                 return NULL;
1160         if (klass->byval_arg.type == MONO_TYPE_STRING) {
1161                 atype = ATYPE_STRING;
1162         } else if (!known_instance_size) {
1163                 return NULL;
1164         } else if (!klass->has_references) {
1165                 if (for_box)
1166                         atype = ATYPE_FREEPTR_FOR_BOX;
1167                 else
1168                         atype = ATYPE_FREEPTR;
1169         } else {
1170                 return NULL;
1171                 /*
1172                  * disabled because we currently do a runtime choice anyway, to
1173                  * deal with multiple appdomains.
1174                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1175                         atype = ATYPE_GCJ;
1176                 else
1177                         atype = ATYPE_NORMAL;
1178                 */
1179         }
1180         return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1181 }
1182
1183 MonoMethod*
1184 mono_gc_get_managed_array_allocator (MonoClass *klass)
1185 {
1186         return NULL;
1187 }
1188
1189 /**
1190  * mono_gc_get_managed_allocator_by_type:
1191  *
1192  *   Return a managed allocator method corresponding to allocator type ATYPE.
1193  */
1194 MonoMethod*
1195 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1196 {
1197         int offset = -1;
1198         MonoMethod *res;
1199         gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1200         MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1201         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1202
1203         mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1204
1205         res = cache [atype];
1206         if (res)
1207                 return res;
1208
1209         res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1210         mono_os_mutex_lock (&mono_gc_lock);
1211         if (cache [atype]) {
1212                 mono_free_method (res);
1213                 res = cache [atype];
1214         } else {
1215                 mono_memory_barrier ();
1216                 cache [atype] = res;
1217         }
1218         mono_os_mutex_unlock (&mono_gc_lock);
1219         return res;
1220 }
1221
1222 guint32
1223 mono_gc_get_managed_allocator_types (void)
1224 {
1225         return ATYPE_NUM;
1226 }
1227
1228 MonoMethod*
1229 mono_gc_get_write_barrier (void)
1230 {
1231         g_assert_not_reached ();
1232         return NULL;
1233 }
1234
1235 #else
1236
1237 static G_GNUC_UNUSED gboolean
1238 mono_gc_is_critical_method (MonoMethod *method)
1239 {
1240         return FALSE;
1241 }
1242
1243 MonoMethod*
1244 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1245 {
1246         return NULL;
1247 }
1248
1249 MonoMethod*
1250 mono_gc_get_managed_array_allocator (MonoClass *klass)
1251 {
1252         return NULL;
1253 }
1254
1255 MonoMethod*
1256 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1257 {
1258         return NULL;
1259 }
1260
1261 guint32
1262 mono_gc_get_managed_allocator_types (void)
1263 {
1264         return 0;
1265 }
1266
1267 MonoMethod*
1268 mono_gc_get_write_barrier (void)
1269 {
1270         g_assert_not_reached ();
1271         return NULL;
1272 }
1273
1274 #endif
1275
1276 MonoMethod*
1277 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1278 {
1279         g_assert_not_reached ();
1280         return NULL;
1281 }
1282
1283 int
1284 mono_gc_get_aligned_size_for_allocator (int size)
1285 {
1286         return size;
1287 }
1288
1289 const char *
1290 mono_gc_get_gc_name (void)
1291 {
1292         return "boehm";
1293 }
1294
1295 void*
1296 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1297 {
1298         return GC_call_with_alloc_lock (func, data);
1299 }
1300
1301 char*
1302 mono_gc_get_description (void)
1303 {
1304         return g_strdup (DEFAULT_GC_NAME);
1305 }
1306
1307 void
1308 mono_gc_set_desktop_mode (void)
1309 {
1310         GC_dont_expand = 1;
1311 }
1312
1313 gboolean
1314 mono_gc_is_moving (void)
1315 {
1316         return FALSE;
1317 }
1318
1319 gboolean
1320 mono_gc_is_disabled (void)
1321 {
1322         if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1323                 return TRUE;
1324         else
1325                 return FALSE;
1326 }
1327
1328 void
1329 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1330 {
1331         g_assert_not_reached ();
1332 }
1333
1334
1335 guint8*
1336 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1337 {
1338         g_assert_not_reached ();
1339         return NULL;
1340 }
1341
1342 gboolean
1343 mono_gc_card_table_nursery_check (void)
1344 {
1345         g_assert_not_reached ();
1346         return TRUE;
1347 }
1348
1349 void*
1350 mono_gc_get_nursery (int *shift_bits, size_t *size)
1351 {
1352         return NULL;
1353 }
1354
1355 void
1356 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1357 {
1358 }
1359
1360 gboolean
1361 mono_gc_precise_stack_mark_enabled (void)
1362 {
1363         return FALSE;
1364 }
1365
1366 FILE *
1367 mono_gc_get_logfile (void)
1368 {
1369         return NULL;
1370 }
1371
1372 void
1373 mono_gc_params_set (const char* options)
1374 {
1375 }
1376
1377 void
1378 mono_gc_debug_set (const char* options)
1379 {
1380 }
1381
1382 void
1383 mono_gc_conservatively_scan_area (void *start, void *end)
1384 {
1385         g_assert_not_reached ();
1386 }
1387
1388 void *
1389 mono_gc_scan_object (void *obj, void *gc_data)
1390 {
1391         g_assert_not_reached ();
1392         return NULL;
1393 }
1394
1395 gsize*
1396 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1397 {
1398         g_assert_not_reached ();
1399         return NULL;
1400 }
1401
1402 void
1403 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1404 {
1405 }
1406
1407 void
1408 mono_gc_set_stack_end (void *stack_end)
1409 {
1410 }
1411
1412 void mono_gc_set_skip_thread (gboolean value)
1413 {
1414 }
1415
1416 void
1417 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1418 {
1419         guint offset = 0;
1420
1421 #ifndef GC_DEBUG
1422         /* This assertion is not valid when GC_DEBUG is defined */
1423         g_assert (GC_base (obj) == (char*)obj - offset);
1424 #endif
1425
1426         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1427 }
1428
1429 #ifndef HOST_WIN32
1430 int
1431 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1432 {
1433         /* it is being replaced by GC_pthread_create on some
1434          * platforms, see libgc/include/gc_pthread_redirects.h */
1435         return pthread_create (new_thread, attr, start_routine, arg);
1436 }
1437 #endif
1438
1439 #ifdef HOST_WIN32
1440 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1441 {
1442         return GC_DllMain (module_handle, reason, reserved);
1443 }
1444 #endif
1445
1446 guint
1447 mono_gc_get_vtable_bits (MonoClass *klass)
1448 {
1449         if (fin_callbacks.is_class_finalization_aware) {
1450                 if (fin_callbacks.is_class_finalization_aware (klass))
1451                         return BOEHM_GC_BIT_FINALIZER_AWARE;
1452         }
1453         return 0;
1454 }
1455
1456 /*
1457  * mono_gc_register_altstack:
1458  *
1459  *   Register the dimensions of the normal stack and altstack with the collector.
1460  * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1461  */
1462 void
1463 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1464 {
1465         GC_register_altstack (stack, stack_size, altstack, altstack_size);
1466 }
1467
1468 int
1469 mono_gc_get_los_limit (void)
1470 {
1471         return G_MAXINT;
1472 }
1473
1474 void
1475 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1476 {
1477         mono_unichar2 *new_end = str->chars + new_length;
1478         
1479         /* zero the discarded string. This null-delimits the string and allows 
1480          * the space to be reclaimed by SGen. */
1481          
1482         memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1483         str->length = new_length;
1484 }
1485
1486 gboolean
1487 mono_gc_user_markers_supported (void)
1488 {
1489         return FALSE;
1490 }
1491
1492 void *
1493 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1494 {
1495         g_assert_not_reached ();
1496         return NULL;
1497 }
1498
1499 /* Toggleref support */
1500
1501 void
1502 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1503 {
1504         if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1505             g_error ("GC_toggleref_add failed\n");
1506 }
1507
1508 void
1509 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1510 {
1511         GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1512 }
1513
1514 /* Test support code */
1515
1516 static MonoToggleRefStatus
1517 test_toggleref_callback (MonoObject *obj)
1518 {
1519         static MonoClassField *mono_toggleref_test_field;
1520         MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1521
1522         if (!mono_toggleref_test_field) {
1523                 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1524                 g_assert (mono_toggleref_test_field);
1525         }
1526
1527         mono_field_get_value (obj, mono_toggleref_test_field, &status);
1528         printf ("toggleref-cb obj %d\n", status);
1529         return status;
1530 }
1531
1532 static void
1533 register_test_toggleref_callback (void)
1534 {
1535         mono_gc_toggleref_register_callback (test_toggleref_callback);
1536 }
1537
1538 static gboolean
1539 is_finalization_aware (MonoObject *obj)
1540 {
1541         MonoVTable *vt = obj->vtable;
1542         return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1543 }
1544
1545 static void
1546 fin_notifier (MonoObject *obj)
1547 {
1548         if (is_finalization_aware (obj))
1549                 fin_callbacks.object_queued_for_finalization (obj);
1550 }
1551
1552 void
1553 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1554 {
1555         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1556                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1557
1558         fin_callbacks = *callbacks;
1559
1560         GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1561 }
1562
1563 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1564
1565 static inline gboolean
1566 slot_occupied (HandleData *handles, guint slot) {
1567         return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1568 }
1569
1570 static inline void
1571 vacate_slot (HandleData *handles, guint slot) {
1572         handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1573 }
1574
1575 static inline void
1576 occupy_slot (HandleData *handles, guint slot) {
1577         handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1578 }
1579
1580 static int
1581 find_first_unset (guint32 bitmap)
1582 {
1583         int i;
1584         for (i = 0; i < 32; ++i) {
1585                 if (!(bitmap & (1 << i)))
1586                         return i;
1587         }
1588         return -1;
1589 }
1590
1591 static void
1592 handle_data_alloc_entries (HandleData *handles)
1593 {
1594         handles->size = 32;
1595         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1596                 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1597                 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1598         } else {
1599                 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1600         }
1601         handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1602 }
1603
1604 static gint
1605 handle_data_next_unset (HandleData *handles)
1606 {
1607         gint slot;
1608         for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1609                 if (handles->bitmap [slot] == 0xffffffff)
1610                         continue;
1611                 handles->slot_hint = slot;
1612                 return find_first_unset (handles->bitmap [slot]);
1613         }
1614         return -1;
1615 }
1616
1617 static gint
1618 handle_data_first_unset (HandleData *handles)
1619 {
1620         gint slot;
1621         for (slot = 0; slot < handles->slot_hint; ++slot) {
1622                 if (handles->bitmap [slot] == 0xffffffff)
1623                         continue;
1624                 handles->slot_hint = slot;
1625                 return find_first_unset (handles->bitmap [slot]);
1626         }
1627         return -1;
1628 }
1629
1630 /* Returns the index of the current slot in the bitmap. */
1631 static void
1632 handle_data_grow (HandleData *handles, gboolean track)
1633 {
1634         guint32 *new_bitmap;
1635         guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1636
1637         /* resize and copy the bitmap */
1638         new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1639         memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1640         g_free (handles->bitmap);
1641         handles->bitmap = new_bitmap;
1642
1643         /* resize and copy the entries */
1644         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1645                 gpointer *entries;
1646                 guint16 *domain_ids;
1647                 gint i;
1648                 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1649                 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1650                 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1651                 for (i = 0; i < handles->size; ++i) {
1652                         MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1653                         if (obj) {
1654                                 mono_gc_weak_link_add (&(entries [i]), obj, track);
1655                                 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1656                         } else {
1657                                 g_assert (!handles->entries [i]);
1658                         }
1659                 }
1660                 g_free (handles->entries);
1661                 g_free (handles->domain_ids);
1662                 handles->entries = entries;
1663                 handles->domain_ids = domain_ids;
1664         } else {
1665                 gpointer *entries;
1666                 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1667                 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1668                 mono_gc_free_fixed (handles->entries);
1669                 handles->entries = entries;
1670         }
1671         handles->slot_hint = handles->size / BITMAP_SIZE;
1672         handles->size = new_size;
1673 }
1674
1675 static guint32
1676 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1677 {
1678         gint slot, i;
1679         guint32 res;
1680         lock_handles (handles);
1681         if (!handles->size)
1682                 handle_data_alloc_entries (handles);
1683         i = handle_data_next_unset (handles);
1684         if (i == -1 && handles->slot_hint != 0)
1685                 i = handle_data_first_unset (handles);
1686         if (i == -1) {
1687                 handle_data_grow (handles, track);
1688                 i = 0;
1689         }
1690         slot = handles->slot_hint * BITMAP_SIZE + i;
1691         occupy_slot (handles, slot);
1692         handles->entries [slot] = NULL;
1693         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1694                 /*FIXME, what to use when obj == null?*/
1695                 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1696                 if (obj)
1697                         mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1698         } else {
1699                 handles->entries [slot] = obj;
1700         }
1701
1702 #ifndef DISABLE_PERFCOUNTERS
1703         mono_perfcounters->gc_num_handles++;
1704 #endif
1705         unlock_handles (handles);
1706         res = MONO_GC_HANDLE (slot, handles->type);
1707         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1708         return res;
1709 }
1710
1711 /**
1712  * mono_gchandle_new:
1713  * @obj: managed object to get a handle for
1714  * @pinned: whether the object should be pinned
1715  *
1716  * This returns a handle that wraps the object, this is used to keep a
1717  * reference to a managed object from the unmanaged world and preventing the
1718  * object from being disposed.
1719  * 
1720  * If @pinned is false the address of the object can not be obtained, if it is
1721  * true the address of the object can be obtained.  This will also pin the
1722  * object so it will not be possible by a moving garbage collector to move the
1723  * object. 
1724  * 
1725  * Returns: a handle that can be used to access the object from
1726  * unmanaged code.
1727  */
1728 guint32
1729 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1730 {
1731         return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1732 }
1733
1734 /**
1735  * mono_gchandle_new_weakref:
1736  * @obj: managed object to get a handle for
1737  * @track_resurrection: Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization.
1738  *
1739  * This returns a weak handle that wraps the object, this is used to
1740  * keep a reference to a managed object from the unmanaged world.
1741  * Unlike the mono_gchandle_new the object can be reclaimed by the
1742  * garbage collector.  In this case the value of the GCHandle will be
1743  * set to zero.
1744  * 
1745  * If @track_resurrection is TRUE the object will be tracked through
1746  * finalization and if the object is resurrected during the execution
1747  * of the finalizer, then the returned weakref will continue to hold
1748  * a reference to the object.   If @track_resurrection is FALSE, then
1749  * the weak reference's target will become NULL as soon as the object
1750  * is passed on to the finalizer.
1751  * 
1752  * Returns: a handle that can be used to access the object from
1753  * unmanaged code.
1754  */
1755 guint32
1756 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1757 {
1758         return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1759 }
1760
1761 /**
1762  * mono_gchandle_get_target:
1763  * @gchandle: a GCHandle's handle.
1764  *
1765  * The handle was previously created by calling `mono_gchandle_new` or
1766  * `mono_gchandle_new_weakref`.
1767  *
1768  * Returns: A pointer to the `MonoObject*` represented by the handle or
1769  * NULL for a collected object if using a weakref handle.
1770  */
1771 MonoObject*
1772 mono_gchandle_get_target (guint32 gchandle)
1773 {
1774         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1775         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1776         HandleData *handles = &gc_handles [type];
1777         MonoObject *obj = NULL;
1778         if (type >= HANDLE_TYPE_MAX)
1779                 return NULL;
1780
1781         lock_handles (handles);
1782         if (slot < handles->size && slot_occupied (handles, slot)) {
1783                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1784                         obj = mono_gc_weak_link_get (&handles->entries [slot]);
1785                 } else {
1786                         obj = (MonoObject *)handles->entries [slot];
1787                 }
1788         } else {
1789                 /* print a warning? */
1790         }
1791         unlock_handles (handles);
1792         /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1793         return obj;
1794 }
1795
1796 void
1797 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1798 {
1799         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1800         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1801         HandleData *handles = &gc_handles [type];
1802         MonoObject *old_obj = NULL;
1803
1804         g_assert (type < HANDLE_TYPE_MAX);
1805         lock_handles (handles);
1806         if (slot < handles->size && slot_occupied (handles, slot)) {
1807                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1808                         old_obj = (MonoObject *)handles->entries [slot];
1809                         if (handles->entries [slot])
1810                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1811                         if (obj)
1812                                 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1813                         /*FIXME, what to use when obj == null?*/
1814                         handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1815                 } else {
1816                         handles->entries [slot] = obj;
1817                 }
1818         } else {
1819                 /* print a warning? */
1820         }
1821         /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1822         unlock_handles (handles);
1823 }
1824
1825 gboolean
1826 mono_gc_is_null (void)
1827 {
1828         return FALSE;
1829 }
1830
1831 /**
1832  * mono_gchandle_is_in_domain:
1833  * @gchandle: a GCHandle's handle.
1834  * @domain: An application domain.
1835  *
1836  * Use this function to determine if the @gchandle points to an
1837  * object allocated in the specified @domain.
1838  *
1839  * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1840  */
1841 gboolean
1842 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1843 {
1844         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1845         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1846         HandleData *handles = &gc_handles [type];
1847         gboolean result = FALSE;
1848
1849         if (type >= HANDLE_TYPE_MAX)
1850                 return FALSE;
1851
1852         lock_handles (handles);
1853         if (slot < handles->size && slot_occupied (handles, slot)) {
1854                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1855                         result = domain->domain_id == handles->domain_ids [slot];
1856                 } else {
1857                         MonoObject *obj;
1858                         obj = (MonoObject *)handles->entries [slot];
1859                         if (obj == NULL)
1860                                 result = TRUE;
1861                         else
1862                                 result = domain == mono_object_domain (obj);
1863                 }
1864         } else {
1865                 /* print a warning? */
1866         }
1867         unlock_handles (handles);
1868         return result;
1869 }
1870
1871 /**
1872  * mono_gchandle_free:
1873  * @gchandle: a GCHandle's handle.
1874  *
1875  * Frees the @gchandle handle.  If there are no outstanding
1876  * references, the garbage collector can reclaim the memory of the
1877  * object wrapped. 
1878  */
1879 void
1880 mono_gchandle_free (guint32 gchandle)
1881 {
1882         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1883         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1884         HandleData *handles = &gc_handles [type];
1885         if (type >= HANDLE_TYPE_MAX)
1886                 return;
1887
1888         lock_handles (handles);
1889         if (slot < handles->size && slot_occupied (handles, slot)) {
1890                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1891                         if (handles->entries [slot])
1892                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1893                 } else {
1894                         handles->entries [slot] = NULL;
1895                 }
1896                 vacate_slot (handles, slot);
1897         } else {
1898                 /* print a warning? */
1899         }
1900 #ifndef DISABLE_PERFCOUNTERS
1901         mono_perfcounters->gc_num_handles--;
1902 #endif
1903         /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1904         unlock_handles (handles);
1905         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1906 }
1907
1908 /**
1909  * mono_gchandle_free_domain:
1910  * @domain: domain that is unloading
1911  *
1912  * Function used internally to cleanup any GC handle for objects belonging
1913  * to the specified domain during appdomain unload.
1914  */
1915 void
1916 mono_gchandle_free_domain (MonoDomain *domain)
1917 {
1918         guint type;
1919
1920         for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1921                 guint slot;
1922                 HandleData *handles = &gc_handles [type];
1923                 lock_handles (handles);
1924                 for (slot = 0; slot < handles->size; ++slot) {
1925                         if (!slot_occupied (handles, slot))
1926                                 continue;
1927                         if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1928                                 if (domain->domain_id == handles->domain_ids [slot]) {
1929                                         vacate_slot (handles, slot);
1930                                         if (handles->entries [slot])
1931                                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1932                                 }
1933                         } else {
1934                                 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1935                                         vacate_slot (handles, slot);
1936                                         handles->entries [slot] = NULL;
1937                                 }
1938                         }
1939                 }
1940                 unlock_handles (handles);
1941         }
1942
1943 }
1944 #else
1945
1946 #ifdef _MSC_VER
1947 // Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty.
1948 void __mono_win32_boehm_gc_quiet_lnk4221(void) {}
1949 #endif
1950 #endif /* no Boehm GC */