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