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