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