Took changes from miguel's branch.
[mono-project.git] / mono / metadata / boehm-gc.c
blob71225b3f78fb23f03981eeaedc6f884fbc149a64
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;
314 guint64
315 mono_gc_get_allocated_bytes_for_current_thread (void)
317 return 0;
321 * mono_gc_get_generation:
322 * \param object a managed object
324 * Get the garbage collector's generation that \p object belongs to.
325 * Use this has a hint only.
327 * \returns a garbage collector generation number
330 mono_gc_get_generation (MonoObject *object)
332 return 0;
336 * mono_gc_collection_count:
337 * \param generation a GC generation number
339 * Get how many times a garbage collection has been performed
340 * for the given \p generation number.
342 * \returns the number of garbage collections
345 mono_gc_collection_count (int generation)
347 return GC_gc_no;
350 void
351 mono_gc_stop_world ()
353 g_assert ("mono_gc_stop_world is not supported in Boehm");
356 void
357 mono_gc_restart_world ()
359 g_assert ("mono_gc_restart_world is not supported in Boehm");
363 * mono_gc_add_memory_pressure:
364 * \param value amount of bytes
366 * Adjust the garbage collector's view of how many bytes of memory
367 * are indirectly referenced by managed objects (for example unmanaged
368 * memory holding image or other binary data).
369 * This is a hint only to the garbage collector algorithm.
370 * Note that negative amounts of p value will decrease the memory
371 * pressure.
373 void
374 mono_gc_add_memory_pressure (gint64 value)
379 * mono_gc_get_used_size:
381 * Get the approximate amount of memory used by managed objects.
383 * Returns: the amount of memory used in bytes
385 int64_t
386 mono_gc_get_used_size (void)
388 return GC_get_heap_size () - GC_get_free_bytes ();
392 * mono_gc_get_heap_size:
394 * Get the amount of memory used by the garbage collector.
396 * Returns: the size of the heap in bytes
398 int64_t
399 mono_gc_get_heap_size (void)
401 return GC_get_heap_size ();
404 gboolean
405 mono_gc_is_gc_thread (void)
407 return GC_thread_is_registered ();
410 gpointer
411 mono_gc_thread_attach (MonoThreadInfo* info)
413 struct GC_stack_base sb;
414 int res;
416 /* TODO: use GC_get_stack_base instead of baseptr. */
417 sb.mem_base = info->stack_end;
418 res = GC_register_my_thread (&sb);
419 if (res == GC_UNIMPLEMENTED)
420 return NULL; /* Cannot happen with GC v7+. */
422 info->handle_stack = mono_handle_stack_alloc ();
424 return info;
427 void
428 mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
430 MonoNativeThreadId tid;
432 tid = mono_thread_info_get_tid (p);
434 if (p->runtime_thread)
435 mono_threads_add_joinable_thread ((gpointer)tid);
437 mono_handle_stack_free (p->handle_stack);
438 p->handle_stack = NULL;
441 gboolean
442 mono_gc_thread_in_critical_region (MonoThreadInfo *info)
444 return FALSE;
447 gboolean
448 mono_object_is_alive (MonoObject* o)
450 return GC_is_marked ((ptr_t)o);
454 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
456 return 1;
459 static gint64 gc_start_time;
461 static void
462 on_gc_notification (GC_EventType event)
464 MonoProfilerGCEvent e;
466 switch (event) {
467 case GC_EVENT_PRE_STOP_WORLD:
468 e = MONO_GC_EVENT_PRE_STOP_WORLD;
469 MONO_GC_WORLD_STOP_BEGIN ();
470 break;
472 case GC_EVENT_POST_STOP_WORLD:
473 e = MONO_GC_EVENT_POST_STOP_WORLD;
474 MONO_GC_WORLD_STOP_END ();
475 break;
477 case GC_EVENT_PRE_START_WORLD:
478 e = MONO_GC_EVENT_PRE_START_WORLD;
479 MONO_GC_WORLD_RESTART_BEGIN (1);
480 break;
482 case GC_EVENT_POST_START_WORLD:
483 e = MONO_GC_EVENT_POST_START_WORLD;
484 MONO_GC_WORLD_RESTART_END (1);
485 break;
487 case GC_EVENT_START:
488 e = MONO_GC_EVENT_START;
489 MONO_GC_BEGIN (1);
490 #ifndef DISABLE_PERFCOUNTERS
491 if (mono_perfcounters)
492 mono_atomic_inc_i32 (&mono_perfcounters->gc_collections0);
493 #endif
494 mono_atomic_inc_i32 (&mono_gc_stats.major_gc_count);
495 gc_start_time = mono_100ns_ticks ();
496 break;
498 case GC_EVENT_END:
499 e = MONO_GC_EVENT_END;
500 MONO_GC_END (1);
501 #if defined(ENABLE_DTRACE) && defined(__sun__)
502 /* This works around a dtrace -G problem on Solaris.
503 Limit its actual use to when the probe is enabled. */
504 if (MONO_GC_END_ENABLED ())
505 sleep(0);
506 #endif
508 #ifndef DISABLE_PERFCOUNTERS
509 if (mono_perfcounters) {
510 guint64 heap_size = GC_get_heap_size ();
511 guint64 used_size = heap_size - GC_get_free_bytes ();
512 /* FIXME: change these to mono_atomic_store_i64 () */
513 UnlockedWrite64 (&mono_perfcounters->gc_total_bytes, used_size);
514 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
515 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
516 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
518 #endif
519 UnlockedAdd64 (&mono_gc_stats.major_gc_time, mono_100ns_ticks () - gc_start_time);
520 mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10);
521 break;
522 default:
523 break;
526 switch (event) {
527 case GC_EVENT_MARK_START:
528 case GC_EVENT_MARK_END:
529 case GC_EVENT_RECLAIM_START:
530 case GC_EVENT_RECLAIM_END:
531 break;
532 default:
533 MONO_PROFILER_RAISE (gc_event, (e, 0, TRUE));
534 break;
537 switch (event) {
538 case GC_EVENT_PRE_STOP_WORLD:
539 mono_thread_info_suspend_lock ();
540 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0, TRUE));
541 break;
542 case GC_EVENT_POST_START_WORLD:
543 mono_thread_info_suspend_unlock ();
544 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0, TRUE));
545 break;
546 default:
547 break;
552 static void
553 on_gc_heap_resize (size_t new_size)
555 guint64 heap_size = GC_get_heap_size ();
556 #ifndef DISABLE_PERFCOUNTERS
557 if (mono_perfcounters) {
558 /* FIXME: change these to mono_atomic_store_i64 () */
559 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
560 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
561 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
563 #endif
565 MONO_PROFILER_RAISE (gc_resize, (new_size));
568 typedef struct {
569 char *start;
570 char *end;
571 } RootData;
573 static gpointer
574 register_root (gpointer arg)
576 RootData* root_data = (RootData*)arg;
577 g_hash_table_insert (roots, root_data->start, root_data->end);
578 return NULL;
582 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
584 RootData root_data;
585 root_data.start = start;
586 /* Boehm root processing requires one byte past end of region to be scanned */
587 root_data.end = start + size + 1;
588 GC_call_with_alloc_lock (register_root, &root_data);
589 MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg));
590 return TRUE;
594 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg)
596 return mono_gc_register_root (start, size, descr, source, key, msg);
599 static gpointer
600 deregister_root (gpointer arg)
602 gboolean removed = g_hash_table_remove (roots, arg);
603 g_assert (removed);
604 return NULL;
607 void
608 mono_gc_deregister_root (char* addr)
610 GC_call_with_alloc_lock (deregister_root, addr);
611 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr));
614 static void
615 push_root (gpointer key, gpointer value, gpointer user_data)
617 GC_push_all (key, value);
620 static void
621 push_handle_stack (HandleStack* stack)
623 HandleChunk *cur = stack->bottom;
624 HandleChunk *last = stack->top;
626 if (!cur)
627 return;
629 while (cur) {
630 if (cur->size > 0)
631 GC_push_all ((gpointer)&cur->elems[0], (char*)(cur->elems + cur->size) + 1);
632 if (cur == last)
633 break;
634 cur = cur->next;
638 static void
639 mono_push_other_roots (void)
641 g_hash_table_foreach (roots, push_root, NULL);
642 FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) {
643 HandleStack* stack = info->handle_stack;
644 if (stack)
645 push_handle_stack (stack);
646 } FOREACH_THREAD_END
647 if (default_push_other_roots)
648 default_push_other_roots ();
651 static void
652 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
654 /* libgc requires that we use HIDE_POINTER... */
655 *link_addr = (void*)HIDE_POINTER (obj);
656 if (track)
657 GC_REGISTER_LONG_LINK (link_addr, obj);
658 else
659 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
662 static void
663 mono_gc_weak_link_remove (void **link_addr, gboolean track)
665 if (track)
666 GC_unregister_long_link (link_addr);
667 else
668 GC_unregister_disappearing_link (link_addr);
669 *link_addr = NULL;
672 static gpointer
673 reveal_link (gpointer link_addr)
675 void **link_a = (void **)link_addr;
676 return REVEAL_POINTER (*link_a);
679 static MonoObject *
680 mono_gc_weak_link_get (void **link_addr)
682 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
683 if (obj == (MonoObject *) -1)
684 return NULL;
685 return obj;
688 void*
689 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
691 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
694 void*
695 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
697 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
700 void*
701 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
703 /* libgc has no usable support for arrays... */
704 return GC_NO_DESCRIPTOR;
707 void*
708 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
710 /* It seems there are issues when the bitmap doesn't fit: play it safe */
711 if (numbits >= 30)
712 return GC_NO_DESCRIPTOR;
713 else
714 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
717 void*
718 mono_gc_make_vector_descr (void)
720 return NULL;
723 void*
724 mono_gc_make_root_descr_all_refs (int numbits)
726 return NULL;
729 MonoObject*
730 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
732 void *start = GC_MALLOC_UNCOLLECTABLE (size);
733 MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg));
734 return (MonoObject*)start;
737 MonoObject*
738 mono_gc_alloc_fixed_no_descriptor (size_t size, MonoGCRootSource source, void *key, const char *msg)
740 return mono_gc_alloc_fixed (size, 0, source, key, msg);
743 void
744 mono_gc_free_fixed (void* addr)
746 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr));
747 GC_FREE (addr);
750 MonoObject*
751 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
753 MonoObject *obj;
755 if (!m_class_has_references (vtable->klass)) {
756 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
757 if (G_UNLIKELY (!obj))
758 return NULL;
760 obj->vtable = vtable;
761 obj->synchronisation = NULL;
763 memset (mono_object_get_data (obj), 0, size - MONO_ABI_SIZEOF (MonoObject));
764 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
765 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
766 if (G_UNLIKELY (!obj))
767 return NULL;
768 } else {
769 obj = (MonoObject *)GC_MALLOC (size);
770 if (G_UNLIKELY (!obj))
771 return NULL;
773 obj->vtable = vtable;
776 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
777 MONO_PROFILER_RAISE (gc_allocation, (obj));
779 return obj;
782 MonoArray*
783 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
785 MonoArray *obj;
787 if (!m_class_has_references (vtable->klass)) {
788 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
789 if (G_UNLIKELY (!obj))
790 return NULL;
792 obj->obj.vtable = vtable;
793 obj->obj.synchronisation = NULL;
795 memset (mono_object_get_data ((MonoObject*)obj), 0, size - MONO_ABI_SIZEOF (MonoObject));
796 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
797 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
798 if (G_UNLIKELY (!obj))
799 return NULL;
800 } else {
801 obj = (MonoArray *)GC_MALLOC (size);
802 if (G_UNLIKELY (!obj))
803 return NULL;
805 obj->obj.vtable = vtable;
808 obj->max_length = max_length;
810 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
811 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
813 return obj;
816 MonoArray*
817 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
819 MonoArray *obj;
821 if (!m_class_has_references (vtable->klass)) {
822 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
823 if (G_UNLIKELY (!obj))
824 return NULL;
826 obj->obj.vtable = vtable;
827 obj->obj.synchronisation = NULL;
829 memset (mono_object_get_data ((MonoObject*)obj), 0, size - MONO_ABI_SIZEOF (MonoObject));
830 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
831 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
832 if (G_UNLIKELY (!obj))
833 return NULL;
834 } else {
835 obj = (MonoArray *)GC_MALLOC (size);
836 if (G_UNLIKELY (!obj))
837 return NULL;
839 obj->obj.vtable = vtable;
842 obj->max_length = max_length;
844 if (bounds_size)
845 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
847 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
848 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
850 return obj;
853 MonoString*
854 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
856 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
857 if (G_UNLIKELY (!obj))
858 return NULL;
860 obj->object.vtable = vtable;
861 obj->object.synchronisation = NULL;
862 obj->length = len;
863 obj->chars [len] = 0;
865 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
866 MONO_PROFILER_RAISE (gc_allocation, (&obj->object));
868 return obj;
871 MonoObject*
872 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
874 return mono_gc_alloc_obj (vtable, size);
877 MonoObject*
878 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
880 return mono_gc_alloc_obj (vtable, size);
884 mono_gc_invoke_finalizers (void)
886 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
887 * the 'mem_freed' variable is not initialized when there are no
888 * objects to finalize, which leads to strange behavior later on.
889 * The check is necessary to work around that bug.
891 if (GC_should_invoke_finalizers ())
892 return GC_invoke_finalizers ();
893 return 0;
896 MonoBoolean
897 mono_gc_pending_finalizers (void)
899 return GC_should_invoke_finalizers ();
902 void
903 mono_gc_wbarrier_set_field_internal (MonoObject *obj, gpointer field_ptr, MonoObject* value)
905 *(void**)field_ptr = value;
908 void
909 mono_gc_wbarrier_set_arrayref_internal (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
911 *(void**)slot_ptr = value;
914 void
915 mono_gc_wbarrier_arrayref_copy_internal (gpointer dest_ptr, gpointer src_ptr, int count)
917 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
920 void
921 mono_gc_wbarrier_generic_store_internal (gpointer ptr, MonoObject* value)
923 *(void**)ptr = value;
926 void
927 mono_gc_wbarrier_generic_store_atomic_internal (gpointer ptr, MonoObject *value)
929 mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
932 void
933 mono_gc_wbarrier_generic_nostore_internal (gpointer ptr)
937 void
938 mono_gc_wbarrier_value_copy_internal (gpointer dest, gpointer src, int count, MonoClass *klass)
940 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
943 void
944 mono_gc_wbarrier_object_copy_internal (MonoObject* obj, MonoObject *src)
946 /* do not copy the sync state */
947 mono_gc_memmove_aligned (mono_object_get_data (obj), (char*)src + MONO_ABI_SIZEOF (MonoObject),
948 m_class_get_instance_size (mono_object_class (obj)) - MONO_ABI_SIZEOF (MonoObject));
951 void
952 mono_gc_clear_domain (MonoDomain *domain)
956 void
957 mono_gc_suspend_finalizers (void)
962 mono_gc_get_suspend_signal (void)
964 return GC_get_suspend_signal ();
968 mono_gc_get_restart_signal (void)
970 return GC_get_thr_restart_signal ();
973 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
974 extern __thread void* GC_thread_tls;
975 #include "metadata-internals.h"
977 static int
978 shift_amount (int v)
980 int i = 0;
981 while (!(v & (1 << i)))
982 i++;
983 return i;
986 enum {
987 ATYPE_FREEPTR,
988 ATYPE_FREEPTR_FOR_BOX,
989 ATYPE_NORMAL,
990 ATYPE_GCJ,
991 ATYPE_STRING,
992 ATYPE_NUM
995 static MonoMethod*
996 create_allocator (int atype, int tls_key, gboolean slowpath)
998 int index_var, bytes_var, my_fl_var, my_entry_var;
999 guint32 no_freelist_branch, not_small_enough_branch = 0;
1000 guint32 size_overflow_branch = 0;
1001 MonoMethodBuilder *mb;
1002 MonoMethod *res;
1003 MonoMethodSignature *csig;
1004 const char *name = NULL;
1005 WrapperInfo *info;
1007 g_assert_not_reached ();
1009 if (atype == ATYPE_FREEPTR) {
1010 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
1011 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
1012 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
1013 } else if (atype == ATYPE_NORMAL) {
1014 name = slowpath ? "SlowAlloc" : "Alloc";
1015 } else if (atype == ATYPE_GCJ) {
1016 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
1017 } else if (atype == ATYPE_STRING) {
1018 name = slowpath ? "SlowAllocString" : "AllocString";
1019 } else {
1020 g_assert_not_reached ();
1023 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
1025 if (atype == ATYPE_STRING) {
1026 csig->ret = m_class_get_byval_arg (mono_defaults.string_class);
1027 csig->params [0] = mono_get_int_type ();
1028 csig->params [1] = mono_get_int32_type ();
1029 } else {
1030 csig->ret = mono_get_object_type ();
1031 csig->params [0] = mono_get_int_type ();
1032 csig->params [1] = mono_get_int32_type ();
1035 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
1037 if (slowpath)
1038 goto always_slowpath;
1040 bytes_var = mono_mb_add_local (mb, mono_get_int32_type ());
1041 if (atype == ATYPE_STRING) {
1042 /* a string alloator method takes the args: (vtable, len) */
1043 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
1044 mono_mb_emit_ldarg (mb, 1);
1045 mono_mb_emit_icon (mb, 1);
1046 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1047 mono_mb_emit_icon (mb, 1);
1048 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1049 // sizeof (MonoString) might include padding
1050 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
1051 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1052 mono_mb_emit_stloc (mb, bytes_var);
1053 } else {
1054 mono_mb_emit_ldarg (mb, 1);
1055 mono_mb_emit_stloc (mb, bytes_var);
1058 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
1059 if (atype == ATYPE_STRING) {
1060 /* check for size */
1061 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
1062 mono_mb_emit_ldloc (mb, bytes_var);
1063 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
1064 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
1065 /* check for overflow */
1066 mono_mb_emit_ldloc (mb, bytes_var);
1067 mono_mb_emit_icon (mb, sizeof (MonoString));
1068 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1071 /* int index = INDEX_FROM_BYTES(bytes); */
1072 index_var = mono_mb_add_local (mb, mono_get_int32_type ());
1074 mono_mb_emit_ldloc (mb, bytes_var);
1075 mono_mb_emit_icon (mb, GRANULARITY - 1);
1076 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1077 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
1078 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
1079 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
1080 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1081 /* index var is already adjusted into bytes */
1082 mono_mb_emit_stloc (mb, index_var);
1084 my_fl_var = mono_mb_add_local (mb, mono_get_int_type ());
1085 my_entry_var = mono_mb_add_local (mb, mono_get_int_type ());
1086 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1087 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1088 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
1089 mono_mb_emit_i4 (mb, tls_key);
1090 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
1091 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1092 + G_STRUCT_OFFSET (struct thread_local_freelists,
1093 ptrfree_freelists));
1094 else if (atype == ATYPE_NORMAL)
1095 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1096 + G_STRUCT_OFFSET (struct thread_local_freelists,
1097 normal_freelists));
1098 else if (atype == ATYPE_GCJ)
1099 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1100 + G_STRUCT_OFFSET (struct thread_local_freelists,
1101 gcj_freelists));
1102 else
1103 g_assert_not_reached ();
1104 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1105 mono_mb_emit_ldloc (mb, index_var);
1106 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1107 mono_mb_emit_stloc (mb, my_fl_var);
1109 /* my_entry = *my_fl; */
1110 mono_mb_emit_ldloc (mb, my_fl_var);
1111 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1112 mono_mb_emit_stloc (mb, my_entry_var);
1114 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1115 mono_mb_emit_ldloc (mb, my_entry_var);
1116 mono_mb_emit_icon (mb, HBLKSIZE);
1117 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1119 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1120 mono_mb_emit_ldloc (mb, my_fl_var);
1121 mono_mb_emit_ldloc (mb, my_entry_var);
1122 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1123 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1125 /* set the vtable and clear the words in the object */
1126 mono_mb_emit_ldloc (mb, my_entry_var);
1127 mono_mb_emit_ldarg (mb, 0);
1128 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1130 if (atype == ATYPE_FREEPTR) {
1131 int start_var, end_var, start_loop;
1132 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1134 start_var = mono_mb_add_local (mb, mono_get_int_type ());
1135 end_var = mono_mb_add_local (mb, mono_get_int_type ());
1136 mono_mb_emit_ldloc (mb, my_entry_var);
1137 mono_mb_emit_ldloc (mb, bytes_var);
1138 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1139 mono_mb_emit_stloc (mb, end_var);
1140 mono_mb_emit_ldloc (mb, my_entry_var);
1141 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1142 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1143 mono_mb_emit_stloc (mb, start_var);
1145 * do {
1146 * *start++ = NULL;
1147 * } while (start < end);
1149 start_loop = mono_mb_get_label (mb);
1150 mono_mb_emit_ldloc (mb, start_var);
1151 mono_mb_emit_icon (mb, 0);
1152 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1153 mono_mb_emit_ldloc (mb, start_var);
1154 mono_mb_emit_icon (mb, sizeof (gpointer));
1155 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1156 mono_mb_emit_stloc (mb, start_var);
1158 mono_mb_emit_ldloc (mb, start_var);
1159 mono_mb_emit_ldloc (mb, end_var);
1160 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1161 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1162 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1163 /* need to clear just the sync pointer */
1164 mono_mb_emit_ldloc (mb, my_entry_var);
1165 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
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_I);
1171 if (atype == ATYPE_STRING) {
1172 /* need to set length and clear the last char */
1173 /* s->length = len; */
1174 mono_mb_emit_ldloc (mb, my_entry_var);
1175 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1176 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1177 mono_mb_emit_ldarg (mb, 1);
1178 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1179 /* s->chars [len] = 0; */
1180 mono_mb_emit_ldloc (mb, my_entry_var);
1181 mono_mb_emit_ldloc (mb, bytes_var);
1182 mono_mb_emit_icon (mb, 2);
1183 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1184 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1185 mono_mb_emit_icon (mb, 0);
1186 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1189 /* return my_entry; */
1190 mono_mb_emit_ldloc (mb, my_entry_var);
1191 mono_mb_emit_byte (mb, MONO_CEE_RET);
1193 mono_mb_patch_short_branch (mb, no_freelist_branch);
1194 if (not_small_enough_branch > 0)
1195 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1196 if (size_overflow_branch > 0)
1197 mono_mb_patch_short_branch (mb, size_overflow_branch);
1199 /* the slow path: we just call back into the runtime */
1200 always_slowpath:
1201 if (atype == ATYPE_STRING) {
1202 mono_mb_emit_ldarg (mb, 1);
1203 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1204 } else {
1205 mono_mb_emit_ldarg (mb, 0);
1206 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1209 mono_mb_emit_byte (mb, MONO_CEE_RET);
1211 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1212 info->d.alloc.gc_name = "boehm";
1213 info->d.alloc.alloc_type = atype;
1214 mb->init_locals = FALSE;
1216 res = mono_mb_create (mb, csig, 8, info);
1217 mono_mb_free (mb);
1219 return res;
1222 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1223 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1225 gboolean
1226 mono_gc_is_critical_method (MonoMethod *method)
1228 int i;
1230 for (i = 0; i < ATYPE_NUM; ++i)
1231 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1232 return TRUE;
1234 return FALSE;
1238 * If possible, generate a managed method that can quickly allocate objects in class
1239 * @klass. The method will typically have an thread-local inline allocation sequence.
1240 * The signature of the called method is:
1241 * object allocate (MonoVTable *vtable)
1242 * The thread local alloc logic is taken from libgc/pthread_support.c.
1244 MonoMethod*
1245 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1247 int atype;
1250 * Tls implementation changed, we jump to tls native getters/setters.
1251 * Is boehm managed allocator ok with this ? Do we even care ?
1253 return NULL;
1255 if (!SMALL_ENOUGH (m_class_get_instance_size (klass)))
1256 return NULL;
1257 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1258 return NULL;
1259 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1260 return NULL;
1261 if (m_class_get_rank (klass))
1262 return NULL;
1263 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (klass)))
1264 return NULL;
1265 if (m_class_get_byval_arg (klass)->type == MONO_TYPE_STRING) {
1266 atype = ATYPE_STRING;
1267 } else if (!known_instance_size) {
1268 return NULL;
1269 } else if (!m_class_has_references (klass)) {
1270 if (for_box)
1271 atype = ATYPE_FREEPTR_FOR_BOX;
1272 else
1273 atype = ATYPE_FREEPTR;
1274 } else {
1275 return NULL;
1277 * disabled because we currently do a runtime choice anyway, to
1278 * deal with multiple appdomains.
1279 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1280 atype = ATYPE_GCJ;
1281 else
1282 atype = ATYPE_NORMAL;
1285 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1288 MonoMethod*
1289 mono_gc_get_managed_array_allocator (MonoClass *klass)
1291 return NULL;
1295 * mono_gc_get_managed_allocator_by_type:
1297 * Return a managed allocator method corresponding to allocator type ATYPE.
1299 MonoMethod*
1300 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1302 MonoMethod *res;
1303 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1304 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1306 return NULL;
1308 res = cache [atype];
1309 if (res)
1310 return res;
1312 res = create_allocator (atype, -1, slowpath);
1313 mono_os_mutex_lock (&mono_gc_lock);
1314 if (cache [atype]) {
1315 mono_free_method (res);
1316 res = cache [atype];
1317 } else {
1318 mono_memory_barrier ();
1319 cache [atype] = res;
1321 mono_os_mutex_unlock (&mono_gc_lock);
1322 return res;
1325 guint32
1326 mono_gc_get_managed_allocator_types (void)
1328 return ATYPE_NUM;
1331 MonoMethod*
1332 mono_gc_get_write_barrier (void)
1334 g_assert_not_reached ();
1335 return NULL;
1338 #else
1340 gboolean
1341 mono_gc_is_critical_method (MonoMethod *method)
1343 return FALSE;
1346 MonoMethod*
1347 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1349 return NULL;
1352 MonoMethod*
1353 mono_gc_get_managed_array_allocator (MonoClass *klass)
1355 return NULL;
1358 MonoMethod*
1359 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1361 return NULL;
1364 guint32
1365 mono_gc_get_managed_allocator_types (void)
1367 return 0;
1370 MonoMethod*
1371 mono_gc_get_write_barrier (void)
1373 g_assert_not_reached ();
1374 return NULL;
1377 #endif
1379 MonoMethod*
1380 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1382 g_assert_not_reached ();
1383 return NULL;
1387 mono_gc_get_aligned_size_for_allocator (int size)
1389 return size;
1392 const char *
1393 mono_gc_get_gc_name (void)
1395 return "boehm";
1398 void*
1399 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1401 return GC_call_with_alloc_lock (func, data);
1404 char*
1405 mono_gc_get_description (void)
1407 return g_strdup (DEFAULT_GC_NAME);
1410 void
1411 mono_gc_set_desktop_mode (void)
1413 GC_dont_expand = 1;
1416 gboolean
1417 mono_gc_is_moving (void)
1419 return FALSE;
1422 gboolean
1423 mono_gc_is_disabled (void)
1425 if (GC_dont_gc || gc_dont_gc_env)
1426 return TRUE;
1427 else
1428 return FALSE;
1431 void
1432 mono_gc_wbarrier_range_copy (gpointer _dest, gconstpointer _src, int size)
1434 g_assert_not_reached ();
1437 MonoRangeCopyFunction
1438 mono_gc_get_range_copy_func (void)
1440 return &mono_gc_wbarrier_range_copy;
1443 guint8*
1444 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1446 g_assert_not_reached ();
1447 return NULL;
1450 guint8*
1451 mono_gc_get_target_card_table (int *shift_bits, target_mgreg_t *card_mask)
1453 *shift_bits = 0;
1454 *card_mask = 0;
1455 return NULL;
1458 gboolean
1459 mono_gc_card_table_nursery_check (void)
1461 g_assert_not_reached ();
1462 return TRUE;
1465 void*
1466 mono_gc_get_nursery (int *shift_bits, size_t *size)
1468 return NULL;
1471 gboolean
1472 mono_gc_precise_stack_mark_enabled (void)
1474 return FALSE;
1477 FILE *
1478 mono_gc_get_logfile (void)
1480 return NULL;
1483 void
1484 mono_gc_params_set (const char* options)
1488 void
1489 mono_gc_debug_set (const char* options)
1493 void
1494 mono_gc_conservatively_scan_area (void *start, void *end)
1496 g_assert_not_reached ();
1499 void *
1500 mono_gc_scan_object (void *obj, void *gc_data)
1502 g_assert_not_reached ();
1503 return NULL;
1506 gsize*
1507 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1509 g_assert_not_reached ();
1510 return NULL;
1513 void
1514 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1518 void
1519 mono_gc_set_stack_end (void *stack_end)
1523 void
1524 mono_gc_skip_thread_changing (gboolean skip)
1527 * Unlike SGen, Boehm doesn't respect our thread info flags. We need to
1528 * inform Boehm manually to skip/not skip the current thread.
1531 if (skip)
1532 GC_start_blocking ();
1533 else
1534 GC_end_blocking ();
1537 void
1538 mono_gc_skip_thread_changed (gboolean skip)
1542 void
1543 mono_gc_register_for_finalization (MonoObject *obj, MonoFinalizationProc user_data)
1545 guint offset = 0;
1547 #ifndef GC_DEBUG
1548 /* This assertion is not valid when GC_DEBUG is defined */
1549 g_assert (GC_base (obj) == (char*)obj - offset);
1550 #endif
1552 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1555 #ifndef HOST_WIN32
1557 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1559 /* it is being replaced by GC_pthread_create on some
1560 * platforms, see libgc/include/gc_pthread_redirects.h */
1561 return pthread_create (new_thread, attr, start_routine, arg);
1563 #endif
1565 #ifdef HOST_WIN32
1566 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1568 return GC_DllMain (module_handle, reason, reserved);
1570 #endif
1572 MonoVTable *
1573 mono_gc_get_vtable (MonoObject *obj)
1575 // No pointer tagging.
1576 return obj->vtable;
1579 guint
1580 mono_gc_get_vtable_bits (MonoClass *klass)
1582 if (fin_callbacks.is_class_finalization_aware) {
1583 if (fin_callbacks.is_class_finalization_aware (klass))
1584 return BOEHM_GC_BIT_FINALIZER_AWARE;
1586 return 0;
1590 * mono_gc_register_altstack:
1592 * Register the dimensions of the normal stack and altstack with the collector.
1593 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1595 void
1596 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1598 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1602 mono_gc_get_los_limit (void)
1604 return G_MAXINT;
1607 void
1608 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1610 mono_unichar2 *new_end = str->chars + new_length;
1612 /* zero the discarded string. This null-delimits the string and allows
1613 * the space to be reclaimed by SGen. */
1615 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1616 str->length = new_length;
1619 gboolean
1620 mono_gc_user_markers_supported (void)
1622 return FALSE;
1625 void *
1626 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1628 g_assert_not_reached ();
1629 return NULL;
1632 /* Toggleref support */
1634 void
1635 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1637 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1638 g_error ("GC_toggleref_add failed\n");
1641 void
1642 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1644 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1647 /* Test support code */
1649 static MonoToggleRefStatus
1650 test_toggleref_callback (MonoObject *obj)
1652 static MonoClassField *mono_toggleref_test_field;
1653 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1655 if (!mono_toggleref_test_field) {
1656 mono_toggleref_test_field = mono_class_get_field_from_name_full (mono_object_class (obj), "__test", NULL);
1657 g_assert (mono_toggleref_test_field);
1660 mono_field_get_value_internal (obj, mono_toggleref_test_field, &status);
1661 printf ("toggleref-cb obj %d\n", status);
1662 return status;
1665 static void
1666 register_test_toggleref_callback (void)
1668 mono_gc_toggleref_register_callback (test_toggleref_callback);
1671 static gboolean
1672 is_finalization_aware (MonoObject *obj)
1674 MonoVTable *vt = obj->vtable;
1675 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1678 static void
1679 fin_notifier (MonoObject *obj)
1681 if (is_finalization_aware (obj))
1682 fin_callbacks.object_queued_for_finalization (obj);
1685 void
1686 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1688 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1689 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1691 fin_callbacks = *callbacks;
1693 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1696 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1698 static inline gboolean
1699 slot_occupied (HandleData *handles, guint slot) {
1700 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1703 static inline void
1704 vacate_slot (HandleData *handles, guint slot) {
1705 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1708 static inline void
1709 occupy_slot (HandleData *handles, guint slot) {
1710 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1713 static int
1714 find_first_unset (guint32 bitmap)
1716 int i;
1717 for (i = 0; i < 32; ++i) {
1718 if (!(bitmap & (1 << i)))
1719 return i;
1721 return -1;
1724 static void
1725 handle_data_alloc_entries (HandleData *handles)
1727 handles->size = 32;
1728 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1729 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1730 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1731 } else {
1732 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)");
1734 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1737 static gint
1738 handle_data_next_unset (HandleData *handles)
1740 gint slot;
1741 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1742 if (handles->bitmap [slot] == 0xffffffff)
1743 continue;
1744 handles->slot_hint = slot;
1745 return find_first_unset (handles->bitmap [slot]);
1747 return -1;
1750 static gint
1751 handle_data_first_unset (HandleData *handles)
1753 gint slot;
1754 for (slot = 0; slot < handles->slot_hint; ++slot) {
1755 if (handles->bitmap [slot] == 0xffffffff)
1756 continue;
1757 handles->slot_hint = slot;
1758 return find_first_unset (handles->bitmap [slot]);
1760 return -1;
1763 /* Returns the index of the current slot in the bitmap. */
1764 static void
1765 handle_data_grow (HandleData *handles, gboolean track)
1767 guint32 *new_bitmap;
1768 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1770 /* resize and copy the bitmap */
1771 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1772 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1773 g_free (handles->bitmap);
1774 handles->bitmap = new_bitmap;
1776 /* resize and copy the entries */
1777 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1778 gpointer *entries;
1779 guint16 *domain_ids;
1780 gint i;
1781 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1782 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1783 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1784 for (i = 0; i < handles->size; ++i) {
1785 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1786 if (obj) {
1787 mono_gc_weak_link_add (&(entries [i]), obj, track);
1788 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1789 } else {
1790 g_assert (!handles->entries [i]);
1793 g_free (handles->entries);
1794 g_free (handles->domain_ids);
1795 handles->entries = entries;
1796 handles->domain_ids = domain_ids;
1797 } else {
1798 gpointer *entries;
1799 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)");
1800 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1801 mono_gc_free_fixed (handles->entries);
1802 handles->entries = entries;
1804 handles->slot_hint = handles->size / BITMAP_SIZE;
1805 handles->size = new_size;
1808 static guint32
1809 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1811 gint slot, i;
1812 guint32 res;
1813 lock_handles (handles);
1814 if (!handles->size)
1815 handle_data_alloc_entries (handles);
1816 i = handle_data_next_unset (handles);
1817 if (i == -1 && handles->slot_hint != 0)
1818 i = handle_data_first_unset (handles);
1819 if (i == -1) {
1820 handle_data_grow (handles, track);
1821 i = 0;
1823 slot = handles->slot_hint * BITMAP_SIZE + i;
1824 occupy_slot (handles, slot);
1825 handles->entries [slot] = NULL;
1826 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1827 /*FIXME, what to use when obj == null?*/
1828 handles->domain_ids [slot] = (obj ? mono_object_get_domain_internal (obj) : mono_domain_get ())->domain_id;
1829 if (obj)
1830 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1831 } else {
1832 handles->entries [slot] = obj;
1835 #ifndef DISABLE_PERFCOUNTERS
1836 mono_atomic_inc_i32 (&mono_perfcounters->gc_num_handles);
1837 #endif
1838 unlock_handles (handles);
1839 res = MONO_GC_HANDLE (slot, handles->type);
1840 MONO_PROFILER_RAISE (gc_handle_created, (res, (MonoGCHandleType)handles->type, obj));
1841 return res;
1845 * mono_gchandle_new_internal:
1846 * \param obj managed object to get a handle for
1847 * \param pinned whether the object should be pinned
1849 * This returns a handle that wraps the object, this is used to keep a
1850 * reference to a managed object from the unmanaged world and preventing the
1851 * object from being disposed.
1853 * If \p pinned is false the address of the object can not be obtained, if it is
1854 * true the address of the object can be obtained. This will also pin the
1855 * object so it will not be possible by a moving garbage collector to move the
1856 * object.
1858 * \returns a handle that can be used to access the object from
1859 * unmanaged code.
1861 guint32
1862 mono_gchandle_new_internal (MonoObject *obj, gboolean pinned)
1864 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1868 * mono_gchandle_new_weakref_internal:
1869 * \param obj managed object to get a handle for
1870 * \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.
1872 * This returns a weak handle that wraps the object, this is used to
1873 * keep a reference to a managed object from the unmanaged world.
1874 * Unlike the \c mono_gchandle_new_internal the object can be reclaimed by the
1875 * garbage collector. In this case the value of the GCHandle will be
1876 * set to zero.
1878 * If \p track_resurrection is TRUE the object will be tracked through
1879 * finalization and if the object is resurrected during the execution
1880 * of the finalizer, then the returned weakref will continue to hold
1881 * a reference to the object. If \p track_resurrection is FALSE, then
1882 * the weak reference's target will become NULL as soon as the object
1883 * is passed on to the finalizer.
1885 * \returns a handle that can be used to access the object from
1886 * unmanaged code.
1888 guint32
1889 mono_gchandle_new_weakref_internal (MonoObject *obj, gboolean track_resurrection)
1891 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1895 * mono_gchandle_get_target_internal:
1896 * \param gchandle a GCHandle's handle.
1898 * The handle was previously created by calling \c mono_gchandle_new_internal or
1899 * \c mono_gchandle_new_weakref.
1901 * \returns A pointer to the \c MonoObject* represented by the handle or
1902 * NULL for a collected object if using a weakref handle.
1904 MonoObject*
1905 mono_gchandle_get_target_internal (guint32 gchandle)
1907 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1908 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1909 HandleData *handles = &gc_handles [type];
1910 MonoObject *obj = NULL;
1911 if (type >= HANDLE_TYPE_MAX)
1912 return NULL;
1914 lock_handles (handles);
1915 if (slot < handles->size && slot_occupied (handles, slot)) {
1916 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1917 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1918 } else {
1919 obj = (MonoObject *)handles->entries [slot];
1921 } else {
1922 /* print a warning? */
1924 unlock_handles (handles);
1925 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1926 return obj;
1929 void
1930 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1932 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1933 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1934 HandleData *handles = &gc_handles [type];
1935 MonoObject *old_obj = NULL;
1937 g_assert (type < HANDLE_TYPE_MAX);
1938 lock_handles (handles);
1939 if (slot < handles->size && slot_occupied (handles, slot)) {
1940 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1941 old_obj = (MonoObject *)handles->entries [slot];
1942 (void)old_obj;
1943 if (handles->entries [slot])
1944 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1945 if (obj)
1946 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1947 /*FIXME, what to use when obj == null?*/
1948 handles->domain_ids [slot] = (obj ? mono_object_get_domain_internal (obj) : mono_domain_get ())->domain_id;
1949 } else {
1950 handles->entries [slot] = obj;
1952 } else {
1953 /* print a warning? */
1955 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1956 unlock_handles (handles);
1959 gboolean
1960 mono_gc_is_null (void)
1962 return FALSE;
1966 * mono_gchandle_is_in_domain:
1967 * \param gchandle a GCHandle's handle.
1968 * \param domain An application domain.
1970 * Use this function to determine if the \p gchandle points to an
1971 * object allocated in the specified \p domain.
1973 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1975 gboolean
1976 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1978 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1979 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1980 HandleData *handles = &gc_handles [type];
1981 gboolean result = FALSE;
1983 if (type >= HANDLE_TYPE_MAX)
1984 return FALSE;
1986 lock_handles (handles);
1987 if (slot < handles->size && slot_occupied (handles, slot)) {
1988 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1989 result = domain->domain_id == handles->domain_ids [slot];
1990 } else {
1991 MonoObject *obj;
1992 obj = (MonoObject *)handles->entries [slot];
1993 if (obj == NULL)
1994 result = TRUE;
1995 else
1996 result = domain == mono_object_domain (obj);
1998 } else {
1999 /* print a warning? */
2001 unlock_handles (handles);
2002 return result;
2006 * mono_gchandle_free_internal:
2007 * \param gchandle a GCHandle's handle.
2009 * Frees the \p gchandle handle. If there are no outstanding
2010 * references, the garbage collector can reclaim the memory of the
2011 * object wrapped.
2013 void
2014 mono_gchandle_free_internal (guint32 gchandle)
2016 if (!gchandle)
2017 return;
2019 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
2020 guint type = MONO_GC_HANDLE_TYPE (gchandle);
2021 HandleData *handles = &gc_handles [type];
2022 if (type >= HANDLE_TYPE_MAX)
2023 return;
2025 lock_handles (handles);
2026 if (slot < handles->size && slot_occupied (handles, slot)) {
2027 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
2028 if (handles->entries [slot])
2029 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
2030 } else {
2031 handles->entries [slot] = NULL;
2033 vacate_slot (handles, slot);
2034 } else {
2035 /* print a warning? */
2037 #ifndef DISABLE_PERFCOUNTERS
2038 mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles);
2039 #endif
2040 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
2041 unlock_handles (handles);
2042 MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, (MonoGCHandleType)handles->type));
2046 * mono_gchandle_free_domain:
2047 * \param domain domain that is unloading
2049 * Function used internally to cleanup any GC handle for objects belonging
2050 * to the specified domain during appdomain unload.
2052 void
2053 mono_gchandle_free_domain (MonoDomain *domain)
2055 guint type;
2057 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
2058 guint slot;
2059 HandleData *handles = &gc_handles [type];
2060 lock_handles (handles);
2061 for (slot = 0; slot < handles->size; ++slot) {
2062 if (!slot_occupied (handles, slot))
2063 continue;
2064 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
2065 if (domain->domain_id == handles->domain_ids [slot]) {
2066 vacate_slot (handles, slot);
2067 if (handles->entries [slot])
2068 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
2070 } else {
2071 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
2072 vacate_slot (handles, slot);
2073 handles->entries [slot] = NULL;
2077 unlock_handles (handles);
2082 void
2083 mono_gc_register_obj_with_weak_fields (void *obj)
2085 g_error ("Weak fields not supported by boehm gc");
2088 gboolean
2089 mono_gc_ephemeron_array_add (MonoObject *obj)
2091 return TRUE;
2094 #else
2096 MONO_EMPTY_SOURCE_FILE (boehm_gc);
2097 #endif /* no Boehm GC */