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