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