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