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