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