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