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