[packaging] Bundle the msvc compiled monograph.exe on Windows
[mono-project.git] / mono / metadata / boehm-gc.c
blob2169b55f6519c15a370892337c8545b32d8a26a5
1 /**
2 * \file
3 * GC implementation using either the installed or included Boehm GC.
5 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
6 * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
7 * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 */
11 #include "config.h"
13 #include <string.h>
15 #define GC_I_HIDE_POINTERS
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/method-builder.h>
21 #include <mono/metadata/method-builder-ilgen.h>
22 #include <mono/metadata/method-builder-ilgen-internals.h>
23 #include <mono/metadata/opcodes.h>
24 #include <mono/metadata/domain-internals.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/marshal.h>
27 #include <mono/metadata/runtime.h>
28 #include <mono/metadata/handle.h>
29 #include <mono/metadata/sgen-toggleref.h>
30 #include <mono/metadata/w32handle.h>
31 #include <mono/utils/atomic.h>
32 #include <mono/utils/mono-logger-internals.h>
33 #include <mono/utils/mono-memory-model.h>
34 #include <mono/utils/mono-time.h>
35 #include <mono/utils/mono-threads.h>
36 #include <mono/utils/dtrace.h>
37 #include <mono/utils/gc_wrapper.h>
38 #include <mono/utils/mono-os-mutex.h>
39 #include <mono/utils/mono-counters.h>
40 #include <mono/utils/mono-compiler.h>
41 #include <mono/utils/unlocked.h>
44 #if HAVE_BOEHM_GC
46 #undef TRUE
47 #undef FALSE
48 #define THREAD_LOCAL_ALLOC 1
49 #include "private/pthread_support.h"
51 #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
52 void *pthread_get_stackaddr_np(pthread_t);
53 #endif
55 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
56 /*Boehm max heap cannot be smaller than 16MB*/
57 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
58 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
60 static gboolean gc_initialized = FALSE;
61 static mono_mutex_t mono_gc_lock;
63 typedef void (*GC_push_other_roots_proc)(void);
65 static GC_push_other_roots_proc default_push_other_roots;
66 static GHashTable *roots;
68 static void
69 mono_push_other_roots(void);
71 static void
72 register_test_toggleref_callback (void);
74 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
75 static MonoGCFinalizerCallbacks fin_callbacks;
77 /* GC Handles */
79 static mono_mutex_t handle_section;
80 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
81 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
83 typedef struct {
84 guint32 *bitmap;
85 gpointer *entries;
86 guint32 size;
87 guint8 type;
88 guint slot_hint : 24; /* starting slot for search in bitmap */
89 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
90 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
91 guint16 *domain_ids;
92 } HandleData;
94 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
96 /* weak and weak-track arrays will be allocated in malloc memory
98 static HandleData gc_handles [] = {
99 EMPTY_HANDLE_DATA (HANDLE_WEAK),
100 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
101 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
102 EMPTY_HANDLE_DATA (HANDLE_PINNED)
105 static void
106 mono_gc_warning (char *msg, GC_word arg)
108 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
111 static void on_gc_notification (GC_EventType event);
112 static void on_gc_heap_resize (size_t new_size);
114 void
115 mono_gc_base_init (void)
117 char *env;
119 if (gc_initialized)
120 return;
122 mono_counters_init ();
124 #ifndef HOST_WIN32
125 mono_w32handle_init ();
126 #endif
129 * Handle the case when we are called from a thread different from the main thread,
130 * confusing libgc.
131 * FIXME: Move this to libgc where it belongs.
133 * we used to do this only when running on valgrind,
134 * but it happens also in other setups.
136 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
138 size_t size;
139 void *sstart;
140 pthread_attr_t attr;
141 pthread_getattr_np (pthread_self (), &attr);
142 pthread_attr_getstack (&attr, &sstart, &size);
143 pthread_attr_destroy (&attr);
144 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
145 /* apparently with some linuxthreads implementations sstart can be NULL,
146 * fallback to the more imprecise method (bug# 78096).
148 if (sstart) {
149 GC_stackbottom = (char*)sstart + size;
150 } else {
151 int dummy;
152 gsize stack_bottom = (gsize)&dummy;
153 stack_bottom += 4095;
154 stack_bottom &= ~4095;
155 GC_stackbottom = (char*)stack_bottom;
158 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
159 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
160 #elif defined(__OpenBSD__)
161 # include <pthread_np.h>
163 stack_t ss;
164 int rslt;
166 rslt = pthread_stackseg_np(pthread_self(), &ss);
167 g_assert (rslt == 0);
169 GC_stackbottom = (char*)ss.ss_sp;
171 #else
173 int dummy;
174 gsize stack_bottom = (gsize)&dummy;
175 stack_bottom += 4095;
176 stack_bottom &= ~4095;
177 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
178 GC_stackbottom = (char*)stack_bottom;
180 #endif
182 roots = g_hash_table_new (NULL, NULL);
183 default_push_other_roots = GC_push_other_roots;
184 GC_push_other_roots = mono_push_other_roots;
186 #if !defined(HOST_ANDROID)
187 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
188 GC_no_dls = TRUE;
189 #endif
191 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
192 char **opts = g_strsplit (env, ",", -1);
193 for (char **ptr = opts; ptr && *ptr; ptr ++) {
194 char *opt = *ptr;
195 if (!strcmp (opt, "do-not-finalize")) {
196 mono_do_not_finalize = 1;
197 } else if (!strcmp (opt, "log-finalizers")) {
198 mono_log_finalizers = 1;
201 g_free (env);
205 GC_init ();
207 GC_set_warn_proc (mono_gc_warning);
208 GC_finalize_on_demand = 1;
209 GC_finalizer_notifier = mono_gc_finalize_notify;
211 GC_init_gcj_malloc (5, NULL);
212 GC_allow_register_threads ();
214 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
215 char **ptr, **opts = g_strsplit (env, ",", -1);
216 for (ptr = opts; *ptr; ++ptr) {
217 char *opt = *ptr;
218 if (g_str_has_prefix (opt, "max-heap-size=")) {
219 size_t max_heap;
221 opt = strchr (opt, '=') + 1;
222 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
223 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
224 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
225 exit (1);
227 GC_set_max_heap_size (max_heap);
228 } else {
229 fprintf (stderr, "max-heap-size must be an integer.\n");
230 exit (1);
232 continue;
233 } else if (g_str_has_prefix (opt, "toggleref-test")) {
234 register_test_toggleref_callback ();
235 continue;
236 } else {
237 /* Could be a parameter for sgen */
239 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
240 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
241 exit (1);
245 g_free (env);
246 g_strfreev (opts);
249 mono_thread_callbacks_init ();
250 mono_thread_info_init (sizeof (MonoThreadInfo));
251 mono_os_mutex_init (&mono_gc_lock);
252 mono_os_mutex_init_recursive (&handle_section);
254 mono_thread_info_attach ();
256 GC_set_on_collection_event (on_gc_notification);
257 GC_on_heap_resize = on_gc_heap_resize;
259 gc_initialized = TRUE;
262 void
263 mono_gc_base_cleanup (void)
265 GC_finalizer_notifier = NULL;
269 * mono_gc_collect:
270 * \param generation GC generation identifier
272 * Perform a garbage collection for the given generation, higher numbers
273 * mean usually older objects. Collecting a high-numbered generation
274 * implies collecting also the lower-numbered generations.
275 * The maximum value for \p generation can be retrieved with a call to
276 * \c mono_gc_max_generation, so this function is usually called as:
278 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
280 void
281 mono_gc_collect (int generation)
283 #ifndef DISABLE_PERFCOUNTERS
284 mono_atomic_inc_i32 (&mono_perfcounters->gc_induced);
285 #endif
286 GC_gcollect ();
290 * mono_gc_max_generation:
292 * Get the maximum generation number used by the current garbage
293 * collector. The value will be 0 for the Boehm collector, 1 or more
294 * for the generational collectors.
296 * Returns: the maximum generation number.
299 mono_gc_max_generation (void)
301 return 0;
305 * mono_gc_get_generation:
306 * \param object a managed object
308 * Get the garbage collector's generation that \p object belongs to.
309 * Use this has a hint only.
311 * \returns a garbage collector generation number
314 mono_gc_get_generation (MonoObject *object)
316 return 0;
320 * mono_gc_collection_count:
321 * \param generation a GC generation number
323 * Get how many times a garbage collection has been performed
324 * for the given \p generation number.
326 * \returns the number of garbage collections
329 mono_gc_collection_count (int generation)
331 return GC_gc_no;
335 * mono_gc_add_memory_pressure:
336 * \param value amount of bytes
338 * Adjust the garbage collector's view of how many bytes of memory
339 * are indirectly referenced by managed objects (for example unmanaged
340 * memory holding image or other binary data).
341 * This is a hint only to the garbage collector algorithm.
342 * Note that negative amounts of p value will decrease the memory
343 * pressure.
345 void
346 mono_gc_add_memory_pressure (gint64 value)
351 * mono_gc_get_used_size:
353 * Get the approximate amount of memory used by managed objects.
355 * Returns: the amount of memory used in bytes
357 int64_t
358 mono_gc_get_used_size (void)
360 return GC_get_heap_size () - GC_get_free_bytes ();
364 * mono_gc_get_heap_size:
366 * Get the amount of memory used by the garbage collector.
368 * Returns: the size of the heap in bytes
370 int64_t
371 mono_gc_get_heap_size (void)
373 return GC_get_heap_size ();
376 gboolean
377 mono_gc_is_gc_thread (void)
379 return GC_thread_is_registered ();
382 gpointer
383 mono_gc_thread_attach (MonoThreadInfo* info)
385 struct GC_stack_base sb;
386 int res;
388 /* TODO: use GC_get_stack_base instead of baseptr. */
389 sb.mem_base = info->stack_end;
390 res = GC_register_my_thread (&sb);
391 if (res == GC_UNIMPLEMENTED)
392 return NULL; /* Cannot happen with GC v7+. */
394 info->handle_stack = mono_handle_stack_alloc ();
396 return info;
399 void
400 mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
402 MonoNativeThreadId tid;
404 tid = mono_thread_info_get_tid (p);
406 if (p->runtime_thread)
407 mono_threads_add_joinable_thread ((gpointer)tid);
409 mono_handle_stack_free (p->handle_stack);
410 p->handle_stack = NULL;
413 gboolean
414 mono_gc_thread_in_critical_region (MonoThreadInfo *info)
416 return FALSE;
419 gboolean
420 mono_object_is_alive (MonoObject* o)
422 return GC_is_marked ((ptr_t)o);
426 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
428 return 1;
431 static gint64 gc_start_time;
433 static void
434 on_gc_notification (GC_EventType event)
436 MonoProfilerGCEvent e;
438 switch (event) {
439 case GC_EVENT_PRE_STOP_WORLD:
440 e = MONO_GC_EVENT_PRE_STOP_WORLD;
441 MONO_GC_WORLD_STOP_BEGIN ();
442 break;
444 case GC_EVENT_POST_STOP_WORLD:
445 e = MONO_GC_EVENT_POST_STOP_WORLD;
446 MONO_GC_WORLD_STOP_END ();
447 break;
449 case GC_EVENT_PRE_START_WORLD:
450 e = MONO_GC_EVENT_PRE_START_WORLD;
451 MONO_GC_WORLD_RESTART_BEGIN (1);
452 break;
454 case GC_EVENT_POST_START_WORLD:
455 e = MONO_GC_EVENT_POST_START_WORLD;
456 MONO_GC_WORLD_RESTART_END (1);
457 break;
459 case GC_EVENT_START:
460 e = MONO_GC_EVENT_START;
461 MONO_GC_BEGIN (1);
462 #ifndef DISABLE_PERFCOUNTERS
463 if (mono_perfcounters)
464 mono_atomic_inc_i32 (&mono_perfcounters->gc_collections0);
465 #endif
466 mono_atomic_inc_i32 (&mono_gc_stats.major_gc_count);
467 gc_start_time = mono_100ns_ticks ();
468 break;
470 case GC_EVENT_END:
471 e = MONO_GC_EVENT_END;
472 MONO_GC_END (1);
473 #if defined(ENABLE_DTRACE) && defined(__sun__)
474 /* This works around a dtrace -G problem on Solaris.
475 Limit its actual use to when the probe is enabled. */
476 if (MONO_GC_END_ENABLED ())
477 sleep(0);
478 #endif
480 #ifndef DISABLE_PERFCOUNTERS
481 if (mono_perfcounters) {
482 guint64 heap_size = GC_get_heap_size ();
483 guint64 used_size = heap_size - GC_get_free_bytes ();
484 /* FIXME: change these to mono_atomic_store_i64 () */
485 UnlockedWrite64 (&mono_perfcounters->gc_total_bytes, used_size);
486 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
487 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
488 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
490 #endif
491 UnlockedAdd64 (&mono_gc_stats.major_gc_time, mono_100ns_ticks () - gc_start_time);
492 mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10);
493 break;
494 default:
495 break;
498 switch (event) {
499 case GC_EVENT_MARK_START:
500 case GC_EVENT_MARK_END:
501 case GC_EVENT_RECLAIM_START:
502 case GC_EVENT_RECLAIM_END:
503 break;
504 default:
505 MONO_PROFILER_RAISE (gc_event, (e, 0));
506 MONO_PROFILER_RAISE (gc_event2, (e, 0, TRUE));
507 break;
510 switch (event) {
511 case GC_EVENT_PRE_STOP_WORLD:
512 mono_thread_info_suspend_lock ();
513 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0));
514 MONO_PROFILER_RAISE (gc_event2, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0, TRUE));
515 break;
516 case GC_EVENT_POST_START_WORLD:
517 mono_thread_info_suspend_unlock ();
518 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0));
519 MONO_PROFILER_RAISE (gc_event2, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0, TRUE));
520 break;
521 default:
522 break;
527 static void
528 on_gc_heap_resize (size_t new_size)
530 guint64 heap_size = GC_get_heap_size ();
531 #ifndef DISABLE_PERFCOUNTERS
532 if (mono_perfcounters) {
533 /* FIXME: change these to mono_atomic_store_i64 () */
534 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
535 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
536 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
538 #endif
540 MONO_PROFILER_RAISE (gc_resize, (new_size));
543 typedef struct {
544 char *start;
545 char *end;
546 } RootData;
548 static gpointer
549 register_root (gpointer arg)
551 RootData* root_data = arg;
552 g_hash_table_insert (roots, root_data->start, root_data->end);
553 return NULL;
557 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
559 RootData root_data;
560 root_data.start = start;
561 /* Boehm root processing requires one byte past end of region to be scanned */
562 root_data.end = start + size + 1;
563 GC_call_with_alloc_lock (register_root, &root_data);
564 MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg));
565 return TRUE;
569 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg)
571 return mono_gc_register_root (start, size, descr, source, key, msg);
574 static gpointer
575 deregister_root (gpointer arg)
577 gboolean removed = g_hash_table_remove (roots, arg);
578 g_assert (removed);
579 return NULL;
582 void
583 mono_gc_deregister_root (char* addr)
585 GC_call_with_alloc_lock (deregister_root, addr);
586 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr));
589 static void
590 push_root (gpointer key, gpointer value, gpointer user_data)
592 GC_push_all (key, value);
595 static void
596 push_handle_stack (HandleStack* stack)
598 HandleChunk *cur = stack->bottom;
599 HandleChunk *last = stack->top;
601 if (!cur)
602 return;
604 while (cur) {
605 if (cur->size > 0)
606 GC_push_all ((gpointer)&cur->elems[0], (char*)(cur->elems + cur->size) + 1);
607 if (cur == last)
608 break;
609 cur = cur->next;
613 static void
614 mono_push_other_roots (void)
616 g_hash_table_foreach (roots, push_root, NULL);
617 FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) {
618 HandleStack* stack = (HandleStack*)info->handle_stack;
619 if (stack)
620 push_handle_stack (stack);
621 } FOREACH_THREAD_END
622 if (default_push_other_roots)
623 default_push_other_roots ();
626 static void
627 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
629 /* libgc requires that we use HIDE_POINTER... */
630 *link_addr = (void*)HIDE_POINTER (obj);
631 if (track)
632 GC_REGISTER_LONG_LINK (link_addr, obj);
633 else
634 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
637 static void
638 mono_gc_weak_link_remove (void **link_addr, gboolean track)
640 if (track)
641 GC_unregister_long_link (link_addr);
642 else
643 GC_unregister_disappearing_link (link_addr);
644 *link_addr = NULL;
647 static gpointer
648 reveal_link (gpointer link_addr)
650 void **link_a = (void **)link_addr;
651 return REVEAL_POINTER (*link_a);
654 static MonoObject *
655 mono_gc_weak_link_get (void **link_addr)
657 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
658 if (obj == (MonoObject *) -1)
659 return NULL;
660 return obj;
663 void*
664 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
666 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
669 void*
670 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
672 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
675 void*
676 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
678 /* libgc has no usable support for arrays... */
679 return GC_NO_DESCRIPTOR;
682 void*
683 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
685 /* It seems there are issues when the bitmap doesn't fit: play it safe */
686 if (numbits >= 30)
687 return GC_NO_DESCRIPTOR;
688 else
689 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
692 void*
693 mono_gc_make_vector_descr (void)
695 return NULL;
698 void*
699 mono_gc_make_root_descr_all_refs (int numbits)
701 return NULL;
704 void*
705 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
707 void *start = GC_MALLOC_UNCOLLECTABLE (size);
708 MONO_PROFILER_RAISE (gc_root_register, ((const mono_byte *) start, size, source, key, msg));
709 return start;
712 void
713 mono_gc_free_fixed (void* addr)
715 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte *) addr));
716 GC_FREE (addr);
719 void *
720 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
722 MonoObject *obj;
724 if (!m_class_has_references (vtable->klass)) {
725 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
726 if (G_UNLIKELY (!obj))
727 return NULL;
729 obj->vtable = vtable;
730 obj->synchronisation = NULL;
732 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
733 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
734 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
735 if (G_UNLIKELY (!obj))
736 return NULL;
737 } else {
738 obj = (MonoObject *)GC_MALLOC (size);
739 if (G_UNLIKELY (!obj))
740 return NULL;
742 obj->vtable = vtable;
745 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
746 MONO_PROFILER_RAISE (gc_allocation, (obj));
748 return obj;
751 void *
752 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
754 MonoArray *obj;
756 if (!m_class_has_references (vtable->klass)) {
757 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
758 if (G_UNLIKELY (!obj))
759 return NULL;
761 obj->obj.vtable = vtable;
762 obj->obj.synchronisation = NULL;
764 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
765 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
766 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
767 if (G_UNLIKELY (!obj))
768 return NULL;
769 } else {
770 obj = (MonoArray *)GC_MALLOC (size);
771 if (G_UNLIKELY (!obj))
772 return NULL;
774 obj->obj.vtable = vtable;
777 obj->max_length = max_length;
779 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
780 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
782 return obj;
785 void *
786 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
788 MonoArray *obj;
790 if (!m_class_has_references (vtable->klass)) {
791 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
792 if (G_UNLIKELY (!obj))
793 return NULL;
795 obj->obj.vtable = vtable;
796 obj->obj.synchronisation = NULL;
798 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
799 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
800 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
801 if (G_UNLIKELY (!obj))
802 return NULL;
803 } else {
804 obj = (MonoArray *)GC_MALLOC (size);
805 if (G_UNLIKELY (!obj))
806 return NULL;
808 obj->obj.vtable = vtable;
811 obj->max_length = max_length;
813 if (bounds_size)
814 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
816 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
817 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
819 return obj;
822 void *
823 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
825 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
826 if (G_UNLIKELY (!obj))
827 return NULL;
829 obj->object.vtable = vtable;
830 obj->object.synchronisation = NULL;
831 obj->length = len;
832 obj->chars [len] = 0;
834 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
835 MONO_PROFILER_RAISE (gc_allocation, (&obj->object));
837 return obj;
840 void*
841 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
843 return mono_gc_alloc_obj (vtable, size);
846 void*
847 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
849 return mono_gc_alloc_obj (vtable, size);
853 mono_gc_invoke_finalizers (void)
855 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
856 * the 'mem_freed' variable is not initialized when there are no
857 * objects to finalize, which leads to strange behavior later on.
858 * The check is necessary to work around that bug.
860 if (GC_should_invoke_finalizers ())
861 return GC_invoke_finalizers ();
862 return 0;
865 MonoBoolean
866 mono_gc_pending_finalizers (void)
868 return GC_should_invoke_finalizers ();
871 void
872 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
874 *(void**)field_ptr = value;
877 void
878 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
880 *(void**)slot_ptr = value;
883 void
884 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
886 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
889 void
890 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
892 *(void**)ptr = value;
895 void
896 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
898 mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
901 void
902 mono_gc_wbarrier_generic_nostore (gpointer ptr)
906 void
907 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
909 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
912 void
913 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
915 /* do not copy the sync state */
916 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
917 m_class_get_instance_size (mono_object_class (obj)) - sizeof (MonoObject));
920 void
921 mono_gc_clear_domain (MonoDomain *domain)
925 void
926 mono_gc_suspend_finalizers (void)
931 mono_gc_get_suspend_signal (void)
933 return GC_get_suspend_signal ();
937 mono_gc_get_restart_signal (void)
939 return GC_get_thr_restart_signal ();
942 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
943 extern __thread void* GC_thread_tls;
944 #include "metadata-internals.h"
946 static int
947 shift_amount (int v)
949 int i = 0;
950 while (!(v & (1 << i)))
951 i++;
952 return i;
955 enum {
956 ATYPE_FREEPTR,
957 ATYPE_FREEPTR_FOR_BOX,
958 ATYPE_NORMAL,
959 ATYPE_GCJ,
960 ATYPE_STRING,
961 ATYPE_NUM
964 static MonoMethod*
965 create_allocator (int atype, int tls_key, gboolean slowpath)
967 int index_var, bytes_var, my_fl_var, my_entry_var;
968 guint32 no_freelist_branch, not_small_enough_branch = 0;
969 guint32 size_overflow_branch = 0;
970 MonoMethodBuilder *mb;
971 MonoMethod *res;
972 MonoMethodSignature *csig;
973 const char *name = NULL;
974 WrapperInfo *info;
976 g_assert_not_reached ();
978 if (atype == ATYPE_FREEPTR) {
979 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
980 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
981 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
982 } else if (atype == ATYPE_NORMAL) {
983 name = slowpath ? "SlowAlloc" : "Alloc";
984 } else if (atype == ATYPE_GCJ) {
985 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
986 } else if (atype == ATYPE_STRING) {
987 name = slowpath ? "SlowAllocString" : "AllocString";
988 } else {
989 g_assert_not_reached ();
992 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
994 if (atype == ATYPE_STRING) {
995 csig->ret = m_class_get_byval_arg (mono_defaults.string_class);
996 csig->params [0] = m_class_get_byval_arg (mono_defaults.int_class);
997 csig->params [1] = m_class_get_byval_arg (mono_defaults.int32_class);
998 } else {
999 csig->ret = m_class_get_byval_arg (mono_defaults.object_class);
1000 csig->params [0] = m_class_get_byval_arg (mono_defaults.int_class);
1001 csig->params [1] = m_class_get_byval_arg (mono_defaults.int32_class);
1004 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
1006 if (slowpath)
1007 goto always_slowpath;
1009 bytes_var = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.int32_class));
1010 if (atype == ATYPE_STRING) {
1011 /* a string alloator method takes the args: (vtable, len) */
1012 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
1013 mono_mb_emit_ldarg (mb, 1);
1014 mono_mb_emit_icon (mb, 1);
1015 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1016 mono_mb_emit_icon (mb, 1);
1017 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1018 // sizeof (MonoString) might include padding
1019 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
1020 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1021 mono_mb_emit_stloc (mb, bytes_var);
1022 } else {
1023 mono_mb_emit_ldarg (mb, 1);
1024 mono_mb_emit_stloc (mb, bytes_var);
1027 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
1028 if (atype == ATYPE_STRING) {
1029 /* check for size */
1030 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
1031 mono_mb_emit_ldloc (mb, bytes_var);
1032 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
1033 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
1034 /* check for overflow */
1035 mono_mb_emit_ldloc (mb, bytes_var);
1036 mono_mb_emit_icon (mb, sizeof (MonoString));
1037 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1040 /* int index = INDEX_FROM_BYTES(bytes); */
1041 index_var = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.int32_class));
1043 mono_mb_emit_ldloc (mb, bytes_var);
1044 mono_mb_emit_icon (mb, GRANULARITY - 1);
1045 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1046 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
1047 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
1048 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
1049 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1050 /* index var is already adjusted into bytes */
1051 mono_mb_emit_stloc (mb, index_var);
1053 my_fl_var = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.int_class));
1054 my_entry_var = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.int_class));
1055 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1056 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1057 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
1058 mono_mb_emit_i4 (mb, tls_key);
1059 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
1060 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1061 + G_STRUCT_OFFSET (struct thread_local_freelists,
1062 ptrfree_freelists));
1063 else if (atype == ATYPE_NORMAL)
1064 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1065 + G_STRUCT_OFFSET (struct thread_local_freelists,
1066 normal_freelists));
1067 else if (atype == ATYPE_GCJ)
1068 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1069 + G_STRUCT_OFFSET (struct thread_local_freelists,
1070 gcj_freelists));
1071 else
1072 g_assert_not_reached ();
1073 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1074 mono_mb_emit_ldloc (mb, index_var);
1075 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1076 mono_mb_emit_stloc (mb, my_fl_var);
1078 /* my_entry = *my_fl; */
1079 mono_mb_emit_ldloc (mb, my_fl_var);
1080 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1081 mono_mb_emit_stloc (mb, my_entry_var);
1083 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1084 mono_mb_emit_ldloc (mb, my_entry_var);
1085 mono_mb_emit_icon (mb, HBLKSIZE);
1086 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1088 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1089 mono_mb_emit_ldloc (mb, my_fl_var);
1090 mono_mb_emit_ldloc (mb, my_entry_var);
1091 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1092 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1094 /* set the vtable and clear the words in the object */
1095 mono_mb_emit_ldloc (mb, my_entry_var);
1096 mono_mb_emit_ldarg (mb, 0);
1097 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1099 if (atype == ATYPE_FREEPTR) {
1100 int start_var, end_var, start_loop;
1101 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1103 start_var = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.int_class));
1104 end_var = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.int_class));
1105 mono_mb_emit_ldloc (mb, my_entry_var);
1106 mono_mb_emit_ldloc (mb, bytes_var);
1107 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1108 mono_mb_emit_stloc (mb, end_var);
1109 mono_mb_emit_ldloc (mb, my_entry_var);
1110 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1111 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1112 mono_mb_emit_stloc (mb, start_var);
1114 * do {
1115 * *start++ = NULL;
1116 * } while (start < end);
1118 start_loop = mono_mb_get_label (mb);
1119 mono_mb_emit_ldloc (mb, start_var);
1120 mono_mb_emit_icon (mb, 0);
1121 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1122 mono_mb_emit_ldloc (mb, start_var);
1123 mono_mb_emit_icon (mb, sizeof (gpointer));
1124 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1125 mono_mb_emit_stloc (mb, start_var);
1127 mono_mb_emit_ldloc (mb, start_var);
1128 mono_mb_emit_ldloc (mb, end_var);
1129 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1130 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1131 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1132 /* need to clear just the sync pointer */
1133 mono_mb_emit_ldloc (mb, my_entry_var);
1134 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1135 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1136 mono_mb_emit_icon (mb, 0);
1137 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1140 if (atype == ATYPE_STRING) {
1141 /* need to set length and clear the last char */
1142 /* s->length = len; */
1143 mono_mb_emit_ldloc (mb, my_entry_var);
1144 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1145 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1146 mono_mb_emit_ldarg (mb, 1);
1147 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1148 /* s->chars [len] = 0; */
1149 mono_mb_emit_ldloc (mb, my_entry_var);
1150 mono_mb_emit_ldloc (mb, bytes_var);
1151 mono_mb_emit_icon (mb, 2);
1152 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1153 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1154 mono_mb_emit_icon (mb, 0);
1155 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1158 /* return my_entry; */
1159 mono_mb_emit_ldloc (mb, my_entry_var);
1160 mono_mb_emit_byte (mb, MONO_CEE_RET);
1162 mono_mb_patch_short_branch (mb, no_freelist_branch);
1163 if (not_small_enough_branch > 0)
1164 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1165 if (size_overflow_branch > 0)
1166 mono_mb_patch_short_branch (mb, size_overflow_branch);
1168 /* the slow path: we just call back into the runtime */
1169 always_slowpath:
1170 if (atype == ATYPE_STRING) {
1171 mono_mb_emit_ldarg (mb, 1);
1172 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1173 } else {
1174 mono_mb_emit_ldarg (mb, 0);
1175 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1178 mono_mb_emit_byte (mb, MONO_CEE_RET);
1180 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1181 info->d.alloc.gc_name = "boehm";
1182 info->d.alloc.alloc_type = atype;
1183 mb->init_locals = FALSE;
1185 res = mono_mb_create (mb, csig, 8, info);
1186 mono_mb_free (mb);
1188 return res;
1191 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1192 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1194 gboolean
1195 mono_gc_is_critical_method (MonoMethod *method)
1197 int i;
1199 for (i = 0; i < ATYPE_NUM; ++i)
1200 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1201 return TRUE;
1203 return FALSE;
1207 * If possible, generate a managed method that can quickly allocate objects in class
1208 * @klass. The method will typically have an thread-local inline allocation sequence.
1209 * The signature of the called method is:
1210 * object allocate (MonoVTable *vtable)
1211 * The thread local alloc logic is taken from libgc/pthread_support.c.
1213 MonoMethod*
1214 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1216 int atype;
1219 * Tls implementation changed, we jump to tls native getters/setters.
1220 * Is boehm managed allocator ok with this ? Do we even care ?
1222 return NULL;
1224 if (!SMALL_ENOUGH (m_class_get_instance_size (klass)))
1225 return NULL;
1226 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1227 return NULL;
1228 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1229 return NULL;
1230 if (m_class_get_rank (klass))
1231 return NULL;
1232 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (klass)))
1233 return NULL;
1234 if (m_class_get_byval_arg (klass)->type == MONO_TYPE_STRING) {
1235 atype = ATYPE_STRING;
1236 } else if (!known_instance_size) {
1237 return NULL;
1238 } else if (!m_class_has_references (klass)) {
1239 if (for_box)
1240 atype = ATYPE_FREEPTR_FOR_BOX;
1241 else
1242 atype = ATYPE_FREEPTR;
1243 } else {
1244 return NULL;
1246 * disabled because we currently do a runtime choice anyway, to
1247 * deal with multiple appdomains.
1248 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1249 atype = ATYPE_GCJ;
1250 else
1251 atype = ATYPE_NORMAL;
1254 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1257 MonoMethod*
1258 mono_gc_get_managed_array_allocator (MonoClass *klass)
1260 return NULL;
1264 * mono_gc_get_managed_allocator_by_type:
1266 * Return a managed allocator method corresponding to allocator type ATYPE.
1268 MonoMethod*
1269 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1271 MonoMethod *res;
1272 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1273 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1275 return NULL;
1277 res = cache [atype];
1278 if (res)
1279 return res;
1281 res = create_allocator (atype, -1, slowpath);
1282 mono_os_mutex_lock (&mono_gc_lock);
1283 if (cache [atype]) {
1284 mono_free_method (res);
1285 res = cache [atype];
1286 } else {
1287 mono_memory_barrier ();
1288 cache [atype] = res;
1290 mono_os_mutex_unlock (&mono_gc_lock);
1291 return res;
1294 guint32
1295 mono_gc_get_managed_allocator_types (void)
1297 return ATYPE_NUM;
1300 MonoMethod*
1301 mono_gc_get_write_barrier (void)
1303 g_assert_not_reached ();
1304 return NULL;
1307 #else
1309 gboolean
1310 mono_gc_is_critical_method (MonoMethod *method)
1312 return FALSE;
1315 MonoMethod*
1316 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1318 return NULL;
1321 MonoMethod*
1322 mono_gc_get_managed_array_allocator (MonoClass *klass)
1324 return NULL;
1327 MonoMethod*
1328 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1330 return NULL;
1333 guint32
1334 mono_gc_get_managed_allocator_types (void)
1336 return 0;
1339 MonoMethod*
1340 mono_gc_get_write_barrier (void)
1342 g_assert_not_reached ();
1343 return NULL;
1346 #endif
1348 MonoMethod*
1349 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1351 g_assert_not_reached ();
1352 return NULL;
1356 mono_gc_get_aligned_size_for_allocator (int size)
1358 return size;
1361 const char *
1362 mono_gc_get_gc_name (void)
1364 return "boehm";
1367 void*
1368 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1370 return GC_call_with_alloc_lock (func, data);
1373 char*
1374 mono_gc_get_description (void)
1376 return g_strdup (DEFAULT_GC_NAME);
1379 void
1380 mono_gc_set_desktop_mode (void)
1382 GC_dont_expand = 1;
1385 gboolean
1386 mono_gc_is_moving (void)
1388 return FALSE;
1391 gboolean
1392 mono_gc_is_disabled (void)
1394 if (GC_dont_gc || g_hasenv ("GC_DONT_GC"))
1395 return TRUE;
1396 else
1397 return FALSE;
1400 void
1401 mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
1403 g_assert_not_reached ();
1406 void*
1407 mono_gc_get_range_copy_func (void)
1409 return &mono_gc_wbarrier_range_copy;
1412 guint8*
1413 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1415 g_assert_not_reached ();
1416 return NULL;
1419 gboolean
1420 mono_gc_card_table_nursery_check (void)
1422 g_assert_not_reached ();
1423 return TRUE;
1426 void*
1427 mono_gc_get_nursery (int *shift_bits, size_t *size)
1429 return NULL;
1432 gboolean
1433 mono_gc_precise_stack_mark_enabled (void)
1435 return FALSE;
1438 FILE *
1439 mono_gc_get_logfile (void)
1441 return NULL;
1444 void
1445 mono_gc_params_set (const char* options)
1449 void
1450 mono_gc_debug_set (const char* options)
1454 void
1455 mono_gc_conservatively_scan_area (void *start, void *end)
1457 g_assert_not_reached ();
1460 void *
1461 mono_gc_scan_object (void *obj, void *gc_data)
1463 g_assert_not_reached ();
1464 return NULL;
1467 gsize*
1468 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1470 g_assert_not_reached ();
1471 return NULL;
1474 void
1475 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1479 void
1480 mono_gc_set_stack_end (void *stack_end)
1484 void
1485 mono_gc_skip_thread_changing (gboolean skip)
1488 * Unlike SGen, Boehm doesn't respect our thread info flags. We need to
1489 * inform Boehm manually to skip/not skip the current thread.
1492 if (skip)
1493 GC_start_blocking ();
1494 else
1495 GC_end_blocking ();
1498 void
1499 mono_gc_skip_thread_changed (gboolean skip)
1503 void
1504 mono_gc_register_for_finalization (MonoObject *obj, MonoFinalizationProc user_data)
1506 guint offset = 0;
1508 #ifndef GC_DEBUG
1509 /* This assertion is not valid when GC_DEBUG is defined */
1510 g_assert (GC_base (obj) == (char*)obj - offset);
1511 #endif
1513 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1516 #ifndef HOST_WIN32
1518 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1520 /* it is being replaced by GC_pthread_create on some
1521 * platforms, see libgc/include/gc_pthread_redirects.h */
1522 return pthread_create (new_thread, attr, start_routine, arg);
1524 #endif
1526 #ifdef HOST_WIN32
1527 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1529 return GC_DllMain (module_handle, reason, reserved);
1531 #endif
1533 MonoVTable *
1534 mono_gc_get_vtable (MonoObject *obj)
1536 // No pointer tagging.
1537 return obj->vtable;
1540 guint
1541 mono_gc_get_vtable_bits (MonoClass *klass)
1543 if (fin_callbacks.is_class_finalization_aware) {
1544 if (fin_callbacks.is_class_finalization_aware (klass))
1545 return BOEHM_GC_BIT_FINALIZER_AWARE;
1547 return 0;
1551 * mono_gc_register_altstack:
1553 * Register the dimensions of the normal stack and altstack with the collector.
1554 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1556 void
1557 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1559 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1563 mono_gc_get_los_limit (void)
1565 return G_MAXINT;
1568 void
1569 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1571 mono_unichar2 *new_end = str->chars + new_length;
1573 /* zero the discarded string. This null-delimits the string and allows
1574 * the space to be reclaimed by SGen. */
1576 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1577 str->length = new_length;
1580 gboolean
1581 mono_gc_user_markers_supported (void)
1583 return FALSE;
1586 void *
1587 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1589 g_assert_not_reached ();
1590 return NULL;
1593 /* Toggleref support */
1595 void
1596 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1598 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1599 g_error ("GC_toggleref_add failed\n");
1602 void
1603 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1605 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1608 /* Test support code */
1610 static MonoToggleRefStatus
1611 test_toggleref_callback (MonoObject *obj)
1613 static MonoClassField *mono_toggleref_test_field;
1614 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1616 if (!mono_toggleref_test_field) {
1617 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1618 g_assert (mono_toggleref_test_field);
1621 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1622 printf ("toggleref-cb obj %d\n", status);
1623 return status;
1626 static void
1627 register_test_toggleref_callback (void)
1629 mono_gc_toggleref_register_callback (test_toggleref_callback);
1632 static gboolean
1633 is_finalization_aware (MonoObject *obj)
1635 MonoVTable *vt = obj->vtable;
1636 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1639 static void
1640 fin_notifier (MonoObject *obj)
1642 if (is_finalization_aware (obj))
1643 fin_callbacks.object_queued_for_finalization (obj);
1646 void
1647 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1649 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1650 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1652 fin_callbacks = *callbacks;
1654 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1657 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1659 static inline gboolean
1660 slot_occupied (HandleData *handles, guint slot) {
1661 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1664 static inline void
1665 vacate_slot (HandleData *handles, guint slot) {
1666 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1669 static inline void
1670 occupy_slot (HandleData *handles, guint slot) {
1671 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1674 static int
1675 find_first_unset (guint32 bitmap)
1677 int i;
1678 for (i = 0; i < 32; ++i) {
1679 if (!(bitmap & (1 << i)))
1680 return i;
1682 return -1;
1685 static void
1686 handle_data_alloc_entries (HandleData *handles)
1688 handles->size = 32;
1689 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1690 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1691 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1692 } else {
1693 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)");
1695 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1698 static gint
1699 handle_data_next_unset (HandleData *handles)
1701 gint slot;
1702 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1703 if (handles->bitmap [slot] == 0xffffffff)
1704 continue;
1705 handles->slot_hint = slot;
1706 return find_first_unset (handles->bitmap [slot]);
1708 return -1;
1711 static gint
1712 handle_data_first_unset (HandleData *handles)
1714 gint slot;
1715 for (slot = 0; slot < handles->slot_hint; ++slot) {
1716 if (handles->bitmap [slot] == 0xffffffff)
1717 continue;
1718 handles->slot_hint = slot;
1719 return find_first_unset (handles->bitmap [slot]);
1721 return -1;
1724 /* Returns the index of the current slot in the bitmap. */
1725 static void
1726 handle_data_grow (HandleData *handles, gboolean track)
1728 guint32 *new_bitmap;
1729 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1731 /* resize and copy the bitmap */
1732 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1733 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1734 g_free (handles->bitmap);
1735 handles->bitmap = new_bitmap;
1737 /* resize and copy the entries */
1738 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1739 gpointer *entries;
1740 guint16 *domain_ids;
1741 gint i;
1742 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1743 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1744 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1745 for (i = 0; i < handles->size; ++i) {
1746 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1747 if (obj) {
1748 mono_gc_weak_link_add (&(entries [i]), obj, track);
1749 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1750 } else {
1751 g_assert (!handles->entries [i]);
1754 g_free (handles->entries);
1755 g_free (handles->domain_ids);
1756 handles->entries = entries;
1757 handles->domain_ids = domain_ids;
1758 } else {
1759 gpointer *entries;
1760 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Boehm)");
1761 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1762 mono_gc_free_fixed (handles->entries);
1763 handles->entries = entries;
1765 handles->slot_hint = handles->size / BITMAP_SIZE;
1766 handles->size = new_size;
1769 static guint32
1770 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1772 gint slot, i;
1773 guint32 res;
1774 lock_handles (handles);
1775 if (!handles->size)
1776 handle_data_alloc_entries (handles);
1777 i = handle_data_next_unset (handles);
1778 if (i == -1 && handles->slot_hint != 0)
1779 i = handle_data_first_unset (handles);
1780 if (i == -1) {
1781 handle_data_grow (handles, track);
1782 i = 0;
1784 slot = handles->slot_hint * BITMAP_SIZE + i;
1785 occupy_slot (handles, slot);
1786 handles->entries [slot] = NULL;
1787 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1788 /*FIXME, what to use when obj == null?*/
1789 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1790 if (obj)
1791 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1792 } else {
1793 handles->entries [slot] = obj;
1796 #ifndef DISABLE_PERFCOUNTERS
1797 mono_atomic_inc_i32 (&mono_perfcounters->gc_num_handles);
1798 #endif
1799 unlock_handles (handles);
1800 res = MONO_GC_HANDLE (slot, handles->type);
1801 MONO_PROFILER_RAISE (gc_handle_created, (res, handles->type, obj));
1802 return res;
1806 * mono_gchandle_new:
1807 * \param obj managed object to get a handle for
1808 * \param pinned whether the object should be pinned
1810 * This returns a handle that wraps the object, this is used to keep a
1811 * reference to a managed object from the unmanaged world and preventing the
1812 * object from being disposed.
1814 * If \p pinned is false the address of the object can not be obtained, if it is
1815 * true the address of the object can be obtained. This will also pin the
1816 * object so it will not be possible by a moving garbage collector to move the
1817 * object.
1819 * \returns a handle that can be used to access the object from
1820 * unmanaged code.
1822 guint32
1823 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1825 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1829 * mono_gchandle_new_weakref:
1830 * \param obj managed object to get a handle for
1831 * \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.
1833 * This returns a weak handle that wraps the object, this is used to
1834 * keep a reference to a managed object from the unmanaged world.
1835 * Unlike the \c mono_gchandle_new the object can be reclaimed by the
1836 * garbage collector. In this case the value of the GCHandle will be
1837 * set to zero.
1839 * If \p track_resurrection is TRUE the object will be tracked through
1840 * finalization and if the object is resurrected during the execution
1841 * of the finalizer, then the returned weakref will continue to hold
1842 * a reference to the object. If \p track_resurrection is FALSE, then
1843 * the weak reference's target will become NULL as soon as the object
1844 * is passed on to the finalizer.
1846 * \returns a handle that can be used to access the object from
1847 * unmanaged code.
1849 guint32
1850 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1852 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1856 * mono_gchandle_get_target:
1857 * \param gchandle a GCHandle's handle.
1859 * The handle was previously created by calling \c mono_gchandle_new or
1860 * \c mono_gchandle_new_weakref.
1862 * \returns A pointer to the \c MonoObject* represented by the handle or
1863 * NULL for a collected object if using a weakref handle.
1865 MonoObject*
1866 mono_gchandle_get_target (guint32 gchandle)
1868 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1869 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1870 HandleData *handles = &gc_handles [type];
1871 MonoObject *obj = NULL;
1872 if (type >= HANDLE_TYPE_MAX)
1873 return NULL;
1875 lock_handles (handles);
1876 if (slot < handles->size && slot_occupied (handles, slot)) {
1877 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1878 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1879 } else {
1880 obj = (MonoObject *)handles->entries [slot];
1882 } else {
1883 /* print a warning? */
1885 unlock_handles (handles);
1886 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1887 return obj;
1890 void
1891 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1893 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1894 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1895 HandleData *handles = &gc_handles [type];
1896 MonoObject *old_obj = NULL;
1898 g_assert (type < HANDLE_TYPE_MAX);
1899 lock_handles (handles);
1900 if (slot < handles->size && slot_occupied (handles, slot)) {
1901 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1902 old_obj = (MonoObject *)handles->entries [slot];
1903 if (handles->entries [slot])
1904 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1905 if (obj)
1906 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1907 /*FIXME, what to use when obj == null?*/
1908 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1909 } else {
1910 handles->entries [slot] = obj;
1912 } else {
1913 /* print a warning? */
1915 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1916 unlock_handles (handles);
1919 gboolean
1920 mono_gc_is_null (void)
1922 return FALSE;
1926 * mono_gchandle_is_in_domain:
1927 * \param gchandle a GCHandle's handle.
1928 * \param domain An application domain.
1930 * Use this function to determine if the \p gchandle points to an
1931 * object allocated in the specified \p domain.
1933 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1935 gboolean
1936 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1938 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1939 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1940 HandleData *handles = &gc_handles [type];
1941 gboolean result = FALSE;
1943 if (type >= HANDLE_TYPE_MAX)
1944 return FALSE;
1946 lock_handles (handles);
1947 if (slot < handles->size && slot_occupied (handles, slot)) {
1948 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1949 result = domain->domain_id == handles->domain_ids [slot];
1950 } else {
1951 MonoObject *obj;
1952 obj = (MonoObject *)handles->entries [slot];
1953 if (obj == NULL)
1954 result = TRUE;
1955 else
1956 result = domain == mono_object_domain (obj);
1958 } else {
1959 /* print a warning? */
1961 unlock_handles (handles);
1962 return result;
1966 * mono_gchandle_free:
1967 * \param gchandle a GCHandle's handle.
1969 * Frees the \p gchandle handle. If there are no outstanding
1970 * references, the garbage collector can reclaim the memory of the
1971 * object wrapped.
1973 void
1974 mono_gchandle_free (guint32 gchandle)
1976 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1977 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1978 HandleData *handles = &gc_handles [type];
1979 if (type >= HANDLE_TYPE_MAX)
1980 return;
1982 lock_handles (handles);
1983 if (slot < handles->size && slot_occupied (handles, slot)) {
1984 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1985 if (handles->entries [slot])
1986 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1987 } else {
1988 handles->entries [slot] = NULL;
1990 vacate_slot (handles, slot);
1991 } else {
1992 /* print a warning? */
1994 #ifndef DISABLE_PERFCOUNTERS
1995 mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles);
1996 #endif
1997 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1998 unlock_handles (handles);
1999 MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, handles->type));
2003 * mono_gchandle_free_domain:
2004 * \param domain domain that is unloading
2006 * Function used internally to cleanup any GC handle for objects belonging
2007 * to the specified domain during appdomain unload.
2009 void
2010 mono_gchandle_free_domain (MonoDomain *domain)
2012 guint type;
2014 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
2015 guint slot;
2016 HandleData *handles = &gc_handles [type];
2017 lock_handles (handles);
2018 for (slot = 0; slot < handles->size; ++slot) {
2019 if (!slot_occupied (handles, slot))
2020 continue;
2021 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
2022 if (domain->domain_id == handles->domain_ids [slot]) {
2023 vacate_slot (handles, slot);
2024 if (handles->entries [slot])
2025 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
2027 } else {
2028 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
2029 vacate_slot (handles, slot);
2030 handles->entries [slot] = NULL;
2034 unlock_handles (handles);
2039 void
2040 mono_gc_register_obj_with_weak_fields (void *obj)
2042 g_error ("Weak fields not supported by boehm gc");
2045 #else
2047 MONO_EMPTY_SOURCE_FILE (boehm_gc);
2048 #endif /* no Boehm GC */