[Mono.Runtime.Tests] Exclude simd tests
[mono-project.git] / mono / metadata / boehm-gc.c
blob8b8f48a7f01b407858ddd766c540e87d4e2b08e8
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/metadata/abi-details.h>
32 #include <mono/utils/atomic.h>
33 #include <mono/utils/mono-logger-internals.h>
34 #include <mono/utils/mono-memory-model.h>
35 #include <mono/utils/mono-time.h>
36 #include <mono/utils/mono-threads.h>
37 #include <mono/utils/dtrace.h>
38 #include <mono/utils/gc_wrapper.h>
39 #include <mono/utils/mono-os-mutex.h>
40 #include <mono/utils/mono-counters.h>
41 #include <mono/utils/mono-compiler.h>
42 #include <mono/utils/unlocked.h>
43 #include <mono/metadata/icall-decl.h>
45 #if HAVE_BOEHM_GC
47 #undef TRUE
48 #undef FALSE
49 #define THREAD_LOCAL_ALLOC 1
50 #include "private/pthread_support.h"
52 #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
53 void *pthread_get_stackaddr_np(pthread_t);
54 #endif
56 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
57 /*Boehm max heap cannot be smaller than 16MB*/
58 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
59 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
61 static gboolean gc_initialized = FALSE;
62 static gboolean gc_dont_gc_env = FALSE;
63 static mono_mutex_t mono_gc_lock;
65 typedef void (*GC_push_other_roots_proc)(void);
67 static GC_push_other_roots_proc default_push_other_roots;
68 static GHashTable *roots;
70 static void
71 mono_push_other_roots(void);
73 static void
74 register_test_toggleref_callback (void);
76 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
77 static MonoGCFinalizerCallbacks fin_callbacks;
79 /* GC Handles */
81 static mono_mutex_t handle_section;
82 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
83 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
85 typedef struct {
86 guint32 *bitmap;
87 gpointer *entries;
88 guint32 size;
89 guint8 type;
90 guint slot_hint : 24; /* starting slot for search in bitmap */
91 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
92 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
93 guint16 *domain_ids;
94 } HandleData;
96 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
98 /* weak and weak-track arrays will be allocated in malloc memory
100 static HandleData gc_handles [] = {
101 EMPTY_HANDLE_DATA (HANDLE_WEAK),
102 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
103 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
104 EMPTY_HANDLE_DATA (HANDLE_PINNED)
107 static void
108 mono_gc_warning (char *msg, GC_word arg)
110 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
113 static void on_gc_notification (GC_EventType event);
114 static void on_gc_heap_resize (size_t new_size);
116 void
117 mono_gc_base_init (void)
119 char *env;
121 if (gc_initialized)
122 return;
124 mono_counters_init ();
126 #ifndef HOST_WIN32
127 mono_w32handle_init ();
128 #endif
131 * Handle the case when we are called from a thread different from the main thread,
132 * confusing libgc.
133 * FIXME: Move this to libgc where it belongs.
135 * we used to do this only when running on valgrind,
136 * but it happens also in other setups.
138 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
140 size_t size;
141 void *sstart;
142 pthread_attr_t attr;
143 pthread_getattr_np (pthread_self (), &attr);
144 pthread_attr_getstack (&attr, &sstart, &size);
145 pthread_attr_destroy (&attr);
146 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
147 /* apparently with some linuxthreads implementations sstart can be NULL,
148 * fallback to the more imprecise method (bug# 78096).
150 if (sstart) {
151 GC_stackbottom = (char*)sstart + size;
152 } else {
153 int dummy;
154 gsize stack_bottom = (gsize)&dummy;
155 stack_bottom += 4095;
156 stack_bottom &= ~4095;
157 GC_stackbottom = (char*)stack_bottom;
160 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
161 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
162 #elif defined(__OpenBSD__)
163 # include <pthread_np.h>
165 stack_t ss;
166 int rslt;
168 rslt = pthread_stackseg_np(pthread_self(), &ss);
169 g_assert (rslt == 0);
171 GC_stackbottom = (char*)ss.ss_sp;
173 #else
175 int dummy;
176 gsize stack_bottom = (gsize)&dummy;
177 stack_bottom += 4095;
178 stack_bottom &= ~4095;
179 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
180 GC_stackbottom = (char*)stack_bottom;
182 #endif
184 roots = g_hash_table_new (NULL, NULL);
185 default_push_other_roots = GC_push_other_roots;
186 GC_push_other_roots = mono_push_other_roots;
188 #if !defined(HOST_ANDROID)
189 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
190 GC_no_dls = TRUE;
191 #endif
193 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
194 char **opts = g_strsplit (env, ",", -1);
195 for (char **ptr = opts; ptr && *ptr; ptr ++) {
196 char *opt = *ptr;
197 if (!strcmp (opt, "do-not-finalize")) {
198 mono_do_not_finalize = 1;
199 } else if (!strcmp (opt, "log-finalizers")) {
200 mono_log_finalizers = 1;
203 g_free (env);
207 /* cache value rather than calling during collection since g_hasenv may take locks and can deadlock */
208 gc_dont_gc_env = g_hasenv ("GC_DONT_GC");
210 GC_init ();
212 GC_set_warn_proc (mono_gc_warning);
213 GC_finalize_on_demand = 1;
214 GC_finalizer_notifier = mono_gc_finalize_notify;
216 GC_init_gcj_malloc (5, NULL);
217 GC_allow_register_threads ();
219 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
220 char **ptr, **opts = g_strsplit (env, ",", -1);
221 for (ptr = opts; *ptr; ++ptr) {
222 char *opt = *ptr;
223 if (g_str_has_prefix (opt, "max-heap-size=")) {
224 size_t max_heap;
226 opt = strchr (opt, '=') + 1;
227 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
228 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
229 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
230 exit (1);
232 GC_set_max_heap_size (max_heap);
233 } else {
234 fprintf (stderr, "max-heap-size must be an integer.\n");
235 exit (1);
237 continue;
238 } else if (g_str_has_prefix (opt, "toggleref-test")) {
239 register_test_toggleref_callback ();
240 continue;
241 } else {
242 /* Could be a parameter for sgen */
244 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
245 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
246 exit (1);
250 g_free (env);
251 g_strfreev (opts);
254 mono_thread_callbacks_init ();
255 mono_thread_info_init (sizeof (MonoThreadInfo));
256 mono_os_mutex_init (&mono_gc_lock);
257 mono_os_mutex_init_recursive (&handle_section);
259 mono_thread_info_attach ();
261 GC_set_on_collection_event (on_gc_notification);
262 GC_on_heap_resize = on_gc_heap_resize;
264 gc_initialized = TRUE;
267 void
268 mono_gc_base_cleanup (void)
270 GC_finalizer_notifier = NULL;
273 void
274 mono_gc_init_icalls (void)
279 * mono_gc_collect:
280 * \param generation GC generation identifier
282 * Perform a garbage collection for the given generation, higher numbers
283 * mean usually older objects. Collecting a high-numbered generation
284 * implies collecting also the lower-numbered generations.
285 * The maximum value for \p generation can be retrieved with a call to
286 * \c mono_gc_max_generation, so this function is usually called as:
288 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
290 void
291 mono_gc_collect (int generation)
293 #ifndef DISABLE_PERFCOUNTERS
294 mono_atomic_inc_i32 (&mono_perfcounters->gc_induced);
295 #endif
296 GC_gcollect ();
300 * mono_gc_max_generation:
302 * Get the maximum generation number used by the current garbage
303 * collector. The value will be 0 for the Boehm collector, 1 or more
304 * for the generational collectors.
306 * Returns: the maximum generation number.
309 mono_gc_max_generation (void)
311 return 0;
315 * mono_gc_get_generation:
316 * \param object a managed object
318 * Get the garbage collector's generation that \p object belongs to.
319 * Use this has a hint only.
321 * \returns a garbage collector generation number
324 mono_gc_get_generation (MonoObject *object)
326 return 0;
330 * mono_gc_collection_count:
331 * \param generation a GC generation number
333 * Get how many times a garbage collection has been performed
334 * for the given \p generation number.
336 * \returns the number of garbage collections
339 mono_gc_collection_count (int generation)
341 return GC_gc_no;
345 * mono_gc_add_memory_pressure:
346 * \param value amount of bytes
348 * Adjust the garbage collector's view of how many bytes of memory
349 * are indirectly referenced by managed objects (for example unmanaged
350 * memory holding image or other binary data).
351 * This is a hint only to the garbage collector algorithm.
352 * Note that negative amounts of p value will decrease the memory
353 * pressure.
355 void
356 mono_gc_add_memory_pressure (gint64 value)
361 * mono_gc_get_used_size:
363 * Get the approximate amount of memory used by managed objects.
365 * Returns: the amount of memory used in bytes
367 int64_t
368 mono_gc_get_used_size (void)
370 return GC_get_heap_size () - GC_get_free_bytes ();
374 * mono_gc_get_heap_size:
376 * Get the amount of memory used by the garbage collector.
378 * Returns: the size of the heap in bytes
380 int64_t
381 mono_gc_get_heap_size (void)
383 return GC_get_heap_size ();
386 gboolean
387 mono_gc_is_gc_thread (void)
389 return GC_thread_is_registered ();
392 gpointer
393 mono_gc_thread_attach (MonoThreadInfo* info)
395 struct GC_stack_base sb;
396 int res;
398 /* TODO: use GC_get_stack_base instead of baseptr. */
399 sb.mem_base = info->stack_end;
400 res = GC_register_my_thread (&sb);
401 if (res == GC_UNIMPLEMENTED)
402 return NULL; /* Cannot happen with GC v7+. */
404 info->handle_stack = mono_handle_stack_alloc ();
406 return info;
409 void
410 mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
412 MonoNativeThreadId tid;
414 tid = mono_thread_info_get_tid (p);
416 if (p->runtime_thread)
417 mono_threads_add_joinable_thread ((gpointer)tid);
419 mono_handle_stack_free (p->handle_stack);
420 p->handle_stack = NULL;
423 gboolean
424 mono_gc_thread_in_critical_region (MonoThreadInfo *info)
426 return FALSE;
429 gboolean
430 mono_object_is_alive (MonoObject* o)
432 return GC_is_marked ((ptr_t)o);
436 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
438 return 1;
441 static gint64 gc_start_time;
443 static void
444 on_gc_notification (GC_EventType event)
446 MonoProfilerGCEvent e;
448 switch (event) {
449 case GC_EVENT_PRE_STOP_WORLD:
450 e = MONO_GC_EVENT_PRE_STOP_WORLD;
451 MONO_GC_WORLD_STOP_BEGIN ();
452 break;
454 case GC_EVENT_POST_STOP_WORLD:
455 e = MONO_GC_EVENT_POST_STOP_WORLD;
456 MONO_GC_WORLD_STOP_END ();
457 break;
459 case GC_EVENT_PRE_START_WORLD:
460 e = MONO_GC_EVENT_PRE_START_WORLD;
461 MONO_GC_WORLD_RESTART_BEGIN (1);
462 break;
464 case GC_EVENT_POST_START_WORLD:
465 e = MONO_GC_EVENT_POST_START_WORLD;
466 MONO_GC_WORLD_RESTART_END (1);
467 break;
469 case GC_EVENT_START:
470 e = MONO_GC_EVENT_START;
471 MONO_GC_BEGIN (1);
472 #ifndef DISABLE_PERFCOUNTERS
473 if (mono_perfcounters)
474 mono_atomic_inc_i32 (&mono_perfcounters->gc_collections0);
475 #endif
476 mono_atomic_inc_i32 (&mono_gc_stats.major_gc_count);
477 gc_start_time = mono_100ns_ticks ();
478 break;
480 case GC_EVENT_END:
481 e = MONO_GC_EVENT_END;
482 MONO_GC_END (1);
483 #if defined(ENABLE_DTRACE) && defined(__sun__)
484 /* This works around a dtrace -G problem on Solaris.
485 Limit its actual use to when the probe is enabled. */
486 if (MONO_GC_END_ENABLED ())
487 sleep(0);
488 #endif
490 #ifndef DISABLE_PERFCOUNTERS
491 if (mono_perfcounters) {
492 guint64 heap_size = GC_get_heap_size ();
493 guint64 used_size = heap_size - GC_get_free_bytes ();
494 /* FIXME: change these to mono_atomic_store_i64 () */
495 UnlockedWrite64 (&mono_perfcounters->gc_total_bytes, used_size);
496 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
497 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
498 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
500 #endif
501 UnlockedAdd64 (&mono_gc_stats.major_gc_time, mono_100ns_ticks () - gc_start_time);
502 mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10);
503 break;
504 default:
505 break;
508 switch (event) {
509 case GC_EVENT_MARK_START:
510 case GC_EVENT_MARK_END:
511 case GC_EVENT_RECLAIM_START:
512 case GC_EVENT_RECLAIM_END:
513 break;
514 default:
515 MONO_PROFILER_RAISE (gc_event, (e, 0, TRUE));
516 break;
519 switch (event) {
520 case GC_EVENT_PRE_STOP_WORLD:
521 mono_thread_info_suspend_lock ();
522 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0, TRUE));
523 break;
524 case GC_EVENT_POST_START_WORLD:
525 mono_thread_info_suspend_unlock ();
526 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0, TRUE));
527 break;
528 default:
529 break;
534 static void
535 on_gc_heap_resize (size_t new_size)
537 guint64 heap_size = GC_get_heap_size ();
538 #ifndef DISABLE_PERFCOUNTERS
539 if (mono_perfcounters) {
540 /* FIXME: change these to mono_atomic_store_i64 () */
541 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
542 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
543 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
545 #endif
547 MONO_PROFILER_RAISE (gc_resize, (new_size));
550 typedef struct {
551 char *start;
552 char *end;
553 } RootData;
555 static gpointer
556 register_root (gpointer arg)
558 RootData* root_data = (RootData*)arg;
559 g_hash_table_insert (roots, root_data->start, root_data->end);
560 return NULL;
564 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
566 RootData root_data;
567 root_data.start = start;
568 /* Boehm root processing requires one byte past end of region to be scanned */
569 root_data.end = start + size + 1;
570 GC_call_with_alloc_lock (register_root, &root_data);
571 MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg));
572 return TRUE;
576 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg)
578 return mono_gc_register_root (start, size, descr, source, key, msg);
581 static gpointer
582 deregister_root (gpointer arg)
584 gboolean removed = g_hash_table_remove (roots, arg);
585 g_assert (removed);
586 return NULL;
589 void
590 mono_gc_deregister_root (char* addr)
592 GC_call_with_alloc_lock (deregister_root, addr);
593 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr));
596 static void
597 push_root (gpointer key, gpointer value, gpointer user_data)
599 GC_push_all (key, value);
602 static void
603 push_handle_stack (HandleStack* stack)
605 HandleChunk *cur = stack->bottom;
606 HandleChunk *last = stack->top;
608 if (!cur)
609 return;
611 while (cur) {
612 if (cur->size > 0)
613 GC_push_all ((gpointer)&cur->elems[0], (char*)(cur->elems + cur->size) + 1);
614 if (cur == last)
615 break;
616 cur = cur->next;
620 static void
621 mono_push_other_roots (void)
623 g_hash_table_foreach (roots, push_root, NULL);
624 FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) {
625 HandleStack* stack = info->handle_stack;
626 if (stack)
627 push_handle_stack (stack);
628 } FOREACH_THREAD_END
629 if (default_push_other_roots)
630 default_push_other_roots ();
633 static void
634 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
636 /* libgc requires that we use HIDE_POINTER... */
637 *link_addr = (void*)HIDE_POINTER (obj);
638 if (track)
639 GC_REGISTER_LONG_LINK (link_addr, obj);
640 else
641 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
644 static void
645 mono_gc_weak_link_remove (void **link_addr, gboolean track)
647 if (track)
648 GC_unregister_long_link (link_addr);
649 else
650 GC_unregister_disappearing_link (link_addr);
651 *link_addr = NULL;
654 static gpointer
655 reveal_link (gpointer link_addr)
657 void **link_a = (void **)link_addr;
658 return REVEAL_POINTER (*link_a);
661 static MonoObject *
662 mono_gc_weak_link_get (void **link_addr)
664 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
665 if (obj == (MonoObject *) -1)
666 return NULL;
667 return obj;
670 void*
671 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
673 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
676 void*
677 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
679 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
682 void*
683 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
685 /* libgc has no usable support for arrays... */
686 return GC_NO_DESCRIPTOR;
689 void*
690 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
692 /* It seems there are issues when the bitmap doesn't fit: play it safe */
693 if (numbits >= 30)
694 return GC_NO_DESCRIPTOR;
695 else
696 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
699 void*
700 mono_gc_make_vector_descr (void)
702 return NULL;
705 void*
706 mono_gc_make_root_descr_all_refs (int numbits)
708 return NULL;
711 MonoObject*
712 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
714 void *start = GC_MALLOC_UNCOLLECTABLE (size);
715 MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg));
716 return (MonoObject*)start;
719 MonoObject*
720 mono_gc_alloc_fixed_no_descriptor (size_t size, MonoGCRootSource source, void *key, const char *msg)
722 return mono_gc_alloc_fixed (size, 0, source, key, msg);
725 void
726 mono_gc_free_fixed (void* addr)
728 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr));
729 GC_FREE (addr);
732 MonoObject*
733 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
735 MonoObject *obj;
737 if (!m_class_has_references (vtable->klass)) {
738 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
739 if (G_UNLIKELY (!obj))
740 return NULL;
742 obj->vtable = vtable;
743 obj->synchronisation = NULL;
745 memset (mono_object_get_data (obj), 0, size - MONO_ABI_SIZEOF (MonoObject));
746 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
747 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
748 if (G_UNLIKELY (!obj))
749 return NULL;
750 } else {
751 obj = (MonoObject *)GC_MALLOC (size);
752 if (G_UNLIKELY (!obj))
753 return NULL;
755 obj->vtable = vtable;
758 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
759 MONO_PROFILER_RAISE (gc_allocation, (obj));
761 return obj;
764 MonoArray*
765 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
767 MonoArray *obj;
769 if (!m_class_has_references (vtable->klass)) {
770 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
771 if (G_UNLIKELY (!obj))
772 return NULL;
774 obj->obj.vtable = vtable;
775 obj->obj.synchronisation = NULL;
777 memset (mono_object_get_data ((MonoObject*)obj), 0, size - MONO_ABI_SIZEOF (MonoObject));
778 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
779 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
780 if (G_UNLIKELY (!obj))
781 return NULL;
782 } else {
783 obj = (MonoArray *)GC_MALLOC (size);
784 if (G_UNLIKELY (!obj))
785 return NULL;
787 obj->obj.vtable = vtable;
790 obj->max_length = max_length;
792 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
793 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
795 return obj;
798 MonoArray*
799 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
801 MonoArray *obj;
803 if (!m_class_has_references (vtable->klass)) {
804 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
805 if (G_UNLIKELY (!obj))
806 return NULL;
808 obj->obj.vtable = vtable;
809 obj->obj.synchronisation = NULL;
811 memset (mono_object_get_data ((MonoObject*)obj), 0, size - MONO_ABI_SIZEOF (MonoObject));
812 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
813 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
814 if (G_UNLIKELY (!obj))
815 return NULL;
816 } else {
817 obj = (MonoArray *)GC_MALLOC (size);
818 if (G_UNLIKELY (!obj))
819 return NULL;
821 obj->obj.vtable = vtable;
824 obj->max_length = max_length;
826 if (bounds_size)
827 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
829 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
830 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
832 return obj;
835 MonoString*
836 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
838 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
839 if (G_UNLIKELY (!obj))
840 return NULL;
842 obj->object.vtable = vtable;
843 obj->object.synchronisation = NULL;
844 obj->length = len;
845 obj->chars [len] = 0;
847 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
848 MONO_PROFILER_RAISE (gc_allocation, (&obj->object));
850 return obj;
853 MonoObject*
854 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
856 return mono_gc_alloc_obj (vtable, size);
859 MonoObject*
860 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
862 return mono_gc_alloc_obj (vtable, size);
866 mono_gc_invoke_finalizers (void)
868 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
869 * the 'mem_freed' variable is not initialized when there are no
870 * objects to finalize, which leads to strange behavior later on.
871 * The check is necessary to work around that bug.
873 if (GC_should_invoke_finalizers ())
874 return GC_invoke_finalizers ();
875 return 0;
878 MonoBoolean
879 mono_gc_pending_finalizers (void)
881 return GC_should_invoke_finalizers ();
884 void
885 mono_gc_wbarrier_set_field_internal (MonoObject *obj, gpointer field_ptr, MonoObject* value)
887 *(void**)field_ptr = value;
890 void
891 mono_gc_wbarrier_set_arrayref_internal (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
893 *(void**)slot_ptr = value;
896 void
897 mono_gc_wbarrier_arrayref_copy_internal (gpointer dest_ptr, gpointer src_ptr, int count)
899 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
902 void
903 mono_gc_wbarrier_generic_store_internal (gpointer ptr, MonoObject* value)
905 *(void**)ptr = value;
908 void
909 mono_gc_wbarrier_generic_store_atomic_internal (gpointer ptr, MonoObject *value)
911 mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
914 void
915 mono_gc_wbarrier_generic_nostore_internal (gpointer ptr)
919 void
920 mono_gc_wbarrier_value_copy_internal (gpointer dest, gpointer src, int count, MonoClass *klass)
922 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
925 void
926 mono_gc_wbarrier_object_copy_internal (MonoObject* obj, MonoObject *src)
928 /* do not copy the sync state */
929 mono_gc_memmove_aligned (mono_object_get_data (obj), (char*)src + MONO_ABI_SIZEOF (MonoObject),
930 m_class_get_instance_size (mono_object_class (obj)) - MONO_ABI_SIZEOF (MonoObject));
933 void
934 mono_gc_clear_domain (MonoDomain *domain)
938 void
939 mono_gc_suspend_finalizers (void)
944 mono_gc_get_suspend_signal (void)
946 return GC_get_suspend_signal ();
950 mono_gc_get_restart_signal (void)
952 return GC_get_thr_restart_signal ();
955 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
956 extern __thread void* GC_thread_tls;
957 #include "metadata-internals.h"
959 static int
960 shift_amount (int v)
962 int i = 0;
963 while (!(v & (1 << i)))
964 i++;
965 return i;
968 enum {
969 ATYPE_FREEPTR,
970 ATYPE_FREEPTR_FOR_BOX,
971 ATYPE_NORMAL,
972 ATYPE_GCJ,
973 ATYPE_STRING,
974 ATYPE_NUM
977 static MonoMethod*
978 create_allocator (int atype, int tls_key, gboolean slowpath)
980 int index_var, bytes_var, my_fl_var, my_entry_var;
981 guint32 no_freelist_branch, not_small_enough_branch = 0;
982 guint32 size_overflow_branch = 0;
983 MonoMethodBuilder *mb;
984 MonoMethod *res;
985 MonoMethodSignature *csig;
986 const char *name = NULL;
987 WrapperInfo *info;
989 g_assert_not_reached ();
991 if (atype == ATYPE_FREEPTR) {
992 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
993 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
994 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
995 } else if (atype == ATYPE_NORMAL) {
996 name = slowpath ? "SlowAlloc" : "Alloc";
997 } else if (atype == ATYPE_GCJ) {
998 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
999 } else if (atype == ATYPE_STRING) {
1000 name = slowpath ? "SlowAllocString" : "AllocString";
1001 } else {
1002 g_assert_not_reached ();
1005 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
1007 if (atype == ATYPE_STRING) {
1008 csig->ret = m_class_get_byval_arg (mono_defaults.string_class);
1009 csig->params [0] = mono_get_int_type ();
1010 csig->params [1] = mono_get_int32_type ();
1011 } else {
1012 csig->ret = mono_get_object_type ();
1013 csig->params [0] = mono_get_int_type ();
1014 csig->params [1] = mono_get_int32_type ();
1017 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
1019 if (slowpath)
1020 goto always_slowpath;
1022 bytes_var = mono_mb_add_local (mb, mono_get_int32_type ());
1023 if (atype == ATYPE_STRING) {
1024 /* a string alloator method takes the args: (vtable, len) */
1025 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
1026 mono_mb_emit_ldarg (mb, 1);
1027 mono_mb_emit_icon (mb, 1);
1028 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1029 mono_mb_emit_icon (mb, 1);
1030 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1031 // sizeof (MonoString) might include padding
1032 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
1033 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1034 mono_mb_emit_stloc (mb, bytes_var);
1035 } else {
1036 mono_mb_emit_ldarg (mb, 1);
1037 mono_mb_emit_stloc (mb, bytes_var);
1040 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
1041 if (atype == ATYPE_STRING) {
1042 /* check for size */
1043 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
1044 mono_mb_emit_ldloc (mb, bytes_var);
1045 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
1046 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
1047 /* check for overflow */
1048 mono_mb_emit_ldloc (mb, bytes_var);
1049 mono_mb_emit_icon (mb, sizeof (MonoString));
1050 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1053 /* int index = INDEX_FROM_BYTES(bytes); */
1054 index_var = mono_mb_add_local (mb, mono_get_int32_type ());
1056 mono_mb_emit_ldloc (mb, bytes_var);
1057 mono_mb_emit_icon (mb, GRANULARITY - 1);
1058 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1059 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
1060 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
1061 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
1062 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1063 /* index var is already adjusted into bytes */
1064 mono_mb_emit_stloc (mb, index_var);
1066 my_fl_var = mono_mb_add_local (mb, mono_get_int_type ());
1067 my_entry_var = mono_mb_add_local (mb, mono_get_int_type ());
1068 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1069 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1070 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
1071 mono_mb_emit_i4 (mb, tls_key);
1072 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
1073 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1074 + G_STRUCT_OFFSET (struct thread_local_freelists,
1075 ptrfree_freelists));
1076 else if (atype == ATYPE_NORMAL)
1077 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1078 + G_STRUCT_OFFSET (struct thread_local_freelists,
1079 normal_freelists));
1080 else if (atype == ATYPE_GCJ)
1081 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1082 + G_STRUCT_OFFSET (struct thread_local_freelists,
1083 gcj_freelists));
1084 else
1085 g_assert_not_reached ();
1086 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1087 mono_mb_emit_ldloc (mb, index_var);
1088 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1089 mono_mb_emit_stloc (mb, my_fl_var);
1091 /* my_entry = *my_fl; */
1092 mono_mb_emit_ldloc (mb, my_fl_var);
1093 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1094 mono_mb_emit_stloc (mb, my_entry_var);
1096 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1097 mono_mb_emit_ldloc (mb, my_entry_var);
1098 mono_mb_emit_icon (mb, HBLKSIZE);
1099 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1101 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1102 mono_mb_emit_ldloc (mb, my_fl_var);
1103 mono_mb_emit_ldloc (mb, my_entry_var);
1104 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1105 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1107 /* set the vtable and clear the words in the object */
1108 mono_mb_emit_ldloc (mb, my_entry_var);
1109 mono_mb_emit_ldarg (mb, 0);
1110 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1112 if (atype == ATYPE_FREEPTR) {
1113 int start_var, end_var, start_loop;
1114 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1116 start_var = mono_mb_add_local (mb, mono_get_int_type ());
1117 end_var = mono_mb_add_local (mb, mono_get_int_type ());
1118 mono_mb_emit_ldloc (mb, my_entry_var);
1119 mono_mb_emit_ldloc (mb, bytes_var);
1120 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1121 mono_mb_emit_stloc (mb, end_var);
1122 mono_mb_emit_ldloc (mb, my_entry_var);
1123 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1124 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1125 mono_mb_emit_stloc (mb, start_var);
1127 * do {
1128 * *start++ = NULL;
1129 * } while (start < end);
1131 start_loop = mono_mb_get_label (mb);
1132 mono_mb_emit_ldloc (mb, start_var);
1133 mono_mb_emit_icon (mb, 0);
1134 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1135 mono_mb_emit_ldloc (mb, start_var);
1136 mono_mb_emit_icon (mb, sizeof (gpointer));
1137 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1138 mono_mb_emit_stloc (mb, start_var);
1140 mono_mb_emit_ldloc (mb, start_var);
1141 mono_mb_emit_ldloc (mb, end_var);
1142 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1143 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1144 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1145 /* need to clear just the sync pointer */
1146 mono_mb_emit_ldloc (mb, my_entry_var);
1147 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1148 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1149 mono_mb_emit_icon (mb, 0);
1150 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1153 if (atype == ATYPE_STRING) {
1154 /* need to set length and clear the last char */
1155 /* s->length = len; */
1156 mono_mb_emit_ldloc (mb, my_entry_var);
1157 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1158 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1159 mono_mb_emit_ldarg (mb, 1);
1160 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1161 /* s->chars [len] = 0; */
1162 mono_mb_emit_ldloc (mb, my_entry_var);
1163 mono_mb_emit_ldloc (mb, bytes_var);
1164 mono_mb_emit_icon (mb, 2);
1165 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1166 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1167 mono_mb_emit_icon (mb, 0);
1168 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1171 /* return my_entry; */
1172 mono_mb_emit_ldloc (mb, my_entry_var);
1173 mono_mb_emit_byte (mb, MONO_CEE_RET);
1175 mono_mb_patch_short_branch (mb, no_freelist_branch);
1176 if (not_small_enough_branch > 0)
1177 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1178 if (size_overflow_branch > 0)
1179 mono_mb_patch_short_branch (mb, size_overflow_branch);
1181 /* the slow path: we just call back into the runtime */
1182 always_slowpath:
1183 if (atype == ATYPE_STRING) {
1184 mono_mb_emit_ldarg (mb, 1);
1185 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1186 } else {
1187 mono_mb_emit_ldarg (mb, 0);
1188 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1191 mono_mb_emit_byte (mb, MONO_CEE_RET);
1193 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1194 info->d.alloc.gc_name = "boehm";
1195 info->d.alloc.alloc_type = atype;
1196 mb->init_locals = FALSE;
1198 res = mono_mb_create (mb, csig, 8, info);
1199 mono_mb_free (mb);
1201 return res;
1204 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1205 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1207 gboolean
1208 mono_gc_is_critical_method (MonoMethod *method)
1210 int i;
1212 for (i = 0; i < ATYPE_NUM; ++i)
1213 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1214 return TRUE;
1216 return FALSE;
1220 * If possible, generate a managed method that can quickly allocate objects in class
1221 * @klass. The method will typically have an thread-local inline allocation sequence.
1222 * The signature of the called method is:
1223 * object allocate (MonoVTable *vtable)
1224 * The thread local alloc logic is taken from libgc/pthread_support.c.
1226 MonoMethod*
1227 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1229 int atype;
1232 * Tls implementation changed, we jump to tls native getters/setters.
1233 * Is boehm managed allocator ok with this ? Do we even care ?
1235 return NULL;
1237 if (!SMALL_ENOUGH (m_class_get_instance_size (klass)))
1238 return NULL;
1239 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1240 return NULL;
1241 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1242 return NULL;
1243 if (m_class_get_rank (klass))
1244 return NULL;
1245 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (klass)))
1246 return NULL;
1247 if (m_class_get_byval_arg (klass)->type == MONO_TYPE_STRING) {
1248 atype = ATYPE_STRING;
1249 } else if (!known_instance_size) {
1250 return NULL;
1251 } else if (!m_class_has_references (klass)) {
1252 if (for_box)
1253 atype = ATYPE_FREEPTR_FOR_BOX;
1254 else
1255 atype = ATYPE_FREEPTR;
1256 } else {
1257 return NULL;
1259 * disabled because we currently do a runtime choice anyway, to
1260 * deal with multiple appdomains.
1261 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1262 atype = ATYPE_GCJ;
1263 else
1264 atype = ATYPE_NORMAL;
1267 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1270 MonoMethod*
1271 mono_gc_get_managed_array_allocator (MonoClass *klass)
1273 return NULL;
1277 * mono_gc_get_managed_allocator_by_type:
1279 * Return a managed allocator method corresponding to allocator type ATYPE.
1281 MonoMethod*
1282 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1284 MonoMethod *res;
1285 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1286 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1288 return NULL;
1290 res = cache [atype];
1291 if (res)
1292 return res;
1294 res = create_allocator (atype, -1, slowpath);
1295 mono_os_mutex_lock (&mono_gc_lock);
1296 if (cache [atype]) {
1297 mono_free_method (res);
1298 res = cache [atype];
1299 } else {
1300 mono_memory_barrier ();
1301 cache [atype] = res;
1303 mono_os_mutex_unlock (&mono_gc_lock);
1304 return res;
1307 guint32
1308 mono_gc_get_managed_allocator_types (void)
1310 return ATYPE_NUM;
1313 MonoMethod*
1314 mono_gc_get_write_barrier (void)
1316 g_assert_not_reached ();
1317 return NULL;
1320 #else
1322 gboolean
1323 mono_gc_is_critical_method (MonoMethod *method)
1325 return FALSE;
1328 MonoMethod*
1329 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1331 return NULL;
1334 MonoMethod*
1335 mono_gc_get_managed_array_allocator (MonoClass *klass)
1337 return NULL;
1340 MonoMethod*
1341 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1343 return NULL;
1346 guint32
1347 mono_gc_get_managed_allocator_types (void)
1349 return 0;
1352 MonoMethod*
1353 mono_gc_get_write_barrier (void)
1355 g_assert_not_reached ();
1356 return NULL;
1359 #endif
1361 MonoMethod*
1362 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1364 g_assert_not_reached ();
1365 return NULL;
1369 mono_gc_get_aligned_size_for_allocator (int size)
1371 return size;
1374 const char *
1375 mono_gc_get_gc_name (void)
1377 return "boehm";
1380 void*
1381 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1383 return GC_call_with_alloc_lock (func, data);
1386 char*
1387 mono_gc_get_description (void)
1389 return g_strdup (DEFAULT_GC_NAME);
1392 void
1393 mono_gc_set_desktop_mode (void)
1395 GC_dont_expand = 1;
1398 gboolean
1399 mono_gc_is_moving (void)
1401 return FALSE;
1404 gboolean
1405 mono_gc_is_disabled (void)
1407 if (GC_dont_gc || gc_dont_gc_env)
1408 return TRUE;
1409 else
1410 return FALSE;
1413 void
1414 mono_gc_wbarrier_range_copy (gpointer _dest, gconstpointer _src, int size)
1416 g_assert_not_reached ();
1419 MonoRangeCopyFunction
1420 mono_gc_get_range_copy_func (void)
1422 return &mono_gc_wbarrier_range_copy;
1425 guint8*
1426 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1428 g_assert_not_reached ();
1429 return NULL;
1432 guint8*
1433 mono_gc_get_target_card_table (int *shift_bits, target_mgreg_t *card_mask)
1435 *shift_bits = 0;
1436 *card_mask = 0;
1437 return NULL;
1440 gboolean
1441 mono_gc_card_table_nursery_check (void)
1443 g_assert_not_reached ();
1444 return TRUE;
1447 void*
1448 mono_gc_get_nursery (int *shift_bits, size_t *size)
1450 return NULL;
1453 gboolean
1454 mono_gc_precise_stack_mark_enabled (void)
1456 return FALSE;
1459 FILE *
1460 mono_gc_get_logfile (void)
1462 return NULL;
1465 void
1466 mono_gc_params_set (const char* options)
1470 void
1471 mono_gc_debug_set (const char* options)
1475 void
1476 mono_gc_conservatively_scan_area (void *start, void *end)
1478 g_assert_not_reached ();
1481 void *
1482 mono_gc_scan_object (void *obj, void *gc_data)
1484 g_assert_not_reached ();
1485 return NULL;
1488 gsize*
1489 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1491 g_assert_not_reached ();
1492 return NULL;
1495 void
1496 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1500 void
1501 mono_gc_set_stack_end (void *stack_end)
1505 void
1506 mono_gc_skip_thread_changing (gboolean skip)
1509 * Unlike SGen, Boehm doesn't respect our thread info flags. We need to
1510 * inform Boehm manually to skip/not skip the current thread.
1513 if (skip)
1514 GC_start_blocking ();
1515 else
1516 GC_end_blocking ();
1519 void
1520 mono_gc_skip_thread_changed (gboolean skip)
1524 void
1525 mono_gc_register_for_finalization (MonoObject *obj, MonoFinalizationProc user_data)
1527 guint offset = 0;
1529 #ifndef GC_DEBUG
1530 /* This assertion is not valid when GC_DEBUG is defined */
1531 g_assert (GC_base (obj) == (char*)obj - offset);
1532 #endif
1534 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1537 #ifndef HOST_WIN32
1539 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1541 /* it is being replaced by GC_pthread_create on some
1542 * platforms, see libgc/include/gc_pthread_redirects.h */
1543 return pthread_create (new_thread, attr, start_routine, arg);
1545 #endif
1547 #ifdef HOST_WIN32
1548 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1550 return GC_DllMain (module_handle, reason, reserved);
1552 #endif
1554 MonoVTable *
1555 mono_gc_get_vtable (MonoObject *obj)
1557 // No pointer tagging.
1558 return obj->vtable;
1561 guint
1562 mono_gc_get_vtable_bits (MonoClass *klass)
1564 if (fin_callbacks.is_class_finalization_aware) {
1565 if (fin_callbacks.is_class_finalization_aware (klass))
1566 return BOEHM_GC_BIT_FINALIZER_AWARE;
1568 return 0;
1572 * mono_gc_register_altstack:
1574 * Register the dimensions of the normal stack and altstack with the collector.
1575 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1577 void
1578 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1580 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1584 mono_gc_get_los_limit (void)
1586 return G_MAXINT;
1589 void
1590 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1592 mono_unichar2 *new_end = str->chars + new_length;
1594 /* zero the discarded string. This null-delimits the string and allows
1595 * the space to be reclaimed by SGen. */
1597 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1598 str->length = new_length;
1601 gboolean
1602 mono_gc_user_markers_supported (void)
1604 return FALSE;
1607 void *
1608 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1610 g_assert_not_reached ();
1611 return NULL;
1614 /* Toggleref support */
1616 void
1617 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1619 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1620 g_error ("GC_toggleref_add failed\n");
1623 void
1624 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1626 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1629 /* Test support code */
1631 static MonoToggleRefStatus
1632 test_toggleref_callback (MonoObject *obj)
1634 static MonoClassField *mono_toggleref_test_field;
1635 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1637 if (!mono_toggleref_test_field) {
1638 mono_toggleref_test_field = mono_class_get_field_from_name_full (mono_object_class (obj), "__test", NULL);
1639 g_assert (mono_toggleref_test_field);
1642 mono_field_get_value_internal (obj, mono_toggleref_test_field, &status);
1643 printf ("toggleref-cb obj %d\n", status);
1644 return status;
1647 static void
1648 register_test_toggleref_callback (void)
1650 mono_gc_toggleref_register_callback (test_toggleref_callback);
1653 static gboolean
1654 is_finalization_aware (MonoObject *obj)
1656 MonoVTable *vt = obj->vtable;
1657 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1660 static void
1661 fin_notifier (MonoObject *obj)
1663 if (is_finalization_aware (obj))
1664 fin_callbacks.object_queued_for_finalization (obj);
1667 void
1668 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1670 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1671 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1673 fin_callbacks = *callbacks;
1675 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1678 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1680 static inline gboolean
1681 slot_occupied (HandleData *handles, guint slot) {
1682 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1685 static inline void
1686 vacate_slot (HandleData *handles, guint slot) {
1687 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1690 static inline void
1691 occupy_slot (HandleData *handles, guint slot) {
1692 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1695 static int
1696 find_first_unset (guint32 bitmap)
1698 int i;
1699 for (i = 0; i < 32; ++i) {
1700 if (!(bitmap & (1 << i)))
1701 return i;
1703 return -1;
1706 static void
1707 handle_data_alloc_entries (HandleData *handles)
1709 handles->size = 32;
1710 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1711 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1712 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1713 } else {
1714 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)");
1716 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1719 static gint
1720 handle_data_next_unset (HandleData *handles)
1722 gint slot;
1723 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1724 if (handles->bitmap [slot] == 0xffffffff)
1725 continue;
1726 handles->slot_hint = slot;
1727 return find_first_unset (handles->bitmap [slot]);
1729 return -1;
1732 static gint
1733 handle_data_first_unset (HandleData *handles)
1735 gint slot;
1736 for (slot = 0; slot < handles->slot_hint; ++slot) {
1737 if (handles->bitmap [slot] == 0xffffffff)
1738 continue;
1739 handles->slot_hint = slot;
1740 return find_first_unset (handles->bitmap [slot]);
1742 return -1;
1745 /* Returns the index of the current slot in the bitmap. */
1746 static void
1747 handle_data_grow (HandleData *handles, gboolean track)
1749 guint32 *new_bitmap;
1750 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1752 /* resize and copy the bitmap */
1753 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1754 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1755 g_free (handles->bitmap);
1756 handles->bitmap = new_bitmap;
1758 /* resize and copy the entries */
1759 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1760 gpointer *entries;
1761 guint16 *domain_ids;
1762 gint i;
1763 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1764 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1765 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1766 for (i = 0; i < handles->size; ++i) {
1767 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1768 if (obj) {
1769 mono_gc_weak_link_add (&(entries [i]), obj, track);
1770 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1771 } else {
1772 g_assert (!handles->entries [i]);
1775 g_free (handles->entries);
1776 g_free (handles->domain_ids);
1777 handles->entries = entries;
1778 handles->domain_ids = domain_ids;
1779 } else {
1780 gpointer *entries;
1781 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)");
1782 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1783 mono_gc_free_fixed (handles->entries);
1784 handles->entries = entries;
1786 handles->slot_hint = handles->size / BITMAP_SIZE;
1787 handles->size = new_size;
1790 static guint32
1791 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1793 gint slot, i;
1794 guint32 res;
1795 lock_handles (handles);
1796 if (!handles->size)
1797 handle_data_alloc_entries (handles);
1798 i = handle_data_next_unset (handles);
1799 if (i == -1 && handles->slot_hint != 0)
1800 i = handle_data_first_unset (handles);
1801 if (i == -1) {
1802 handle_data_grow (handles, track);
1803 i = 0;
1805 slot = handles->slot_hint * BITMAP_SIZE + i;
1806 occupy_slot (handles, slot);
1807 handles->entries [slot] = NULL;
1808 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1809 /*FIXME, what to use when obj == null?*/
1810 handles->domain_ids [slot] = (obj ? mono_object_get_domain_internal (obj) : mono_domain_get ())->domain_id;
1811 if (obj)
1812 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1813 } else {
1814 handles->entries [slot] = obj;
1817 #ifndef DISABLE_PERFCOUNTERS
1818 mono_atomic_inc_i32 (&mono_perfcounters->gc_num_handles);
1819 #endif
1820 unlock_handles (handles);
1821 res = MONO_GC_HANDLE (slot, handles->type);
1822 MONO_PROFILER_RAISE (gc_handle_created, (res, (MonoGCHandleType)handles->type, obj));
1823 return res;
1827 * mono_gchandle_new_internal:
1828 * \param obj managed object to get a handle for
1829 * \param pinned whether the object should be pinned
1831 * This returns a handle that wraps the object, this is used to keep a
1832 * reference to a managed object from the unmanaged world and preventing the
1833 * object from being disposed.
1835 * If \p pinned is false the address of the object can not be obtained, if it is
1836 * true the address of the object can be obtained. This will also pin the
1837 * object so it will not be possible by a moving garbage collector to move the
1838 * object.
1840 * \returns a handle that can be used to access the object from
1841 * unmanaged code.
1843 guint32
1844 mono_gchandle_new_internal (MonoObject *obj, gboolean pinned)
1846 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1850 * mono_gchandle_new_weakref_internal:
1851 * \param obj managed object to get a handle for
1852 * \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.
1854 * This returns a weak handle that wraps the object, this is used to
1855 * keep a reference to a managed object from the unmanaged world.
1856 * Unlike the \c mono_gchandle_new_internal the object can be reclaimed by the
1857 * garbage collector. In this case the value of the GCHandle will be
1858 * set to zero.
1860 * If \p track_resurrection is TRUE the object will be tracked through
1861 * finalization and if the object is resurrected during the execution
1862 * of the finalizer, then the returned weakref will continue to hold
1863 * a reference to the object. If \p track_resurrection is FALSE, then
1864 * the weak reference's target will become NULL as soon as the object
1865 * is passed on to the finalizer.
1867 * \returns a handle that can be used to access the object from
1868 * unmanaged code.
1870 guint32
1871 mono_gchandle_new_weakref_internal (MonoObject *obj, gboolean track_resurrection)
1873 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1877 * mono_gchandle_get_target_internal:
1878 * \param gchandle a GCHandle's handle.
1880 * The handle was previously created by calling \c mono_gchandle_new_internal or
1881 * \c mono_gchandle_new_weakref.
1883 * \returns A pointer to the \c MonoObject* represented by the handle or
1884 * NULL for a collected object if using a weakref handle.
1886 MonoObject*
1887 mono_gchandle_get_target_internal (guint32 gchandle)
1889 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1890 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1891 HandleData *handles = &gc_handles [type];
1892 MonoObject *obj = NULL;
1893 if (type >= HANDLE_TYPE_MAX)
1894 return NULL;
1896 lock_handles (handles);
1897 if (slot < handles->size && slot_occupied (handles, slot)) {
1898 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1899 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1900 } else {
1901 obj = (MonoObject *)handles->entries [slot];
1903 } else {
1904 /* print a warning? */
1906 unlock_handles (handles);
1907 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1908 return obj;
1911 void
1912 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1914 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1915 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1916 HandleData *handles = &gc_handles [type];
1917 MonoObject *old_obj = NULL;
1919 g_assert (type < HANDLE_TYPE_MAX);
1920 lock_handles (handles);
1921 if (slot < handles->size && slot_occupied (handles, slot)) {
1922 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1923 old_obj = (MonoObject *)handles->entries [slot];
1924 if (handles->entries [slot])
1925 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1926 if (obj)
1927 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1928 /*FIXME, what to use when obj == null?*/
1929 handles->domain_ids [slot] = (obj ? mono_object_get_domain_internal (obj) : mono_domain_get ())->domain_id;
1930 } else {
1931 handles->entries [slot] = obj;
1933 } else {
1934 /* print a warning? */
1936 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1937 unlock_handles (handles);
1940 gboolean
1941 mono_gc_is_null (void)
1943 return FALSE;
1947 * mono_gchandle_is_in_domain:
1948 * \param gchandle a GCHandle's handle.
1949 * \param domain An application domain.
1951 * Use this function to determine if the \p gchandle points to an
1952 * object allocated in the specified \p domain.
1954 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1956 gboolean
1957 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1959 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1960 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1961 HandleData *handles = &gc_handles [type];
1962 gboolean result = FALSE;
1964 if (type >= HANDLE_TYPE_MAX)
1965 return FALSE;
1967 lock_handles (handles);
1968 if (slot < handles->size && slot_occupied (handles, slot)) {
1969 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1970 result = domain->domain_id == handles->domain_ids [slot];
1971 } else {
1972 MonoObject *obj;
1973 obj = (MonoObject *)handles->entries [slot];
1974 if (obj == NULL)
1975 result = TRUE;
1976 else
1977 result = domain == mono_object_domain (obj);
1979 } else {
1980 /* print a warning? */
1982 unlock_handles (handles);
1983 return result;
1987 * mono_gchandle_free_internal:
1988 * \param gchandle a GCHandle's handle.
1990 * Frees the \p gchandle handle. If there are no outstanding
1991 * references, the garbage collector can reclaim the memory of the
1992 * object wrapped.
1994 void
1995 mono_gchandle_free_internal (guint32 gchandle)
1997 if (!gchandle)
1998 return;
2000 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
2001 guint type = MONO_GC_HANDLE_TYPE (gchandle);
2002 HandleData *handles = &gc_handles [type];
2003 if (type >= HANDLE_TYPE_MAX)
2004 return;
2006 lock_handles (handles);
2007 if (slot < handles->size && slot_occupied (handles, slot)) {
2008 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
2009 if (handles->entries [slot])
2010 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
2011 } else {
2012 handles->entries [slot] = NULL;
2014 vacate_slot (handles, slot);
2015 } else {
2016 /* print a warning? */
2018 #ifndef DISABLE_PERFCOUNTERS
2019 mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles);
2020 #endif
2021 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
2022 unlock_handles (handles);
2023 MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, (MonoGCHandleType)handles->type));
2027 * mono_gchandle_free_domain:
2028 * \param domain domain that is unloading
2030 * Function used internally to cleanup any GC handle for objects belonging
2031 * to the specified domain during appdomain unload.
2033 void
2034 mono_gchandle_free_domain (MonoDomain *domain)
2036 guint type;
2038 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
2039 guint slot;
2040 HandleData *handles = &gc_handles [type];
2041 lock_handles (handles);
2042 for (slot = 0; slot < handles->size; ++slot) {
2043 if (!slot_occupied (handles, slot))
2044 continue;
2045 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
2046 if (domain->domain_id == handles->domain_ids [slot]) {
2047 vacate_slot (handles, slot);
2048 if (handles->entries [slot])
2049 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
2051 } else {
2052 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
2053 vacate_slot (handles, slot);
2054 handles->entries [slot] = NULL;
2058 unlock_handles (handles);
2063 void
2064 mono_gc_register_obj_with_weak_fields (void *obj)
2066 g_error ("Weak fields not supported by boehm gc");
2069 gboolean
2070 mono_gc_ephemeron_array_add (MonoObject *obj)
2072 return TRUE;
2075 #else
2077 MONO_EMPTY_SOURCE_FILE (boehm_gc);
2078 #endif /* no Boehm GC */