Merge pull request #2223 from lobrien/master
[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-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_mutex_lock (&handle_section)
68 #define unlock_handles(handles) mono_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_oom_fn = mono_gc_out_of_memory;
194         GC_set_warn_proc (mono_gc_warning);
195         GC_finalize_on_demand = 1;
196         GC_finalizer_notifier = mono_gc_finalize_notify;
197
198         GC_init_gcj_malloc (5, NULL);
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 = (gpointer)mono_runtime_is_critical_method;
238
239         mono_threads_init (&cb, sizeof (MonoThreadInfo));
240         mono_mutex_init (&mono_gc_lock);
241         mono_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 extern int GC_thread_register_foreign (void *base_addr);
374
375 gboolean
376 mono_gc_register_thread (void *baseptr)
377 {
378         return mono_thread_info_attach (baseptr) != NULL;
379 }
380
381 static void*
382 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
383 {
384         if (mono_gc_is_gc_thread())
385                 return info;
386 #if !defined(HOST_WIN32)
387         return GC_thread_register_foreign (baseptr) ? info : NULL;
388 #else
389         return NULL;
390 #endif
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 ((gpointer)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 (GCEventType 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_notify_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 = 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 = 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 = GC_MALLOC_ATOMIC (size);
632
633                 obj->vtable = vtable;
634                 obj->synchronisation = NULL;
635
636                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
637         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
638                 obj = GC_GCJ_MALLOC (size, vtable);
639         } else {
640                 obj = GC_MALLOC (size);
641
642                 obj->vtable = vtable;
643         }
644
645         if (G_UNLIKELY (alloc_events))
646                 mono_profiler_allocation (obj);
647
648         return obj;
649 }
650
651 void *
652 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
653 {
654         MonoArray *obj;
655
656         if (!vtable->klass->has_references) {
657                 obj = GC_MALLOC_ATOMIC (size);
658
659                 obj->obj.vtable = vtable;
660                 obj->obj.synchronisation = NULL;
661
662                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
663         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
664                 obj = GC_GCJ_MALLOC (size, vtable);
665         } else {
666                 obj = GC_MALLOC (size);
667
668                 obj->obj.vtable = vtable;
669         }
670
671         obj->max_length = max_length;
672
673         if (G_UNLIKELY (alloc_events))
674                 mono_profiler_allocation (&obj->obj);
675
676         return obj;
677 }
678
679 void *
680 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
681 {
682         MonoArray *obj;
683
684         if (!vtable->klass->has_references) {
685                 obj = GC_MALLOC_ATOMIC (size);
686
687                 obj->obj.vtable = vtable;
688                 obj->obj.synchronisation = NULL;
689
690                 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
691         } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
692                 obj = GC_GCJ_MALLOC (size, vtable);
693         } else {
694                 obj = GC_MALLOC (size);
695
696                 obj->obj.vtable = vtable;
697         }
698
699         obj->max_length = max_length;
700
701         if (bounds_size)
702                 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
703
704         if (G_UNLIKELY (alloc_events))
705                 mono_profiler_allocation (&obj->obj);
706
707         return obj;
708 }
709
710 void *
711 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
712 {
713         MonoString *obj = GC_MALLOC_ATOMIC (size);
714
715         obj->object.vtable = vtable;
716         obj->object.synchronisation = NULL;
717         obj->length = len;
718         obj->chars [len] = 0;
719
720         if (G_UNLIKELY (alloc_events))
721                 mono_profiler_allocation (&obj->object);
722
723         return obj;
724 }
725
726 int
727 mono_gc_invoke_finalizers (void)
728 {
729         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
730          * the 'mem_freed' variable is not initialized when there are no
731          * objects to finalize, which leads to strange behavior later on.
732          * The check is necessary to work around that bug.
733          */
734         if (GC_should_invoke_finalizers ())
735                 return GC_invoke_finalizers ();
736         return 0;
737 }
738
739 gboolean
740 mono_gc_pending_finalizers (void)
741 {
742         return GC_should_invoke_finalizers ();
743 }
744
745 void
746 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
747 {
748         *(void**)field_ptr = value;
749 }
750
751 void
752 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
753 {
754         *(void**)slot_ptr = value;
755 }
756
757 void
758 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
759 {
760         mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
761 }
762
763 void
764 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
765 {
766         *(void**)ptr = value;
767 }
768
769 void
770 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
771 {
772         InterlockedWritePointer (ptr, value);
773 }
774
775 void
776 mono_gc_wbarrier_generic_nostore (gpointer ptr)
777 {
778 }
779
780 void
781 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
782 {
783         mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
784 }
785
786 void
787 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
788 {
789         /* do not copy the sync state */
790         mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
791                         mono_object_class (obj)->instance_size - sizeof (MonoObject));
792 }
793
794 void
795 mono_gc_clear_domain (MonoDomain *domain)
796 {
797 }
798
799 int
800 mono_gc_get_suspend_signal (void)
801 {
802         return GC_get_suspend_signal ();
803 }
804
805 int
806 mono_gc_get_restart_signal (void)
807 {
808         return GC_get_restart_signal ();
809 }
810
811 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
812 extern __thread MONO_TLS_FAST void* GC_thread_tls;
813 #include "metadata-internals.h"
814
815 static int
816 shift_amount (int v)
817 {
818         int i = 0;
819         while (!(v & (1 << i)))
820                 i++;
821         return i;
822 }
823
824 enum {
825         ATYPE_FREEPTR,
826         ATYPE_FREEPTR_FOR_BOX,
827         ATYPE_NORMAL,
828         ATYPE_GCJ,
829         ATYPE_STRING,
830         ATYPE_NUM
831 };
832
833 static MonoMethod*
834 create_allocator (int atype, int tls_key, gboolean slowpath)
835 {
836         int index_var, bytes_var, my_fl_var, my_entry_var;
837         guint32 no_freelist_branch, not_small_enough_branch = 0;
838         guint32 size_overflow_branch = 0;
839         MonoMethodBuilder *mb;
840         MonoMethod *res;
841         MonoMethodSignature *csig;
842         const char *name = NULL;
843         WrapperInfo *info;
844
845         if (atype == ATYPE_FREEPTR) {
846                 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
847         } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
848                 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
849         } else if (atype == ATYPE_NORMAL) {
850                 name = slowpath ? "SlowAlloc" : "Alloc";
851         } else if (atype == ATYPE_GCJ) {
852                 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
853         } else if (atype == ATYPE_STRING) {
854                 name = slowpath ? "SlowAllocString" : "AllocString";
855         } else {
856                 g_assert_not_reached ();
857         }
858
859         csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
860
861         if (atype == ATYPE_STRING) {
862                 csig->ret = &mono_defaults.string_class->byval_arg;
863                 csig->params [0] = &mono_defaults.int_class->byval_arg;
864                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
865         } else {
866                 csig->ret = &mono_defaults.object_class->byval_arg;
867                 csig->params [0] = &mono_defaults.int_class->byval_arg;
868                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
869         }
870
871         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
872
873         if (slowpath)
874                 goto always_slowpath;
875
876         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
877         if (atype == ATYPE_STRING) {
878                 /* a string alloator method takes the args: (vtable, len) */
879                 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
880                 mono_mb_emit_ldarg (mb, 1);
881                 mono_mb_emit_icon (mb, 1);
882                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
883                 mono_mb_emit_icon (mb, 1);
884                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
885                 // sizeof (MonoString) might include padding
886                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
887                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
888                 mono_mb_emit_stloc (mb, bytes_var);
889         } else {
890                 mono_mb_emit_ldarg (mb, 1);
891                 mono_mb_emit_stloc (mb, bytes_var);
892         }
893
894         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
895         if (atype == ATYPE_STRING) {
896                 /* check for size */
897                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
898                 mono_mb_emit_ldloc (mb, bytes_var);
899                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
900                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
901                 /* check for overflow */
902                 mono_mb_emit_ldloc (mb, bytes_var);
903                 mono_mb_emit_icon (mb, sizeof (MonoString));
904                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
905         }
906
907         /* int index = INDEX_FROM_BYTES(bytes); */
908         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
909         
910         mono_mb_emit_ldloc (mb, bytes_var);
911         mono_mb_emit_icon (mb, GRANULARITY - 1);
912         mono_mb_emit_byte (mb, MONO_CEE_ADD);
913         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
914         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
915         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
916         mono_mb_emit_byte (mb, MONO_CEE_SHL);
917         /* index var is already adjusted into bytes */
918         mono_mb_emit_stloc (mb, index_var);
919
920         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
921         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
922         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
923         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
924         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
925         mono_mb_emit_i4 (mb, tls_key);
926         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
927                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
928         else if (atype == ATYPE_NORMAL)
929                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
930         else if (atype == ATYPE_GCJ)
931                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
932         else
933                 g_assert_not_reached ();
934         mono_mb_emit_byte (mb, MONO_CEE_ADD);
935         mono_mb_emit_ldloc (mb, index_var);
936         mono_mb_emit_byte (mb, MONO_CEE_ADD);
937         mono_mb_emit_stloc (mb, my_fl_var);
938
939         /* my_entry = *my_fl; */
940         mono_mb_emit_ldloc (mb, my_fl_var);
941         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
942         mono_mb_emit_stloc (mb, my_entry_var);
943
944         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
945         mono_mb_emit_ldloc (mb, my_entry_var);
946         mono_mb_emit_icon (mb, HBLKSIZE);
947         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
948
949         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
950         mono_mb_emit_ldloc (mb, my_fl_var);
951         mono_mb_emit_ldloc (mb, my_entry_var);
952         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
953         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
954
955         /* set the vtable and clear the words in the object */
956         mono_mb_emit_ldloc (mb, my_entry_var);
957         mono_mb_emit_ldarg (mb, 0);
958         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
959
960         if (atype == ATYPE_FREEPTR) {
961                 int start_var, end_var, start_loop;
962                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
963                  */
964                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
965                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
966                 mono_mb_emit_ldloc (mb, my_entry_var);
967                 mono_mb_emit_ldloc (mb, bytes_var);
968                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
969                 mono_mb_emit_stloc (mb, end_var);
970                 mono_mb_emit_ldloc (mb, my_entry_var);
971                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
972                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
973                 mono_mb_emit_stloc (mb, start_var);
974                 /*
975                  * do {
976                  *      *start++ = NULL;
977                  * } while (start < end);
978                  */
979                 start_loop = mono_mb_get_label (mb);
980                 mono_mb_emit_ldloc (mb, start_var);
981                 mono_mb_emit_icon (mb, 0);
982                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
983                 mono_mb_emit_ldloc (mb, start_var);
984                 mono_mb_emit_icon (mb, sizeof (gpointer));
985                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
986                 mono_mb_emit_stloc (mb, start_var);
987
988                 mono_mb_emit_ldloc (mb, start_var);
989                 mono_mb_emit_ldloc (mb, end_var);
990                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
991                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
992         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
993                 /* need to clear just the sync pointer */
994                 mono_mb_emit_ldloc (mb, my_entry_var);
995                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
996                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
997                 mono_mb_emit_icon (mb, 0);
998                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
999         }
1000
1001         if (atype == ATYPE_STRING) {
1002                 /* need to set length and clear the last char */
1003                 /* s->length = len; */
1004                 mono_mb_emit_ldloc (mb, my_entry_var);
1005                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1006                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1007                 mono_mb_emit_ldarg (mb, 1);
1008                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1009                 /* s->chars [len] = 0; */
1010                 mono_mb_emit_ldloc (mb, my_entry_var);
1011                 mono_mb_emit_ldloc (mb, bytes_var);
1012                 mono_mb_emit_icon (mb, 2);
1013                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1014                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1015                 mono_mb_emit_icon (mb, 0);
1016                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1017         }
1018
1019         /* return my_entry; */
1020         mono_mb_emit_ldloc (mb, my_entry_var);
1021         mono_mb_emit_byte (mb, MONO_CEE_RET);
1022         
1023         mono_mb_patch_short_branch (mb, no_freelist_branch);
1024         if (not_small_enough_branch > 0)
1025                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1026         if (size_overflow_branch > 0)
1027                 mono_mb_patch_short_branch (mb, size_overflow_branch);
1028
1029         /* the slow path: we just call back into the runtime */
1030  always_slowpath:
1031         if (atype == ATYPE_STRING) {
1032                 mono_mb_emit_ldarg (mb, 1);
1033                 mono_mb_emit_icall (mb, mono_string_alloc);
1034         } else {
1035                 mono_mb_emit_ldarg (mb, 0);
1036                 mono_mb_emit_icall (mb, mono_object_new_specific);
1037         }
1038
1039         mono_mb_emit_byte (mb, MONO_CEE_RET);
1040
1041         info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1042         info->d.alloc.gc_name = "boehm";
1043         info->d.alloc.alloc_type = atype;
1044
1045         res = mono_mb_create (mb, csig, 8, info);
1046         mono_mb_free (mb);
1047         mono_method_get_header (res)->init_locals = FALSE;
1048
1049         return res;
1050 }
1051
1052 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1053 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1054
1055 static G_GNUC_UNUSED gboolean
1056 mono_gc_is_critical_method (MonoMethod *method)
1057 {
1058         int i;
1059
1060         for (i = 0; i < ATYPE_NUM; ++i)
1061                 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1062                         return TRUE;
1063
1064         return FALSE;
1065 }
1066
1067 /*
1068  * If possible, generate a managed method that can quickly allocate objects in class
1069  * @klass. The method will typically have an thread-local inline allocation sequence.
1070  * The signature of the called method is:
1071  *      object allocate (MonoVTable *vtable)
1072  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1073  * keep in sync.
1074  * The thread local alloc logic is taken from libgc/pthread_support.c.
1075  */
1076
1077 MonoMethod*
1078 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1079 {
1080         int offset = -1;
1081         int atype;
1082         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1083
1084         /*g_print ("thread tls: %d\n", offset);*/
1085         if (offset == -1)
1086                 return NULL;
1087         if (!SMALL_ENOUGH (klass->instance_size))
1088                 return NULL;
1089         if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
1090                 return NULL;
1091         if (klass->rank)
1092                 return NULL;
1093         if (mono_class_is_open_constructed_type (&klass->byval_arg))
1094                 return NULL;
1095         if (klass->byval_arg.type == MONO_TYPE_STRING) {
1096                 atype = ATYPE_STRING;
1097         } else if (!known_instance_size) {
1098                 return NULL;
1099         } else if (!klass->has_references) {
1100                 if (for_box)
1101                         atype = ATYPE_FREEPTR_FOR_BOX;
1102                 else
1103                         atype = ATYPE_FREEPTR;
1104         } else {
1105                 return NULL;
1106                 /*
1107                  * disabled because we currently do a runtime choice anyway, to
1108                  * deal with multiple appdomains.
1109                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1110                         atype = ATYPE_GCJ;
1111                 else
1112                         atype = ATYPE_NORMAL;
1113                 */
1114         }
1115         return mono_gc_get_managed_allocator_by_type (atype, FALSE);
1116 }
1117
1118 MonoMethod*
1119 mono_gc_get_managed_array_allocator (MonoClass *klass)
1120 {
1121         return NULL;
1122 }
1123
1124 /**
1125  * mono_gc_get_managed_allocator_by_type:
1126  *
1127  *   Return a managed allocator method corresponding to allocator type ATYPE.
1128  */
1129 MonoMethod*
1130 mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
1131 {
1132         int offset = -1;
1133         MonoMethod *res;
1134         MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1135         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1136
1137         mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1138
1139         res = cache [atype];
1140         if (res)
1141                 return res;
1142
1143         res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1144         mono_mutex_lock (&mono_gc_lock);
1145         if (cache [atype]) {
1146                 mono_free_method (res);
1147                 res = cache [atype];
1148         } else {
1149                 mono_memory_barrier ();
1150                 cache [atype] = res;
1151         }
1152         mono_mutex_unlock (&mono_gc_lock);
1153         return res;
1154 }
1155
1156 guint32
1157 mono_gc_get_managed_allocator_types (void)
1158 {
1159         return ATYPE_NUM;
1160 }
1161
1162 MonoMethod*
1163 mono_gc_get_write_barrier (void)
1164 {
1165         g_assert_not_reached ();
1166         return NULL;
1167 }
1168
1169 #else
1170
1171 static G_GNUC_UNUSED gboolean
1172 mono_gc_is_critical_method (MonoMethod *method)
1173 {
1174         return FALSE;
1175 }
1176
1177 MonoMethod*
1178 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1179 {
1180         return NULL;
1181 }
1182
1183 MonoMethod*
1184 mono_gc_get_managed_array_allocator (MonoClass *klass)
1185 {
1186         return NULL;
1187 }
1188
1189 MonoMethod*
1190 mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
1191 {
1192         return NULL;
1193 }
1194
1195 guint32
1196 mono_gc_get_managed_allocator_types (void)
1197 {
1198         return 0;
1199 }
1200
1201 MonoMethod*
1202 mono_gc_get_write_barrier (void)
1203 {
1204         g_assert_not_reached ();
1205         return NULL;
1206 }
1207
1208 #endif
1209
1210 MonoMethod*
1211 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1212 {
1213         g_assert_not_reached ();
1214         return NULL;
1215 }
1216
1217 int
1218 mono_gc_get_aligned_size_for_allocator (int size)
1219 {
1220         return size;
1221 }
1222
1223 const char *
1224 mono_gc_get_gc_name (void)
1225 {
1226         return "boehm";
1227 }
1228
1229 void*
1230 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1231 {
1232         return GC_call_with_alloc_lock (func, data);
1233 }
1234
1235 char*
1236 mono_gc_get_description (void)
1237 {
1238         return g_strdup (DEFAULT_GC_NAME);
1239 }
1240
1241 void
1242 mono_gc_set_desktop_mode (void)
1243 {
1244         GC_dont_expand = 1;
1245 }
1246
1247 gboolean
1248 mono_gc_is_moving (void)
1249 {
1250         return FALSE;
1251 }
1252
1253 gboolean
1254 mono_gc_is_disabled (void)
1255 {
1256         if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1257                 return TRUE;
1258         else
1259                 return FALSE;
1260 }
1261
1262 void
1263 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1264 {
1265         g_assert_not_reached ();
1266 }
1267
1268
1269 guint8*
1270 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1271 {
1272         g_assert_not_reached ();
1273         return NULL;
1274 }
1275
1276 gboolean
1277 mono_gc_card_table_nursery_check (void)
1278 {
1279         g_assert_not_reached ();
1280         return TRUE;
1281 }
1282
1283 void*
1284 mono_gc_get_nursery (int *shift_bits, size_t *size)
1285 {
1286         return NULL;
1287 }
1288
1289 void
1290 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1291 {
1292 }
1293
1294 gboolean
1295 mono_gc_precise_stack_mark_enabled (void)
1296 {
1297         return FALSE;
1298 }
1299
1300 FILE *
1301 mono_gc_get_logfile (void)
1302 {
1303         return NULL;
1304 }
1305
1306 void
1307 mono_gc_conservatively_scan_area (void *start, void *end)
1308 {
1309         g_assert_not_reached ();
1310 }
1311
1312 void *
1313 mono_gc_scan_object (void *obj, void *gc_data)
1314 {
1315         g_assert_not_reached ();
1316         return NULL;
1317 }
1318
1319 gsize*
1320 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1321 {
1322         g_assert_not_reached ();
1323         return NULL;
1324 }
1325
1326 void
1327 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1328 {
1329 }
1330
1331 void
1332 mono_gc_set_stack_end (void *stack_end)
1333 {
1334 }
1335
1336 void mono_gc_set_skip_thread (gboolean value)
1337 {
1338 }
1339
1340 void
1341 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1342 {
1343         guint offset = 0;
1344
1345 #ifndef GC_DEBUG
1346         /* This assertion is not valid when GC_DEBUG is defined */
1347         g_assert (GC_base (obj) == (char*)obj - offset);
1348 #endif
1349
1350         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1351 }
1352
1353 #ifndef HOST_WIN32
1354 int
1355 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1356 {
1357         /* it is being replaced by GC_pthread_create on some
1358          * platforms, see libgc/include/gc_pthread_redirects.h */
1359         return pthread_create (new_thread, attr, start_routine, arg);
1360 }
1361 #endif
1362
1363 #ifdef HOST_WIN32
1364 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1365 {
1366         return GC_DllMain (module_handle, reason, reserved);
1367 }
1368 #endif
1369
1370 guint
1371 mono_gc_get_vtable_bits (MonoClass *klass)
1372 {
1373         if (fin_callbacks.is_class_finalization_aware) {
1374                 if (fin_callbacks.is_class_finalization_aware (klass))
1375                         return BOEHM_GC_BIT_FINALIZER_AWARE;
1376         }
1377         return 0;
1378 }
1379
1380 /*
1381  * mono_gc_register_altstack:
1382  *
1383  *   Register the dimensions of the normal stack and altstack with the collector.
1384  * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1385  */
1386 void
1387 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1388 {
1389         GC_register_altstack (stack, stack_size, altstack, altstack_size);
1390 }
1391
1392 int
1393 mono_gc_get_los_limit (void)
1394 {
1395         return G_MAXINT;
1396 }
1397
1398 void
1399 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1400 {
1401         mono_unichar2 *new_end = str->chars + new_length;
1402         
1403         /* zero the discarded string. This null-delimits the string and allows 
1404          * the space to be reclaimed by SGen. */
1405          
1406         memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1407         str->length = new_length;
1408 }
1409
1410 gboolean
1411 mono_gc_user_markers_supported (void)
1412 {
1413         return FALSE;
1414 }
1415
1416 void *
1417 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1418 {
1419         g_assert_not_reached ();
1420         return NULL;
1421 }
1422
1423 gboolean
1424 mono_gc_set_allow_synchronous_major (gboolean flag)
1425 {
1426         return flag;
1427 }
1428 /* Toggleref support */
1429
1430 void
1431 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1432 {
1433         GC_toggleref_add ((GC_PTR)object, (int)strong_ref);
1434 }
1435
1436 void
1437 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1438 {
1439         GC_toggleref_register_callback ((int (*) (GC_PTR obj)) proccess_toggleref);
1440 }
1441
1442 /* Test support code */
1443
1444 static MonoToggleRefStatus
1445 test_toggleref_callback (MonoObject *obj)
1446 {
1447         static MonoClassField *mono_toggleref_test_field;
1448         int status = MONO_TOGGLE_REF_DROP;
1449
1450         if (!mono_toggleref_test_field) {
1451                 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1452                 g_assert (mono_toggleref_test_field);
1453         }
1454
1455         mono_field_get_value (obj, mono_toggleref_test_field, &status);
1456         printf ("toggleref-cb obj %d\n", status);
1457         return status;
1458 }
1459
1460 static void
1461 register_test_toggleref_callback (void)
1462 {
1463         mono_gc_toggleref_register_callback (test_toggleref_callback);
1464 }
1465
1466 static gboolean
1467 is_finalization_aware (MonoObject *obj)
1468 {
1469         MonoVTable *vt = obj->vtable;
1470         return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1471 }
1472
1473 static void
1474 fin_notifier (MonoObject *obj)
1475 {
1476         if (is_finalization_aware (obj))
1477                 fin_callbacks.object_queued_for_finalization (obj);
1478 }
1479
1480 void
1481 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1482 {
1483         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1484                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1485
1486         fin_callbacks = *callbacks;
1487
1488         GC_set_finalizer_notify_proc ((void (*) (GC_PTR))fin_notifier);
1489 }
1490
1491 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1492
1493 static inline gboolean
1494 slot_occupied (HandleData *handles, guint slot) {
1495         return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1496 }
1497
1498 static inline void
1499 vacate_slot (HandleData *handles, guint slot) {
1500         handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1501 }
1502
1503 static inline void
1504 occupy_slot (HandleData *handles, guint slot) {
1505         handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1506 }
1507
1508 static int
1509 find_first_unset (guint32 bitmap)
1510 {
1511         int i;
1512         for (i = 0; i < 32; ++i) {
1513                 if (!(bitmap & (1 << i)))
1514                         return i;
1515         }
1516         return -1;
1517 }
1518
1519 static void
1520 handle_data_alloc_entries (HandleData *handles)
1521 {
1522         handles->size = 32;
1523         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1524                 handles->entries = g_malloc0 (sizeof (*handles->entries) * handles->size);
1525                 handles->domain_ids = g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1526         } else {
1527                 handles->entries = mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1528         }
1529         handles->bitmap = g_malloc0 (handles->size / CHAR_BIT);
1530 }
1531
1532 static gint
1533 handle_data_next_unset (HandleData *handles)
1534 {
1535         gint slot;
1536         for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1537                 if (handles->bitmap [slot] == 0xffffffff)
1538                         continue;
1539                 handles->slot_hint = slot;
1540                 return find_first_unset (handles->bitmap [slot]);
1541         }
1542         return -1;
1543 }
1544
1545 static gint
1546 handle_data_first_unset (HandleData *handles)
1547 {
1548         gint slot;
1549         for (slot = 0; slot < handles->slot_hint; ++slot) {
1550                 if (handles->bitmap [slot] == 0xffffffff)
1551                         continue;
1552                 handles->slot_hint = slot;
1553                 return find_first_unset (handles->bitmap [slot]);
1554         }
1555         return -1;
1556 }
1557
1558 /* Returns the index of the current slot in the bitmap. */
1559 static void
1560 handle_data_grow (HandleData *handles, gboolean track)
1561 {
1562         guint32 *new_bitmap;
1563         guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1564
1565         /* resize and copy the bitmap */
1566         new_bitmap = g_malloc0 (new_size / CHAR_BIT);
1567         memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1568         g_free (handles->bitmap);
1569         handles->bitmap = new_bitmap;
1570
1571         /* resize and copy the entries */
1572         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1573                 gpointer *entries;
1574                 guint16 *domain_ids;
1575                 gint i;
1576                 domain_ids = g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1577                 entries = g_malloc0 (sizeof (*handles->entries) * new_size);
1578                 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1579                 for (i = 0; i < handles->size; ++i) {
1580                         MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1581                         if (obj) {
1582                                 mono_gc_weak_link_add (&(entries [i]), obj, track);
1583                                 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1584                         } else {
1585                                 g_assert (!handles->entries [i]);
1586                         }
1587                 }
1588                 g_free (handles->entries);
1589                 g_free (handles->domain_ids);
1590                 handles->entries = entries;
1591                 handles->domain_ids = domain_ids;
1592         } else {
1593                 gpointer *entries;
1594                 entries = mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1595                 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1596                 mono_gc_free_fixed (handles->entries);
1597                 handles->entries = entries;
1598         }
1599         handles->slot_hint = handles->size / BITMAP_SIZE;
1600         handles->size = new_size;
1601 }
1602
1603 static guint32
1604 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1605 {
1606         gint slot, i;
1607         guint32 res;
1608         lock_handles (handles);
1609         if (!handles->size)
1610                 handle_data_alloc_entries (handles);
1611         i = handle_data_next_unset (handles);
1612         if (i == -1 && handles->slot_hint != 0)
1613                 i = handle_data_first_unset (handles);
1614         if (i == -1) {
1615                 handle_data_grow (handles, track);
1616                 i = 0;
1617         }
1618         slot = handles->slot_hint * BITMAP_SIZE + i;
1619         occupy_slot (handles, slot);
1620         handles->entries [slot] = NULL;
1621         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1622                 /*FIXME, what to use when obj == null?*/
1623                 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1624                 if (obj)
1625                         mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1626         } else {
1627                 handles->entries [slot] = obj;
1628         }
1629
1630 #ifndef DISABLE_PERFCOUNTERS
1631         mono_perfcounters->gc_num_handles++;
1632 #endif
1633         unlock_handles (handles);
1634         res = MONO_GC_HANDLE (slot, handles->type);
1635         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1636         return res;
1637 }
1638
1639 /**
1640  * mono_gchandle_new:
1641  * @obj: managed object to get a handle for
1642  * @pinned: whether the object should be pinned
1643  *
1644  * This returns a handle that wraps the object, this is used to keep a
1645  * reference to a managed object from the unmanaged world and preventing the
1646  * object from being disposed.
1647  * 
1648  * If @pinned is false the address of the object can not be obtained, if it is
1649  * true the address of the object can be obtained.  This will also pin the
1650  * object so it will not be possible by a moving garbage collector to move the
1651  * object. 
1652  * 
1653  * Returns: a handle that can be used to access the object from
1654  * unmanaged code.
1655  */
1656 guint32
1657 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1658 {
1659         return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1660 }
1661
1662 /**
1663  * mono_gchandle_new_weakref:
1664  * @obj: managed object to get a handle for
1665  * @pinned: whether the object should be pinned
1666  *
1667  * This returns a weak handle that wraps the object, this is used to
1668  * keep a reference to a managed object from the unmanaged world.
1669  * Unlike the mono_gchandle_new the object can be reclaimed by the
1670  * garbage collector.  In this case the value of the GCHandle will be
1671  * set to zero.
1672  * 
1673  * If @pinned is false the address of the object can not be obtained, if it is
1674  * true the address of the object can be obtained.  This will also pin the
1675  * object so it will not be possible by a moving garbage collector to move the
1676  * object. 
1677  * 
1678  * Returns: a handle that can be used to access the object from
1679  * unmanaged code.
1680  */
1681 guint32
1682 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1683 {
1684         return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1685 }
1686
1687 /**
1688  * mono_gchandle_get_target:
1689  * @gchandle: a GCHandle's handle.
1690  *
1691  * The handle was previously created by calling mono_gchandle_new or
1692  * mono_gchandle_new_weakref. 
1693  *
1694  * Returns a pointer to the MonoObject represented by the handle or
1695  * NULL for a collected object if using a weakref handle.
1696  */
1697 MonoObject*
1698 mono_gchandle_get_target (guint32 gchandle)
1699 {
1700         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1701         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1702         HandleData *handles = &gc_handles [type];
1703         MonoObject *obj = NULL;
1704         if (type >= HANDLE_TYPE_MAX)
1705                 return NULL;
1706
1707         lock_handles (handles);
1708         if (slot < handles->size && slot_occupied (handles, slot)) {
1709                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1710                         obj = mono_gc_weak_link_get (&handles->entries [slot]);
1711                 } else {
1712                         obj = handles->entries [slot];
1713                 }
1714         } else {
1715                 /* print a warning? */
1716         }
1717         unlock_handles (handles);
1718         /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1719         return obj;
1720 }
1721
1722 void
1723 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1724 {
1725         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1726         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1727         HandleData *handles = &gc_handles [type];
1728         MonoObject *old_obj = NULL;
1729
1730         g_assert (type < HANDLE_TYPE_MAX);
1731         lock_handles (handles);
1732         if (slot < handles->size && slot_occupied (handles, slot)) {
1733                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1734                         old_obj = handles->entries [slot];
1735                         if (handles->entries [slot])
1736                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1737                         if (obj)
1738                                 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1739                         /*FIXME, what to use when obj == null?*/
1740                         handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1741                 } else {
1742                         handles->entries [slot] = obj;
1743                 }
1744         } else {
1745                 /* print a warning? */
1746         }
1747         /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1748         unlock_handles (handles);
1749 }
1750
1751 /**
1752  * mono_gchandle_is_in_domain:
1753  * @gchandle: a GCHandle's handle.
1754  * @domain: An application domain.
1755  *
1756  * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
1757  */
1758 gboolean
1759 mono_gc_is_null (void)
1760 {
1761         return FALSE;
1762 }
1763
1764 gboolean
1765 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1766 {
1767         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1768         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1769         HandleData *handles = &gc_handles [type];
1770         gboolean result = FALSE;
1771
1772         if (type >= HANDLE_TYPE_MAX)
1773                 return FALSE;
1774
1775         lock_handles (handles);
1776         if (slot < handles->size && slot_occupied (handles, slot)) {
1777                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1778                         result = domain->domain_id == handles->domain_ids [slot];
1779                 } else {
1780                         MonoObject *obj;
1781                         obj = handles->entries [slot];
1782                         if (obj == NULL)
1783                                 result = TRUE;
1784                         else
1785                                 result = domain == mono_object_domain (obj);
1786                 }
1787         } else {
1788                 /* print a warning? */
1789         }
1790         unlock_handles (handles);
1791         return result;
1792 }
1793
1794 /**
1795  * mono_gchandle_free:
1796  * @gchandle: a GCHandle's handle.
1797  *
1798  * Frees the @gchandle handle.  If there are no outstanding
1799  * references, the garbage collector can reclaim the memory of the
1800  * object wrapped. 
1801  */
1802 void
1803 mono_gchandle_free (guint32 gchandle)
1804 {
1805         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1806         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1807         HandleData *handles = &gc_handles [type];
1808         if (type >= HANDLE_TYPE_MAX)
1809                 return;
1810
1811         lock_handles (handles);
1812         if (slot < handles->size && slot_occupied (handles, slot)) {
1813                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1814                         if (handles->entries [slot])
1815                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1816                 } else {
1817                         handles->entries [slot] = NULL;
1818                 }
1819                 vacate_slot (handles, slot);
1820         } else {
1821                 /* print a warning? */
1822         }
1823 #ifndef DISABLE_PERFCOUNTERS
1824         mono_perfcounters->gc_num_handles--;
1825 #endif
1826         /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1827         unlock_handles (handles);
1828         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1829 }
1830
1831 /**
1832  * mono_gchandle_free_domain:
1833  * @domain: domain that is unloading
1834  *
1835  * Function used internally to cleanup any GC handle for objects belonging
1836  * to the specified domain during appdomain unload.
1837  */
1838 void
1839 mono_gchandle_free_domain (MonoDomain *domain)
1840 {
1841         guint type;
1842
1843         for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1844                 guint slot;
1845                 HandleData *handles = &gc_handles [type];
1846                 lock_handles (handles);
1847                 for (slot = 0; slot < handles->size; ++slot) {
1848                         if (!slot_occupied (handles, slot))
1849                                 continue;
1850                         if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1851                                 if (domain->domain_id == handles->domain_ids [slot]) {
1852                                         vacate_slot (handles, slot);
1853                                         if (handles->entries [slot])
1854                                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1855                                 }
1856                         } else {
1857                                 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1858                                         vacate_slot (handles, slot);
1859                                         handles->entries [slot] = NULL;
1860                                 }
1861                         }
1862                 }
1863                 unlock_handles (handles);
1864         }
1865
1866 }
1867
1868 #endif /* no Boehm GC */