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