Merge pull request #4050 from akoeplinger/profile-speedup
[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         MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
263         MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
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  * @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 @generation can be retrieved with a call to
282  * mono_gc_max_generation(), so this function is usually called as:
283  *
284  *      mono_gc_collect (mono_gc_max_generation ());
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  * @object: a managed object
313  *
314  * Get the garbage collector's generation that @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  * @generation: a GC generation number
328  *
329  * Get how many times a garbage collection has been performed
330  * for the given @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  * @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 @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 void
542 mono_gc_deregister_root (char* addr)
543 {
544 #ifndef HOST_WIN32
545         /* FIXME: libgc doesn't define this work win32 for some reason */
546         /* FIXME: No size info */
547         GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
548 #endif
549 }
550
551 static void
552 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
553 {
554         /* libgc requires that we use HIDE_POINTER... */
555         *link_addr = (void*)HIDE_POINTER (obj);
556         if (track)
557                 GC_REGISTER_LONG_LINK (link_addr, obj);
558         else
559                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
560 }
561
562 static void
563 mono_gc_weak_link_remove (void **link_addr, gboolean track)
564 {
565         if (track)
566                 GC_unregister_long_link (link_addr);
567         else
568                 GC_unregister_disappearing_link (link_addr);
569         *link_addr = NULL;
570 }
571
572 static gpointer
573 reveal_link (gpointer link_addr)
574 {
575         void **link_a = (void **)link_addr;
576         return REVEAL_POINTER (*link_a);
577 }
578
579 static MonoObject *
580 mono_gc_weak_link_get (void **link_addr)
581 {
582         MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
583         if (obj == (MonoObject *) -1)
584                 return NULL;
585         return obj;
586 }
587
588 void*
589 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
590 {
591         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
592 }
593
594 void*
595 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
596 {
597         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
598 }
599
600 void*
601 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
602 {
603         /* libgc has no usable support for arrays... */
604         return GC_NO_DESCRIPTOR;
605 }
606
607 void*
608 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
609 {
610         /* It seems there are issues when the bitmap doesn't fit: play it safe */
611         if (numbits >= 30)
612                 return GC_NO_DESCRIPTOR;
613         else
614                 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
615 }
616
617 void*
618 mono_gc_make_root_descr_all_refs (int numbits)
619 {
620         return NULL;
621 }
622
623 void*
624 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
625 {
626         /* To help track down typed allocation bugs */
627         /*
628         static int count;
629         count ++;
630         if (count == atoi (g_getenv ("COUNT2")))
631                 printf ("HIT!\n");
632         if (count > atoi (g_getenv ("COUNT2")))
633                 return GC_MALLOC (size);
634         */
635
636         if (descr)
637                 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
638         else
639                 return GC_MALLOC (size);
640 }
641
642 void
643 mono_gc_free_fixed (void* addr)
644 {
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 MONO_TLS_FAST 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         if (atype == ATYPE_FREEPTR) {
905                 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
906         } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
907                 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
908         } else if (atype == ATYPE_NORMAL) {
909                 name = slowpath ? "SlowAlloc" : "Alloc";
910         } else if (atype == ATYPE_GCJ) {
911                 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
912         } else if (atype == ATYPE_STRING) {
913                 name = slowpath ? "SlowAllocString" : "AllocString";
914         } else {
915                 g_assert_not_reached ();
916         }
917
918         csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
919
920         if (atype == ATYPE_STRING) {
921                 csig->ret = &mono_defaults.string_class->byval_arg;
922                 csig->params [0] = &mono_defaults.int_class->byval_arg;
923                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
924         } else {
925                 csig->ret = &mono_defaults.object_class->byval_arg;
926                 csig->params [0] = &mono_defaults.int_class->byval_arg;
927                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
928         }
929
930         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
931
932         if (slowpath)
933                 goto always_slowpath;
934
935         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
936         if (atype == ATYPE_STRING) {
937                 /* a string alloator method takes the args: (vtable, len) */
938                 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
939                 mono_mb_emit_ldarg (mb, 1);
940                 mono_mb_emit_icon (mb, 1);
941                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
942                 mono_mb_emit_icon (mb, 1);
943                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
944                 // sizeof (MonoString) might include padding
945                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
946                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
947                 mono_mb_emit_stloc (mb, bytes_var);
948         } else {
949                 mono_mb_emit_ldarg (mb, 1);
950                 mono_mb_emit_stloc (mb, bytes_var);
951         }
952
953         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
954         if (atype == ATYPE_STRING) {
955                 /* check for size */
956                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
957                 mono_mb_emit_ldloc (mb, bytes_var);
958                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
959                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
960                 /* check for overflow */
961                 mono_mb_emit_ldloc (mb, bytes_var);
962                 mono_mb_emit_icon (mb, sizeof (MonoString));
963                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
964         }
965
966         /* int index = INDEX_FROM_BYTES(bytes); */
967         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
968         
969         mono_mb_emit_ldloc (mb, bytes_var);
970         mono_mb_emit_icon (mb, GRANULARITY - 1);
971         mono_mb_emit_byte (mb, MONO_CEE_ADD);
972         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
973         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
974         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
975         mono_mb_emit_byte (mb, MONO_CEE_SHL);
976         /* index var is already adjusted into bytes */
977         mono_mb_emit_stloc (mb, index_var);
978
979         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
980         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
981         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
982         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
983         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
984         mono_mb_emit_i4 (mb, tls_key);
985         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
986                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
987                                         + G_STRUCT_OFFSET (struct thread_local_freelists,
988                                                            ptrfree_freelists));
989         else if (atype == ATYPE_NORMAL)
990                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
991                                         + G_STRUCT_OFFSET (struct thread_local_freelists,
992                                                            normal_freelists));
993         else if (atype == ATYPE_GCJ)
994                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
995                                         + G_STRUCT_OFFSET (struct thread_local_freelists,
996                                                            gcj_freelists));
997         else
998                 g_assert_not_reached ();
999         mono_mb_emit_byte (mb, MONO_CEE_ADD);
1000         mono_mb_emit_ldloc (mb, index_var);
1001         mono_mb_emit_byte (mb, MONO_CEE_ADD);
1002         mono_mb_emit_stloc (mb, my_fl_var);
1003
1004         /* my_entry = *my_fl; */
1005         mono_mb_emit_ldloc (mb, my_fl_var);
1006         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1007         mono_mb_emit_stloc (mb, my_entry_var);
1008
1009         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1010         mono_mb_emit_ldloc (mb, my_entry_var);
1011         mono_mb_emit_icon (mb, HBLKSIZE);
1012         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1013
1014         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1015         mono_mb_emit_ldloc (mb, my_fl_var);
1016         mono_mb_emit_ldloc (mb, my_entry_var);
1017         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1018         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1019
1020         /* set the vtable and clear the words in the object */
1021         mono_mb_emit_ldloc (mb, my_entry_var);
1022         mono_mb_emit_ldarg (mb, 0);
1023         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1024
1025         if (atype == ATYPE_FREEPTR) {
1026                 int start_var, end_var, start_loop;
1027                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1028                  */
1029                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1030                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1031                 mono_mb_emit_ldloc (mb, my_entry_var);
1032                 mono_mb_emit_ldloc (mb, bytes_var);
1033                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1034                 mono_mb_emit_stloc (mb, end_var);
1035                 mono_mb_emit_ldloc (mb, my_entry_var);
1036                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1037                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1038                 mono_mb_emit_stloc (mb, start_var);
1039                 /*
1040                  * do {
1041                  *      *start++ = NULL;
1042                  * } while (start < end);
1043                  */
1044                 start_loop = mono_mb_get_label (mb);
1045                 mono_mb_emit_ldloc (mb, start_var);
1046                 mono_mb_emit_icon (mb, 0);
1047                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1048                 mono_mb_emit_ldloc (mb, start_var);
1049                 mono_mb_emit_icon (mb, sizeof (gpointer));
1050                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1051                 mono_mb_emit_stloc (mb, start_var);
1052
1053                 mono_mb_emit_ldloc (mb, start_var);
1054                 mono_mb_emit_ldloc (mb, end_var);
1055                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1056                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1057         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1058                 /* need to clear just the sync pointer */
1059                 mono_mb_emit_ldloc (mb, my_entry_var);
1060                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1061                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1062                 mono_mb_emit_icon (mb, 0);
1063                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1064         }
1065
1066         if (atype == ATYPE_STRING) {
1067                 /* need to set length and clear the last char */
1068                 /* s->length = len; */
1069                 mono_mb_emit_ldloc (mb, my_entry_var);
1070                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1071                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1072                 mono_mb_emit_ldarg (mb, 1);
1073                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1074                 /* s->chars [len] = 0; */
1075                 mono_mb_emit_ldloc (mb, my_entry_var);
1076                 mono_mb_emit_ldloc (mb, bytes_var);
1077                 mono_mb_emit_icon (mb, 2);
1078                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1079                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1080                 mono_mb_emit_icon (mb, 0);
1081                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1082         }
1083
1084         /* return my_entry; */
1085         mono_mb_emit_ldloc (mb, my_entry_var);
1086         mono_mb_emit_byte (mb, MONO_CEE_RET);
1087         
1088         mono_mb_patch_short_branch (mb, no_freelist_branch);
1089         if (not_small_enough_branch > 0)
1090                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1091         if (size_overflow_branch > 0)
1092                 mono_mb_patch_short_branch (mb, size_overflow_branch);
1093
1094         /* the slow path: we just call back into the runtime */
1095  always_slowpath:
1096         if (atype == ATYPE_STRING) {
1097                 mono_mb_emit_ldarg (mb, 1);
1098                 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1099         } else {
1100                 mono_mb_emit_ldarg (mb, 0);
1101                 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1102         }
1103
1104         mono_mb_emit_byte (mb, MONO_CEE_RET);
1105
1106         info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1107         info->d.alloc.gc_name = "boehm";
1108         info->d.alloc.alloc_type = atype;
1109         mb->init_locals = FALSE;
1110
1111         res = mono_mb_create (mb, csig, 8, info);
1112         mono_mb_free (mb);
1113
1114         return res;
1115 }
1116
1117 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1118 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1119
1120 gboolean
1121 mono_gc_is_critical_method (MonoMethod *method)
1122 {
1123         int i;
1124
1125         for (i = 0; i < ATYPE_NUM; ++i)
1126                 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1127                         return TRUE;
1128
1129         return FALSE;
1130 }
1131
1132 /*
1133  * If possible, generate a managed method that can quickly allocate objects in class
1134  * @klass. The method will typically have an thread-local inline allocation sequence.
1135  * The signature of the called method is:
1136  *      object allocate (MonoVTable *vtable)
1137  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1138  * keep in sync.
1139  * The thread local alloc logic is taken from libgc/pthread_support.c.
1140  */
1141
1142 MonoMethod*
1143 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1144 {
1145         int offset = -1;
1146         int atype;
1147         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1148
1149         /*g_print ("thread tls: %d\n", offset);*/
1150         if (offset == -1)
1151                 return NULL;
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         int offset = -1;
1200         MonoMethod *res;
1201         gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1202         MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1203         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1204
1205         mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1206
1207         res = cache [atype];
1208         if (res)
1209                 return res;
1210
1211         res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1212         mono_os_mutex_lock (&mono_gc_lock);
1213         if (cache [atype]) {
1214                 mono_free_method (res);
1215                 res = cache [atype];
1216         } else {
1217                 mono_memory_barrier ();
1218                 cache [atype] = res;
1219         }
1220         mono_os_mutex_unlock (&mono_gc_lock);
1221         return res;
1222 }
1223
1224 guint32
1225 mono_gc_get_managed_allocator_types (void)
1226 {
1227         return ATYPE_NUM;
1228 }
1229
1230 MonoMethod*
1231 mono_gc_get_write_barrier (void)
1232 {
1233         g_assert_not_reached ();
1234         return NULL;
1235 }
1236
1237 #else
1238
1239 gboolean
1240 mono_gc_is_critical_method (MonoMethod *method)
1241 {
1242         return FALSE;
1243 }
1244
1245 MonoMethod*
1246 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1247 {
1248         return NULL;
1249 }
1250
1251 MonoMethod*
1252 mono_gc_get_managed_array_allocator (MonoClass *klass)
1253 {
1254         return NULL;
1255 }
1256
1257 MonoMethod*
1258 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1259 {
1260         return NULL;
1261 }
1262
1263 guint32
1264 mono_gc_get_managed_allocator_types (void)
1265 {
1266         return 0;
1267 }
1268
1269 MonoMethod*
1270 mono_gc_get_write_barrier (void)
1271 {
1272         g_assert_not_reached ();
1273         return NULL;
1274 }
1275
1276 #endif
1277
1278 MonoMethod*
1279 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1280 {
1281         g_assert_not_reached ();
1282         return NULL;
1283 }
1284
1285 int
1286 mono_gc_get_aligned_size_for_allocator (int size)
1287 {
1288         return size;
1289 }
1290
1291 const char *
1292 mono_gc_get_gc_name (void)
1293 {
1294         return "boehm";
1295 }
1296
1297 void*
1298 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1299 {
1300         return GC_call_with_alloc_lock (func, data);
1301 }
1302
1303 char*
1304 mono_gc_get_description (void)
1305 {
1306         return g_strdup (DEFAULT_GC_NAME);
1307 }
1308
1309 void
1310 mono_gc_set_desktop_mode (void)
1311 {
1312         GC_dont_expand = 1;
1313 }
1314
1315 gboolean
1316 mono_gc_is_moving (void)
1317 {
1318         return FALSE;
1319 }
1320
1321 gboolean
1322 mono_gc_is_disabled (void)
1323 {
1324         if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1325                 return TRUE;
1326         else
1327                 return FALSE;
1328 }
1329
1330 void
1331 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1332 {
1333         g_assert_not_reached ();
1334 }
1335
1336
1337 guint8*
1338 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1339 {
1340         g_assert_not_reached ();
1341         return NULL;
1342 }
1343
1344 gboolean
1345 mono_gc_card_table_nursery_check (void)
1346 {
1347         g_assert_not_reached ();
1348         return TRUE;
1349 }
1350
1351 void*
1352 mono_gc_get_nursery (int *shift_bits, size_t *size)
1353 {
1354         return NULL;
1355 }
1356
1357 gboolean
1358 mono_gc_precise_stack_mark_enabled (void)
1359 {
1360         return FALSE;
1361 }
1362
1363 FILE *
1364 mono_gc_get_logfile (void)
1365 {
1366         return NULL;
1367 }
1368
1369 void
1370 mono_gc_params_set (const char* options)
1371 {
1372 }
1373
1374 void
1375 mono_gc_debug_set (const char* options)
1376 {
1377 }
1378
1379 void
1380 mono_gc_conservatively_scan_area (void *start, void *end)
1381 {
1382         g_assert_not_reached ();
1383 }
1384
1385 void *
1386 mono_gc_scan_object (void *obj, void *gc_data)
1387 {
1388         g_assert_not_reached ();
1389         return NULL;
1390 }
1391
1392 gsize*
1393 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1394 {
1395         g_assert_not_reached ();
1396         return NULL;
1397 }
1398
1399 void
1400 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1401 {
1402 }
1403
1404 void
1405 mono_gc_set_stack_end (void *stack_end)
1406 {
1407 }
1408
1409 void mono_gc_set_skip_thread (gboolean value)
1410 {
1411 }
1412
1413 void
1414 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1415 {
1416         guint offset = 0;
1417
1418 #ifndef GC_DEBUG
1419         /* This assertion is not valid when GC_DEBUG is defined */
1420         g_assert (GC_base (obj) == (char*)obj - offset);
1421 #endif
1422
1423         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1424 }
1425
1426 #ifndef HOST_WIN32
1427 int
1428 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1429 {
1430         /* it is being replaced by GC_pthread_create on some
1431          * platforms, see libgc/include/gc_pthread_redirects.h */
1432         return pthread_create (new_thread, attr, start_routine, arg);
1433 }
1434 #endif
1435
1436 #ifdef HOST_WIN32
1437 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1438 {
1439         return GC_DllMain (module_handle, reason, reserved);
1440 }
1441 #endif
1442
1443 guint
1444 mono_gc_get_vtable_bits (MonoClass *klass)
1445 {
1446         if (fin_callbacks.is_class_finalization_aware) {
1447                 if (fin_callbacks.is_class_finalization_aware (klass))
1448                         return BOEHM_GC_BIT_FINALIZER_AWARE;
1449         }
1450         return 0;
1451 }
1452
1453 /*
1454  * mono_gc_register_altstack:
1455  *
1456  *   Register the dimensions of the normal stack and altstack with the collector.
1457  * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1458  */
1459 void
1460 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1461 {
1462         GC_register_altstack (stack, stack_size, altstack, altstack_size);
1463 }
1464
1465 int
1466 mono_gc_get_los_limit (void)
1467 {
1468         return G_MAXINT;
1469 }
1470
1471 void
1472 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1473 {
1474         mono_unichar2 *new_end = str->chars + new_length;
1475         
1476         /* zero the discarded string. This null-delimits the string and allows 
1477          * the space to be reclaimed by SGen. */
1478          
1479         memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1480         str->length = new_length;
1481 }
1482
1483 gboolean
1484 mono_gc_user_markers_supported (void)
1485 {
1486         return FALSE;
1487 }
1488
1489 void *
1490 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1491 {
1492         g_assert_not_reached ();
1493         return NULL;
1494 }
1495
1496 /* Toggleref support */
1497
1498 void
1499 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1500 {
1501         if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1502             g_error ("GC_toggleref_add failed\n");
1503 }
1504
1505 void
1506 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1507 {
1508         GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1509 }
1510
1511 /* Test support code */
1512
1513 static MonoToggleRefStatus
1514 test_toggleref_callback (MonoObject *obj)
1515 {
1516         static MonoClassField *mono_toggleref_test_field;
1517         MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1518
1519         if (!mono_toggleref_test_field) {
1520                 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1521                 g_assert (mono_toggleref_test_field);
1522         }
1523
1524         mono_field_get_value (obj, mono_toggleref_test_field, &status);
1525         printf ("toggleref-cb obj %d\n", status);
1526         return status;
1527 }
1528
1529 static void
1530 register_test_toggleref_callback (void)
1531 {
1532         mono_gc_toggleref_register_callback (test_toggleref_callback);
1533 }
1534
1535 static gboolean
1536 is_finalization_aware (MonoObject *obj)
1537 {
1538         MonoVTable *vt = obj->vtable;
1539         return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1540 }
1541
1542 static void
1543 fin_notifier (MonoObject *obj)
1544 {
1545         if (is_finalization_aware (obj))
1546                 fin_callbacks.object_queued_for_finalization (obj);
1547 }
1548
1549 void
1550 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1551 {
1552         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1553                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1554
1555         fin_callbacks = *callbacks;
1556
1557         GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1558 }
1559
1560 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1561
1562 static inline gboolean
1563 slot_occupied (HandleData *handles, guint slot) {
1564         return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1565 }
1566
1567 static inline void
1568 vacate_slot (HandleData *handles, guint slot) {
1569         handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1570 }
1571
1572 static inline void
1573 occupy_slot (HandleData *handles, guint slot) {
1574         handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1575 }
1576
1577 static int
1578 find_first_unset (guint32 bitmap)
1579 {
1580         int i;
1581         for (i = 0; i < 32; ++i) {
1582                 if (!(bitmap & (1 << i)))
1583                         return i;
1584         }
1585         return -1;
1586 }
1587
1588 static void
1589 handle_data_alloc_entries (HandleData *handles)
1590 {
1591         handles->size = 32;
1592         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1593                 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1594                 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1595         } else {
1596                 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1597         }
1598         handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1599 }
1600
1601 static gint
1602 handle_data_next_unset (HandleData *handles)
1603 {
1604         gint slot;
1605         for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1606                 if (handles->bitmap [slot] == 0xffffffff)
1607                         continue;
1608                 handles->slot_hint = slot;
1609                 return find_first_unset (handles->bitmap [slot]);
1610         }
1611         return -1;
1612 }
1613
1614 static gint
1615 handle_data_first_unset (HandleData *handles)
1616 {
1617         gint slot;
1618         for (slot = 0; slot < handles->slot_hint; ++slot) {
1619                 if (handles->bitmap [slot] == 0xffffffff)
1620                         continue;
1621                 handles->slot_hint = slot;
1622                 return find_first_unset (handles->bitmap [slot]);
1623         }
1624         return -1;
1625 }
1626
1627 /* Returns the index of the current slot in the bitmap. */
1628 static void
1629 handle_data_grow (HandleData *handles, gboolean track)
1630 {
1631         guint32 *new_bitmap;
1632         guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1633
1634         /* resize and copy the bitmap */
1635         new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1636         memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1637         g_free (handles->bitmap);
1638         handles->bitmap = new_bitmap;
1639
1640         /* resize and copy the entries */
1641         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1642                 gpointer *entries;
1643                 guint16 *domain_ids;
1644                 gint i;
1645                 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1646                 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1647                 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1648                 for (i = 0; i < handles->size; ++i) {
1649                         MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1650                         if (obj) {
1651                                 mono_gc_weak_link_add (&(entries [i]), obj, track);
1652                                 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1653                         } else {
1654                                 g_assert (!handles->entries [i]);
1655                         }
1656                 }
1657                 g_free (handles->entries);
1658                 g_free (handles->domain_ids);
1659                 handles->entries = entries;
1660                 handles->domain_ids = domain_ids;
1661         } else {
1662                 gpointer *entries;
1663                 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1664                 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1665                 mono_gc_free_fixed (handles->entries);
1666                 handles->entries = entries;
1667         }
1668         handles->slot_hint = handles->size / BITMAP_SIZE;
1669         handles->size = new_size;
1670 }
1671
1672 static guint32
1673 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1674 {
1675         gint slot, i;
1676         guint32 res;
1677         lock_handles (handles);
1678         if (!handles->size)
1679                 handle_data_alloc_entries (handles);
1680         i = handle_data_next_unset (handles);
1681         if (i == -1 && handles->slot_hint != 0)
1682                 i = handle_data_first_unset (handles);
1683         if (i == -1) {
1684                 handle_data_grow (handles, track);
1685                 i = 0;
1686         }
1687         slot = handles->slot_hint * BITMAP_SIZE + i;
1688         occupy_slot (handles, slot);
1689         handles->entries [slot] = NULL;
1690         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1691                 /*FIXME, what to use when obj == null?*/
1692                 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1693                 if (obj)
1694                         mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1695         } else {
1696                 handles->entries [slot] = obj;
1697         }
1698
1699 #ifndef DISABLE_PERFCOUNTERS
1700         mono_perfcounters->gc_num_handles++;
1701 #endif
1702         unlock_handles (handles);
1703         res = MONO_GC_HANDLE (slot, handles->type);
1704         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1705         return res;
1706 }
1707
1708 /**
1709  * mono_gchandle_new:
1710  * @obj: managed object to get a handle for
1711  * @pinned: whether the object should be pinned
1712  *
1713  * This returns a handle that wraps the object, this is used to keep a
1714  * reference to a managed object from the unmanaged world and preventing the
1715  * object from being disposed.
1716  * 
1717  * If @pinned is false the address of the object can not be obtained, if it is
1718  * true the address of the object can be obtained.  This will also pin the
1719  * object so it will not be possible by a moving garbage collector to move the
1720  * object. 
1721  * 
1722  * Returns: a handle that can be used to access the object from
1723  * unmanaged code.
1724  */
1725 guint32
1726 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1727 {
1728         return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1729 }
1730
1731 /**
1732  * mono_gchandle_new_weakref:
1733  * @obj: managed object to get a handle for
1734  * @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.
1735  *
1736  * This returns a weak handle that wraps the object, this is used to
1737  * keep a reference to a managed object from the unmanaged world.
1738  * Unlike the mono_gchandle_new the object can be reclaimed by the
1739  * garbage collector.  In this case the value of the GCHandle will be
1740  * set to zero.
1741  * 
1742  * If @track_resurrection is TRUE the object will be tracked through
1743  * finalization and if the object is resurrected during the execution
1744  * of the finalizer, then the returned weakref will continue to hold
1745  * a reference to the object.   If @track_resurrection is FALSE, then
1746  * the weak reference's target will become NULL as soon as the object
1747  * is passed on to the finalizer.
1748  * 
1749  * Returns: a handle that can be used to access the object from
1750  * unmanaged code.
1751  */
1752 guint32
1753 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1754 {
1755         return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1756 }
1757
1758 /**
1759  * mono_gchandle_get_target:
1760  * @gchandle: a GCHandle's handle.
1761  *
1762  * The handle was previously created by calling `mono_gchandle_new` or
1763  * `mono_gchandle_new_weakref`.
1764  *
1765  * Returns: A pointer to the `MonoObject*` represented by the handle or
1766  * NULL for a collected object if using a weakref handle.
1767  */
1768 MonoObject*
1769 mono_gchandle_get_target (guint32 gchandle)
1770 {
1771         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1772         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1773         HandleData *handles = &gc_handles [type];
1774         MonoObject *obj = NULL;
1775         if (type >= HANDLE_TYPE_MAX)
1776                 return NULL;
1777
1778         lock_handles (handles);
1779         if (slot < handles->size && slot_occupied (handles, slot)) {
1780                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1781                         obj = mono_gc_weak_link_get (&handles->entries [slot]);
1782                 } else {
1783                         obj = (MonoObject *)handles->entries [slot];
1784                 }
1785         } else {
1786                 /* print a warning? */
1787         }
1788         unlock_handles (handles);
1789         /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1790         return obj;
1791 }
1792
1793 void
1794 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1795 {
1796         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1797         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1798         HandleData *handles = &gc_handles [type];
1799         MonoObject *old_obj = NULL;
1800
1801         g_assert (type < HANDLE_TYPE_MAX);
1802         lock_handles (handles);
1803         if (slot < handles->size && slot_occupied (handles, slot)) {
1804                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1805                         old_obj = (MonoObject *)handles->entries [slot];
1806                         if (handles->entries [slot])
1807                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1808                         if (obj)
1809                                 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1810                         /*FIXME, what to use when obj == null?*/
1811                         handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1812                 } else {
1813                         handles->entries [slot] = obj;
1814                 }
1815         } else {
1816                 /* print a warning? */
1817         }
1818         /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1819         unlock_handles (handles);
1820 }
1821
1822 gboolean
1823 mono_gc_is_null (void)
1824 {
1825         return FALSE;
1826 }
1827
1828 /**
1829  * mono_gchandle_is_in_domain:
1830  * @gchandle: a GCHandle's handle.
1831  * @domain: An application domain.
1832  *
1833  * Use this function to determine if the @gchandle points to an
1834  * object allocated in the specified @domain.
1835  *
1836  * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1837  */
1838 gboolean
1839 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1840 {
1841         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1842         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1843         HandleData *handles = &gc_handles [type];
1844         gboolean result = FALSE;
1845
1846         if (type >= HANDLE_TYPE_MAX)
1847                 return FALSE;
1848
1849         lock_handles (handles);
1850         if (slot < handles->size && slot_occupied (handles, slot)) {
1851                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1852                         result = domain->domain_id == handles->domain_ids [slot];
1853                 } else {
1854                         MonoObject *obj;
1855                         obj = (MonoObject *)handles->entries [slot];
1856                         if (obj == NULL)
1857                                 result = TRUE;
1858                         else
1859                                 result = domain == mono_object_domain (obj);
1860                 }
1861         } else {
1862                 /* print a warning? */
1863         }
1864         unlock_handles (handles);
1865         return result;
1866 }
1867
1868 /**
1869  * mono_gchandle_free:
1870  * @gchandle: a GCHandle's handle.
1871  *
1872  * Frees the @gchandle handle.  If there are no outstanding
1873  * references, the garbage collector can reclaim the memory of the
1874  * object wrapped. 
1875  */
1876 void
1877 mono_gchandle_free (guint32 gchandle)
1878 {
1879         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1880         guint type = MONO_GC_HANDLE_TYPE (gchandle);
1881         HandleData *handles = &gc_handles [type];
1882         if (type >= HANDLE_TYPE_MAX)
1883                 return;
1884
1885         lock_handles (handles);
1886         if (slot < handles->size && slot_occupied (handles, slot)) {
1887                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1888                         if (handles->entries [slot])
1889                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1890                 } else {
1891                         handles->entries [slot] = NULL;
1892                 }
1893                 vacate_slot (handles, slot);
1894         } else {
1895                 /* print a warning? */
1896         }
1897 #ifndef DISABLE_PERFCOUNTERS
1898         mono_perfcounters->gc_num_handles--;
1899 #endif
1900         /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1901         unlock_handles (handles);
1902         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1903 }
1904
1905 /**
1906  * mono_gchandle_free_domain:
1907  * @domain: domain that is unloading
1908  *
1909  * Function used internally to cleanup any GC handle for objects belonging
1910  * to the specified domain during appdomain unload.
1911  */
1912 void
1913 mono_gchandle_free_domain (MonoDomain *domain)
1914 {
1915         guint type;
1916
1917         for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1918                 guint slot;
1919                 HandleData *handles = &gc_handles [type];
1920                 lock_handles (handles);
1921                 for (slot = 0; slot < handles->size; ++slot) {
1922                         if (!slot_occupied (handles, slot))
1923                                 continue;
1924                         if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1925                                 if (domain->domain_id == handles->domain_ids [slot]) {
1926                                         vacate_slot (handles, slot);
1927                                         if (handles->entries [slot])
1928                                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1929                                 }
1930                         } else {
1931                                 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1932                                         vacate_slot (handles, slot);
1933                                         handles->entries [slot] = NULL;
1934                                 }
1935                         }
1936                 }
1937                 unlock_handles (handles);
1938         }
1939
1940 }
1941 #else
1942
1943 MONO_EMPTY_SOURCE_FILE (boehm_gc);
1944 #endif /* no Boehm GC */