[metadata] split IL generation code into seperate compilation units. (#7487)
[mono-project.git] / mono / metadata / boehm-gc.c
blob2e99dd9bb9e2e8aca78537e4c88c9a2e7b98f7a3
1 /**
2 * \file
3 * GC implementation using either the installed or included Boehm GC.
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 */
11 #include "config.h"
13 #include <string.h>
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/method-builder-ilgen.h>
22 #include <mono/metadata/method-builder-ilgen-internals.h>
23 #include <mono/metadata/opcodes.h>
24 #include <mono/metadata/domain-internals.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/marshal.h>
27 #include <mono/metadata/runtime.h>
28 #include <mono/metadata/handle.h>
29 #include <mono/metadata/sgen-toggleref.h>
30 #include <mono/metadata/w32handle.h>
31 #include <mono/utils/atomic.h>
32 #include <mono/utils/mono-logger-internals.h>
33 #include <mono/utils/mono-memory-model.h>
34 #include <mono/utils/mono-time.h>
35 #include <mono/utils/mono-threads.h>
36 #include <mono/utils/dtrace.h>
37 #include <mono/utils/gc_wrapper.h>
38 #include <mono/utils/mono-os-mutex.h>
39 #include <mono/utils/mono-counters.h>
40 #include <mono/utils/mono-compiler.h>
41 #include <mono/utils/unlocked.h>
43 #if HAVE_BOEHM_GC
45 #undef TRUE
46 #undef FALSE
47 #define THREAD_LOCAL_ALLOC 1
48 #include "private/pthread_support.h"
50 #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
51 void *pthread_get_stackaddr_np(pthread_t);
52 #endif
54 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
55 /*Boehm max heap cannot be smaller than 16MB*/
56 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
57 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
59 static gboolean gc_initialized = FALSE;
60 static mono_mutex_t mono_gc_lock;
62 typedef void (*GC_push_other_roots_proc)(void);
64 static GC_push_other_roots_proc default_push_other_roots;
65 static GHashTable *roots;
67 static void
68 mono_push_other_roots(void);
70 static void
71 register_test_toggleref_callback (void);
73 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
74 static MonoGCFinalizerCallbacks fin_callbacks;
76 /* GC Handles */
78 static mono_mutex_t handle_section;
79 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
80 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
82 typedef struct {
83 guint32 *bitmap;
84 gpointer *entries;
85 guint32 size;
86 guint8 type;
87 guint slot_hint : 24; /* starting slot for search in bitmap */
88 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
89 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
90 guint16 *domain_ids;
91 } HandleData;
93 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
95 /* weak and weak-track arrays will be allocated in malloc memory
97 static HandleData gc_handles [] = {
98 EMPTY_HANDLE_DATA (HANDLE_WEAK),
99 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
100 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
101 EMPTY_HANDLE_DATA (HANDLE_PINNED)
104 static void
105 mono_gc_warning (char *msg, GC_word arg)
107 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
110 static void on_gc_notification (GC_EventType event);
111 static void on_gc_heap_resize (size_t new_size);
113 void
114 mono_gc_base_init (void)
116 char *env;
118 if (gc_initialized)
119 return;
121 mono_counters_init ();
123 #ifndef HOST_WIN32
124 mono_w32handle_init ();
125 #endif
128 * Handle the case when we are called from a thread different from the main thread,
129 * confusing libgc.
130 * FIXME: Move this to libgc where it belongs.
132 * we used to do this only when running on valgrind,
133 * but it happens also in other setups.
135 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
137 size_t size;
138 void *sstart;
139 pthread_attr_t attr;
140 pthread_getattr_np (pthread_self (), &attr);
141 pthread_attr_getstack (&attr, &sstart, &size);
142 pthread_attr_destroy (&attr);
143 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
144 /* apparently with some linuxthreads implementations sstart can be NULL,
145 * fallback to the more imprecise method (bug# 78096).
147 if (sstart) {
148 GC_stackbottom = (char*)sstart + size;
149 } else {
150 int dummy;
151 gsize stack_bottom = (gsize)&dummy;
152 stack_bottom += 4095;
153 stack_bottom &= ~4095;
154 GC_stackbottom = (char*)stack_bottom;
157 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
158 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
159 #elif defined(__OpenBSD__)
160 # include <pthread_np.h>
162 stack_t ss;
163 int rslt;
165 rslt = pthread_stackseg_np(pthread_self(), &ss);
166 g_assert (rslt == 0);
168 GC_stackbottom = (char*)ss.ss_sp;
170 #else
172 int dummy;
173 gsize stack_bottom = (gsize)&dummy;
174 stack_bottom += 4095;
175 stack_bottom &= ~4095;
176 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
177 GC_stackbottom = (char*)stack_bottom;
179 #endif
181 roots = g_hash_table_new (NULL, NULL);
182 default_push_other_roots = GC_push_other_roots;
183 GC_push_other_roots = mono_push_other_roots;
185 #if !defined(HOST_ANDROID)
186 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
187 GC_no_dls = TRUE;
188 #endif
190 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
191 char **opts = g_strsplit (env, ",", -1);
192 for (char **ptr = opts; ptr && *ptr; ptr ++) {
193 char *opt = *ptr;
194 if (!strcmp (opt, "do-not-finalize")) {
195 mono_do_not_finalize = 1;
196 } else if (!strcmp (opt, "log-finalizers")) {
197 log_finalizers = 1;
200 g_free (env);
204 GC_init ();
206 GC_set_warn_proc (mono_gc_warning);
207 GC_finalize_on_demand = 1;
208 GC_finalizer_notifier = mono_gc_finalize_notify;
210 GC_init_gcj_malloc (5, NULL);
211 GC_allow_register_threads ();
213 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
214 char **ptr, **opts = g_strsplit (env, ",", -1);
215 for (ptr = opts; *ptr; ++ptr) {
216 char *opt = *ptr;
217 if (g_str_has_prefix (opt, "max-heap-size=")) {
218 size_t max_heap;
220 opt = strchr (opt, '=') + 1;
221 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
222 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
223 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
224 exit (1);
226 GC_set_max_heap_size (max_heap);
227 } else {
228 fprintf (stderr, "max-heap-size must be an integer.\n");
229 exit (1);
231 continue;
232 } else if (g_str_has_prefix (opt, "toggleref-test")) {
233 register_test_toggleref_callback ();
234 continue;
235 } else {
236 /* Could be a parameter for sgen */
238 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
239 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
240 exit (1);
244 g_free (env);
245 g_strfreev (opts);
248 mono_thread_callbacks_init ();
249 mono_thread_info_init (sizeof (MonoThreadInfo));
250 mono_os_mutex_init (&mono_gc_lock);
251 mono_os_mutex_init_recursive (&handle_section);
253 mono_thread_info_attach ();
255 GC_set_on_collection_event (on_gc_notification);
256 GC_on_heap_resize = on_gc_heap_resize;
258 gc_initialized = TRUE;
261 void
262 mono_gc_base_cleanup (void)
264 GC_finalizer_notifier = NULL;
268 * mono_gc_collect:
269 * \param generation GC generation identifier
271 * Perform a garbage collection for the given generation, higher numbers
272 * mean usually older objects. Collecting a high-numbered generation
273 * implies collecting also the lower-numbered generations.
274 * The maximum value for \p generation can be retrieved with a call to
275 * \c mono_gc_max_generation, so this function is usually called as:
277 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
279 void
280 mono_gc_collect (int generation)
282 #ifndef DISABLE_PERFCOUNTERS
283 mono_atomic_inc_i32 (&mono_perfcounters->gc_induced);
284 #endif
285 GC_gcollect ();
289 * mono_gc_max_generation:
291 * Get the maximum generation number used by the current garbage
292 * collector. The value will be 0 for the Boehm collector, 1 or more
293 * for the generational collectors.
295 * Returns: the maximum generation number.
298 mono_gc_max_generation (void)
300 return 0;
304 * mono_gc_get_generation:
305 * \param object a managed object
307 * Get the garbage collector's generation that \p object belongs to.
308 * Use this has a hint only.
310 * \returns a garbage collector generation number
313 mono_gc_get_generation (MonoObject *object)
315 return 0;
319 * mono_gc_collection_count:
320 * \param generation a GC generation number
322 * Get how many times a garbage collection has been performed
323 * for the given \p generation number.
325 * \returns the number of garbage collections
328 mono_gc_collection_count (int generation)
330 return GC_gc_no;
334 * mono_gc_add_memory_pressure:
335 * \param value amount of bytes
337 * Adjust the garbage collector's view of how many bytes of memory
338 * are indirectly referenced by managed objects (for example unmanaged
339 * memory holding image or other binary data).
340 * This is a hint only to the garbage collector algorithm.
341 * Note that negative amounts of p value will decrease the memory
342 * pressure.
344 void
345 mono_gc_add_memory_pressure (gint64 value)
350 * mono_gc_get_used_size:
352 * Get the approximate amount of memory used by managed objects.
354 * Returns: the amount of memory used in bytes
356 int64_t
357 mono_gc_get_used_size (void)
359 return GC_get_heap_size () - GC_get_free_bytes ();
363 * mono_gc_get_heap_size:
365 * Get the amount of memory used by the garbage collector.
367 * Returns: the size of the heap in bytes
369 int64_t
370 mono_gc_get_heap_size (void)
372 return GC_get_heap_size ();
375 gboolean
376 mono_gc_is_gc_thread (void)
378 return GC_thread_is_registered ();
381 gpointer
382 mono_gc_thread_attach (MonoThreadInfo* info)
384 struct GC_stack_base sb;
385 int res;
387 /* TODO: use GC_get_stack_base instead of baseptr. */
388 sb.mem_base = info->stack_end;
389 res = GC_register_my_thread (&sb);
390 if (res == GC_UNIMPLEMENTED)
391 return NULL; /* Cannot happen with GC v7+. */
393 info->handle_stack = mono_handle_stack_alloc ();
395 return info;
398 void
399 mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
401 MonoNativeThreadId tid;
403 tid = mono_thread_info_get_tid (p);
405 if (p->runtime_thread)
406 mono_threads_add_joinable_thread ((gpointer)tid);
408 mono_handle_stack_free (p->handle_stack);
409 p->handle_stack = NULL;
412 gboolean
413 mono_gc_thread_in_critical_region (MonoThreadInfo *info)
415 return FALSE;
418 gboolean
419 mono_object_is_alive (MonoObject* o)
421 return GC_is_marked ((ptr_t)o);
425 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
427 return 1;
430 static gint64 gc_start_time;
432 static void
433 on_gc_notification (GC_EventType event)
435 MonoProfilerGCEvent e;
437 switch (event) {
438 case GC_EVENT_PRE_STOP_WORLD:
439 e = MONO_GC_EVENT_PRE_STOP_WORLD;
440 MONO_GC_WORLD_STOP_BEGIN ();
441 break;
443 case GC_EVENT_POST_STOP_WORLD:
444 e = MONO_GC_EVENT_POST_STOP_WORLD;
445 MONO_GC_WORLD_STOP_END ();
446 break;
448 case GC_EVENT_PRE_START_WORLD:
449 e = MONO_GC_EVENT_PRE_START_WORLD;
450 MONO_GC_WORLD_RESTART_BEGIN (1);
451 break;
453 case GC_EVENT_POST_START_WORLD:
454 e = MONO_GC_EVENT_POST_START_WORLD;
455 MONO_GC_WORLD_RESTART_END (1);
456 break;
458 case GC_EVENT_START:
459 e = MONO_GC_EVENT_START;
460 MONO_GC_BEGIN (1);
461 #ifndef DISABLE_PERFCOUNTERS
462 if (mono_perfcounters)
463 mono_atomic_inc_i32 (&mono_perfcounters->gc_collections0);
464 #endif
465 mono_atomic_inc_i32 (&gc_stats.major_gc_count);
466 gc_start_time = mono_100ns_ticks ();
467 break;
469 case GC_EVENT_END:
470 e = MONO_GC_EVENT_END;
471 MONO_GC_END (1);
472 #if defined(ENABLE_DTRACE) && defined(__sun__)
473 /* This works around a dtrace -G problem on Solaris.
474 Limit its actual use to when the probe is enabled. */
475 if (MONO_GC_END_ENABLED ())
476 sleep(0);
477 #endif
479 #ifndef DISABLE_PERFCOUNTERS
480 if (mono_perfcounters) {
481 guint64 heap_size = GC_get_heap_size ();
482 guint64 used_size = heap_size - GC_get_free_bytes ();
483 /* FIXME: change these to mono_atomic_store_i64 () */
484 UnlockedWrite64 (&mono_perfcounters->gc_total_bytes, used_size);
485 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
486 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
487 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
489 #endif
490 UnlockedAdd64 (&gc_stats.major_gc_time, mono_100ns_ticks () - gc_start_time);
491 mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10);
492 break;
493 default:
494 break;
497 switch (event) {
498 case GC_EVENT_MARK_START:
499 case GC_EVENT_MARK_END:
500 case GC_EVENT_RECLAIM_START:
501 case GC_EVENT_RECLAIM_END:
502 break;
503 default:
504 MONO_PROFILER_RAISE (gc_event, (e, 0));
505 MONO_PROFILER_RAISE (gc_event2, (e, 0, TRUE));
506 break;
509 switch (event) {
510 case GC_EVENT_PRE_STOP_WORLD:
511 mono_thread_info_suspend_lock ();
512 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0));
513 MONO_PROFILER_RAISE (gc_event2, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0, TRUE));
514 break;
515 case GC_EVENT_POST_START_WORLD:
516 mono_thread_info_suspend_unlock ();
517 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0));
518 MONO_PROFILER_RAISE (gc_event2, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0, TRUE));
519 break;
520 default:
521 break;
526 static void
527 on_gc_heap_resize (size_t new_size)
529 guint64 heap_size = GC_get_heap_size ();
530 #ifndef DISABLE_PERFCOUNTERS
531 if (mono_perfcounters) {
532 /* FIXME: change these to mono_atomic_store_i64 () */
533 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
534 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
535 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
537 #endif
539 MONO_PROFILER_RAISE (gc_resize, (new_size));
542 typedef struct {
543 char *start;
544 char *end;
545 } RootData;
547 static gpointer
548 register_root (gpointer arg)
550 RootData* root_data = arg;
551 g_hash_table_insert (roots, root_data->start, root_data->end);
552 return NULL;
556 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
558 RootData root_data;
559 root_data.start = start;
560 /* Boehm root processing requires one byte past end of region to be scanned */
561 root_data.end = start + size + 1;
562 GC_call_with_alloc_lock (register_root, &root_data);
563 MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg));
564 return TRUE;
568 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg)
570 return mono_gc_register_root (start, size, descr, source, key, msg);
573 static gpointer
574 deregister_root (gpointer arg)
576 gboolean removed = g_hash_table_remove (roots, arg);
577 g_assert (removed);
578 return NULL;
581 void
582 mono_gc_deregister_root (char* addr)
584 GC_call_with_alloc_lock (deregister_root, addr);
585 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr));
588 static void
589 push_root (gpointer key, gpointer value, gpointer user_data)
591 GC_push_all (key, value);
594 static void
595 push_handle_stack (HandleStack* stack)
597 HandleChunk *cur = stack->bottom;
598 HandleChunk *last = stack->top;
600 if (!cur)
601 return;
603 while (cur) {
604 if (cur->size > 0)
605 GC_push_all ((gpointer)&cur->elems[0], (char*)(cur->elems + cur->size) + 1);
606 if (cur == last)
607 break;
608 cur = cur->next;
612 static void
613 mono_push_other_roots (void)
615 g_hash_table_foreach (roots, push_root, NULL);
616 FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) {
617 HandleStack* stack = (HandleStack*)info->handle_stack;
618 if (stack)
619 push_handle_stack (stack);
620 } FOREACH_THREAD_END
621 if (default_push_other_roots)
622 default_push_other_roots ();
625 static void
626 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
628 /* libgc requires that we use HIDE_POINTER... */
629 *link_addr = (void*)HIDE_POINTER (obj);
630 if (track)
631 GC_REGISTER_LONG_LINK (link_addr, obj);
632 else
633 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
636 static void
637 mono_gc_weak_link_remove (void **link_addr, gboolean track)
639 if (track)
640 GC_unregister_long_link (link_addr);
641 else
642 GC_unregister_disappearing_link (link_addr);
643 *link_addr = NULL;
646 static gpointer
647 reveal_link (gpointer link_addr)
649 void **link_a = (void **)link_addr;
650 return REVEAL_POINTER (*link_a);
653 static MonoObject *
654 mono_gc_weak_link_get (void **link_addr)
656 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
657 if (obj == (MonoObject *) -1)
658 return NULL;
659 return obj;
662 void*
663 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
665 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
668 void*
669 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
671 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
674 void*
675 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
677 /* libgc has no usable support for arrays... */
678 return GC_NO_DESCRIPTOR;
681 void*
682 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
684 /* It seems there are issues when the bitmap doesn't fit: play it safe */
685 if (numbits >= 30)
686 return GC_NO_DESCRIPTOR;
687 else
688 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
691 void*
692 mono_gc_make_vector_descr (void)
694 return NULL;
697 void*
698 mono_gc_make_root_descr_all_refs (int numbits)
700 return NULL;
703 void*
704 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
706 void *start = GC_MALLOC_UNCOLLECTABLE (size);
707 MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg));
708 return start;
711 void
712 mono_gc_free_fixed (void* addr)
714 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr));
715 GC_FREE (addr);
718 void *
719 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
721 MonoObject *obj;
723 if (!m_class_has_references (vtable->klass)) {
724 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
725 if (G_UNLIKELY (!obj))
726 return NULL;
728 obj->vtable = vtable;
729 obj->synchronisation = NULL;
731 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
732 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
733 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
734 if (G_UNLIKELY (!obj))
735 return NULL;
736 } else {
737 obj = (MonoObject *)GC_MALLOC (size);
738 if (G_UNLIKELY (!obj))
739 return NULL;
741 obj->vtable = vtable;
744 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
745 MONO_PROFILER_RAISE (gc_allocation, (obj));
747 return obj;
750 void *
751 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
753 MonoArray *obj;
755 if (!m_class_has_references (vtable->klass)) {
756 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
757 if (G_UNLIKELY (!obj))
758 return NULL;
760 obj->obj.vtable = vtable;
761 obj->obj.synchronisation = NULL;
763 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
764 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
765 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
766 if (G_UNLIKELY (!obj))
767 return NULL;
768 } else {
769 obj = (MonoArray *)GC_MALLOC (size);
770 if (G_UNLIKELY (!obj))
771 return NULL;
773 obj->obj.vtable = vtable;
776 obj->max_length = max_length;
778 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
779 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
781 return obj;
784 void *
785 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
787 MonoArray *obj;
789 if (!m_class_has_references (vtable->klass)) {
790 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
791 if (G_UNLIKELY (!obj))
792 return NULL;
794 obj->obj.vtable = vtable;
795 obj->obj.synchronisation = NULL;
797 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
798 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
799 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
800 if (G_UNLIKELY (!obj))
801 return NULL;
802 } else {
803 obj = (MonoArray *)GC_MALLOC (size);
804 if (G_UNLIKELY (!obj))
805 return NULL;
807 obj->obj.vtable = vtable;
810 obj->max_length = max_length;
812 if (bounds_size)
813 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
815 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
816 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
818 return obj;
821 void *
822 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
824 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
825 if (G_UNLIKELY (!obj))
826 return NULL;
828 obj->object.vtable = vtable;
829 obj->object.synchronisation = NULL;
830 obj->length = len;
831 obj->chars [len] = 0;
833 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
834 MONO_PROFILER_RAISE (gc_allocation, (&obj->object));
836 return obj;
839 void*
840 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
842 return mono_gc_alloc_obj (vtable, size);
845 void*
846 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
848 return mono_gc_alloc_obj (vtable, size);
852 mono_gc_invoke_finalizers (void)
854 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
855 * the 'mem_freed' variable is not initialized when there are no
856 * objects to finalize, which leads to strange behavior later on.
857 * The check is necessary to work around that bug.
859 if (GC_should_invoke_finalizers ())
860 return GC_invoke_finalizers ();
861 return 0;
864 MonoBoolean
865 mono_gc_pending_finalizers (void)
867 return GC_should_invoke_finalizers ();
870 void
871 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
873 *(void**)field_ptr = value;
876 void
877 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
879 *(void**)slot_ptr = value;
882 void
883 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
885 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
888 void
889 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
891 *(void**)ptr = value;
894 void
895 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
897 mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
900 void
901 mono_gc_wbarrier_generic_nostore (gpointer ptr)
905 void
906 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
908 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
911 void
912 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
914 /* do not copy the sync state */
915 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
916 m_class_get_instance_size (mono_object_class (obj)) - sizeof (MonoObject));
919 void
920 mono_gc_clear_domain (MonoDomain *domain)
924 void
925 mono_gc_suspend_finalizers (void)
930 mono_gc_get_suspend_signal (void)
932 return GC_get_suspend_signal ();
936 mono_gc_get_restart_signal (void)
938 return GC_get_thr_restart_signal ();
941 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
942 extern __thread void* GC_thread_tls;
943 #include "metadata-internals.h"
945 static int
946 shift_amount (int v)
948 int i = 0;
949 while (!(v & (1 << i)))
950 i++;
951 return i;
954 enum {
955 ATYPE_FREEPTR,
956 ATYPE_FREEPTR_FOR_BOX,
957 ATYPE_NORMAL,
958 ATYPE_GCJ,
959 ATYPE_STRING,
960 ATYPE_NUM
963 static MonoMethod*
964 create_allocator (int atype, int tls_key, gboolean slowpath)
966 int index_var, bytes_var, my_fl_var, my_entry_var;
967 guint32 no_freelist_branch, not_small_enough_branch = 0;
968 guint32 size_overflow_branch = 0;
969 MonoMethodBuilder *mb;
970 MonoMethod *res;
971 MonoMethodSignature *csig;
972 const char *name = NULL;
973 WrapperInfo *info;
975 g_assert_not_reached ();
977 if (atype == ATYPE_FREEPTR) {
978 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
979 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
980 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
981 } else if (atype == ATYPE_NORMAL) {
982 name = slowpath ? "SlowAlloc" : "Alloc";
983 } else if (atype == ATYPE_GCJ) {
984 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
985 } else if (atype == ATYPE_STRING) {
986 name = slowpath ? "SlowAllocString" : "AllocString";
987 } else {
988 g_assert_not_reached ();
991 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
993 if (atype == ATYPE_STRING) {
994 csig->ret = &mono_defaults.string_class->byval_arg;
995 csig->params [0] = &mono_defaults.int_class->byval_arg;
996 csig->params [1] = &mono_defaults.int32_class->byval_arg;
997 } else {
998 csig->ret = &mono_defaults.object_class->byval_arg;
999 csig->params [0] = &mono_defaults.int_class->byval_arg;
1000 csig->params [1] = &mono_defaults.int32_class->byval_arg;
1003 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
1005 if (slowpath)
1006 goto always_slowpath;
1008 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
1009 if (atype == ATYPE_STRING) {
1010 /* a string alloator method takes the args: (vtable, len) */
1011 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
1012 mono_mb_emit_ldarg (mb, 1);
1013 mono_mb_emit_icon (mb, 1);
1014 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1015 mono_mb_emit_icon (mb, 1);
1016 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1017 // sizeof (MonoString) might include padding
1018 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
1019 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1020 mono_mb_emit_stloc (mb, bytes_var);
1021 } else {
1022 mono_mb_emit_ldarg (mb, 1);
1023 mono_mb_emit_stloc (mb, bytes_var);
1026 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
1027 if (atype == ATYPE_STRING) {
1028 /* check for size */
1029 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
1030 mono_mb_emit_ldloc (mb, bytes_var);
1031 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
1032 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
1033 /* check for overflow */
1034 mono_mb_emit_ldloc (mb, bytes_var);
1035 mono_mb_emit_icon (mb, sizeof (MonoString));
1036 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1039 /* int index = INDEX_FROM_BYTES(bytes); */
1040 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
1042 mono_mb_emit_ldloc (mb, bytes_var);
1043 mono_mb_emit_icon (mb, GRANULARITY - 1);
1044 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1045 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
1046 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
1047 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
1048 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1049 /* index var is already adjusted into bytes */
1050 mono_mb_emit_stloc (mb, index_var);
1052 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1053 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1054 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1055 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1056 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
1057 mono_mb_emit_i4 (mb, tls_key);
1058 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
1059 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1060 + G_STRUCT_OFFSET (struct thread_local_freelists,
1061 ptrfree_freelists));
1062 else if (atype == ATYPE_NORMAL)
1063 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1064 + G_STRUCT_OFFSET (struct thread_local_freelists,
1065 normal_freelists));
1066 else if (atype == ATYPE_GCJ)
1067 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1068 + G_STRUCT_OFFSET (struct thread_local_freelists,
1069 gcj_freelists));
1070 else
1071 g_assert_not_reached ();
1072 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1073 mono_mb_emit_ldloc (mb, index_var);
1074 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1075 mono_mb_emit_stloc (mb, my_fl_var);
1077 /* my_entry = *my_fl; */
1078 mono_mb_emit_ldloc (mb, my_fl_var);
1079 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1080 mono_mb_emit_stloc (mb, my_entry_var);
1082 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1083 mono_mb_emit_ldloc (mb, my_entry_var);
1084 mono_mb_emit_icon (mb, HBLKSIZE);
1085 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1087 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1088 mono_mb_emit_ldloc (mb, my_fl_var);
1089 mono_mb_emit_ldloc (mb, my_entry_var);
1090 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1091 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1093 /* set the vtable and clear the words in the object */
1094 mono_mb_emit_ldloc (mb, my_entry_var);
1095 mono_mb_emit_ldarg (mb, 0);
1096 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1098 if (atype == ATYPE_FREEPTR) {
1099 int start_var, end_var, start_loop;
1100 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1102 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1103 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1104 mono_mb_emit_ldloc (mb, my_entry_var);
1105 mono_mb_emit_ldloc (mb, bytes_var);
1106 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1107 mono_mb_emit_stloc (mb, end_var);
1108 mono_mb_emit_ldloc (mb, my_entry_var);
1109 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1110 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1111 mono_mb_emit_stloc (mb, start_var);
1113 * do {
1114 * *start++ = NULL;
1115 * } while (start < end);
1117 start_loop = mono_mb_get_label (mb);
1118 mono_mb_emit_ldloc (mb, start_var);
1119 mono_mb_emit_icon (mb, 0);
1120 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1121 mono_mb_emit_ldloc (mb, start_var);
1122 mono_mb_emit_icon (mb, sizeof (gpointer));
1123 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1124 mono_mb_emit_stloc (mb, start_var);
1126 mono_mb_emit_ldloc (mb, start_var);
1127 mono_mb_emit_ldloc (mb, end_var);
1128 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1129 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1130 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1131 /* need to clear just the sync pointer */
1132 mono_mb_emit_ldloc (mb, my_entry_var);
1133 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1134 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1135 mono_mb_emit_icon (mb, 0);
1136 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1139 if (atype == ATYPE_STRING) {
1140 /* need to set length and clear the last char */
1141 /* s->length = len; */
1142 mono_mb_emit_ldloc (mb, my_entry_var);
1143 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1144 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1145 mono_mb_emit_ldarg (mb, 1);
1146 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1147 /* s->chars [len] = 0; */
1148 mono_mb_emit_ldloc (mb, my_entry_var);
1149 mono_mb_emit_ldloc (mb, bytes_var);
1150 mono_mb_emit_icon (mb, 2);
1151 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1152 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1153 mono_mb_emit_icon (mb, 0);
1154 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1157 /* return my_entry; */
1158 mono_mb_emit_ldloc (mb, my_entry_var);
1159 mono_mb_emit_byte (mb, MONO_CEE_RET);
1161 mono_mb_patch_short_branch (mb, no_freelist_branch);
1162 if (not_small_enough_branch > 0)
1163 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1164 if (size_overflow_branch > 0)
1165 mono_mb_patch_short_branch (mb, size_overflow_branch);
1167 /* the slow path: we just call back into the runtime */
1168 always_slowpath:
1169 if (atype == ATYPE_STRING) {
1170 mono_mb_emit_ldarg (mb, 1);
1171 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1172 } else {
1173 mono_mb_emit_ldarg (mb, 0);
1174 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1177 mono_mb_emit_byte (mb, MONO_CEE_RET);
1179 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1180 info->d.alloc.gc_name = "boehm";
1181 info->d.alloc.alloc_type = atype;
1182 mb->init_locals = FALSE;
1184 res = mono_mb_create (mb, csig, 8, info);
1185 mono_mb_free (mb);
1187 return res;
1190 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1191 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1193 gboolean
1194 mono_gc_is_critical_method (MonoMethod *method)
1196 int i;
1198 for (i = 0; i < ATYPE_NUM; ++i)
1199 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1200 return TRUE;
1202 return FALSE;
1206 * If possible, generate a managed method that can quickly allocate objects in class
1207 * @klass. The method will typically have an thread-local inline allocation sequence.
1208 * The signature of the called method is:
1209 * object allocate (MonoVTable *vtable)
1210 * The thread local alloc logic is taken from libgc/pthread_support.c.
1212 MonoMethod*
1213 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1215 int atype;
1218 * Tls implementation changed, we jump to tls native getters/setters.
1219 * Is boehm managed allocator ok with this ? Do we even care ?
1221 return NULL;
1223 if (!SMALL_ENOUGH (klass->instance_size))
1224 return NULL;
1225 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1226 return NULL;
1227 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1228 return NULL;
1229 if (klass->rank)
1230 return NULL;
1231 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1232 return NULL;
1233 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1234 atype = ATYPE_STRING;
1235 } else if (!known_instance_size) {
1236 return NULL;
1237 } else if (!klass->has_references) {
1238 if (for_box)
1239 atype = ATYPE_FREEPTR_FOR_BOX;
1240 else
1241 atype = ATYPE_FREEPTR;
1242 } else {
1243 return NULL;
1245 * disabled because we currently do a runtime choice anyway, to
1246 * deal with multiple appdomains.
1247 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1248 atype = ATYPE_GCJ;
1249 else
1250 atype = ATYPE_NORMAL;
1253 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1256 MonoMethod*
1257 mono_gc_get_managed_array_allocator (MonoClass *klass)
1259 return NULL;
1263 * mono_gc_get_managed_allocator_by_type:
1265 * Return a managed allocator method corresponding to allocator type ATYPE.
1267 MonoMethod*
1268 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1270 MonoMethod *res;
1271 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1272 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1274 return NULL;
1276 res = cache [atype];
1277 if (res)
1278 return res;
1280 res = create_allocator (atype, -1, slowpath);
1281 mono_os_mutex_lock (&mono_gc_lock);
1282 if (cache [atype]) {
1283 mono_free_method (res);
1284 res = cache [atype];
1285 } else {
1286 mono_memory_barrier ();
1287 cache [atype] = res;
1289 mono_os_mutex_unlock (&mono_gc_lock);
1290 return res;
1293 guint32
1294 mono_gc_get_managed_allocator_types (void)
1296 return ATYPE_NUM;
1299 MonoMethod*
1300 mono_gc_get_write_barrier (void)
1302 g_assert_not_reached ();
1303 return NULL;
1306 #else
1308 gboolean
1309 mono_gc_is_critical_method (MonoMethod *method)
1311 return FALSE;
1314 MonoMethod*
1315 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1317 return NULL;
1320 MonoMethod*
1321 mono_gc_get_managed_array_allocator (MonoClass *klass)
1323 return NULL;
1326 MonoMethod*
1327 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1329 return NULL;
1332 guint32
1333 mono_gc_get_managed_allocator_types (void)
1335 return 0;
1338 MonoMethod*
1339 mono_gc_get_write_barrier (void)
1341 g_assert_not_reached ();
1342 return NULL;
1345 #endif
1347 MonoMethod*
1348 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1350 g_assert_not_reached ();
1351 return NULL;
1355 mono_gc_get_aligned_size_for_allocator (int size)
1357 return size;
1360 const char *
1361 mono_gc_get_gc_name (void)
1363 return "boehm";
1366 void*
1367 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1369 return GC_call_with_alloc_lock (func, data);
1372 char*
1373 mono_gc_get_description (void)
1375 return g_strdup (DEFAULT_GC_NAME);
1378 void
1379 mono_gc_set_desktop_mode (void)
1381 GC_dont_expand = 1;
1384 gboolean
1385 mono_gc_is_moving (void)
1387 return FALSE;
1390 gboolean
1391 mono_gc_is_disabled (void)
1393 if (GC_dont_gc || g_hasenv ("GC_DONT_GC"))
1394 return TRUE;
1395 else
1396 return FALSE;
1399 void
1400 mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
1402 g_assert_not_reached ();
1405 void*
1406 mono_gc_get_range_copy_func (void)
1408 return &mono_gc_wbarrier_range_copy;
1411 guint8*
1412 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1414 g_assert_not_reached ();
1415 return NULL;
1418 gboolean
1419 mono_gc_card_table_nursery_check (void)
1421 g_assert_not_reached ();
1422 return TRUE;
1425 void*
1426 mono_gc_get_nursery (int *shift_bits, size_t *size)
1428 return NULL;
1431 gboolean
1432 mono_gc_precise_stack_mark_enabled (void)
1434 return FALSE;
1437 FILE *
1438 mono_gc_get_logfile (void)
1440 return NULL;
1443 void
1444 mono_gc_params_set (const char* options)
1448 void
1449 mono_gc_debug_set (const char* options)
1453 void
1454 mono_gc_conservatively_scan_area (void *start, void *end)
1456 g_assert_not_reached ();
1459 void *
1460 mono_gc_scan_object (void *obj, void *gc_data)
1462 g_assert_not_reached ();
1463 return NULL;
1466 gsize*
1467 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1469 g_assert_not_reached ();
1470 return NULL;
1473 void
1474 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1478 void
1479 mono_gc_set_stack_end (void *stack_end)
1483 void
1484 mono_gc_skip_thread_changing (gboolean skip)
1487 * Unlike SGen, Boehm doesn't respect our thread info flags. We need to
1488 * inform Boehm manually to skip/not skip the current thread.
1491 if (skip)
1492 GC_start_blocking ();
1493 else
1494 GC_end_blocking ();
1497 void
1498 mono_gc_skip_thread_changed (gboolean skip)
1502 void
1503 mono_gc_register_for_finalization (MonoObject *obj, MonoFinalizationProc user_data)
1505 guint offset = 0;
1507 #ifndef GC_DEBUG
1508 /* This assertion is not valid when GC_DEBUG is defined */
1509 g_assert (GC_base (obj) == (char*)obj - offset);
1510 #endif
1512 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1515 #ifndef HOST_WIN32
1517 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1519 /* it is being replaced by GC_pthread_create on some
1520 * platforms, see libgc/include/gc_pthread_redirects.h */
1521 return pthread_create (new_thread, attr, start_routine, arg);
1523 #endif
1525 #ifdef HOST_WIN32
1526 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1528 return GC_DllMain (module_handle, reason, reserved);
1530 #endif
1532 MonoVTable *
1533 mono_gc_get_vtable (MonoObject *obj)
1535 // No pointer tagging.
1536 return obj->vtable;
1539 guint
1540 mono_gc_get_vtable_bits (MonoClass *klass)
1542 if (fin_callbacks.is_class_finalization_aware) {
1543 if (fin_callbacks.is_class_finalization_aware (klass))
1544 return BOEHM_GC_BIT_FINALIZER_AWARE;
1546 return 0;
1550 * mono_gc_register_altstack:
1552 * Register the dimensions of the normal stack and altstack with the collector.
1553 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1555 void
1556 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1558 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1562 mono_gc_get_los_limit (void)
1564 return G_MAXINT;
1567 void
1568 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1570 mono_unichar2 *new_end = str->chars + new_length;
1572 /* zero the discarded string. This null-delimits the string and allows
1573 * the space to be reclaimed by SGen. */
1575 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1576 str->length = new_length;
1579 gboolean
1580 mono_gc_user_markers_supported (void)
1582 return FALSE;
1585 void *
1586 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1588 g_assert_not_reached ();
1589 return NULL;
1592 /* Toggleref support */
1594 void
1595 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1597 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1598 g_error ("GC_toggleref_add failed\n");
1601 void
1602 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1604 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1607 /* Test support code */
1609 static MonoToggleRefStatus
1610 test_toggleref_callback (MonoObject *obj)
1612 static MonoClassField *mono_toggleref_test_field;
1613 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1615 if (!mono_toggleref_test_field) {
1616 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1617 g_assert (mono_toggleref_test_field);
1620 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1621 printf ("toggleref-cb obj %d\n", status);
1622 return status;
1625 static void
1626 register_test_toggleref_callback (void)
1628 mono_gc_toggleref_register_callback (test_toggleref_callback);
1631 static gboolean
1632 is_finalization_aware (MonoObject *obj)
1634 MonoVTable *vt = obj->vtable;
1635 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1638 static void
1639 fin_notifier (MonoObject *obj)
1641 if (is_finalization_aware (obj))
1642 fin_callbacks.object_queued_for_finalization (obj);
1645 void
1646 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1648 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1649 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1651 fin_callbacks = *callbacks;
1653 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1656 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1658 static inline gboolean
1659 slot_occupied (HandleData *handles, guint slot) {
1660 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1663 static inline void
1664 vacate_slot (HandleData *handles, guint slot) {
1665 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1668 static inline void
1669 occupy_slot (HandleData *handles, guint slot) {
1670 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1673 static int
1674 find_first_unset (guint32 bitmap)
1676 int i;
1677 for (i = 0; i < 32; ++i) {
1678 if (!(bitmap & (1 << i)))
1679 return i;
1681 return -1;
1684 static void
1685 handle_data_alloc_entries (HandleData *handles)
1687 handles->size = 32;
1688 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1689 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1690 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1691 } else {
1692 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)");
1694 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1697 static gint
1698 handle_data_next_unset (HandleData *handles)
1700 gint slot;
1701 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1702 if (handles->bitmap [slot] == 0xffffffff)
1703 continue;
1704 handles->slot_hint = slot;
1705 return find_first_unset (handles->bitmap [slot]);
1707 return -1;
1710 static gint
1711 handle_data_first_unset (HandleData *handles)
1713 gint slot;
1714 for (slot = 0; slot < handles->slot_hint; ++slot) {
1715 if (handles->bitmap [slot] == 0xffffffff)
1716 continue;
1717 handles->slot_hint = slot;
1718 return find_first_unset (handles->bitmap [slot]);
1720 return -1;
1723 /* Returns the index of the current slot in the bitmap. */
1724 static void
1725 handle_data_grow (HandleData *handles, gboolean track)
1727 guint32 *new_bitmap;
1728 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1730 /* resize and copy the bitmap */
1731 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1732 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1733 g_free (handles->bitmap);
1734 handles->bitmap = new_bitmap;
1736 /* resize and copy the entries */
1737 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1738 gpointer *entries;
1739 guint16 *domain_ids;
1740 gint i;
1741 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1742 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1743 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1744 for (i = 0; i < handles->size; ++i) {
1745 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1746 if (obj) {
1747 mono_gc_weak_link_add (&(entries [i]), obj, track);
1748 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1749 } else {
1750 g_assert (!handles->entries [i]);
1753 g_free (handles->entries);
1754 g_free (handles->domain_ids);
1755 handles->entries = entries;
1756 handles->domain_ids = domain_ids;
1757 } else {
1758 gpointer *entries;
1759 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)");
1760 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1761 mono_gc_free_fixed (handles->entries);
1762 handles->entries = entries;
1764 handles->slot_hint = handles->size / BITMAP_SIZE;
1765 handles->size = new_size;
1768 static guint32
1769 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1771 gint slot, i;
1772 guint32 res;
1773 lock_handles (handles);
1774 if (!handles->size)
1775 handle_data_alloc_entries (handles);
1776 i = handle_data_next_unset (handles);
1777 if (i == -1 && handles->slot_hint != 0)
1778 i = handle_data_first_unset (handles);
1779 if (i == -1) {
1780 handle_data_grow (handles, track);
1781 i = 0;
1783 slot = handles->slot_hint * BITMAP_SIZE + i;
1784 occupy_slot (handles, slot);
1785 handles->entries [slot] = NULL;
1786 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1787 /*FIXME, what to use when obj == null?*/
1788 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1789 if (obj)
1790 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1791 } else {
1792 handles->entries [slot] = obj;
1795 #ifndef DISABLE_PERFCOUNTERS
1796 mono_atomic_inc_i32 (&mono_perfcounters->gc_num_handles);
1797 #endif
1798 unlock_handles (handles);
1799 res = MONO_GC_HANDLE (slot, handles->type);
1800 MONO_PROFILER_RAISE (gc_handle_created, (res, handles->type, obj));
1801 return res;
1805 * mono_gchandle_new:
1806 * \param obj managed object to get a handle for
1807 * \param pinned whether the object should be pinned
1809 * This returns a handle that wraps the object, this is used to keep a
1810 * reference to a managed object from the unmanaged world and preventing the
1811 * object from being disposed.
1813 * If \p pinned is false the address of the object can not be obtained, if it is
1814 * true the address of the object can be obtained. This will also pin the
1815 * object so it will not be possible by a moving garbage collector to move the
1816 * object.
1818 * \returns a handle that can be used to access the object from
1819 * unmanaged code.
1821 guint32
1822 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1824 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1828 * mono_gchandle_new_weakref:
1829 * \param obj managed object to get a handle for
1830 * \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.
1832 * This returns a weak handle that wraps the object, this is used to
1833 * keep a reference to a managed object from the unmanaged world.
1834 * Unlike the \c mono_gchandle_new the object can be reclaimed by the
1835 * garbage collector. In this case the value of the GCHandle will be
1836 * set to zero.
1838 * If \p track_resurrection is TRUE the object will be tracked through
1839 * finalization and if the object is resurrected during the execution
1840 * of the finalizer, then the returned weakref will continue to hold
1841 * a reference to the object. If \p track_resurrection is FALSE, then
1842 * the weak reference's target will become NULL as soon as the object
1843 * is passed on to the finalizer.
1845 * \returns a handle that can be used to access the object from
1846 * unmanaged code.
1848 guint32
1849 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1851 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1855 * mono_gchandle_get_target:
1856 * \param gchandle a GCHandle's handle.
1858 * The handle was previously created by calling \c mono_gchandle_new or
1859 * \c mono_gchandle_new_weakref.
1861 * \returns A pointer to the \c MonoObject* represented by the handle or
1862 * NULL for a collected object if using a weakref handle.
1864 MonoObject*
1865 mono_gchandle_get_target (guint32 gchandle)
1867 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1868 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1869 HandleData *handles = &gc_handles [type];
1870 MonoObject *obj = NULL;
1871 if (type >= HANDLE_TYPE_MAX)
1872 return NULL;
1874 lock_handles (handles);
1875 if (slot < handles->size && slot_occupied (handles, slot)) {
1876 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1877 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1878 } else {
1879 obj = (MonoObject *)handles->entries [slot];
1881 } else {
1882 /* print a warning? */
1884 unlock_handles (handles);
1885 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1886 return obj;
1889 void
1890 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1892 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1893 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1894 HandleData *handles = &gc_handles [type];
1895 MonoObject *old_obj = NULL;
1897 g_assert (type < HANDLE_TYPE_MAX);
1898 lock_handles (handles);
1899 if (slot < handles->size && slot_occupied (handles, slot)) {
1900 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1901 old_obj = (MonoObject *)handles->entries [slot];
1902 if (handles->entries [slot])
1903 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1904 if (obj)
1905 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1906 /*FIXME, what to use when obj == null?*/
1907 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1908 } else {
1909 handles->entries [slot] = obj;
1911 } else {
1912 /* print a warning? */
1914 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1915 unlock_handles (handles);
1918 gboolean
1919 mono_gc_is_null (void)
1921 return FALSE;
1925 * mono_gchandle_is_in_domain:
1926 * \param gchandle a GCHandle's handle.
1927 * \param domain An application domain.
1929 * Use this function to determine if the \p gchandle points to an
1930 * object allocated in the specified \p domain.
1932 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1934 gboolean
1935 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1937 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1938 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1939 HandleData *handles = &gc_handles [type];
1940 gboolean result = FALSE;
1942 if (type >= HANDLE_TYPE_MAX)
1943 return FALSE;
1945 lock_handles (handles);
1946 if (slot < handles->size && slot_occupied (handles, slot)) {
1947 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1948 result = domain->domain_id == handles->domain_ids [slot];
1949 } else {
1950 MonoObject *obj;
1951 obj = (MonoObject *)handles->entries [slot];
1952 if (obj == NULL)
1953 result = TRUE;
1954 else
1955 result = domain == mono_object_domain (obj);
1957 } else {
1958 /* print a warning? */
1960 unlock_handles (handles);
1961 return result;
1965 * mono_gchandle_free:
1966 * \param gchandle a GCHandle's handle.
1968 * Frees the \p gchandle handle. If there are no outstanding
1969 * references, the garbage collector can reclaim the memory of the
1970 * object wrapped.
1972 void
1973 mono_gchandle_free (guint32 gchandle)
1975 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1976 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1977 HandleData *handles = &gc_handles [type];
1978 if (type >= HANDLE_TYPE_MAX)
1979 return;
1981 lock_handles (handles);
1982 if (slot < handles->size && slot_occupied (handles, slot)) {
1983 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1984 if (handles->entries [slot])
1985 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1986 } else {
1987 handles->entries [slot] = NULL;
1989 vacate_slot (handles, slot);
1990 } else {
1991 /* print a warning? */
1993 #ifndef DISABLE_PERFCOUNTERS
1994 mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles);
1995 #endif
1996 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1997 unlock_handles (handles);
1998 MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, handles->type));
2002 * mono_gchandle_free_domain:
2003 * \param domain domain that is unloading
2005 * Function used internally to cleanup any GC handle for objects belonging
2006 * to the specified domain during appdomain unload.
2008 void
2009 mono_gchandle_free_domain (MonoDomain *domain)
2011 guint type;
2013 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
2014 guint slot;
2015 HandleData *handles = &gc_handles [type];
2016 lock_handles (handles);
2017 for (slot = 0; slot < handles->size; ++slot) {
2018 if (!slot_occupied (handles, slot))
2019 continue;
2020 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
2021 if (domain->domain_id == handles->domain_ids [slot]) {
2022 vacate_slot (handles, slot);
2023 if (handles->entries [slot])
2024 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
2026 } else {
2027 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
2028 vacate_slot (handles, slot);
2029 handles->entries [slot] = NULL;
2033 unlock_handles (handles);
2038 void
2039 mono_gc_register_obj_with_weak_fields (void *obj)
2041 g_error ("Weak fields not supported by boehm gc");
2044 #else
2046 MONO_EMPTY_SOURCE_FILE (boehm_gc);
2047 #endif /* no Boehm GC */