Merge pull request #2247 from ivmai/match-ext-libgc-api
[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 gboolean
1431 mono_gc_set_allow_synchronous_major (gboolean flag)
1432 {
1433         return flag;
1434 }
1435 /* Toggleref support */
1436
1437 void
1438 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1439 {
1440         if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1441             g_error ("GC_toggleref_add failed\n");
1442 }
1443
1444 void
1445 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1446 {
1447         GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1448 }
1449
1450 /* Test support code */
1451
1452 static MonoToggleRefStatus
1453 test_toggleref_callback (MonoObject *obj)
1454 {
1455         static MonoClassField *mono_toggleref_test_field;
1456         int status = MONO_TOGGLE_REF_DROP;
1457
1458         if (!mono_toggleref_test_field) {
1459                 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1460                 g_assert (mono_toggleref_test_field);
1461         }
1462
1463         mono_field_get_value (obj, mono_toggleref_test_field, &status);
1464         printf ("toggleref-cb obj %d\n", status);
1465         return status;
1466 }
1467
1468 static void
1469 register_test_toggleref_callback (void)
1470 {
1471         mono_gc_toggleref_register_callback (test_toggleref_callback);
1472 }
1473
1474 static gboolean
1475 is_finalization_aware (MonoObject *obj)
1476 {
1477         MonoVTable *vt = obj->vtable;
1478         return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1479 }
1480
1481 static void
1482 fin_notifier (MonoObject *obj)
1483 {
1484         if (is_finalization_aware (obj))
1485                 fin_callbacks.object_queued_for_finalization (obj);
1486 }
1487
1488 void
1489 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1490 {
1491         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1492                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1493
1494         fin_callbacks = *callbacks;
1495
1496         GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1497 }
1498
1499 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1500
1501 static inline gboolean
1502 slot_occupied (HandleData *handles, guint slot) {
1503         return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1504 }
1505
1506 static inline void
1507 vacate_slot (HandleData *handles, guint slot) {
1508         handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1509 }
1510
1511 static inline void
1512 occupy_slot (HandleData *handles, guint slot) {
1513         handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1514 }
1515
1516 static int
1517 find_first_unset (guint32 bitmap)
1518 {
1519         int i;
1520         for (i = 0; i < 32; ++i) {
1521                 if (!(bitmap & (1 << i)))
1522                         return i;
1523         }
1524         return -1;
1525 }
1526
1527 static void
1528 handle_data_alloc_entries (HandleData *handles)
1529 {
1530         handles->size = 32;
1531         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1532                 handles->entries = g_malloc0 (sizeof (*handles->entries) * handles->size);
1533                 handles->domain_ids = g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1534         } else {
1535                 handles->entries = mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1536         }
1537         handles->bitmap = g_malloc0 (handles->size / CHAR_BIT);
1538 }
1539
1540 static gint
1541 handle_data_next_unset (HandleData *handles)
1542 {
1543         gint slot;
1544         for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1545                 if (handles->bitmap [slot] == 0xffffffff)
1546                         continue;
1547                 handles->slot_hint = slot;
1548                 return find_first_unset (handles->bitmap [slot]);
1549         }
1550         return -1;
1551 }
1552
1553 static gint
1554 handle_data_first_unset (HandleData *handles)
1555 {
1556         gint slot;
1557         for (slot = 0; slot < handles->slot_hint; ++slot) {
1558                 if (handles->bitmap [slot] == 0xffffffff)
1559                         continue;
1560                 handles->slot_hint = slot;
1561                 return find_first_unset (handles->bitmap [slot]);
1562         }
1563         return -1;
1564 }
1565
1566 /* Returns the index of the current slot in the bitmap. */
1567 static void
1568 handle_data_grow (HandleData *handles, gboolean track)
1569 {
1570         guint32 *new_bitmap;
1571         guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1572
1573         /* resize and copy the bitmap */
1574         new_bitmap = g_malloc0 (new_size / CHAR_BIT);
1575         memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1576         g_free (handles->bitmap);
1577         handles->bitmap = new_bitmap;
1578
1579         /* resize and copy the entries */
1580         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1581                 gpointer *entries;
1582                 guint16 *domain_ids;
1583                 gint i;
1584                 domain_ids = g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1585                 entries = g_malloc0 (sizeof (*handles->entries) * new_size);
1586                 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1587                 for (i = 0; i < handles->size; ++i) {
1588                         MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1589                         if (obj) {
1590                                 mono_gc_weak_link_add (&(entries [i]), obj, track);
1591                                 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1592                         } else {
1593                                 g_assert (!handles->entries [i]);
1594                         }
1595                 }
1596                 g_free (handles->entries);
1597                 g_free (handles->domain_ids);
1598                 handles->entries = entries;
1599                 handles->domain_ids = domain_ids;
1600         } else {
1601                 gpointer *entries;
1602                 entries = mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1603                 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1604                 mono_gc_free_fixed (handles->entries);
1605                 handles->entries = entries;
1606         }
1607         handles->slot_hint = handles->size / BITMAP_SIZE;
1608         handles->size = new_size;
1609 }
1610
1611 static guint32
1612 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1613 {
1614         gint slot, i;
1615         guint32 res;
1616         lock_handles (handles);
1617         if (!handles->size)
1618                 handle_data_alloc_entries (handles);
1619         i = handle_data_next_unset (handles);
1620         if (i == -1 && handles->slot_hint != 0)
1621                 i = handle_data_first_unset (handles);
1622         if (i == -1) {
1623                 handle_data_grow (handles, track);
1624                 i = 0;
1625         }
1626         slot = handles->slot_hint * BITMAP_SIZE + i;
1627         occupy_slot (handles, slot);
1628         handles->entries [slot] = NULL;
1629         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1630                 /*FIXME, what to use when obj == null?*/
1631                 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1632                 if (obj)
1633                         mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1634         } else {
1635                 handles->entries [slot] = obj;
1636         }
1637
1638 #ifndef DISABLE_PERFCOUNTERS
1639         mono_perfcounters->gc_num_handles++;
1640 #endif
1641         unlock_handles (handles);
1642         res = MONO_GC_HANDLE (slot, handles->type);
1643         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1644         return res;
1645 }
1646
1647 /**
1648  * mono_gchandle_new:
1649  * @obj: managed object to get a handle for
1650  * @pinned: whether the object should be pinned
1651  *
1652  * This returns a handle that wraps the object, this is used to keep a
1653  * reference to a managed object from the unmanaged world and preventing the
1654  * object from being disposed.
1655  * 
1656  * If @pinned is false the address of the object can not be obtained, if it is
1657  * true the address of the object can be obtained.  This will also pin the
1658  * object so it will not be possible by a moving garbage collector to move the
1659  * object. 
1660  * 
1661  * Returns: a handle that can be used to access the object from
1662  * unmanaged code.
1663  */
1664 guint32
1665 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1666 {
1667         return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1668 }
1669
1670 /**
1671  * mono_gchandle_new_weakref:
1672  * @obj: managed object to get a handle for
1673  * @pinned: whether the object should be pinned
1674  *
1675  * This returns a weak handle that wraps the object, this is used to
1676  * keep a reference to a managed object from the unmanaged world.
1677  * Unlike the mono_gchandle_new the object can be reclaimed by the
1678  * garbage collector.  In this case the value of the GCHandle will be
1679  * set to zero.
1680  * 
1681  * If @pinned is false the address of the object can not be obtained, if it is
1682  * true the address of the object can be obtained.  This will also pin the
1683  * object so it will not be possible by a moving garbage collector to move the
1684  * object. 
1685  * 
1686  * Returns: a handle that can be used to access the object from
1687  * unmanaged code.
1688  */
1689 guint32
1690 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1691 {
1692         return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1693 }
1694
1695 /**
1696  * mono_gchandle_get_target:
1697  * @gchandle: a GCHandle's handle.
1698  *
1699  * The handle was previously created by calling mono_gchandle_new or
1700  * mono_gchandle_new_weakref. 
1701  *
1702  * Returns a pointer to the MonoObject represented by the handle or
1703  * NULL for a collected object if using a weakref handle.
1704  */
1705 MonoObject*
1706 mono_gchandle_get_target (guint32 gchandle)
1707 {
1708         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1709         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1710         HandleData *handles = &gc_handles [type];
1711         MonoObject *obj = NULL;
1712         if (type >= HANDLE_TYPE_MAX)
1713                 return NULL;
1714
1715         lock_handles (handles);
1716         if (slot < handles->size && slot_occupied (handles, slot)) {
1717                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1718                         obj = mono_gc_weak_link_get (&handles->entries [slot]);
1719                 } else {
1720                         obj = handles->entries [slot];
1721                 }
1722         } else {
1723                 /* print a warning? */
1724         }
1725         unlock_handles (handles);
1726         /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1727         return obj;
1728 }
1729
1730 void
1731 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1732 {
1733         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1734         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1735         HandleData *handles = &gc_handles [type];
1736         MonoObject *old_obj = NULL;
1737
1738         g_assert (type < HANDLE_TYPE_MAX);
1739         lock_handles (handles);
1740         if (slot < handles->size && slot_occupied (handles, slot)) {
1741                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1742                         old_obj = handles->entries [slot];
1743                         if (handles->entries [slot])
1744                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1745                         if (obj)
1746                                 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1747                         /*FIXME, what to use when obj == null?*/
1748                         handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1749                 } else {
1750                         handles->entries [slot] = obj;
1751                 }
1752         } else {
1753                 /* print a warning? */
1754         }
1755         /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1756         unlock_handles (handles);
1757 }
1758
1759 /**
1760  * mono_gchandle_is_in_domain:
1761  * @gchandle: a GCHandle's handle.
1762  * @domain: An application domain.
1763  *
1764  * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
1765  */
1766 gboolean
1767 mono_gc_is_null (void)
1768 {
1769         return FALSE;
1770 }
1771
1772 gboolean
1773 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1774 {
1775         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1776         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1777         HandleData *handles = &gc_handles [type];
1778         gboolean result = FALSE;
1779
1780         if (type >= HANDLE_TYPE_MAX)
1781                 return FALSE;
1782
1783         lock_handles (handles);
1784         if (slot < handles->size && slot_occupied (handles, slot)) {
1785                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1786                         result = domain->domain_id == handles->domain_ids [slot];
1787                 } else {
1788                         MonoObject *obj;
1789                         obj = handles->entries [slot];
1790                         if (obj == NULL)
1791                                 result = TRUE;
1792                         else
1793                                 result = domain == mono_object_domain (obj);
1794                 }
1795         } else {
1796                 /* print a warning? */
1797         }
1798         unlock_handles (handles);
1799         return result;
1800 }
1801
1802 /**
1803  * mono_gchandle_free:
1804  * @gchandle: a GCHandle's handle.
1805  *
1806  * Frees the @gchandle handle.  If there are no outstanding
1807  * references, the garbage collector can reclaim the memory of the
1808  * object wrapped. 
1809  */
1810 void
1811 mono_gchandle_free (guint32 gchandle)
1812 {
1813         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1814         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1815         HandleData *handles = &gc_handles [type];
1816         if (type >= HANDLE_TYPE_MAX)
1817                 return;
1818
1819         lock_handles (handles);
1820         if (slot < handles->size && slot_occupied (handles, slot)) {
1821                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1822                         if (handles->entries [slot])
1823                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1824                 } else {
1825                         handles->entries [slot] = NULL;
1826                 }
1827                 vacate_slot (handles, slot);
1828         } else {
1829                 /* print a warning? */
1830         }
1831 #ifndef DISABLE_PERFCOUNTERS
1832         mono_perfcounters->gc_num_handles--;
1833 #endif
1834         /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1835         unlock_handles (handles);
1836         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1837 }
1838
1839 /**
1840  * mono_gchandle_free_domain:
1841  * @domain: domain that is unloading
1842  *
1843  * Function used internally to cleanup any GC handle for objects belonging
1844  * to the specified domain during appdomain unload.
1845  */
1846 void
1847 mono_gchandle_free_domain (MonoDomain *domain)
1848 {
1849         guint type;
1850
1851         for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1852                 guint slot;
1853                 HandleData *handles = &gc_handles [type];
1854                 lock_handles (handles);
1855                 for (slot = 0; slot < handles->size; ++slot) {
1856                         if (!slot_occupied (handles, slot))
1857                                 continue;
1858                         if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1859                                 if (domain->domain_id == handles->domain_ids [slot]) {
1860                                         vacate_slot (handles, slot);
1861                                         if (handles->entries [slot])
1862                                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1863                                 }
1864                         } else {
1865                                 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1866                                         vacate_slot (handles, slot);
1867                                         handles->entries [slot] = NULL;
1868                                 }
1869                         }
1870                 }
1871                 unlock_handles (handles);
1872         }
1873
1874 }
1875
1876 #endif /* no Boehm GC */