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