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