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