2008-07-11 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / boehm-gc.c
1 /*
2  * boehm-gc.c: GC implementation using either the installed or included Boehm GC.
3  *
4  */
5
6 #include "config.h"
7 #define GC_I_HIDE_POINTERS
8 #include <mono/metadata/gc-internal.h>
9 #include <mono/metadata/mono-gc.h>
10 #include <mono/metadata/gc-internal.h>
11 #include <mono/metadata/profiler-private.h>
12 #include <mono/metadata/class-internals.h>
13 #include <mono/metadata/method-builder.h>
14 #include <mono/metadata/opcodes.h>
15 #include <mono/utils/mono-logger.h>
16 #include <mono/utils/dtrace.h>
17
18 #if HAVE_BOEHM_GC
19
20 #ifdef USE_INCLUDED_LIBGC
21 #undef TRUE
22 #undef FALSE
23 #define THREAD_LOCAL_ALLOC 1
24 #include "private/pthread_support.h"
25 #endif
26
27 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
28
29 static gboolean gc_initialized = FALSE;
30
31 static void
32 mono_gc_warning (char *msg, GC_word arg)
33 {
34         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
35 }
36
37 void
38 mono_gc_base_init (void)
39 {
40         if (gc_initialized)
41                 return;
42
43         /*
44          * Handle the case when we are called from a thread different from the main thread,
45          * confusing libgc.
46          * FIXME: Move this to libgc where it belongs.
47          *
48          * we used to do this only when running on valgrind,
49          * but it happens also in other setups.
50          */
51 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
52         {
53                 size_t size;
54                 void *sstart;
55                 pthread_attr_t attr;
56                 pthread_getattr_np (pthread_self (), &attr);
57                 pthread_attr_getstack (&attr, &sstart, &size);
58                 pthread_attr_destroy (&attr); 
59                 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
60 #ifdef __ia64__
61                 /*
62                  * The calculation above doesn't seem to work on ia64, also we need to set
63                  * GC_register_stackbottom as well, but don't know how.
64                  */
65 #else
66                 /* apparently with some linuxthreads implementations sstart can be NULL,
67                  * fallback to the more imprecise method (bug# 78096).
68                  */
69                 if (sstart) {
70                         GC_stackbottom = (char*)sstart + size;
71                 } else {
72                         int dummy;
73                         gsize stack_bottom = (gsize)&dummy;
74                         stack_bottom += 4095;
75                         stack_bottom &= ~4095;
76                         GC_stackbottom = (char*)stack_bottom;
77                 }
78 #endif
79         }
80 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
81                 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
82 #else
83         {
84                 int dummy;
85                 gsize stack_bottom = (gsize)&dummy;
86                 stack_bottom += 4095;
87                 stack_bottom &= ~4095;
88                 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
89                 GC_stackbottom = (char*)stack_bottom;
90         }
91 #endif
92
93         GC_no_dls = TRUE;
94         GC_init ();
95         GC_oom_fn = mono_gc_out_of_memory;
96         GC_set_warn_proc (mono_gc_warning);
97         GC_finalize_on_demand = 1;
98         GC_finalizer_notifier = mono_gc_finalize_notify;
99
100 #ifdef HAVE_GC_GCJ_MALLOC
101         GC_init_gcj_malloc (5, NULL);
102 #endif
103
104         gc_initialized = TRUE;
105 }
106
107 void
108 mono_gc_collect (int generation)
109 {
110         MONO_PROBE_GC_BEGIN (generation);
111         
112         GC_gcollect ();
113         
114         MONO_PROBE_GC_END (generation);
115 #if defined(ENABLE_DTRACE) && defined(__sun__)
116         /* This works around a dtrace -G problem on Solaris.
117            Limit its actual use to when the probe is enabled. */
118         if (MONO_PROBE_GC_END_ENABLED ())
119                 sleep(0);
120 #endif
121 }
122
123 int
124 mono_gc_max_generation (void)
125 {
126         return 0;
127 }
128
129 int
130 mono_gc_get_generation  (MonoObject *object)
131 {
132         return 0;
133 }
134
135 int
136 mono_gc_collection_count (int generation)
137 {
138         return GC_gc_no;
139 }
140
141 void
142 mono_gc_add_memory_pressure (gint64 value)
143 {
144 }
145
146 gint64
147 mono_gc_get_used_size (void)
148 {
149         return GC_get_heap_size () - GC_get_free_bytes ();
150 }
151
152 gint64
153 mono_gc_get_heap_size (void)
154 {
155         return GC_get_heap_size ();
156 }
157
158 void
159 mono_gc_disable (void)
160 {
161 #ifdef HAVE_GC_ENABLE
162         GC_disable ();
163 #else
164         g_assert_not_reached ();
165 #endif
166 }
167
168 void
169 mono_gc_enable (void)
170 {
171 #ifdef HAVE_GC_ENABLE
172         GC_enable ();
173 #else
174         g_assert_not_reached ();
175 #endif
176 }
177
178 gboolean
179 mono_gc_is_gc_thread (void)
180 {
181 #if GC_VERSION_MAJOR >= 7
182         return TRUE;
183 #elif defined(USE_INCLUDED_LIBGC)
184         return GC_thread_is_registered ();
185 #else
186         return TRUE;
187 #endif
188 }
189
190 extern int GC_thread_register_foreign (void *base_addr);
191
192 gboolean
193 mono_gc_register_thread (void *baseptr)
194 {
195 #if GC_VERSION_MAJOR >= 7
196         struct GC_stack_base sb;
197         int res;
198
199         res = GC_get_stack_base (&sb);
200         if (res != GC_SUCCESS) {
201                 sb.mem_base = baseptr;
202 #ifdef __ia64__
203                 /* Can't determine the register stack bounds */
204                 g_error ("mono_gc_register_thread failed ().\n");
205 #endif
206         }
207         res = GC_register_my_thread (&sb);
208         if ((res != GC_SUCCESS) && (res != GC_DUPLICATE)) {
209                 g_warning ("GC_register_my_thread () failed.\n");
210                 return FALSE;
211         }
212         return TRUE;
213 #else
214         if (mono_gc_is_gc_thread())
215                 return TRUE;
216 #if defined(USE_INCLUDED_LIBGC) && !defined(PLATFORM_WIN32)
217         return GC_thread_register_foreign (baseptr);
218 #else
219         return FALSE;
220 #endif
221 #endif
222 }
223
224 gboolean
225 mono_object_is_alive (MonoObject* o)
226 {
227 #ifdef USE_INCLUDED_LIBGC
228         return GC_is_marked ((gpointer)o);
229 #else
230         return TRUE;
231 #endif
232 }
233
234 #ifdef USE_INCLUDED_LIBGC
235
236 static void
237 on_gc_notification (GCEventType event)
238 {
239         mono_profiler_gc_event ((MonoGCEvent) event, 0);
240 }
241  
242 static void
243 on_gc_heap_resize (size_t new_size)
244 {
245         mono_profiler_gc_heap_resize (new_size);
246 }
247
248 void
249 mono_gc_enable_events (void)
250 {
251         GC_notify_event = on_gc_notification;
252         GC_on_heap_resize = on_gc_heap_resize;
253 }
254
255 #else
256
257 void
258 mono_gc_enable_events (void)
259 {
260 }
261
262 #endif
263
264 int
265 mono_gc_register_root (char *start, size_t size, void *descr)
266 {
267         /* for some strange reason, they want one extra byte on the end */
268         GC_add_roots (start, start + size + 1);
269
270         return TRUE;
271 }
272
273 void
274 mono_gc_deregister_root (char* addr)
275 {
276 #ifndef PLATFORM_WIN32
277         /* FIXME: libgc doesn't define this work win32 for some reason */
278         /* FIXME: No size info */
279         GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
280 #endif
281 }
282
283 void
284 mono_gc_weak_link_add (void **link_addr, MonoObject *obj)
285 {
286         /* libgc requires that we use HIDE_POINTER... */
287         *link_addr = (void*)HIDE_POINTER (obj);
288         GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
289 }
290
291 void
292 mono_gc_weak_link_remove (void **link_addr)
293 {
294         GC_unregister_disappearing_link (link_addr);
295         *link_addr = NULL;
296 }
297
298 MonoObject*
299 mono_gc_weak_link_get (void **link_addr)
300 {
301         MonoObject *obj = REVEAL_POINTER (*link_addr);
302         if (obj == (MonoObject *) -1)
303                 return NULL;
304         return obj;
305 }
306
307 void*
308 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
309 {
310         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
311 }
312
313 void*
314 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
315 {
316         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
317 }
318
319 void*
320 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
321 {
322         /* libgc has no usable support for arrays... */
323         return GC_NO_DESCRIPTOR;
324 }
325
326 void*
327 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
328 {
329 #ifdef HAVE_GC_GCJ_MALLOC
330         /* It seems there are issues when the bitmap doesn't fit: play it safe */
331         if (numbits >= 30)
332                 return GC_NO_DESCRIPTOR;
333         else
334                 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
335 #else
336         return NULL;
337 #endif
338 }
339
340 void*
341 mono_gc_alloc_fixed (size_t size, void *descr)
342 {
343         return GC_MALLOC (size);
344 }
345
346 void
347 mono_gc_free_fixed (void* addr)
348 {
349 }
350
351 int
352 mono_gc_invoke_finalizers (void)
353 {
354         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
355          * the 'mem_freed' variable is not initialized when there are no
356          * objects to finalize, which leads to strange behavior later on.
357          * The check is necessary to work around that bug.
358          */
359         if (GC_should_invoke_finalizers ())
360                 return GC_invoke_finalizers ();
361         return 0;
362 }
363
364 gboolean
365 mono_gc_pending_finalizers (void)
366 {
367         return GC_should_invoke_finalizers ();
368 }
369
370 void
371 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
372 {
373         *(void**)field_ptr = value;
374 }
375
376 void
377 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
378 {
379         *(void**)slot_ptr = value;
380 }
381
382 void
383 mono_gc_wbarrier_arrayref_copy (MonoArray *arr, gpointer slot_ptr, int count)
384 {
385         /* no need to do anything */
386 }
387
388 void
389 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
390 {
391         *(void**)ptr = value;
392 }
393
394 void
395 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
396 {
397 }
398
399 void
400 mono_gc_wbarrier_object (MonoObject *object)
401 {
402 }
403
404 #if defined(USE_INCLUDED_LIBGC) && defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
405 extern __thread MONO_TLS_FAST void* GC_thread_tls;
406 #include "metadata-internals.h"
407
408 static int
409 shift_amount (int v)
410 {
411         int i = 0;
412         while (!(v & (1 << i)))
413                 i++;
414         return i;
415 }
416
417 enum {
418         ATYPE_FREEPTR,
419         ATYPE_FREEPTR_FOR_BOX,
420         ATYPE_NORMAL,
421         ATYPE_GCJ,
422         ATYPE_STRING,
423         ATYPE_NUM
424 };
425
426 static MonoMethod*
427 create_allocator (int atype, int offset)
428 {
429         int index_var, bytes_var, my_fl_var, my_entry_var;
430         guint32 no_freelist_branch, not_small_enough_branch = 0;
431         guint32 size_overflow_branch = 0;
432         MonoMethodBuilder *mb;
433         MonoMethod *res;
434         MonoMethodSignature *csig;
435
436         if (atype == ATYPE_STRING) {
437                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
438                 csig->ret = &mono_defaults.string_class->byval_arg;
439                 csig->params [0] = &mono_defaults.int_class->byval_arg;
440                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
441         } else {
442                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
443                 csig->ret = &mono_defaults.object_class->byval_arg;
444                 csig->params [0] = &mono_defaults.int_class->byval_arg;
445         }
446
447         mb = mono_mb_new (mono_defaults.object_class, "Alloc", MONO_WRAPPER_ALLOC);
448         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
449         if (atype == ATYPE_STRING) {
450                 /* a string alloator method takes the args: (vtable, len) */
451                 /* bytes = (sizeof (MonoString) + ((len + 1) * 2)); */
452                 mono_mb_emit_ldarg (mb, 1);
453                 mono_mb_emit_icon (mb, 1);
454                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
455                 mono_mb_emit_icon (mb, 1);
456                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
457                 // sizeof (MonoString) might include padding
458                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
459                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
460                 mono_mb_emit_stloc (mb, bytes_var);
461         } else {
462                 /* bytes = vtable->klass->instance_size */
463                 mono_mb_emit_ldarg (mb, 0);
464                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
465                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
466                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
467                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
468                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
469                 /* FIXME: assert instance_size stays a 4 byte integer */
470                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_U4);
471                 mono_mb_emit_stloc (mb, bytes_var);
472         }
473
474         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
475         if (atype == ATYPE_STRING) {
476                 /* check for size */
477                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
478                 mono_mb_emit_ldloc (mb, bytes_var);
479                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
480                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
481                 /* check for overflow */
482                 mono_mb_emit_ldloc (mb, bytes_var);
483                 mono_mb_emit_icon (mb, sizeof (MonoString));
484                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
485         }
486
487         /* int index = INDEX_FROM_BYTES(bytes); */
488         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
489         
490         mono_mb_emit_ldloc (mb, bytes_var);
491         mono_mb_emit_icon (mb, GRANULARITY - 1);
492         mono_mb_emit_byte (mb, MONO_CEE_ADD);
493         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
494         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
495         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
496         mono_mb_emit_byte (mb, MONO_CEE_SHL);
497         /* index var is already adjusted into bytes */
498         mono_mb_emit_stloc (mb, index_var);
499
500         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
501         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
502         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
503         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
504         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
505         mono_mb_emit_i4 (mb, offset);
506         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
507                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
508         else if (atype == ATYPE_NORMAL)
509                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
510         else if (atype == ATYPE_GCJ)
511                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
512         else
513                 g_assert_not_reached ();
514         mono_mb_emit_byte (mb, MONO_CEE_ADD);
515         mono_mb_emit_ldloc (mb, index_var);
516         mono_mb_emit_byte (mb, MONO_CEE_ADD);
517         mono_mb_emit_stloc (mb, my_fl_var);
518
519         /* my_entry = *my_fl; */
520         mono_mb_emit_ldloc (mb, my_fl_var);
521         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
522         mono_mb_emit_stloc (mb, my_entry_var);
523
524         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
525         mono_mb_emit_ldloc (mb, my_entry_var);
526         mono_mb_emit_icon (mb, HBLKSIZE);
527         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
528
529         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
530         mono_mb_emit_ldloc (mb, my_fl_var);
531         mono_mb_emit_ldloc (mb, my_entry_var);
532         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
533         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
534
535         /* set the vtable and clear the words in the object */
536         mono_mb_emit_ldloc (mb, my_entry_var);
537         mono_mb_emit_ldarg (mb, 0);
538         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
539
540         if (atype == ATYPE_FREEPTR) {
541                 int start_var, end_var, start_loop;
542                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
543                  */
544                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
545                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
546                 mono_mb_emit_ldloc (mb, my_entry_var);
547                 mono_mb_emit_ldloc (mb, bytes_var);
548                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
549                 mono_mb_emit_stloc (mb, end_var);
550                 mono_mb_emit_ldloc (mb, my_entry_var);
551                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
552                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
553                 mono_mb_emit_stloc (mb, start_var);
554                 /*
555                  * do {
556                  *      *start++ = NULL;
557                  * } while (start < end);
558                  */
559                 start_loop = mono_mb_get_label (mb);
560                 mono_mb_emit_ldloc (mb, start_var);
561                 mono_mb_emit_icon (mb, 0);
562                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
563                 mono_mb_emit_ldloc (mb, start_var);
564                 mono_mb_emit_icon (mb, sizeof (gpointer));
565                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
566                 mono_mb_emit_stloc (mb, start_var);
567
568                 mono_mb_emit_ldloc (mb, start_var);
569                 mono_mb_emit_ldloc (mb, end_var);
570                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
571                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
572         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
573                 /* need to clear just the sync pointer */
574                 mono_mb_emit_ldloc (mb, my_entry_var);
575                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
576                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
577                 mono_mb_emit_icon (mb, 0);
578                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
579         }
580
581         if (atype == ATYPE_STRING) {
582                 /* need to set length and clear the last char */
583                 /* s->length = len; */
584                 mono_mb_emit_ldloc (mb, my_entry_var);
585                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
586                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
587                 mono_mb_emit_ldarg (mb, 1);
588                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
589                 /* s->chars [len] = 0; */
590                 mono_mb_emit_ldloc (mb, my_entry_var);
591                 mono_mb_emit_ldloc (mb, bytes_var);
592                 mono_mb_emit_icon (mb, 2);
593                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
594                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
595                 mono_mb_emit_icon (mb, 0);
596                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
597         }
598
599         /* return my_entry; */
600         mono_mb_emit_ldloc (mb, my_entry_var);
601         mono_mb_emit_byte (mb, MONO_CEE_RET);
602         
603         mono_mb_patch_short_branch (mb, no_freelist_branch);
604         if (not_small_enough_branch > 0)
605                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
606         if (size_overflow_branch > 0)
607                 mono_mb_patch_short_branch (mb, size_overflow_branch);
608         /* the slow path: we just call back into the runtime */
609         if (atype == ATYPE_STRING) {
610                 mono_mb_emit_ldarg (mb, 1);
611                 mono_mb_emit_icall (mb, mono_string_alloc);
612         } else {
613                 mono_mb_emit_ldarg (mb, 0);
614                 mono_mb_emit_icall (mb, mono_object_new_specific);
615         }
616
617         mono_mb_emit_byte (mb, MONO_CEE_RET);
618
619         res = mono_mb_create_method (mb, csig, 8);
620         mono_mb_free (mb);
621         mono_method_get_header (res)->init_locals = FALSE;
622         return res;
623 }
624
625 static MonoMethod* alloc_method_cache [ATYPE_NUM];
626
627 /*
628  * If possible, generate a managed method that can quickly allocate objects in class
629  * @klass. The method will typically have an thread-local inline allocation sequence.
630  * The signature of the called method is:
631  *      object allocate (MonoVTable *vtable)
632  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
633  * keep in sync.
634  * The thread local alloc logic is taken from libgc/pthread_support.c.
635  */
636
637 MonoMethod*
638 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
639 {
640         int offset = -1;
641         int atype;
642         MonoClass *klass = vtable->klass;
643         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
644
645         /*g_print ("thread tls: %d\n", offset);*/
646         if (offset == -1)
647                 return NULL;
648         if (!SMALL_ENOUGH (klass->instance_size))
649                 return NULL;
650         if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
651                 return NULL;
652         if (klass->rank)
653                 return NULL;
654         if (klass->byval_arg.type == MONO_TYPE_STRING) {
655                 atype = ATYPE_STRING;
656         } else if (!klass->has_references) {
657                 if (for_box)
658                         atype = ATYPE_FREEPTR_FOR_BOX;
659                 else
660                         atype = ATYPE_FREEPTR;
661         } else {
662                 return NULL;
663                 /*
664                  * disabled because we currently do a runtime choice anyway, to
665                  * deal with multiple appdomains.
666                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
667                         atype = ATYPE_GCJ;
668                 else
669                         atype = ATYPE_NORMAL;
670                 */
671         }
672         return mono_gc_get_managed_allocator_by_type (atype);
673 }
674
675 /**
676  * mono_gc_get_managed_allocator_id:
677  *
678  *   Return a type for the managed allocator method MANAGED_ALLOC which can later be passed
679  * to mono_gc_get_managed_allocator_by_type () to get back this allocator method. This can be
680  * used by the AOT code to encode references to managed allocator methods.
681  */
682 int
683 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
684 {
685         int i;
686
687         mono_loader_lock ();
688         for (i = 0; i < ATYPE_NUM; ++i) {
689                 if (alloc_method_cache [i] == managed_alloc) {
690                         mono_loader_unlock ();
691                         return i;
692                 }
693         }
694         mono_loader_unlock ();
695
696         return -1;
697 }
698
699 /**
700  * mono_gc_get_managed_allocator_by_type:
701  *
702  *   Return a managed allocator method corresponding to allocator type ATYPE.
703  */
704 MonoMethod*
705 mono_gc_get_managed_allocator_by_type (int atype)
706 {
707         int offset = -1;
708         MonoMethod *res;
709         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
710
711         mono_loader_lock ();
712         res = alloc_method_cache [atype];
713         if (!res)
714                 res = alloc_method_cache [atype] = create_allocator (atype, offset);
715         mono_loader_unlock ();
716         return res;
717 }
718
719 guint32
720 mono_gc_get_managed_allocator_types (void)
721 {
722         return ATYPE_NUM;
723 }
724
725 #else
726
727 MonoMethod*
728 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
729 {
730         return NULL;
731 }
732
733 int
734 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
735 {
736         return -1;
737 }
738
739 MonoMethod*
740 mono_gc_get_managed_allocator_by_type (int atype)
741 {
742         return NULL;
743 }
744
745 guint32
746 mono_gc_get_managed_allocator_types (void)
747 {
748         return 0;
749 }
750
751 #endif
752
753 #endif /* no Boehm GC */
754