[runtime] Remove compile time null gc dependencies from gc.c.
[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  */
8
9 #include "config.h"
10
11 #include <string.h>
12
13 #define GC_I_HIDE_POINTERS
14 #include <mono/metadata/gc-internal.h>
15 #include <mono/metadata/mono-gc.h>
16 #include <mono/metadata/profiler-private.h>
17 #include <mono/metadata/class-internals.h>
18 #include <mono/metadata/method-builder.h>
19 #include <mono/metadata/opcodes.h>
20 #include <mono/metadata/domain-internals.h>
21 #include <mono/metadata/metadata-internals.h>
22 #include <mono/metadata/marshal.h>
23 #include <mono/metadata/runtime.h>
24 #include <mono/metadata/sgen-toggleref.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-logger-internal.h>
27 #include <mono/utils/mono-memory-model.h>
28 #include <mono/utils/mono-time.h>
29 #include <mono/utils/mono-threads.h>
30 #include <mono/utils/dtrace.h>
31 #include <mono/utils/gc_wrapper.h>
32 #include <mono/utils/mono-mutex.h>
33 #include <mono/utils/mono-counters.h>
34
35 #if HAVE_BOEHM_GC
36
37 #undef TRUE
38 #undef FALSE
39 #define THREAD_LOCAL_ALLOC 1
40 #include "private/pthread_support.h"
41
42 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
43 void *pthread_get_stackaddr_np(pthread_t);
44 #endif
45
46 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
47 /*Boehm max heap cannot be smaller than 16MB*/
48 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
49 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
50
51 static gboolean gc_initialized = FALSE;
52 static mono_mutex_t mono_gc_lock;
53
54 static void*
55 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
56 static void
57 boehm_thread_unregister (MonoThreadInfo *p);
58 static void
59 register_test_toggleref_callback (void);
60
61 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
62 static MonoGCFinalizerCallbacks fin_callbacks;
63
64 static void
65 mono_gc_warning (char *msg, GC_word arg)
66 {
67         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
68 }
69
70 void
71 mono_gc_base_init (void)
72 {
73         MonoThreadInfoCallbacks cb;
74         const char *env;
75         int dummy;
76
77         if (gc_initialized)
78                 return;
79
80         mono_counters_init ();
81
82         /*
83          * Handle the case when we are called from a thread different from the main thread,
84          * confusing libgc.
85          * FIXME: Move this to libgc where it belongs.
86          *
87          * we used to do this only when running on valgrind,
88          * but it happens also in other setups.
89          */
90 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
91         {
92                 size_t size;
93                 void *sstart;
94                 pthread_attr_t attr;
95                 pthread_getattr_np (pthread_self (), &attr);
96                 pthread_attr_getstack (&attr, &sstart, &size);
97                 pthread_attr_destroy (&attr); 
98                 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
99 #ifdef __ia64__
100                 /*
101                  * The calculation above doesn't seem to work on ia64, also we need to set
102                  * GC_register_stackbottom as well, but don't know how.
103                  */
104 #else
105                 /* apparently with some linuxthreads implementations sstart can be NULL,
106                  * fallback to the more imprecise method (bug# 78096).
107                  */
108                 if (sstart) {
109                         GC_stackbottom = (char*)sstart + size;
110                 } else {
111                         int dummy;
112                         gsize stack_bottom = (gsize)&dummy;
113                         stack_bottom += 4095;
114                         stack_bottom &= ~4095;
115                         GC_stackbottom = (char*)stack_bottom;
116                 }
117 #endif
118         }
119 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
120                 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
121 #elif defined(__OpenBSD__)
122 #  include <pthread_np.h>
123         {
124                 stack_t ss;
125                 int rslt;
126
127                 rslt = pthread_stackseg_np(pthread_self(), &ss);
128                 g_assert (rslt == 0);
129
130                 GC_stackbottom = (char*)ss.ss_sp;
131         }
132 #elif defined(__native_client__)
133         /* Do nothing, GC_stackbottom is set correctly in libgc */
134 #else
135         {
136                 int dummy;
137                 gsize stack_bottom = (gsize)&dummy;
138                 stack_bottom += 4095;
139                 stack_bottom &= ~4095;
140                 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
141                 GC_stackbottom = (char*)stack_bottom;
142         }
143 #endif
144
145 #if !defined(PLATFORM_ANDROID)
146         /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
147         GC_no_dls = TRUE;
148 #endif
149         {
150                 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
151                         char **opts = g_strsplit (env, ",", -1);
152                         for (char **ptr = opts; ptr && *ptr; ptr ++) {
153                                 char *opt = *ptr;
154                                 if (!strcmp (opt, "do-not-finalize")) {
155                                         do_not_finalize = 1;
156                                 } else if (!strcmp (opt, "log-finalizers")) {
157                                         log_finalizers = 1;
158                                 }
159                         }
160                 }
161         }
162
163         GC_init ();
164
165         GC_oom_fn = mono_gc_out_of_memory;
166         GC_set_warn_proc (mono_gc_warning);
167         GC_finalize_on_demand = 1;
168         GC_finalizer_notifier = mono_gc_finalize_notify;
169
170         GC_init_gcj_malloc (5, NULL);
171
172         if ((env = g_getenv ("MONO_GC_PARAMS"))) {
173                 char **ptr, **opts = g_strsplit (env, ",", -1);
174                 for (ptr = opts; *ptr; ++ptr) {
175                         char *opt = *ptr;
176                         if (g_str_has_prefix (opt, "max-heap-size=")) {
177                                 size_t max_heap;
178
179                                 opt = strchr (opt, '=') + 1;
180                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
181                                         if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
182                                                 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
183                                                 exit (1);
184                                         }
185                                         GC_set_max_heap_size (max_heap);
186                                 } else {
187                                         fprintf (stderr, "max-heap-size must be an integer.\n");
188                                         exit (1);
189                                 }
190                                 continue;
191                         } else if (g_str_has_prefix (opt, "toggleref-test")) {
192                                 register_test_toggleref_callback ();
193                                 continue;
194                         } else {
195                                 /* Could be a parameter for sgen */
196                                 /*
197                                 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
198                                 fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
199                                 exit (1);
200                                 */
201                         }
202                 }
203                 g_strfreev (opts);
204         }
205
206         memset (&cb, 0, sizeof (cb));
207         cb.thread_register = boehm_thread_register;
208         cb.thread_unregister = boehm_thread_unregister;
209         cb.mono_method_is_critical = (gpointer)mono_runtime_is_critical_method;
210
211         mono_threads_init (&cb, sizeof (MonoThreadInfo));
212         mono_mutex_init (&mono_gc_lock);
213
214         mono_thread_info_attach (&dummy);
215
216         mono_gc_enable_events ();
217         gc_initialized = TRUE;
218 }
219
220 /**
221  * mono_gc_collect:
222  * @generation: GC generation identifier
223  *
224  * Perform a garbage collection for the given generation, higher numbers
225  * mean usually older objects. Collecting a high-numbered generation
226  * implies collecting also the lower-numbered generations.
227  * The maximum value for @generation can be retrieved with a call to
228  * mono_gc_max_generation(), so this function is usually called as:
229  *
230  *      mono_gc_collect (mono_gc_max_generation ());
231  */
232 void
233 mono_gc_collect (int generation)
234 {
235 #ifndef DISABLE_PERFCOUNTERS
236         mono_perfcounters->gc_induced++;
237 #endif
238         GC_gcollect ();
239 }
240
241 /**
242  * mono_gc_max_generation:
243  *
244  * Get the maximum generation number used by the current garbage
245  * collector. The value will be 0 for the Boehm collector, 1 or more
246  * for the generational collectors.
247  *
248  * Returns: the maximum generation number.
249  */
250 int
251 mono_gc_max_generation (void)
252 {
253         return 0;
254 }
255
256 /**
257  * mono_gc_get_generation:
258  * @object: a managed object
259  *
260  * Get the garbage collector's generation that @object belongs to.
261  * Use this has a hint only.
262  *
263  * Returns: a garbage collector generation number
264  */
265 int
266 mono_gc_get_generation  (MonoObject *object)
267 {
268         return 0;
269 }
270
271 /**
272  * mono_gc_collection_count:
273  * @generation: a GC generation number
274  *
275  * Get how many times a garbage collection has been performed
276  * for the given @generation number.
277  *
278  * Returns: the number of garbage collections
279  */
280 int
281 mono_gc_collection_count (int generation)
282 {
283         return GC_gc_no;
284 }
285
286 /**
287  * mono_gc_add_memory_pressure:
288  * @value: amount of bytes
289  *
290  * Adjust the garbage collector's view of how many bytes of memory
291  * are indirectly referenced by managed objects (for example unmanaged
292  * memory holding image or other binary data).
293  * This is a hint only to the garbage collector algorithm.
294  * Note that negative amounts of @value will decrease the memory
295  * pressure.
296  */
297 void
298 mono_gc_add_memory_pressure (gint64 value)
299 {
300 }
301
302 /**
303  * mono_gc_get_used_size:
304  *
305  * Get the approximate amount of memory used by managed objects.
306  *
307  * Returns: the amount of memory used in bytes
308  */
309 int64_t
310 mono_gc_get_used_size (void)
311 {
312         return GC_get_heap_size () - GC_get_free_bytes ();
313 }
314
315 /**
316  * mono_gc_get_heap_size:
317  *
318  * Get the amount of memory used by the garbage collector.
319  *
320  * Returns: the size of the heap in bytes
321  */
322 int64_t
323 mono_gc_get_heap_size (void)
324 {
325         return GC_get_heap_size ();
326 }
327
328 gboolean
329 mono_gc_is_gc_thread (void)
330 {
331         return GC_thread_is_registered ();
332 }
333
334 extern int GC_thread_register_foreign (void *base_addr);
335
336 gboolean
337 mono_gc_register_thread (void *baseptr)
338 {
339         return mono_thread_info_attach (baseptr) != NULL;
340 }
341
342 static void*
343 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
344 {
345         if (mono_gc_is_gc_thread())
346                 return info;
347 #if !defined(HOST_WIN32)
348         return GC_thread_register_foreign (baseptr) ? info : NULL;
349 #else
350         return NULL;
351 #endif
352 }
353
354 static void
355 boehm_thread_unregister (MonoThreadInfo *p)
356 {
357         MonoNativeThreadId tid;
358
359         tid = mono_thread_info_get_tid (p);
360
361         if (p->runtime_thread)
362                 mono_threads_add_joinable_thread ((gpointer)tid);
363 }
364
365 gboolean
366 mono_object_is_alive (MonoObject* o)
367 {
368         return GC_is_marked ((gpointer)o);
369 }
370
371 int
372 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
373 {
374         return 1;
375 }
376
377 static gint64 gc_start_time;
378
379 static void
380 on_gc_notification (GCEventType event)
381 {
382         MonoGCEvent e = (MonoGCEvent)event;
383
384         switch (e) {
385         case MONO_GC_EVENT_PRE_STOP_WORLD:
386                 MONO_GC_WORLD_STOP_BEGIN ();
387                 mono_thread_info_suspend_lock ();
388                 break;
389
390         case MONO_GC_EVENT_POST_STOP_WORLD:
391                 MONO_GC_WORLD_STOP_END ();
392                 break;
393
394         case MONO_GC_EVENT_PRE_START_WORLD:
395                 MONO_GC_WORLD_RESTART_BEGIN (1);
396                 break;
397
398         case MONO_GC_EVENT_POST_START_WORLD:
399                 MONO_GC_WORLD_RESTART_END (1);
400                 mono_thread_info_suspend_unlock ();
401                 break;
402
403         case MONO_GC_EVENT_START:
404                 MONO_GC_BEGIN (1);
405 #ifndef DISABLE_PERFCOUNTERS
406                 if (mono_perfcounters)
407                         mono_perfcounters->gc_collections0++;
408 #endif
409                 gc_stats.major_gc_count ++;
410                 gc_start_time = mono_100ns_ticks ();
411                 break;
412
413         case MONO_GC_EVENT_END:
414                 MONO_GC_END (1);
415 #if defined(ENABLE_DTRACE) && defined(__sun__)
416                 /* This works around a dtrace -G problem on Solaris.
417                    Limit its actual use to when the probe is enabled. */
418                 if (MONO_GC_END_ENABLED ())
419                         sleep(0);
420 #endif
421
422 #ifndef DISABLE_PERFCOUNTERS
423                 if (mono_perfcounters) {
424                         guint64 heap_size = GC_get_heap_size ();
425                         guint64 used_size = heap_size - GC_get_free_bytes ();
426                         mono_perfcounters->gc_total_bytes = used_size;
427                         mono_perfcounters->gc_committed_bytes = heap_size;
428                         mono_perfcounters->gc_reserved_bytes = heap_size;
429                         mono_perfcounters->gc_gen0size = heap_size;
430                 }
431 #endif
432                 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
433                 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
434                 break;
435         default:
436                 break;
437         }
438
439         mono_profiler_gc_event (e, 0);
440 }
441  
442 static void
443 on_gc_heap_resize (size_t new_size)
444 {
445         guint64 heap_size = GC_get_heap_size ();
446 #ifndef DISABLE_PERFCOUNTERS
447         if (mono_perfcounters) {
448                 mono_perfcounters->gc_committed_bytes = heap_size;
449                 mono_perfcounters->gc_reserved_bytes = heap_size;
450                 mono_perfcounters->gc_gen0size = heap_size;
451         }
452 #endif
453         mono_profiler_gc_heap_resize (new_size);
454 }
455
456 void
457 mono_gc_enable_events (void)
458 {
459         GC_notify_event = on_gc_notification;
460         GC_on_heap_resize = on_gc_heap_resize;
461 }
462
463 static gboolean alloc_events = FALSE;
464
465 void
466 mono_gc_enable_alloc_events (void)
467 {
468         alloc_events = TRUE;
469 }
470
471 int
472 mono_gc_register_root (char *start, size_t size, void *descr)
473 {
474         /* for some strange reason, they want one extra byte on the end */
475         GC_add_roots (start, start + size + 1);
476
477         return TRUE;
478 }
479
480 void
481 mono_gc_deregister_root (char* addr)
482 {
483 #ifndef HOST_WIN32
484         /* FIXME: libgc doesn't define this work win32 for some reason */
485         /* FIXME: No size info */
486         GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
487 #endif
488 }
489
490 void
491 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
492 {
493         /* libgc requires that we use HIDE_POINTER... */
494         *link_addr = (void*)HIDE_POINTER (obj);
495         if (track)
496                 GC_REGISTER_LONG_LINK (link_addr, obj);
497         else
498                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
499 }
500
501 void
502 mono_gc_weak_link_remove (void **link_addr, gboolean track)
503 {
504         if (track)
505                 GC_unregister_long_link (link_addr);
506         else
507                 GC_unregister_disappearing_link (link_addr);
508         *link_addr = NULL;
509 }
510
511 static gpointer
512 reveal_link (gpointer link_addr)
513 {
514         void **link_a = link_addr;
515         return REVEAL_POINTER (*link_a);
516 }
517
518 MonoObject*
519 mono_gc_weak_link_get (void **link_addr)
520 {
521         MonoObject *obj = GC_call_with_alloc_lock (reveal_link, link_addr);
522         if (obj == (MonoObject *) -1)
523                 return NULL;
524         return obj;
525 }
526
527 void*
528 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
529 {
530         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
531 }
532
533 void*
534 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
535 {
536         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
537 }
538
539 void*
540 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
541 {
542         /* libgc has no usable support for arrays... */
543         return GC_NO_DESCRIPTOR;
544 }
545
546 void*
547 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
548 {
549         /* It seems there are issues when the bitmap doesn't fit: play it safe */
550         if (numbits >= 30)
551                 return GC_NO_DESCRIPTOR;
552         else
553                 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
554 }
555
556 void*
557 mono_gc_make_root_descr_all_refs (int numbits)
558 {
559         return NULL;
560 }
561
562 void*
563 mono_gc_alloc_fixed (size_t size, void *descr)
564 {
565         /* To help track down typed allocation bugs */
566         /*
567         static int count;
568         count ++;
569         if (count == atoi (g_getenv ("COUNT2")))
570                 printf ("HIT!\n");
571         if (count > atoi (g_getenv ("COUNT2")))
572                 return GC_MALLOC (size);
573         */
574
575         if (descr)
576                 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
577         else
578                 return GC_MALLOC (size);
579 }
580
581 void
582 mono_gc_free_fixed (void* addr)
583 {
584 }
585
586 void *
587 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
588 {
589         MonoObject *obj;
590
591         if (!vtable->klass->has_references) {
592                 obj = GC_MALLOC_ATOMIC (size);
593
594                 obj->vtable = vtable;
595                 obj->synchronisation = NULL;
596
597                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
598         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
599                 obj = GC_GCJ_MALLOC (size, vtable);
600         } else {
601                 obj = GC_MALLOC (size);
602
603                 obj->vtable = vtable;
604         }
605
606         if (G_UNLIKELY (alloc_events))
607                 mono_profiler_allocation (obj);
608
609         return obj;
610 }
611
612 void *
613 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
614 {
615         MonoArray *obj;
616
617         if (!vtable->klass->has_references) {
618                 obj = GC_MALLOC_ATOMIC (size);
619
620                 obj->obj.vtable = vtable;
621                 obj->obj.synchronisation = NULL;
622
623                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
624         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
625                 obj = GC_GCJ_MALLOC (size, vtable);
626         } else {
627                 obj = GC_MALLOC (size);
628
629                 obj->obj.vtable = vtable;
630         }
631
632         obj->max_length = max_length;
633
634         if (G_UNLIKELY (alloc_events))
635                 mono_profiler_allocation (&obj->obj);
636
637         return obj;
638 }
639
640 void *
641 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
642 {
643         MonoArray *obj;
644
645         if (!vtable->klass->has_references) {
646                 obj = GC_MALLOC_ATOMIC (size);
647
648                 obj->obj.vtable = vtable;
649                 obj->obj.synchronisation = NULL;
650
651                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
652         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
653                 obj = GC_GCJ_MALLOC (size, vtable);
654         } else {
655                 obj = GC_MALLOC (size);
656
657                 obj->obj.vtable = vtable;
658         }
659
660         obj->max_length = max_length;
661
662         if (bounds_size)
663                 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
664
665         if (G_UNLIKELY (alloc_events))
666                 mono_profiler_allocation (&obj->obj);
667
668         return obj;
669 }
670
671 void *
672 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
673 {
674         MonoString *obj = GC_MALLOC_ATOMIC (size);
675
676         obj->object.vtable = vtable;
677         obj->object.synchronisation = NULL;
678         obj->length = len;
679         obj->chars [len] = 0;
680
681         if (G_UNLIKELY (alloc_events))
682                 mono_profiler_allocation (&obj->object);
683
684         return obj;
685 }
686
687 int
688 mono_gc_invoke_finalizers (void)
689 {
690         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
691          * the 'mem_freed' variable is not initialized when there are no
692          * objects to finalize, which leads to strange behavior later on.
693          * The check is necessary to work around that bug.
694          */
695         if (GC_should_invoke_finalizers ())
696                 return GC_invoke_finalizers ();
697         return 0;
698 }
699
700 gboolean
701 mono_gc_pending_finalizers (void)
702 {
703         return GC_should_invoke_finalizers ();
704 }
705
706 void
707 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
708 {
709         *(void**)field_ptr = value;
710 }
711
712 void
713 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
714 {
715         *(void**)slot_ptr = value;
716 }
717
718 void
719 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
720 {
721         mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
722 }
723
724 void
725 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
726 {
727         *(void**)ptr = value;
728 }
729
730 void
731 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
732 {
733         InterlockedWritePointer (ptr, value);
734 }
735
736 void
737 mono_gc_wbarrier_generic_nostore (gpointer ptr)
738 {
739 }
740
741 void
742 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
743 {
744         mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
745 }
746
747 void
748 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
749 {
750         /* do not copy the sync state */
751         mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
752                         mono_object_class (obj)->instance_size - sizeof (MonoObject));
753 }
754
755 void
756 mono_gc_clear_domain (MonoDomain *domain)
757 {
758 }
759
760 int
761 mono_gc_get_suspend_signal (void)
762 {
763         return GC_get_suspend_signal ();
764 }
765
766 int
767 mono_gc_get_restart_signal (void)
768 {
769         return GC_get_restart_signal ();
770 }
771
772 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
773 extern __thread MONO_TLS_FAST void* GC_thread_tls;
774 #include "metadata-internals.h"
775
776 static int
777 shift_amount (int v)
778 {
779         int i = 0;
780         while (!(v & (1 << i)))
781                 i++;
782         return i;
783 }
784
785 enum {
786         ATYPE_FREEPTR,
787         ATYPE_FREEPTR_FOR_BOX,
788         ATYPE_NORMAL,
789         ATYPE_GCJ,
790         ATYPE_STRING,
791         ATYPE_NUM
792 };
793
794 static MonoMethod*
795 create_allocator (int atype, int tls_key, gboolean slowpath)
796 {
797         int index_var, bytes_var, my_fl_var, my_entry_var;
798         guint32 no_freelist_branch, not_small_enough_branch = 0;
799         guint32 size_overflow_branch = 0;
800         MonoMethodBuilder *mb;
801         MonoMethod *res;
802         MonoMethodSignature *csig;
803         const char *name = NULL;
804         AllocatorWrapperInfo *info;
805
806         if (atype == ATYPE_FREEPTR) {
807                 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
808         } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
809                 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
810         } else if (atype == ATYPE_NORMAL) {
811                 name = slowpath ? "SlowAlloc" : "Alloc";
812         } else if (atype == ATYPE_GCJ) {
813                 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
814         } else if (atype == ATYPE_STRING) {
815                 name = slowpath ? "SlowAllocString" : "AllocString";
816         } else {
817                 g_assert_not_reached ();
818         }
819
820         csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
821
822         if (atype == ATYPE_STRING) {
823                 csig->ret = &mono_defaults.string_class->byval_arg;
824                 csig->params [0] = &mono_defaults.int_class->byval_arg;
825                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
826         } else {
827                 csig->ret = &mono_defaults.object_class->byval_arg;
828                 csig->params [0] = &mono_defaults.int_class->byval_arg;
829                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
830         }
831
832         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
833
834         if (slowpath)
835                 goto always_slowpath;
836
837         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
838         if (atype == ATYPE_STRING) {
839                 /* a string alloator method takes the args: (vtable, len) */
840                 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
841                 mono_mb_emit_ldarg (mb, 1);
842                 mono_mb_emit_icon (mb, 1);
843                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
844                 mono_mb_emit_icon (mb, 1);
845                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
846                 // sizeof (MonoString) might include padding
847                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
848                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
849                 mono_mb_emit_stloc (mb, bytes_var);
850         } else {
851                 mono_mb_emit_ldarg (mb, 1);
852                 mono_mb_emit_stloc (mb, bytes_var);
853         }
854
855         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
856         if (atype == ATYPE_STRING) {
857                 /* check for size */
858                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
859                 mono_mb_emit_ldloc (mb, bytes_var);
860                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
861                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
862                 /* check for overflow */
863                 mono_mb_emit_ldloc (mb, bytes_var);
864                 mono_mb_emit_icon (mb, sizeof (MonoString));
865                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
866         }
867
868         /* int index = INDEX_FROM_BYTES(bytes); */
869         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
870         
871         mono_mb_emit_ldloc (mb, bytes_var);
872         mono_mb_emit_icon (mb, GRANULARITY - 1);
873         mono_mb_emit_byte (mb, MONO_CEE_ADD);
874         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
875         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
876         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
877         mono_mb_emit_byte (mb, MONO_CEE_SHL);
878         /* index var is already adjusted into bytes */
879         mono_mb_emit_stloc (mb, index_var);
880
881         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
882         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
883         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
884         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
885         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
886         mono_mb_emit_i4 (mb, tls_key);
887         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
888                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
889         else if (atype == ATYPE_NORMAL)
890                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
891         else if (atype == ATYPE_GCJ)
892                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
893         else
894                 g_assert_not_reached ();
895         mono_mb_emit_byte (mb, MONO_CEE_ADD);
896         mono_mb_emit_ldloc (mb, index_var);
897         mono_mb_emit_byte (mb, MONO_CEE_ADD);
898         mono_mb_emit_stloc (mb, my_fl_var);
899
900         /* my_entry = *my_fl; */
901         mono_mb_emit_ldloc (mb, my_fl_var);
902         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
903         mono_mb_emit_stloc (mb, my_entry_var);
904
905         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
906         mono_mb_emit_ldloc (mb, my_entry_var);
907         mono_mb_emit_icon (mb, HBLKSIZE);
908         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
909
910         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
911         mono_mb_emit_ldloc (mb, my_fl_var);
912         mono_mb_emit_ldloc (mb, my_entry_var);
913         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
914         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
915
916         /* set the vtable and clear the words in the object */
917         mono_mb_emit_ldloc (mb, my_entry_var);
918         mono_mb_emit_ldarg (mb, 0);
919         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
920
921         if (atype == ATYPE_FREEPTR) {
922                 int start_var, end_var, start_loop;
923                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
924                  */
925                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
926                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
927                 mono_mb_emit_ldloc (mb, my_entry_var);
928                 mono_mb_emit_ldloc (mb, bytes_var);
929                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
930                 mono_mb_emit_stloc (mb, end_var);
931                 mono_mb_emit_ldloc (mb, my_entry_var);
932                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
933                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
934                 mono_mb_emit_stloc (mb, start_var);
935                 /*
936                  * do {
937                  *      *start++ = NULL;
938                  * } while (start < end);
939                  */
940                 start_loop = mono_mb_get_label (mb);
941                 mono_mb_emit_ldloc (mb, start_var);
942                 mono_mb_emit_icon (mb, 0);
943                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
944                 mono_mb_emit_ldloc (mb, start_var);
945                 mono_mb_emit_icon (mb, sizeof (gpointer));
946                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
947                 mono_mb_emit_stloc (mb, start_var);
948
949                 mono_mb_emit_ldloc (mb, start_var);
950                 mono_mb_emit_ldloc (mb, end_var);
951                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
952                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
953         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
954                 /* need to clear just the sync pointer */
955                 mono_mb_emit_ldloc (mb, my_entry_var);
956                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
957                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
958                 mono_mb_emit_icon (mb, 0);
959                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
960         }
961
962         if (atype == ATYPE_STRING) {
963                 /* need to set length and clear the last char */
964                 /* s->length = len; */
965                 mono_mb_emit_ldloc (mb, my_entry_var);
966                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
967                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
968                 mono_mb_emit_ldarg (mb, 1);
969                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
970                 /* s->chars [len] = 0; */
971                 mono_mb_emit_ldloc (mb, my_entry_var);
972                 mono_mb_emit_ldloc (mb, bytes_var);
973                 mono_mb_emit_icon (mb, 2);
974                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
975                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
976                 mono_mb_emit_icon (mb, 0);
977                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
978         }
979
980         /* return my_entry; */
981         mono_mb_emit_ldloc (mb, my_entry_var);
982         mono_mb_emit_byte (mb, MONO_CEE_RET);
983         
984         mono_mb_patch_short_branch (mb, no_freelist_branch);
985         if (not_small_enough_branch > 0)
986                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
987         if (size_overflow_branch > 0)
988                 mono_mb_patch_short_branch (mb, size_overflow_branch);
989
990         /* the slow path: we just call back into the runtime */
991  always_slowpath:
992         if (atype == ATYPE_STRING) {
993                 mono_mb_emit_ldarg (mb, 1);
994                 mono_mb_emit_icall (mb, mono_string_alloc);
995         } else {
996                 mono_mb_emit_ldarg (mb, 0);
997                 mono_mb_emit_icall (mb, mono_object_new_specific);
998         }
999
1000         mono_mb_emit_byte (mb, MONO_CEE_RET);
1001
1002         res = mono_mb_create_method (mb, csig, 8);
1003         mono_mb_free (mb);
1004         mono_method_get_header (res)->init_locals = FALSE;
1005
1006         info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
1007         info->gc_name = "boehm";
1008         info->alloc_type = atype;
1009         mono_marshal_set_wrapper_info (res, info);
1010
1011         return res;
1012 }
1013
1014 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1015 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1016
1017 static G_GNUC_UNUSED gboolean
1018 mono_gc_is_critical_method (MonoMethod *method)
1019 {
1020         int i;
1021
1022         for (i = 0; i < ATYPE_NUM; ++i)
1023                 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1024                         return TRUE;
1025
1026         return FALSE;
1027 }
1028
1029 /*
1030  * If possible, generate a managed method that can quickly allocate objects in class
1031  * @klass. The method will typically have an thread-local inline allocation sequence.
1032  * The signature of the called method is:
1033  *      object allocate (MonoVTable *vtable)
1034  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1035  * keep in sync.
1036  * The thread local alloc logic is taken from libgc/pthread_support.c.
1037  */
1038
1039 MonoMethod*
1040 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1041 {
1042         int offset = -1;
1043         int atype;
1044         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1045
1046         /*g_print ("thread tls: %d\n", offset);*/
1047         if (offset == -1)
1048                 return NULL;
1049         if (!SMALL_ENOUGH (klass->instance_size))
1050                 return NULL;
1051         if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
1052                 return NULL;
1053         if (klass->rank)
1054                 return NULL;
1055         if (mono_class_is_open_constructed_type (&klass->byval_arg))
1056                 return NULL;
1057         if (klass->byval_arg.type == MONO_TYPE_STRING) {
1058                 atype = ATYPE_STRING;
1059         } else if (!klass->has_references) {
1060                 if (for_box)
1061                         atype = ATYPE_FREEPTR_FOR_BOX;
1062                 else
1063                         atype = ATYPE_FREEPTR;
1064         } else {
1065                 return NULL;
1066                 /*
1067                  * disabled because we currently do a runtime choice anyway, to
1068                  * deal with multiple appdomains.
1069                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1070                         atype = ATYPE_GCJ;
1071                 else
1072                         atype = ATYPE_NORMAL;
1073                 */
1074         }
1075         return mono_gc_get_managed_allocator_by_type (atype, FALSE);
1076 }
1077
1078 MonoMethod*
1079 mono_gc_get_managed_array_allocator (MonoClass *klass)
1080 {
1081         return NULL;
1082 }
1083
1084 /**
1085  * mono_gc_get_managed_allocator_by_type:
1086  *
1087  *   Return a managed allocator method corresponding to allocator type ATYPE.
1088  */
1089 MonoMethod*
1090 mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
1091 {
1092         int offset = -1;
1093         MonoMethod *res;
1094         MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1095         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1096
1097         mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1098
1099         res = cache [atype];
1100         if (res)
1101                 return res;
1102
1103         res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1104         mono_mutex_lock (&mono_gc_lock);
1105         if (cache [atype]) {
1106                 mono_free_method (res);
1107                 res = cache [atype];
1108         } else {
1109                 mono_memory_barrier ();
1110                 cache [atype] = res;
1111         }
1112         mono_mutex_unlock (&mono_gc_lock);
1113         return res;
1114 }
1115
1116 guint32
1117 mono_gc_get_managed_allocator_types (void)
1118 {
1119         return ATYPE_NUM;
1120 }
1121
1122 MonoMethod*
1123 mono_gc_get_write_barrier (void)
1124 {
1125         g_assert_not_reached ();
1126         return NULL;
1127 }
1128
1129 #else
1130
1131 static G_GNUC_UNUSED gboolean
1132 mono_gc_is_critical_method (MonoMethod *method)
1133 {
1134         return FALSE;
1135 }
1136
1137 MonoMethod*
1138 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1139 {
1140         return NULL;
1141 }
1142
1143 MonoMethod*
1144 mono_gc_get_managed_array_allocator (MonoClass *klass)
1145 {
1146         return NULL;
1147 }
1148
1149 MonoMethod*
1150 mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
1151 {
1152         return NULL;
1153 }
1154
1155 guint32
1156 mono_gc_get_managed_allocator_types (void)
1157 {
1158         return 0;
1159 }
1160
1161 MonoMethod*
1162 mono_gc_get_write_barrier (void)
1163 {
1164         g_assert_not_reached ();
1165         return NULL;
1166 }
1167
1168 #endif
1169
1170 MonoMethod*
1171 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1172 {
1173         g_assert_not_reached ();
1174         return NULL;
1175 }
1176
1177 int
1178 mono_gc_get_aligned_size_for_allocator (int size)
1179 {
1180         return size;
1181 }
1182
1183 const char *
1184 mono_gc_get_gc_name (void)
1185 {
1186         return "boehm";
1187 }
1188
1189 void*
1190 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1191 {
1192         return GC_call_with_alloc_lock (func, data);
1193 }
1194
1195 char*
1196 mono_gc_get_description (void)
1197 {
1198         return g_strdup (DEFAULT_GC_NAME);
1199 }
1200
1201 void
1202 mono_gc_set_desktop_mode (void)
1203 {
1204         GC_dont_expand = 1;
1205 }
1206
1207 gboolean
1208 mono_gc_is_moving (void)
1209 {
1210         return FALSE;
1211 }
1212
1213 gboolean
1214 mono_gc_is_disabled (void)
1215 {
1216         if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1217                 return TRUE;
1218         else
1219                 return FALSE;
1220 }
1221
1222 void
1223 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1224 {
1225         g_assert_not_reached ();
1226 }
1227
1228
1229 guint8*
1230 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1231 {
1232         g_assert_not_reached ();
1233         return NULL;
1234 }
1235
1236 gboolean
1237 mono_gc_card_table_nursery_check (void)
1238 {
1239         g_assert_not_reached ();
1240         return TRUE;
1241 }
1242
1243 void*
1244 mono_gc_get_nursery (int *shift_bits, size_t *size)
1245 {
1246         return NULL;
1247 }
1248
1249 void
1250 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1251 {
1252 }
1253
1254 gboolean
1255 mono_gc_precise_stack_mark_enabled (void)
1256 {
1257         return FALSE;
1258 }
1259
1260 FILE *
1261 mono_gc_get_logfile (void)
1262 {
1263         return NULL;
1264 }
1265
1266 void
1267 mono_gc_conservatively_scan_area (void *start, void *end)
1268 {
1269         g_assert_not_reached ();
1270 }
1271
1272 void *
1273 mono_gc_scan_object (void *obj, void *gc_data)
1274 {
1275         g_assert_not_reached ();
1276         return NULL;
1277 }
1278
1279 gsize*
1280 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1281 {
1282         g_assert_not_reached ();
1283         return NULL;
1284 }
1285
1286 void
1287 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1288 {
1289 }
1290
1291 void
1292 mono_gc_set_stack_end (void *stack_end)
1293 {
1294 }
1295
1296 void mono_gc_set_skip_thread (gboolean value)
1297 {
1298 }
1299
1300 void
1301 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1302 {
1303         guint offset = 0;
1304
1305 #ifndef GC_DEBUG
1306         /* This assertion is not valid when GC_DEBUG is defined */
1307         g_assert (GC_base (obj) == (char*)obj - offset);
1308 #endif
1309
1310         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1311 }
1312
1313 #ifndef HOST_WIN32
1314 int
1315 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1316 {
1317         /* it is being replaced by GC_pthread_create on some
1318          * platforms, see libgc/include/gc_pthread_redirects.h */
1319         return pthread_create (new_thread, attr, start_routine, arg);
1320 }
1321 #endif
1322
1323 #ifdef HOST_WIN32
1324 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1325 {
1326         return GC_DllMain (module_handle, reason, reserved);
1327 }
1328 #endif
1329
1330 guint
1331 mono_gc_get_vtable_bits (MonoClass *class)
1332 {
1333         if (fin_callbacks.is_class_finalization_aware) {
1334                 if (fin_callbacks.is_class_finalization_aware (class))
1335                         return BOEHM_GC_BIT_FINALIZER_AWARE;
1336         }
1337         return 0;
1338 }
1339
1340 /*
1341  * mono_gc_register_altstack:
1342  *
1343  *   Register the dimensions of the normal stack and altstack with the collector.
1344  * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1345  */
1346 void
1347 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1348 {
1349         GC_register_altstack (stack, stack_size, altstack, altstack_size);
1350 }
1351
1352 int
1353 mono_gc_get_los_limit (void)
1354 {
1355         return G_MAXINT;
1356 }
1357
1358 void
1359 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1360 {
1361         mono_unichar2 *new_end = str->chars + new_length;
1362         
1363         /* zero the discarded string. This null-delimits the string and allows 
1364          * the space to be reclaimed by SGen. */
1365          
1366         memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1367         str->length = new_length;
1368 }
1369
1370 gboolean
1371 mono_gc_user_markers_supported (void)
1372 {
1373         return FALSE;
1374 }
1375
1376 void *
1377 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1378 {
1379         g_assert_not_reached ();
1380         return NULL;
1381 }
1382
1383 gboolean
1384 mono_gc_set_allow_synchronous_major (gboolean flag)
1385 {
1386         return flag;
1387 }
1388 /* Toggleref support */
1389
1390 void
1391 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1392 {
1393         GC_toggleref_add ((GC_PTR)object, (int)strong_ref);
1394 }
1395
1396 void
1397 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1398 {
1399         GC_toggleref_register_callback ((int (*) (GC_PTR obj)) proccess_toggleref);
1400 }
1401
1402 /* Test support code */
1403
1404 static MonoToggleRefStatus
1405 test_toggleref_callback (MonoObject *obj)
1406 {
1407         static MonoClassField *mono_toggleref_test_field;
1408         int status = MONO_TOGGLE_REF_DROP;
1409
1410         if (!mono_toggleref_test_field) {
1411                 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1412                 g_assert (mono_toggleref_test_field);
1413         }
1414
1415         mono_field_get_value (obj, mono_toggleref_test_field, &status);
1416         printf ("toggleref-cb obj %d\n", status);
1417         return status;
1418 }
1419
1420 static void
1421 register_test_toggleref_callback (void)
1422 {
1423         mono_gc_toggleref_register_callback (test_toggleref_callback);
1424 }
1425
1426 static gboolean
1427 is_finalization_aware (MonoObject *obj)
1428 {
1429         MonoVTable *vt = obj->vtable;
1430         return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1431 }
1432
1433 static void
1434 fin_notifier (MonoObject *obj)
1435 {
1436         if (is_finalization_aware (obj))
1437                 fin_callbacks.object_queued_for_finalization (obj);
1438 }
1439
1440 void
1441 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1442 {
1443         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1444                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1445
1446         fin_callbacks = *callbacks;
1447
1448         GC_set_finalizer_notify_proc ((void (*) (GC_PTR))fin_notifier);
1449 }
1450
1451 gboolean
1452 mono_gc_is_null (void)
1453 {
1454         return FALSE;
1455 }
1456
1457 #endif /* no Boehm GC */