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