System.Drawing: added email to icon and test file headers
[mono.git] / mono / metadata / boehm-gc.c
1 /*
2  * boehm-gc.c: GC implementation using either the installed or included Boehm GC.
3  *
4  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
5  * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
6  * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
7  */
8
9 #include "config.h"
10
11 #include <string.h>
12
13 #define GC_I_HIDE_POINTERS
14 #include <mono/metadata/gc-internal.h>
15 #include <mono/metadata/mono-gc.h>
16 #include <mono/metadata/gc-internal.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/metadata/class-internals.h>
19 #include <mono/metadata/method-builder.h>
20 #include <mono/metadata/opcodes.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/marshal.h>
24 #include <mono/metadata/runtime.h>
25 #include <mono/utils/mono-logger-internal.h>
26 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mono-threads.h>
28 #include <mono/utils/dtrace.h>
29 #include <mono/utils/gc_wrapper.h>
30
31 #if HAVE_BOEHM_GC
32
33 #ifdef USE_INCLUDED_LIBGC
34 #undef TRUE
35 #undef FALSE
36 #define THREAD_LOCAL_ALLOC 1
37 #include "private/pthread_support.h"
38 #endif
39
40 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
41 /*Boehm max heap cannot be smaller than 16MB*/
42 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
43 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
44
45 static gboolean gc_initialized = FALSE;
46
47 static void*
48 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
49
50 static void
51 mono_gc_warning (char *msg, GC_word arg)
52 {
53         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
54 }
55
56 void
57 mono_gc_base_init (void)
58 {
59         MonoThreadInfoCallbacks cb;
60         char *env;
61
62         if (gc_initialized)
63                 return;
64
65         /*
66          * Handle the case when we are called from a thread different from the main thread,
67          * confusing libgc.
68          * FIXME: Move this to libgc where it belongs.
69          *
70          * we used to do this only when running on valgrind,
71          * but it happens also in other setups.
72          */
73 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
74         {
75                 size_t size;
76                 void *sstart;
77                 pthread_attr_t attr;
78                 pthread_getattr_np (pthread_self (), &attr);
79                 pthread_attr_getstack (&attr, &sstart, &size);
80                 pthread_attr_destroy (&attr); 
81                 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
82 #ifdef __ia64__
83                 /*
84                  * The calculation above doesn't seem to work on ia64, also we need to set
85                  * GC_register_stackbottom as well, but don't know how.
86                  */
87 #else
88                 /* apparently with some linuxthreads implementations sstart can be NULL,
89                  * fallback to the more imprecise method (bug# 78096).
90                  */
91                 if (sstart) {
92                         GC_stackbottom = (char*)sstart + size;
93                 } else {
94                         int dummy;
95                         gsize stack_bottom = (gsize)&dummy;
96                         stack_bottom += 4095;
97                         stack_bottom &= ~4095;
98                         GC_stackbottom = (char*)stack_bottom;
99                 }
100 #endif
101         }
102 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
103                 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
104 #elif defined(__OpenBSD__)
105 #  include <pthread_np.h>
106         {
107                 stack_t ss;
108                 int rslt;
109
110                 rslt = pthread_stackseg_np(pthread_self(), &ss);
111                 g_assert (rslt == 0);
112
113                 GC_stackbottom = (char*)ss.ss_sp;
114         }
115 #elif defined(__native_client__)
116         /* Do nothing, GC_stackbottom is set correctly in libgc */
117 #else
118         {
119                 int dummy;
120                 gsize stack_bottom = (gsize)&dummy;
121                 stack_bottom += 4095;
122                 stack_bottom &= ~4095;
123                 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
124                 GC_stackbottom = (char*)stack_bottom;
125         }
126 #endif
127
128 #if !defined(PLATFORM_ANDROID)
129         /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
130         GC_no_dls = TRUE;
131 #endif
132         GC_init ();
133         GC_oom_fn = mono_gc_out_of_memory;
134         GC_set_warn_proc (mono_gc_warning);
135         GC_finalize_on_demand = 1;
136         GC_finalizer_notifier = mono_gc_finalize_notify;
137
138 #ifdef HAVE_GC_GCJ_MALLOC
139         GC_init_gcj_malloc (5, NULL);
140 #endif
141
142 #ifdef HAVE_GC_ALLOW_REGISTER_THREADS
143         GC_allow_register_threads();
144 #endif
145
146         if ((env = getenv ("MONO_GC_PARAMS"))) {
147                 char **ptr, **opts = g_strsplit (env, ",", -1);
148                 for (ptr = opts; *ptr; ++ptr) {
149                         char *opt = *ptr;
150                         if (g_str_has_prefix (opt, "max-heap-size=")) {
151                                 glong max_heap;
152
153                                 opt = strchr (opt, '=') + 1;
154                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
155                                         if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
156                                                 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
157                                                 exit (1);
158                                         }
159                                         GC_set_max_heap_size (max_heap);
160                                 } else {
161                                         fprintf (stderr, "max-heap-size must be an integer.\n");
162                                         exit (1);
163                                 }
164                                 continue;
165                         } else {
166                                 /* Could be a parameter for sgen */
167                                 /*
168                                 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
169                                 fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
170                                 exit (1);
171                                 */
172                         }
173                 }
174                 g_strfreev (opts);
175         }
176
177         memset (&cb, 0, sizeof (cb));
178         cb.thread_register = boehm_thread_register;
179         cb.mono_method_is_critical = (gpointer)mono_runtime_is_critical_method;
180 #ifndef HOST_WIN32
181         cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
182 #endif
183         
184         mono_threads_init (&cb, sizeof (MonoThreadInfo));
185
186         mono_gc_enable_events ();
187         gc_initialized = TRUE;
188 }
189
190 /**
191  * mono_gc_collect:
192  * @generation: GC generation identifier
193  *
194  * Perform a garbage collection for the given generation, higher numbers
195  * mean usually older objects. Collecting a high-numbered generation
196  * implies collecting also the lower-numbered generations.
197  * The maximum value for @generation can be retrieved with a call to
198  * mono_gc_max_generation(), so this function is usually called as:
199  *
200  *      mono_gc_collect (mono_gc_max_generation ());
201  */
202 void
203 mono_gc_collect (int generation)
204 {
205         MONO_PROBE_GC_BEGIN (generation);
206
207         mono_perfcounters->gc_induced++;
208         GC_gcollect ();
209         
210         MONO_PROBE_GC_END (generation);
211 #if defined(ENABLE_DTRACE) && defined(__sun__)
212         /* This works around a dtrace -G problem on Solaris.
213            Limit its actual use to when the probe is enabled. */
214         if (MONO_PROBE_GC_END_ENABLED ())
215                 sleep(0);
216 #endif
217 }
218
219 /**
220  * mono_gc_max_generation:
221  *
222  * Get the maximum generation number used by the current garbage
223  * collector. The value will be 0 for the Boehm collector, 1 or more
224  * for the generational collectors.
225  *
226  * Returns: the maximum generation number.
227  */
228 int
229 mono_gc_max_generation (void)
230 {
231         return 0;
232 }
233
234 /**
235  * mono_gc_get_generation:
236  * @object: a managed object
237  *
238  * Get the garbage collector's generation that @object belongs to.
239  * Use this has a hint only.
240  *
241  * Returns: a garbage collector generation number
242  */
243 int
244 mono_gc_get_generation  (MonoObject *object)
245 {
246         return 0;
247 }
248
249 /**
250  * mono_gc_collection_count:
251  * @generation: a GC generation number
252  *
253  * Get how many times a garbage collection has been performed
254  * for the given @generation number.
255  *
256  * Returns: the number of garbage collections
257  */
258 int
259 mono_gc_collection_count (int generation)
260 {
261         return GC_gc_no;
262 }
263
264 /**
265  * mono_gc_add_memory_pressure:
266  * @value: amount of bytes
267  *
268  * Adjust the garbage collector's view of how many bytes of memory
269  * are indirectly referenced by managed objects (for example unmanaged
270  * memory holding image or other binary data).
271  * This is a hint only to the garbage collector algorithm.
272  * Note that negative amounts of @value will decrease the memory
273  * pressure.
274  */
275 void
276 mono_gc_add_memory_pressure (gint64 value)
277 {
278 }
279
280 /**
281  * mono_gc_get_used_size:
282  *
283  * Get the approximate amount of memory used by managed objects.
284  *
285  * Returns: the amount of memory used in bytes
286  */
287 int64_t
288 mono_gc_get_used_size (void)
289 {
290         return GC_get_heap_size () - GC_get_free_bytes ();
291 }
292
293 /**
294  * mono_gc_get_heap_size:
295  *
296  * Get the amount of memory used by the garbage collector.
297  *
298  * Returns: the size of the heap in bytes
299  */
300 int64_t
301 mono_gc_get_heap_size (void)
302 {
303         return GC_get_heap_size ();
304 }
305
306 void
307 mono_gc_disable (void)
308 {
309 #ifdef HAVE_GC_ENABLE
310         GC_disable ();
311 #else
312         g_assert_not_reached ();
313 #endif
314 }
315
316 void
317 mono_gc_enable (void)
318 {
319 #ifdef HAVE_GC_ENABLE
320         GC_enable ();
321 #else
322         g_assert_not_reached ();
323 #endif
324 }
325
326 gboolean
327 mono_gc_is_gc_thread (void)
328 {
329 #if GC_VERSION_MAJOR >= 7
330         return TRUE;
331 #elif defined(USE_INCLUDED_LIBGC)
332         return GC_thread_is_registered ();
333 #else
334         return TRUE;
335 #endif
336 }
337
338 extern int GC_thread_register_foreign (void *base_addr);
339
340 gboolean
341 mono_gc_register_thread (void *baseptr)
342 {
343         return mono_thread_info_attach (baseptr) != NULL;
344 }
345
346 static void*
347 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
348 {
349 #if GC_VERSION_MAJOR >= 7
350         struct GC_stack_base sb;
351         int res;
352
353         res = GC_get_stack_base (&sb);
354         if (res != GC_SUCCESS) {
355                 sb.mem_base = baseptr;
356 #ifdef __ia64__
357                 /* Can't determine the register stack bounds */
358                 g_error ("mono_gc_register_thread failed ().\n");
359 #endif
360         }
361         res = GC_register_my_thread (&sb);
362         if ((res != GC_SUCCESS) && (res != GC_DUPLICATE)) {
363                 g_warning ("GC_register_my_thread () failed.\n");
364                 return NULL;
365         }
366         return info;
367 #else
368         if (mono_gc_is_gc_thread())
369                 return info;
370 #if defined(USE_INCLUDED_LIBGC) && !defined(HOST_WIN32)
371         return GC_thread_register_foreign (baseptr) ? info : NULL;
372 #else
373         return NULL;
374 #endif
375 #endif
376 }
377
378 gboolean
379 mono_object_is_alive (MonoObject* o)
380 {
381 #ifdef USE_INCLUDED_LIBGC
382         return GC_is_marked ((gpointer)o);
383 #else
384         return TRUE;
385 #endif
386 }
387
388 int
389 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
390 {
391         return 1;
392 }
393
394 #ifdef USE_INCLUDED_LIBGC
395
396 static gint64 gc_start_time;
397
398 static void
399 on_gc_notification (GCEventType event)
400 {
401         MonoGCEvent e = (MonoGCEvent)event;
402
403         if (e == MONO_GC_EVENT_PRE_STOP_WORLD) 
404                 mono_thread_info_suspend_lock ();
405         else if (e == MONO_GC_EVENT_POST_START_WORLD)
406                 mono_thread_info_suspend_unlock ();
407         
408         if (e == MONO_GC_EVENT_START) {
409                 if (mono_perfcounters)
410                         mono_perfcounters->gc_collections0++;
411                 mono_stats.major_gc_count ++;
412                 gc_start_time = mono_100ns_ticks ();
413         } else if (e == MONO_GC_EVENT_END) {
414                 if (mono_perfcounters) {
415                         guint64 heap_size = GC_get_heap_size ();
416                         guint64 used_size = heap_size - GC_get_free_bytes ();
417                         mono_perfcounters->gc_total_bytes = used_size;
418                         mono_perfcounters->gc_committed_bytes = heap_size;
419                         mono_perfcounters->gc_reserved_bytes = heap_size;
420                         mono_perfcounters->gc_gen0size = heap_size;
421                 }
422                 mono_stats.major_gc_time_usecs += (mono_100ns_ticks () - gc_start_time) / 10;
423                 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
424         }
425         mono_profiler_gc_event (e, 0);
426 }
427  
428 static void
429 on_gc_heap_resize (size_t new_size)
430 {
431         guint64 heap_size = GC_get_heap_size ();
432         if (mono_perfcounters) {
433                 mono_perfcounters->gc_committed_bytes = heap_size;
434                 mono_perfcounters->gc_reserved_bytes = heap_size;
435                 mono_perfcounters->gc_gen0size = heap_size;
436         }
437         mono_profiler_gc_heap_resize (new_size);
438 }
439
440 void
441 mono_gc_enable_events (void)
442 {
443         GC_notify_event = on_gc_notification;
444         GC_on_heap_resize = on_gc_heap_resize;
445 }
446
447 #else
448
449 void
450 mono_gc_enable_events (void)
451 {
452 }
453
454 #endif
455
456 int
457 mono_gc_register_root (char *start, size_t size, void *descr)
458 {
459         /* for some strange reason, they want one extra byte on the end */
460         GC_add_roots (start, start + size + 1);
461
462         return TRUE;
463 }
464
465 void
466 mono_gc_deregister_root (char* addr)
467 {
468 #ifndef HOST_WIN32
469         /* FIXME: libgc doesn't define this work win32 for some reason */
470         /* FIXME: No size info */
471         GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
472 #endif
473 }
474
475 void
476 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
477 {
478         /* libgc requires that we use HIDE_POINTER... */
479         *link_addr = (void*)HIDE_POINTER (obj);
480         GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
481 }
482
483 void
484 mono_gc_weak_link_remove (void **link_addr)
485 {
486         GC_unregister_disappearing_link (link_addr);
487         *link_addr = NULL;
488 }
489
490 static gpointer
491 reveal_link (gpointer link_addr)
492 {
493         void **link_a = link_addr;
494         return REVEAL_POINTER (*link_a);
495 }
496
497 MonoObject*
498 mono_gc_weak_link_get (void **link_addr)
499 {
500         MonoObject *obj = GC_call_with_alloc_lock (reveal_link, link_addr);
501         if (obj == (MonoObject *) -1)
502                 return NULL;
503         return obj;
504 }
505
506 void*
507 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
508 {
509         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
510 }
511
512 void*
513 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
514 {
515         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
516 }
517
518 void*
519 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
520 {
521         /* libgc has no usable support for arrays... */
522         return GC_NO_DESCRIPTOR;
523 }
524
525 void*
526 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
527 {
528 #ifdef HAVE_GC_GCJ_MALLOC
529         /* It seems there are issues when the bitmap doesn't fit: play it safe */
530         if (numbits >= 30)
531                 return GC_NO_DESCRIPTOR;
532         else
533                 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
534 #else
535         return NULL;
536 #endif
537 }
538
539 void*
540 mono_gc_make_root_descr_all_refs (int numbits)
541 {
542         return NULL;
543 }
544
545 void*
546 mono_gc_alloc_fixed (size_t size, void *descr)
547 {
548         /* To help track down typed allocation bugs */
549         /*
550         static int count;
551         count ++;
552         if (count == atoi (getenv ("COUNT2")))
553                 printf ("HIT!\n");
554         if (count > atoi (getenv ("COUNT2")))
555                 return GC_MALLOC (size);
556         */
557
558         if (descr)
559                 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
560         else
561                 return GC_MALLOC (size);
562 }
563
564 void
565 mono_gc_free_fixed (void* addr)
566 {
567 }
568
569 int
570 mono_gc_invoke_finalizers (void)
571 {
572         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
573          * the 'mem_freed' variable is not initialized when there are no
574          * objects to finalize, which leads to strange behavior later on.
575          * The check is necessary to work around that bug.
576          */
577         if (GC_should_invoke_finalizers ())
578                 return GC_invoke_finalizers ();
579         return 0;
580 }
581
582 gboolean
583 mono_gc_pending_finalizers (void)
584 {
585         return GC_should_invoke_finalizers ();
586 }
587
588 /*
589  * LOCKING: Assumes the domain_finalizers lock is held.
590  */
591 static void
592 add_weak_track_handle_internal (MonoDomain *domain, MonoObject *obj, guint32 gchandle)
593 {
594         GSList *refs;
595
596         if (!domain->track_resurrection_objects_hash)
597                 domain->track_resurrection_objects_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
598
599         refs = g_hash_table_lookup (domain->track_resurrection_objects_hash, obj);
600         refs = g_slist_prepend (refs, GUINT_TO_POINTER (gchandle));
601         g_hash_table_insert (domain->track_resurrection_objects_hash, obj, refs);
602 }
603
604 void
605 mono_gc_add_weak_track_handle (MonoObject *obj, guint32 handle)
606 {
607         MonoDomain *domain;
608
609         if (!obj)
610                 return;
611
612         domain = mono_object_get_domain (obj);
613
614         mono_domain_finalizers_lock (domain);
615
616         add_weak_track_handle_internal (domain, obj, handle);
617
618         g_hash_table_insert (domain->track_resurrection_handles_hash, GUINT_TO_POINTER (handle), obj);
619
620         mono_domain_finalizers_unlock (domain);
621 }
622
623 /*
624  * LOCKING: Assumes the domain_finalizers lock is held.
625  */
626 static void
627 remove_weak_track_handle_internal (MonoDomain *domain, MonoObject *obj, guint32 gchandle)
628 {
629         GSList *refs;
630
631         if (!domain->track_resurrection_objects_hash)
632                 return;
633
634         refs = g_hash_table_lookup (domain->track_resurrection_objects_hash, obj);
635         refs = g_slist_remove (refs, GUINT_TO_POINTER (gchandle));
636         g_hash_table_insert (domain->track_resurrection_objects_hash, obj, refs);
637 }
638
639 void
640 mono_gc_change_weak_track_handle (MonoObject *old_obj, MonoObject *obj, guint32 gchandle)
641 {
642         MonoDomain *domain = mono_domain_get ();
643
644         mono_domain_finalizers_lock (domain);
645
646         if (old_obj)
647                 remove_weak_track_handle_internal (domain, old_obj, gchandle);
648         if (obj)
649                 add_weak_track_handle_internal (domain, obj, gchandle);
650
651         mono_domain_finalizers_unlock (domain);
652 }
653
654 void
655 mono_gc_remove_weak_track_handle (guint32 gchandle)
656 {
657         MonoDomain *domain = mono_domain_get ();
658         MonoObject *obj;
659
660         /* Clean our entries in the two hashes in MonoDomain */
661
662         mono_domain_finalizers_lock (domain);
663
664         /* Get the original object this handle pointed to */
665         obj = g_hash_table_lookup (domain->track_resurrection_handles_hash, GUINT_TO_POINTER (gchandle));
666         if (obj) {
667                 g_hash_table_remove (domain->track_resurrection_handles_hash, GUINT_TO_POINTER (gchandle));
668
669                 remove_weak_track_handle_internal (domain, obj, gchandle);
670         }
671
672         mono_domain_finalizers_unlock (domain);
673 }
674
675 GSList*
676 mono_gc_remove_weak_track_object (MonoDomain *domain, MonoObject *obj)
677 {
678         GSList *refs = NULL;
679
680         if (domain->track_resurrection_objects_hash) {
681                 refs = g_hash_table_lookup (domain->track_resurrection_objects_hash, obj);
682
683                 if (refs)
684                         /*
685                          * Since we don't run finalizers again for resurrected objects,
686                          * no need to keep these around.
687                          */
688                         g_hash_table_remove (domain->track_resurrection_objects_hash, obj);
689         }
690
691         return refs;
692 }
693
694 void
695 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
696 {
697         *(void**)field_ptr = value;
698 }
699
700 void
701 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
702 {
703         *(void**)slot_ptr = value;
704 }
705
706 void
707 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
708 {
709         mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
710 }
711
712 void
713 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
714 {
715         *(void**)ptr = value;
716 }
717
718 void
719 mono_gc_wbarrier_generic_nostore (gpointer ptr)
720 {
721 }
722
723 void
724 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
725 {
726         mono_gc_memmove (dest, src, count * mono_class_value_size (klass, NULL));
727 }
728
729 void
730 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
731 {
732         /* do not copy the sync state */
733         mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
734                         mono_object_class (obj)->instance_size - sizeof (MonoObject));
735 }
736
737 void
738 mono_gc_clear_domain (MonoDomain *domain)
739 {
740 }
741
742 int
743 mono_gc_get_suspend_signal (void)
744 {
745 #ifdef USE_INCLUDED_GC
746         return GC_get_suspend_signal ();
747 #else
748         return -1;
749 #endif
750 }
751
752 #if defined(USE_INCLUDED_LIBGC) && defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
753 extern __thread MONO_TLS_FAST void* GC_thread_tls;
754 #include "metadata-internals.h"
755
756 static int
757 shift_amount (int v)
758 {
759         int i = 0;
760         while (!(v & (1 << i)))
761                 i++;
762         return i;
763 }
764
765 enum {
766         ATYPE_FREEPTR,
767         ATYPE_FREEPTR_FOR_BOX,
768         ATYPE_NORMAL,
769         ATYPE_GCJ,
770         ATYPE_STRING,
771         ATYPE_NUM
772 };
773
774 static MonoMethod*
775 create_allocator (int atype, int offset)
776 {
777         int index_var, bytes_var, my_fl_var, my_entry_var;
778         guint32 no_freelist_branch, not_small_enough_branch = 0;
779         guint32 size_overflow_branch = 0;
780         MonoMethodBuilder *mb;
781         MonoMethod *res;
782         MonoMethodSignature *csig;
783         AllocatorWrapperInfo *info;
784
785         if (atype == ATYPE_STRING) {
786                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
787                 csig->ret = &mono_defaults.string_class->byval_arg;
788                 csig->params [0] = &mono_defaults.int_class->byval_arg;
789                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
790         } else {
791                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
792                 csig->ret = &mono_defaults.object_class->byval_arg;
793                 csig->params [0] = &mono_defaults.int_class->byval_arg;
794         }
795
796         mb = mono_mb_new (mono_defaults.object_class, "Alloc", MONO_WRAPPER_ALLOC);
797         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
798         if (atype == ATYPE_STRING) {
799                 /* a string alloator method takes the args: (vtable, len) */
800                 /* bytes = (sizeof (MonoString) + ((len + 1) * 2)); */
801                 mono_mb_emit_ldarg (mb, 1);
802                 mono_mb_emit_icon (mb, 1);
803                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
804                 mono_mb_emit_icon (mb, 1);
805                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
806                 // sizeof (MonoString) might include padding
807                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
808                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
809                 mono_mb_emit_stloc (mb, bytes_var);
810         } else {
811                 /* bytes = vtable->klass->instance_size */
812                 mono_mb_emit_ldarg (mb, 0);
813                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
814                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
815                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
816                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
817                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
818                 /* FIXME: assert instance_size stays a 4 byte integer */
819                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_U4);
820                 mono_mb_emit_stloc (mb, bytes_var);
821         }
822
823         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
824         if (atype == ATYPE_STRING) {
825                 /* check for size */
826                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
827                 mono_mb_emit_ldloc (mb, bytes_var);
828                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
829                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
830                 /* check for overflow */
831                 mono_mb_emit_ldloc (mb, bytes_var);
832                 mono_mb_emit_icon (mb, sizeof (MonoString));
833                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
834         }
835
836         /* int index = INDEX_FROM_BYTES(bytes); */
837         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
838         
839         mono_mb_emit_ldloc (mb, bytes_var);
840         mono_mb_emit_icon (mb, GRANULARITY - 1);
841         mono_mb_emit_byte (mb, MONO_CEE_ADD);
842         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
843         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
844         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
845         mono_mb_emit_byte (mb, MONO_CEE_SHL);
846         /* index var is already adjusted into bytes */
847         mono_mb_emit_stloc (mb, index_var);
848
849         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
850         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
851         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
852         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
853         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
854         mono_mb_emit_i4 (mb, offset);
855         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
856                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
857         else if (atype == ATYPE_NORMAL)
858                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
859         else if (atype == ATYPE_GCJ)
860                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
861         else
862                 g_assert_not_reached ();
863         mono_mb_emit_byte (mb, MONO_CEE_ADD);
864         mono_mb_emit_ldloc (mb, index_var);
865         mono_mb_emit_byte (mb, MONO_CEE_ADD);
866         mono_mb_emit_stloc (mb, my_fl_var);
867
868         /* my_entry = *my_fl; */
869         mono_mb_emit_ldloc (mb, my_fl_var);
870         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
871         mono_mb_emit_stloc (mb, my_entry_var);
872
873         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
874         mono_mb_emit_ldloc (mb, my_entry_var);
875         mono_mb_emit_icon (mb, HBLKSIZE);
876         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
877
878         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
879         mono_mb_emit_ldloc (mb, my_fl_var);
880         mono_mb_emit_ldloc (mb, my_entry_var);
881         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
882         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
883
884         /* set the vtable and clear the words in the object */
885         mono_mb_emit_ldloc (mb, my_entry_var);
886         mono_mb_emit_ldarg (mb, 0);
887         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
888
889         if (atype == ATYPE_FREEPTR) {
890                 int start_var, end_var, start_loop;
891                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
892                  */
893                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
894                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
895                 mono_mb_emit_ldloc (mb, my_entry_var);
896                 mono_mb_emit_ldloc (mb, bytes_var);
897                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
898                 mono_mb_emit_stloc (mb, end_var);
899                 mono_mb_emit_ldloc (mb, my_entry_var);
900                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
901                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
902                 mono_mb_emit_stloc (mb, start_var);
903                 /*
904                  * do {
905                  *      *start++ = NULL;
906                  * } while (start < end);
907                  */
908                 start_loop = mono_mb_get_label (mb);
909                 mono_mb_emit_ldloc (mb, start_var);
910                 mono_mb_emit_icon (mb, 0);
911                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
912                 mono_mb_emit_ldloc (mb, start_var);
913                 mono_mb_emit_icon (mb, sizeof (gpointer));
914                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
915                 mono_mb_emit_stloc (mb, start_var);
916
917                 mono_mb_emit_ldloc (mb, start_var);
918                 mono_mb_emit_ldloc (mb, end_var);
919                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
920                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
921         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
922                 /* need to clear just the sync pointer */
923                 mono_mb_emit_ldloc (mb, my_entry_var);
924                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
925                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
926                 mono_mb_emit_icon (mb, 0);
927                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
928         }
929
930         if (atype == ATYPE_STRING) {
931                 /* need to set length and clear the last char */
932                 /* s->length = len; */
933                 mono_mb_emit_ldloc (mb, my_entry_var);
934                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
935                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
936                 mono_mb_emit_ldarg (mb, 1);
937                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
938                 /* s->chars [len] = 0; */
939                 mono_mb_emit_ldloc (mb, my_entry_var);
940                 mono_mb_emit_ldloc (mb, bytes_var);
941                 mono_mb_emit_icon (mb, 2);
942                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
943                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
944                 mono_mb_emit_icon (mb, 0);
945                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
946         }
947
948         /* return my_entry; */
949         mono_mb_emit_ldloc (mb, my_entry_var);
950         mono_mb_emit_byte (mb, MONO_CEE_RET);
951         
952         mono_mb_patch_short_branch (mb, no_freelist_branch);
953         if (not_small_enough_branch > 0)
954                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
955         if (size_overflow_branch > 0)
956                 mono_mb_patch_short_branch (mb, size_overflow_branch);
957         /* the slow path: we just call back into the runtime */
958         if (atype == ATYPE_STRING) {
959                 mono_mb_emit_ldarg (mb, 1);
960                 mono_mb_emit_icall (mb, mono_string_alloc);
961         } else {
962                 mono_mb_emit_ldarg (mb, 0);
963                 mono_mb_emit_icall (mb, mono_object_new_specific);
964         }
965
966         mono_mb_emit_byte (mb, MONO_CEE_RET);
967
968         res = mono_mb_create_method (mb, csig, 8);
969         mono_mb_free (mb);
970         mono_method_get_header (res)->init_locals = FALSE;
971
972         info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
973         info->gc_name = "boehm";
974         info->alloc_type = atype;
975         mono_marshal_set_wrapper_info (res, info);
976
977         return res;
978 }
979
980 static MonoMethod* alloc_method_cache [ATYPE_NUM];
981
982 static G_GNUC_UNUSED gboolean
983 mono_gc_is_critical_method (MonoMethod *method)
984 {
985         int i;
986
987         for (i = 0; i < ATYPE_NUM; ++i)
988                 if (method == alloc_method_cache [i])
989                         return TRUE;
990
991         return FALSE;
992 }
993
994 /*
995  * If possible, generate a managed method that can quickly allocate objects in class
996  * @klass. The method will typically have an thread-local inline allocation sequence.
997  * The signature of the called method is:
998  *      object allocate (MonoVTable *vtable)
999  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1000  * keep in sync.
1001  * The thread local alloc logic is taken from libgc/pthread_support.c.
1002  */
1003
1004 MonoMethod*
1005 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
1006 {
1007         int offset = -1;
1008         int atype;
1009         MonoClass *klass = vtable->klass;
1010         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1011
1012         /*g_print ("thread tls: %d\n", offset);*/
1013         if (offset == -1)
1014                 return NULL;
1015         if (!SMALL_ENOUGH (klass->instance_size))
1016                 return NULL;
1017         if (mono_class_has_finalizer (klass) || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
1018                 return NULL;
1019         if (klass->rank)
1020                 return NULL;
1021         if (klass->byval_arg.type == MONO_TYPE_STRING) {
1022                 atype = ATYPE_STRING;
1023         } else if (!klass->has_references) {
1024                 if (for_box)
1025                         atype = ATYPE_FREEPTR_FOR_BOX;
1026                 else
1027                         atype = ATYPE_FREEPTR;
1028         } else {
1029                 return NULL;
1030                 /*
1031                  * disabled because we currently do a runtime choice anyway, to
1032                  * deal with multiple appdomains.
1033                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1034                         atype = ATYPE_GCJ;
1035                 else
1036                         atype = ATYPE_NORMAL;
1037                 */
1038         }
1039         return mono_gc_get_managed_allocator_by_type (atype);
1040 }
1041
1042 MonoMethod*
1043 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
1044 {
1045         return NULL;
1046 }
1047
1048 /**
1049  * mono_gc_get_managed_allocator_by_type:
1050  *
1051  *   Return a managed allocator method corresponding to allocator type ATYPE.
1052  */
1053 MonoMethod*
1054 mono_gc_get_managed_allocator_by_type (int atype)
1055 {
1056         int offset = -1;
1057         MonoMethod *res;
1058         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1059
1060         mono_loader_lock ();
1061         res = alloc_method_cache [atype];
1062         if (!res)
1063                 res = alloc_method_cache [atype] = create_allocator (atype, offset);
1064         mono_loader_unlock ();
1065         return res;
1066 }
1067
1068 guint32
1069 mono_gc_get_managed_allocator_types (void)
1070 {
1071         return ATYPE_NUM;
1072 }
1073
1074 MonoMethod*
1075 mono_gc_get_write_barrier (void)
1076 {
1077         g_assert_not_reached ();
1078         return NULL;
1079 }
1080
1081 #else
1082
1083 static G_GNUC_UNUSED gboolean
1084 mono_gc_is_critical_method (MonoMethod *method)
1085 {
1086         return FALSE;
1087 }
1088
1089 MonoMethod*
1090 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
1091 {
1092         return NULL;
1093 }
1094
1095 MonoMethod*
1096 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
1097 {
1098         return NULL;
1099 }
1100
1101 MonoMethod*
1102 mono_gc_get_managed_allocator_by_type (int atype)
1103 {
1104         return NULL;
1105 }
1106
1107 guint32
1108 mono_gc_get_managed_allocator_types (void)
1109 {
1110         return 0;
1111 }
1112
1113 MonoMethod*
1114 mono_gc_get_write_barrier (void)
1115 {
1116         g_assert_not_reached ();
1117         return NULL;
1118 }
1119
1120 #endif
1121
1122 const char *
1123 mono_gc_get_gc_name (void)
1124 {
1125         return "boehm";
1126 }
1127
1128 void*
1129 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1130 {
1131         return GC_call_with_alloc_lock (func, data);
1132 }
1133
1134 char*
1135 mono_gc_get_description (void)
1136 {
1137         return g_strdup (DEFAULT_GC_NAME);
1138 }
1139
1140 void
1141 mono_gc_set_desktop_mode (void)
1142 {
1143         GC_dont_expand = 1;
1144 }
1145
1146 gboolean
1147 mono_gc_is_moving (void)
1148 {
1149         return FALSE;
1150 }
1151
1152 gboolean
1153 mono_gc_is_disabled (void)
1154 {
1155         if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1156                 return TRUE;
1157         else
1158                 return FALSE;
1159 }
1160
1161 void
1162 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1163 {
1164         g_assert_not_reached ();
1165 }
1166
1167
1168 guint8*
1169 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1170 {
1171         g_assert_not_reached ();
1172         return NULL;
1173 }
1174
1175 void*
1176 mono_gc_get_nursery (int *shift_bits, size_t *size)
1177 {
1178         return NULL;
1179 }
1180
1181 void
1182 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1183 {
1184 }
1185
1186 gboolean
1187 mono_gc_precise_stack_mark_enabled (void)
1188 {
1189         return FALSE;
1190 }
1191
1192 FILE *
1193 mono_gc_get_logfile (void)
1194 {
1195         return NULL;
1196 }
1197
1198 void
1199 mono_gc_conservatively_scan_area (void *start, void *end)
1200 {
1201         g_assert_not_reached ();
1202 }
1203
1204 void *
1205 mono_gc_scan_object (void *obj)
1206 {
1207         g_assert_not_reached ();
1208         return NULL;
1209 }
1210
1211 gsize*
1212 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1213 {
1214         g_assert_not_reached ();
1215         return NULL;
1216 }
1217
1218 void
1219 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1220 {
1221 }
1222
1223 void
1224 mono_gc_set_stack_end (void *stack_end)
1225 {
1226 }
1227
1228 void mono_gc_set_skip_thread (gboolean value)
1229 {
1230 }
1231
1232 /*
1233  * These will call the redefined versions in libgc.
1234  */
1235
1236 #ifndef HOST_WIN32
1237
1238 int
1239 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1240 {
1241         return pthread_create (new_thread, attr, start_routine, arg);
1242 }
1243
1244 int
1245 mono_gc_pthread_join (pthread_t thread, void **retval)
1246 {
1247         return pthread_join (thread, retval);
1248 }
1249
1250 int
1251 mono_gc_pthread_detach (pthread_t thread)
1252 {
1253         return pthread_detach (thread);
1254 }
1255
1256 void
1257 mono_gc_pthread_exit (void *retval)
1258 {
1259         pthread_exit (retval);
1260 }
1261
1262 #endif
1263
1264 #ifdef HOST_WIN32
1265 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1266 {
1267 #ifdef USE_INCLUDED_LIBGC
1268         return GC_DllMain (module_handle, reason, reserved);
1269 #else
1270         return TRUE;
1271 #endif
1272 }
1273 #endif
1274
1275 guint
1276 mono_gc_get_vtable_bits (MonoClass *class)
1277 {
1278         return 0;
1279 }
1280
1281 #endif /* no Boehm GC */