[profiler] Remove some indirection in setting up GC events.
[mono-project.git] / mono / metadata / boehm-gc.c
blobd084501c929424aa8dc14ccb01150eb818cee407
1 /*
2 * boehm-gc.c: GC implementation using either the installed or included Boehm GC.
4 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
5 * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
6 * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8 */
10 #include "config.h"
12 #include <string.h>
14 #define GC_I_HIDE_POINTERS
15 #include <mono/metadata/gc-internals.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/metadata/class-internals.h>
19 #include <mono/metadata/method-builder.h>
20 #include <mono/metadata/opcodes.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/marshal.h>
24 #include <mono/metadata/runtime.h>
25 #include <mono/metadata/handle.h>
26 #include <mono/metadata/sgen-toggleref.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-logger-internals.h>
29 #include <mono/utils/mono-memory-model.h>
30 #include <mono/utils/mono-time.h>
31 #include <mono/utils/mono-threads.h>
32 #include <mono/utils/dtrace.h>
33 #include <mono/utils/gc_wrapper.h>
34 #include <mono/utils/mono-os-mutex.h>
35 #include <mono/utils/mono-counters.h>
37 #if HAVE_BOEHM_GC
39 #undef TRUE
40 #undef FALSE
41 #define THREAD_LOCAL_ALLOC 1
42 #include "private/pthread_support.h"
44 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
45 void *pthread_get_stackaddr_np(pthread_t);
46 #endif
48 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
49 /*Boehm max heap cannot be smaller than 16MB*/
50 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
51 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
53 static gboolean gc_initialized = FALSE;
54 static mono_mutex_t mono_gc_lock;
56 static void*
57 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
58 static void
59 boehm_thread_unregister (MonoThreadInfo *p);
60 static void
61 boehm_thread_detach (MonoThreadInfo *p);
62 static void
63 register_test_toggleref_callback (void);
65 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
66 static MonoGCFinalizerCallbacks fin_callbacks;
68 /* GC Handles */
70 static mono_mutex_t handle_section;
71 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
72 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
74 typedef struct {
75 guint32 *bitmap;
76 gpointer *entries;
77 guint32 size;
78 guint8 type;
79 guint slot_hint : 24; /* starting slot for search in bitmap */
80 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
81 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
82 guint16 *domain_ids;
83 } HandleData;
85 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
87 /* weak and weak-track arrays will be allocated in malloc memory
89 static HandleData gc_handles [] = {
90 EMPTY_HANDLE_DATA (HANDLE_WEAK),
91 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
92 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
93 EMPTY_HANDLE_DATA (HANDLE_PINNED)
96 static void
97 mono_gc_warning (char *msg, GC_word arg)
99 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
102 static void on_gc_notification (GC_EventType event);
103 static void on_gc_heap_resize (size_t new_size);
105 void
106 mono_gc_base_init (void)
108 MonoThreadInfoCallbacks cb;
109 const char *env;
110 int dummy;
112 if (gc_initialized)
113 return;
115 mono_counters_init ();
117 #ifndef HOST_WIN32
118 mono_w32handle_init ();
119 #endif
122 * Handle the case when we are called from a thread different from the main thread,
123 * confusing libgc.
124 * FIXME: Move this to libgc where it belongs.
126 * we used to do this only when running on valgrind,
127 * but it happens also in other setups.
129 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
131 size_t size;
132 void *sstart;
133 pthread_attr_t attr;
134 pthread_getattr_np (pthread_self (), &attr);
135 pthread_attr_getstack (&attr, &sstart, &size);
136 pthread_attr_destroy (&attr);
137 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
138 #ifdef __ia64__
140 * The calculation above doesn't seem to work on ia64, also we need to set
141 * GC_register_stackbottom as well, but don't know how.
143 #else
144 /* apparently with some linuxthreads implementations sstart can be NULL,
145 * fallback to the more imprecise method (bug# 78096).
147 if (sstart) {
148 GC_stackbottom = (char*)sstart + size;
149 } else {
150 int dummy;
151 gsize stack_bottom = (gsize)&dummy;
152 stack_bottom += 4095;
153 stack_bottom &= ~4095;
154 GC_stackbottom = (char*)stack_bottom;
156 #endif
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 #elif defined(__native_client__)
172 /* Do nothing, GC_stackbottom is set correctly in libgc */
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 #if !defined(PLATFORM_ANDROID)
185 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
186 GC_no_dls = TRUE;
187 #endif
189 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
190 char **opts = g_strsplit (env, ",", -1);
191 for (char **ptr = opts; ptr && *ptr; ptr ++) {
192 char *opt = *ptr;
193 if (!strcmp (opt, "do-not-finalize")) {
194 mono_do_not_finalize = 1;
195 } else if (!strcmp (opt, "log-finalizers")) {
196 log_finalizers = 1;
202 GC_init ();
204 GC_set_warn_proc (mono_gc_warning);
205 GC_finalize_on_demand = 1;
206 GC_finalizer_notifier = mono_gc_finalize_notify;
208 GC_init_gcj_malloc (5, NULL);
209 GC_allow_register_threads ();
211 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
212 char **ptr, **opts = g_strsplit (env, ",", -1);
213 for (ptr = opts; *ptr; ++ptr) {
214 char *opt = *ptr;
215 if (g_str_has_prefix (opt, "max-heap-size=")) {
216 size_t max_heap;
218 opt = strchr (opt, '=') + 1;
219 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
220 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
221 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
222 exit (1);
224 GC_set_max_heap_size (max_heap);
225 } else {
226 fprintf (stderr, "max-heap-size must be an integer.\n");
227 exit (1);
229 continue;
230 } else if (g_str_has_prefix (opt, "toggleref-test")) {
231 register_test_toggleref_callback ();
232 continue;
233 } else {
234 /* Could be a parameter for sgen */
236 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
237 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
238 exit (1);
242 g_strfreev (opts);
245 memset (&cb, 0, sizeof (cb));
246 cb.thread_register = boehm_thread_register;
247 cb.thread_unregister = boehm_thread_unregister;
248 cb.thread_detach = boehm_thread_detach;
249 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
251 mono_threads_init (&cb, sizeof (MonoThreadInfo));
252 mono_os_mutex_init (&mono_gc_lock);
253 mono_os_mutex_init_recursive (&handle_section);
255 mono_thread_info_attach (&dummy);
257 GC_set_on_collection_event (on_gc_notification);
258 GC_on_heap_resize = on_gc_heap_resize;
260 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
261 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
263 gc_initialized = TRUE;
266 void
267 mono_gc_base_cleanup (void)
269 GC_finalizer_notifier = NULL;
273 * mono_gc_collect:
274 * @generation: GC generation identifier
276 * Perform a garbage collection for the given generation, higher numbers
277 * mean usually older objects. Collecting a high-numbered generation
278 * implies collecting also the lower-numbered generations.
279 * The maximum value for @generation can be retrieved with a call to
280 * mono_gc_max_generation(), so this function is usually called as:
282 * mono_gc_collect (mono_gc_max_generation ());
284 void
285 mono_gc_collect (int generation)
287 #ifndef DISABLE_PERFCOUNTERS
288 mono_perfcounters->gc_induced++;
289 #endif
290 GC_gcollect ();
294 * mono_gc_max_generation:
296 * Get the maximum generation number used by the current garbage
297 * collector. The value will be 0 for the Boehm collector, 1 or more
298 * for the generational collectors.
300 * Returns: the maximum generation number.
303 mono_gc_max_generation (void)
305 return 0;
309 * mono_gc_get_generation:
310 * @object: a managed object
312 * Get the garbage collector's generation that @object belongs to.
313 * Use this has a hint only.
315 * Returns: a garbage collector generation number
318 mono_gc_get_generation (MonoObject *object)
320 return 0;
324 * mono_gc_collection_count:
325 * @generation: a GC generation number
327 * Get how many times a garbage collection has been performed
328 * for the given @generation number.
330 * Returns: the number of garbage collections
333 mono_gc_collection_count (int generation)
335 return GC_gc_no;
339 * mono_gc_add_memory_pressure:
340 * @value: amount of bytes
342 * Adjust the garbage collector's view of how many bytes of memory
343 * are indirectly referenced by managed objects (for example unmanaged
344 * memory holding image or other binary data).
345 * This is a hint only to the garbage collector algorithm.
346 * Note that negative amounts of @value will decrease the memory
347 * pressure.
349 void
350 mono_gc_add_memory_pressure (gint64 value)
355 * mono_gc_get_used_size:
357 * Get the approximate amount of memory used by managed objects.
359 * Returns: the amount of memory used in bytes
361 int64_t
362 mono_gc_get_used_size (void)
364 return GC_get_heap_size () - GC_get_free_bytes ();
368 * mono_gc_get_heap_size:
370 * Get the amount of memory used by the garbage collector.
372 * Returns: the size of the heap in bytes
374 int64_t
375 mono_gc_get_heap_size (void)
377 return GC_get_heap_size ();
380 gboolean
381 mono_gc_is_gc_thread (void)
383 return GC_thread_is_registered ();
386 gboolean
387 mono_gc_register_thread (void *baseptr)
389 return mono_thread_info_attach (baseptr) != NULL;
392 static void*
393 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
395 struct GC_stack_base sb;
396 int res;
398 /* TODO: use GC_get_stack_base instead of baseptr. */
399 sb.mem_base = baseptr;
400 res = GC_register_my_thread (&sb);
401 if (res == GC_UNIMPLEMENTED)
402 return NULL; /* Cannot happen with GC v7+. */
404 info->handle_stack = mono_handle_stack_alloc ();
406 return info;
409 static void
410 boehm_thread_unregister (MonoThreadInfo *p)
412 MonoNativeThreadId tid;
414 tid = mono_thread_info_get_tid (p);
416 if (p->runtime_thread)
417 mono_threads_add_joinable_thread ((gpointer)tid);
420 static void
421 boehm_thread_detach (MonoThreadInfo *p)
423 if (mono_thread_internal_current_is_attached ())
424 mono_thread_detach_internal (mono_thread_internal_current ());
427 gboolean
428 mono_object_is_alive (MonoObject* o)
430 return GC_is_marked ((ptr_t)o);
434 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
436 return 1;
439 static gint64 gc_start_time;
441 static void
442 on_gc_notification (GC_EventType event)
444 MonoGCEvent e = (MonoGCEvent)event;
446 switch (e) {
447 case MONO_GC_EVENT_PRE_STOP_WORLD:
448 MONO_GC_WORLD_STOP_BEGIN ();
449 break;
451 case MONO_GC_EVENT_POST_STOP_WORLD:
452 MONO_GC_WORLD_STOP_END ();
453 break;
455 case MONO_GC_EVENT_PRE_START_WORLD:
456 MONO_GC_WORLD_RESTART_BEGIN (1);
457 break;
459 case MONO_GC_EVENT_POST_START_WORLD:
460 MONO_GC_WORLD_RESTART_END (1);
461 break;
463 case MONO_GC_EVENT_START:
464 MONO_GC_BEGIN (1);
465 #ifndef DISABLE_PERFCOUNTERS
466 if (mono_perfcounters)
467 mono_perfcounters->gc_collections0++;
468 #endif
469 gc_stats.major_gc_count ++;
470 gc_start_time = mono_100ns_ticks ();
471 break;
473 case MONO_GC_EVENT_END:
474 MONO_GC_END (1);
475 #if defined(ENABLE_DTRACE) && defined(__sun__)
476 /* This works around a dtrace -G problem on Solaris.
477 Limit its actual use to when the probe is enabled. */
478 if (MONO_GC_END_ENABLED ())
479 sleep(0);
480 #endif
482 #ifndef DISABLE_PERFCOUNTERS
483 if (mono_perfcounters) {
484 guint64 heap_size = GC_get_heap_size ();
485 guint64 used_size = heap_size - GC_get_free_bytes ();
486 mono_perfcounters->gc_total_bytes = used_size;
487 mono_perfcounters->gc_committed_bytes = heap_size;
488 mono_perfcounters->gc_reserved_bytes = heap_size;
489 mono_perfcounters->gc_gen0size = heap_size;
491 #endif
492 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
493 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
494 break;
495 default:
496 break;
499 mono_profiler_gc_event (e, 0);
501 switch (e) {
502 case MONO_GC_EVENT_PRE_STOP_WORLD:
503 mono_thread_info_suspend_lock ();
504 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
505 break;
506 case MONO_GC_EVENT_POST_START_WORLD:
507 mono_thread_info_suspend_unlock ();
508 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
509 break;
510 default:
511 break;
516 static void
517 on_gc_heap_resize (size_t new_size)
519 guint64 heap_size = GC_get_heap_size ();
520 #ifndef DISABLE_PERFCOUNTERS
521 if (mono_perfcounters) {
522 mono_perfcounters->gc_committed_bytes = heap_size;
523 mono_perfcounters->gc_reserved_bytes = heap_size;
524 mono_perfcounters->gc_gen0size = heap_size;
526 #endif
527 mono_profiler_gc_heap_resize (new_size);
531 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
533 /* for some strange reason, they want one extra byte on the end */
534 GC_add_roots (start, start + size + 1);
536 return TRUE;
539 void
540 mono_gc_deregister_root (char* addr)
542 #ifndef HOST_WIN32
543 /* FIXME: libgc doesn't define this work win32 for some reason */
544 /* FIXME: No size info */
545 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
546 #endif
549 static void
550 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
552 /* libgc requires that we use HIDE_POINTER... */
553 *link_addr = (void*)HIDE_POINTER (obj);
554 if (track)
555 GC_REGISTER_LONG_LINK (link_addr, obj);
556 else
557 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
560 static void
561 mono_gc_weak_link_remove (void **link_addr, gboolean track)
563 if (track)
564 GC_unregister_long_link (link_addr);
565 else
566 GC_unregister_disappearing_link (link_addr);
567 *link_addr = NULL;
570 static gpointer
571 reveal_link (gpointer link_addr)
573 void **link_a = (void **)link_addr;
574 return REVEAL_POINTER (*link_a);
577 static MonoObject *
578 mono_gc_weak_link_get (void **link_addr)
580 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
581 if (obj == (MonoObject *) -1)
582 return NULL;
583 return obj;
586 void*
587 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
589 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
592 void*
593 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
595 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
598 void*
599 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
601 /* libgc has no usable support for arrays... */
602 return GC_NO_DESCRIPTOR;
605 void*
606 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
608 /* It seems there are issues when the bitmap doesn't fit: play it safe */
609 if (numbits >= 30)
610 return GC_NO_DESCRIPTOR;
611 else
612 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
615 void*
616 mono_gc_make_root_descr_all_refs (int numbits)
618 return NULL;
621 void*
622 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
624 /* To help track down typed allocation bugs */
626 static int count;
627 count ++;
628 if (count == atoi (g_getenv ("COUNT2")))
629 printf ("HIT!\n");
630 if (count > atoi (g_getenv ("COUNT2")))
631 return GC_MALLOC (size);
634 if (descr)
635 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
636 else
637 return GC_MALLOC (size);
640 void
641 mono_gc_free_fixed (void* addr)
645 void *
646 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
648 MonoObject *obj;
650 if (!vtable->klass->has_references) {
651 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
652 if (G_UNLIKELY (!obj))
653 return NULL;
655 obj->vtable = vtable;
656 obj->synchronisation = NULL;
658 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
659 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
660 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
661 if (G_UNLIKELY (!obj))
662 return NULL;
663 } else {
664 obj = (MonoObject *)GC_MALLOC (size);
665 if (G_UNLIKELY (!obj))
666 return NULL;
668 obj->vtable = vtable;
671 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
672 mono_profiler_allocation (obj);
674 return obj;
677 void *
678 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
680 MonoArray *obj;
682 if (!vtable->klass->has_references) {
683 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
684 if (G_UNLIKELY (!obj))
685 return NULL;
687 obj->obj.vtable = vtable;
688 obj->obj.synchronisation = NULL;
690 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
691 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
692 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
693 if (G_UNLIKELY (!obj))
694 return NULL;
695 } else {
696 obj = (MonoArray *)GC_MALLOC (size);
697 if (G_UNLIKELY (!obj))
698 return NULL;
700 obj->obj.vtable = vtable;
703 obj->max_length = max_length;
705 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
706 mono_profiler_allocation (&obj->obj);
708 return obj;
711 void *
712 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
714 MonoArray *obj;
716 if (!vtable->klass->has_references) {
717 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
718 if (G_UNLIKELY (!obj))
719 return NULL;
721 obj->obj.vtable = vtable;
722 obj->obj.synchronisation = NULL;
724 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
725 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
726 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
727 if (G_UNLIKELY (!obj))
728 return NULL;
729 } else {
730 obj = (MonoArray *)GC_MALLOC (size);
731 if (G_UNLIKELY (!obj))
732 return NULL;
734 obj->obj.vtable = vtable;
737 obj->max_length = max_length;
739 if (bounds_size)
740 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
742 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
743 mono_profiler_allocation (&obj->obj);
745 return obj;
748 void *
749 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
751 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
752 if (G_UNLIKELY (!obj))
753 return NULL;
755 obj->object.vtable = vtable;
756 obj->object.synchronisation = NULL;
757 obj->length = len;
758 obj->chars [len] = 0;
760 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
761 mono_profiler_allocation (&obj->object);
763 return obj;
766 void*
767 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
769 return mono_gc_alloc_obj (vtable, size);
772 void*
773 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
775 return mono_gc_alloc_obj (vtable, size);
779 mono_gc_invoke_finalizers (void)
781 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
782 * the 'mem_freed' variable is not initialized when there are no
783 * objects to finalize, which leads to strange behavior later on.
784 * The check is necessary to work around that bug.
786 if (GC_should_invoke_finalizers ())
787 return GC_invoke_finalizers ();
788 return 0;
791 MonoBoolean
792 mono_gc_pending_finalizers (void)
794 return GC_should_invoke_finalizers ();
797 void
798 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
800 *(void**)field_ptr = value;
803 void
804 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
806 *(void**)slot_ptr = value;
809 void
810 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
812 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
815 void
816 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
818 *(void**)ptr = value;
821 void
822 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
824 InterlockedWritePointer ((volatile gpointer *)ptr, value);
827 void
828 mono_gc_wbarrier_generic_nostore (gpointer ptr)
832 void
833 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
835 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
838 void
839 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
841 /* do not copy the sync state */
842 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
843 mono_object_class (obj)->instance_size - sizeof (MonoObject));
846 void
847 mono_gc_clear_domain (MonoDomain *domain)
851 void
852 mono_gc_suspend_finalizers (void)
857 mono_gc_get_suspend_signal (void)
859 return GC_get_suspend_signal ();
863 mono_gc_get_restart_signal (void)
865 return GC_get_thr_restart_signal ();
868 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
869 extern __thread MONO_TLS_FAST void* GC_thread_tls;
870 #include "metadata-internals.h"
872 static int
873 shift_amount (int v)
875 int i = 0;
876 while (!(v & (1 << i)))
877 i++;
878 return i;
881 enum {
882 ATYPE_FREEPTR,
883 ATYPE_FREEPTR_FOR_BOX,
884 ATYPE_NORMAL,
885 ATYPE_GCJ,
886 ATYPE_STRING,
887 ATYPE_NUM
890 static MonoMethod*
891 create_allocator (int atype, int tls_key, gboolean slowpath)
893 int index_var, bytes_var, my_fl_var, my_entry_var;
894 guint32 no_freelist_branch, not_small_enough_branch = 0;
895 guint32 size_overflow_branch = 0;
896 MonoMethodBuilder *mb;
897 MonoMethod *res;
898 MonoMethodSignature *csig;
899 const char *name = NULL;
900 WrapperInfo *info;
902 if (atype == ATYPE_FREEPTR) {
903 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
904 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
905 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
906 } else if (atype == ATYPE_NORMAL) {
907 name = slowpath ? "SlowAlloc" : "Alloc";
908 } else if (atype == ATYPE_GCJ) {
909 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
910 } else if (atype == ATYPE_STRING) {
911 name = slowpath ? "SlowAllocString" : "AllocString";
912 } else {
913 g_assert_not_reached ();
916 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
918 if (atype == ATYPE_STRING) {
919 csig->ret = &mono_defaults.string_class->byval_arg;
920 csig->params [0] = &mono_defaults.int_class->byval_arg;
921 csig->params [1] = &mono_defaults.int32_class->byval_arg;
922 } else {
923 csig->ret = &mono_defaults.object_class->byval_arg;
924 csig->params [0] = &mono_defaults.int_class->byval_arg;
925 csig->params [1] = &mono_defaults.int32_class->byval_arg;
928 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
930 if (slowpath)
931 goto always_slowpath;
933 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
934 if (atype == ATYPE_STRING) {
935 /* a string alloator method takes the args: (vtable, len) */
936 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
937 mono_mb_emit_ldarg (mb, 1);
938 mono_mb_emit_icon (mb, 1);
939 mono_mb_emit_byte (mb, MONO_CEE_ADD);
940 mono_mb_emit_icon (mb, 1);
941 mono_mb_emit_byte (mb, MONO_CEE_SHL);
942 // sizeof (MonoString) might include padding
943 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
944 mono_mb_emit_byte (mb, MONO_CEE_ADD);
945 mono_mb_emit_stloc (mb, bytes_var);
946 } else {
947 mono_mb_emit_ldarg (mb, 1);
948 mono_mb_emit_stloc (mb, bytes_var);
951 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
952 if (atype == ATYPE_STRING) {
953 /* check for size */
954 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
955 mono_mb_emit_ldloc (mb, bytes_var);
956 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
957 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
958 /* check for overflow */
959 mono_mb_emit_ldloc (mb, bytes_var);
960 mono_mb_emit_icon (mb, sizeof (MonoString));
961 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
964 /* int index = INDEX_FROM_BYTES(bytes); */
965 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
967 mono_mb_emit_ldloc (mb, bytes_var);
968 mono_mb_emit_icon (mb, GRANULARITY - 1);
969 mono_mb_emit_byte (mb, MONO_CEE_ADD);
970 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
971 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
972 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
973 mono_mb_emit_byte (mb, MONO_CEE_SHL);
974 /* index var is already adjusted into bytes */
975 mono_mb_emit_stloc (mb, index_var);
977 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
978 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
979 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
980 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
981 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
982 mono_mb_emit_i4 (mb, tls_key);
983 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
984 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
985 + G_STRUCT_OFFSET (struct thread_local_freelists,
986 ptrfree_freelists));
987 else if (atype == ATYPE_NORMAL)
988 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
989 + G_STRUCT_OFFSET (struct thread_local_freelists,
990 normal_freelists));
991 else if (atype == ATYPE_GCJ)
992 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
993 + G_STRUCT_OFFSET (struct thread_local_freelists,
994 gcj_freelists));
995 else
996 g_assert_not_reached ();
997 mono_mb_emit_byte (mb, MONO_CEE_ADD);
998 mono_mb_emit_ldloc (mb, index_var);
999 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1000 mono_mb_emit_stloc (mb, my_fl_var);
1002 /* my_entry = *my_fl; */
1003 mono_mb_emit_ldloc (mb, my_fl_var);
1004 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1005 mono_mb_emit_stloc (mb, my_entry_var);
1007 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1008 mono_mb_emit_ldloc (mb, my_entry_var);
1009 mono_mb_emit_icon (mb, HBLKSIZE);
1010 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1012 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1013 mono_mb_emit_ldloc (mb, my_fl_var);
1014 mono_mb_emit_ldloc (mb, my_entry_var);
1015 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1016 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1018 /* set the vtable and clear the words in the object */
1019 mono_mb_emit_ldloc (mb, my_entry_var);
1020 mono_mb_emit_ldarg (mb, 0);
1021 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1023 if (atype == ATYPE_FREEPTR) {
1024 int start_var, end_var, start_loop;
1025 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1027 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1028 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1029 mono_mb_emit_ldloc (mb, my_entry_var);
1030 mono_mb_emit_ldloc (mb, bytes_var);
1031 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1032 mono_mb_emit_stloc (mb, end_var);
1033 mono_mb_emit_ldloc (mb, my_entry_var);
1034 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1035 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1036 mono_mb_emit_stloc (mb, start_var);
1038 * do {
1039 * *start++ = NULL;
1040 * } while (start < end);
1042 start_loop = mono_mb_get_label (mb);
1043 mono_mb_emit_ldloc (mb, start_var);
1044 mono_mb_emit_icon (mb, 0);
1045 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1046 mono_mb_emit_ldloc (mb, start_var);
1047 mono_mb_emit_icon (mb, sizeof (gpointer));
1048 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1049 mono_mb_emit_stloc (mb, start_var);
1051 mono_mb_emit_ldloc (mb, start_var);
1052 mono_mb_emit_ldloc (mb, end_var);
1053 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1054 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1055 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1056 /* need to clear just the sync pointer */
1057 mono_mb_emit_ldloc (mb, my_entry_var);
1058 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1059 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1060 mono_mb_emit_icon (mb, 0);
1061 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1064 if (atype == ATYPE_STRING) {
1065 /* need to set length and clear the last char */
1066 /* s->length = len; */
1067 mono_mb_emit_ldloc (mb, my_entry_var);
1068 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1069 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1070 mono_mb_emit_ldarg (mb, 1);
1071 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1072 /* s->chars [len] = 0; */
1073 mono_mb_emit_ldloc (mb, my_entry_var);
1074 mono_mb_emit_ldloc (mb, bytes_var);
1075 mono_mb_emit_icon (mb, 2);
1076 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1077 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1078 mono_mb_emit_icon (mb, 0);
1079 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1082 /* return my_entry; */
1083 mono_mb_emit_ldloc (mb, my_entry_var);
1084 mono_mb_emit_byte (mb, MONO_CEE_RET);
1086 mono_mb_patch_short_branch (mb, no_freelist_branch);
1087 if (not_small_enough_branch > 0)
1088 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1089 if (size_overflow_branch > 0)
1090 mono_mb_patch_short_branch (mb, size_overflow_branch);
1092 /* the slow path: we just call back into the runtime */
1093 always_slowpath:
1094 if (atype == ATYPE_STRING) {
1095 mono_mb_emit_ldarg (mb, 1);
1096 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1097 } else {
1098 mono_mb_emit_ldarg (mb, 0);
1099 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1102 mono_mb_emit_byte (mb, MONO_CEE_RET);
1104 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1105 info->d.alloc.gc_name = "boehm";
1106 info->d.alloc.alloc_type = atype;
1107 mb->init_locals = FALSE;
1109 res = mono_mb_create (mb, csig, 8, info);
1110 mono_mb_free (mb);
1112 return res;
1115 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1116 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1118 static G_GNUC_UNUSED gboolean
1119 mono_gc_is_critical_method (MonoMethod *method)
1121 int i;
1123 for (i = 0; i < ATYPE_NUM; ++i)
1124 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1125 return TRUE;
1127 return FALSE;
1131 * If possible, generate a managed method that can quickly allocate objects in class
1132 * @klass. The method will typically have an thread-local inline allocation sequence.
1133 * The signature of the called method is:
1134 * object allocate (MonoVTable *vtable)
1135 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1136 * keep in sync.
1137 * The thread local alloc logic is taken from libgc/pthread_support.c.
1140 MonoMethod*
1141 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1143 int offset = -1;
1144 int atype;
1145 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1147 /*g_print ("thread tls: %d\n", offset);*/
1148 if (offset == -1)
1149 return NULL;
1150 if (!SMALL_ENOUGH (klass->instance_size))
1151 return NULL;
1152 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1153 return NULL;
1154 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1155 return NULL;
1156 if (klass->rank)
1157 return NULL;
1158 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1159 return NULL;
1160 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1161 atype = ATYPE_STRING;
1162 } else if (!known_instance_size) {
1163 return NULL;
1164 } else if (!klass->has_references) {
1165 if (for_box)
1166 atype = ATYPE_FREEPTR_FOR_BOX;
1167 else
1168 atype = ATYPE_FREEPTR;
1169 } else {
1170 return NULL;
1172 * disabled because we currently do a runtime choice anyway, to
1173 * deal with multiple appdomains.
1174 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1175 atype = ATYPE_GCJ;
1176 else
1177 atype = ATYPE_NORMAL;
1180 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1183 MonoMethod*
1184 mono_gc_get_managed_array_allocator (MonoClass *klass)
1186 return NULL;
1190 * mono_gc_get_managed_allocator_by_type:
1192 * Return a managed allocator method corresponding to allocator type ATYPE.
1194 MonoMethod*
1195 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1197 int offset = -1;
1198 MonoMethod *res;
1199 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1200 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1201 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1203 mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1205 res = cache [atype];
1206 if (res)
1207 return res;
1209 res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1210 mono_os_mutex_lock (&mono_gc_lock);
1211 if (cache [atype]) {
1212 mono_free_method (res);
1213 res = cache [atype];
1214 } else {
1215 mono_memory_barrier ();
1216 cache [atype] = res;
1218 mono_os_mutex_unlock (&mono_gc_lock);
1219 return res;
1222 guint32
1223 mono_gc_get_managed_allocator_types (void)
1225 return ATYPE_NUM;
1228 MonoMethod*
1229 mono_gc_get_write_barrier (void)
1231 g_assert_not_reached ();
1232 return NULL;
1235 #else
1237 static G_GNUC_UNUSED gboolean
1238 mono_gc_is_critical_method (MonoMethod *method)
1240 return FALSE;
1243 MonoMethod*
1244 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1246 return NULL;
1249 MonoMethod*
1250 mono_gc_get_managed_array_allocator (MonoClass *klass)
1252 return NULL;
1255 MonoMethod*
1256 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1258 return NULL;
1261 guint32
1262 mono_gc_get_managed_allocator_types (void)
1264 return 0;
1267 MonoMethod*
1268 mono_gc_get_write_barrier (void)
1270 g_assert_not_reached ();
1271 return NULL;
1274 #endif
1276 MonoMethod*
1277 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1279 g_assert_not_reached ();
1280 return NULL;
1284 mono_gc_get_aligned_size_for_allocator (int size)
1286 return size;
1289 const char *
1290 mono_gc_get_gc_name (void)
1292 return "boehm";
1295 void*
1296 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1298 return GC_call_with_alloc_lock (func, data);
1301 char*
1302 mono_gc_get_description (void)
1304 return g_strdup (DEFAULT_GC_NAME);
1307 void
1308 mono_gc_set_desktop_mode (void)
1310 GC_dont_expand = 1;
1313 gboolean
1314 mono_gc_is_moving (void)
1316 return FALSE;
1319 gboolean
1320 mono_gc_is_disabled (void)
1322 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1323 return TRUE;
1324 else
1325 return FALSE;
1328 void
1329 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1331 g_assert_not_reached ();
1335 guint8*
1336 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1338 g_assert_not_reached ();
1339 return NULL;
1342 gboolean
1343 mono_gc_card_table_nursery_check (void)
1345 g_assert_not_reached ();
1346 return TRUE;
1349 void*
1350 mono_gc_get_nursery (int *shift_bits, size_t *size)
1352 return NULL;
1355 void
1356 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1360 gboolean
1361 mono_gc_precise_stack_mark_enabled (void)
1363 return FALSE;
1366 FILE *
1367 mono_gc_get_logfile (void)
1369 return NULL;
1372 void
1373 mono_gc_conservatively_scan_area (void *start, void *end)
1375 g_assert_not_reached ();
1378 void *
1379 mono_gc_scan_object (void *obj, void *gc_data)
1381 g_assert_not_reached ();
1382 return NULL;
1385 gsize*
1386 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1388 g_assert_not_reached ();
1389 return NULL;
1392 void
1393 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1397 void
1398 mono_gc_set_stack_end (void *stack_end)
1402 void mono_gc_set_skip_thread (gboolean value)
1406 void
1407 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1409 guint offset = 0;
1411 #ifndef GC_DEBUG
1412 /* This assertion is not valid when GC_DEBUG is defined */
1413 g_assert (GC_base (obj) == (char*)obj - offset);
1414 #endif
1416 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1419 #ifndef HOST_WIN32
1421 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1423 /* it is being replaced by GC_pthread_create on some
1424 * platforms, see libgc/include/gc_pthread_redirects.h */
1425 return pthread_create (new_thread, attr, start_routine, arg);
1427 #endif
1429 #ifdef HOST_WIN32
1430 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1432 return GC_DllMain (module_handle, reason, reserved);
1434 #endif
1436 guint
1437 mono_gc_get_vtable_bits (MonoClass *klass)
1439 if (fin_callbacks.is_class_finalization_aware) {
1440 if (fin_callbacks.is_class_finalization_aware (klass))
1441 return BOEHM_GC_BIT_FINALIZER_AWARE;
1443 return 0;
1447 * mono_gc_register_altstack:
1449 * Register the dimensions of the normal stack and altstack with the collector.
1450 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1452 void
1453 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1455 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1459 mono_gc_get_los_limit (void)
1461 return G_MAXINT;
1464 void
1465 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1467 mono_unichar2 *new_end = str->chars + new_length;
1469 /* zero the discarded string. This null-delimits the string and allows
1470 * the space to be reclaimed by SGen. */
1472 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1473 str->length = new_length;
1476 gboolean
1477 mono_gc_user_markers_supported (void)
1479 return FALSE;
1482 void *
1483 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1485 g_assert_not_reached ();
1486 return NULL;
1489 /* Toggleref support */
1491 void
1492 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1494 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1495 g_error ("GC_toggleref_add failed\n");
1498 void
1499 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1501 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1504 /* Test support code */
1506 static MonoToggleRefStatus
1507 test_toggleref_callback (MonoObject *obj)
1509 static MonoClassField *mono_toggleref_test_field;
1510 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1512 if (!mono_toggleref_test_field) {
1513 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1514 g_assert (mono_toggleref_test_field);
1517 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1518 printf ("toggleref-cb obj %d\n", status);
1519 return status;
1522 static void
1523 register_test_toggleref_callback (void)
1525 mono_gc_toggleref_register_callback (test_toggleref_callback);
1528 static gboolean
1529 is_finalization_aware (MonoObject *obj)
1531 MonoVTable *vt = obj->vtable;
1532 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1535 static void
1536 fin_notifier (MonoObject *obj)
1538 if (is_finalization_aware (obj))
1539 fin_callbacks.object_queued_for_finalization (obj);
1542 void
1543 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1545 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1546 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1548 fin_callbacks = *callbacks;
1550 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1553 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1555 static inline gboolean
1556 slot_occupied (HandleData *handles, guint slot) {
1557 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1560 static inline void
1561 vacate_slot (HandleData *handles, guint slot) {
1562 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1565 static inline void
1566 occupy_slot (HandleData *handles, guint slot) {
1567 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1570 static int
1571 find_first_unset (guint32 bitmap)
1573 int i;
1574 for (i = 0; i < 32; ++i) {
1575 if (!(bitmap & (1 << i)))
1576 return i;
1578 return -1;
1581 static void
1582 handle_data_alloc_entries (HandleData *handles)
1584 handles->size = 32;
1585 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1586 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1587 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1588 } else {
1589 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1591 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1594 static gint
1595 handle_data_next_unset (HandleData *handles)
1597 gint slot;
1598 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1599 if (handles->bitmap [slot] == 0xffffffff)
1600 continue;
1601 handles->slot_hint = slot;
1602 return find_first_unset (handles->bitmap [slot]);
1604 return -1;
1607 static gint
1608 handle_data_first_unset (HandleData *handles)
1610 gint slot;
1611 for (slot = 0; slot < handles->slot_hint; ++slot) {
1612 if (handles->bitmap [slot] == 0xffffffff)
1613 continue;
1614 handles->slot_hint = slot;
1615 return find_first_unset (handles->bitmap [slot]);
1617 return -1;
1620 /* Returns the index of the current slot in the bitmap. */
1621 static void
1622 handle_data_grow (HandleData *handles, gboolean track)
1624 guint32 *new_bitmap;
1625 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1627 /* resize and copy the bitmap */
1628 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1629 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1630 g_free (handles->bitmap);
1631 handles->bitmap = new_bitmap;
1633 /* resize and copy the entries */
1634 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1635 gpointer *entries;
1636 guint16 *domain_ids;
1637 gint i;
1638 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1639 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1640 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1641 for (i = 0; i < handles->size; ++i) {
1642 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1643 if (obj) {
1644 mono_gc_weak_link_add (&(entries [i]), obj, track);
1645 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1646 } else {
1647 g_assert (!handles->entries [i]);
1650 g_free (handles->entries);
1651 g_free (handles->domain_ids);
1652 handles->entries = entries;
1653 handles->domain_ids = domain_ids;
1654 } else {
1655 gpointer *entries;
1656 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1657 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1658 mono_gc_free_fixed (handles->entries);
1659 handles->entries = entries;
1661 handles->slot_hint = handles->size / BITMAP_SIZE;
1662 handles->size = new_size;
1665 static guint32
1666 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1668 gint slot, i;
1669 guint32 res;
1670 lock_handles (handles);
1671 if (!handles->size)
1672 handle_data_alloc_entries (handles);
1673 i = handle_data_next_unset (handles);
1674 if (i == -1 && handles->slot_hint != 0)
1675 i = handle_data_first_unset (handles);
1676 if (i == -1) {
1677 handle_data_grow (handles, track);
1678 i = 0;
1680 slot = handles->slot_hint * BITMAP_SIZE + i;
1681 occupy_slot (handles, slot);
1682 handles->entries [slot] = NULL;
1683 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1684 /*FIXME, what to use when obj == null?*/
1685 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1686 if (obj)
1687 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1688 } else {
1689 handles->entries [slot] = obj;
1692 #ifndef DISABLE_PERFCOUNTERS
1693 mono_perfcounters->gc_num_handles++;
1694 #endif
1695 unlock_handles (handles);
1696 res = MONO_GC_HANDLE (slot, handles->type);
1697 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1698 return res;
1702 * mono_gchandle_new:
1703 * @obj: managed object to get a handle for
1704 * @pinned: whether the object should be pinned
1706 * This returns a handle that wraps the object, this is used to keep a
1707 * reference to a managed object from the unmanaged world and preventing the
1708 * object from being disposed.
1710 * If @pinned is false the address of the object can not be obtained, if it is
1711 * true the address of the object can be obtained. This will also pin the
1712 * object so it will not be possible by a moving garbage collector to move the
1713 * object.
1715 * Returns: a handle that can be used to access the object from
1716 * unmanaged code.
1718 guint32
1719 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1721 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1725 * mono_gchandle_new_weakref:
1726 * @obj: managed object to get a handle for
1727 * @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.
1729 * This returns a weak handle that wraps the object, this is used to
1730 * keep a reference to a managed object from the unmanaged world.
1731 * Unlike the mono_gchandle_new the object can be reclaimed by the
1732 * garbage collector. In this case the value of the GCHandle will be
1733 * set to zero.
1735 * If @track_resurrection is TRUE the object will be tracked through
1736 * finalization and if the object is resurrected during the execution
1737 * of the finalizer, then the returned weakref will continue to hold
1738 * a reference to the object. If @track_resurrection is FALSE, then
1739 * the weak reference's target will become NULL as soon as the object
1740 * is passed on to the finalizer.
1742 * Returns: a handle that can be used to access the object from
1743 * unmanaged code.
1745 guint32
1746 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1748 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1752 * mono_gchandle_get_target:
1753 * @gchandle: a GCHandle's handle.
1755 * The handle was previously created by calling `mono_gchandle_new` or
1756 * `mono_gchandle_new_weakref`.
1758 * Returns: A pointer to the `MonoObject*` represented by the handle or
1759 * NULL for a collected object if using a weakref handle.
1761 MonoObject*
1762 mono_gchandle_get_target (guint32 gchandle)
1764 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1765 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1766 HandleData *handles = &gc_handles [type];
1767 MonoObject *obj = NULL;
1768 if (type >= HANDLE_TYPE_MAX)
1769 return NULL;
1771 lock_handles (handles);
1772 if (slot < handles->size && slot_occupied (handles, slot)) {
1773 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1774 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1775 } else {
1776 obj = (MonoObject *)handles->entries [slot];
1778 } else {
1779 /* print a warning? */
1781 unlock_handles (handles);
1782 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1783 return obj;
1786 void
1787 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1789 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1790 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1791 HandleData *handles = &gc_handles [type];
1792 MonoObject *old_obj = NULL;
1794 g_assert (type < HANDLE_TYPE_MAX);
1795 lock_handles (handles);
1796 if (slot < handles->size && slot_occupied (handles, slot)) {
1797 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1798 old_obj = (MonoObject *)handles->entries [slot];
1799 if (handles->entries [slot])
1800 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1801 if (obj)
1802 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1803 /*FIXME, what to use when obj == null?*/
1804 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1805 } else {
1806 handles->entries [slot] = obj;
1808 } else {
1809 /* print a warning? */
1811 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1812 unlock_handles (handles);
1815 gboolean
1816 mono_gc_is_null (void)
1818 return FALSE;
1822 * mono_gchandle_is_in_domain:
1823 * @gchandle: a GCHandle's handle.
1824 * @domain: An application domain.
1826 * Use this function to determine if the @gchandle points to an
1827 * object allocated in the specified @domain.
1829 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1831 gboolean
1832 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1834 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1835 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1836 HandleData *handles = &gc_handles [type];
1837 gboolean result = FALSE;
1839 if (type >= HANDLE_TYPE_MAX)
1840 return FALSE;
1842 lock_handles (handles);
1843 if (slot < handles->size && slot_occupied (handles, slot)) {
1844 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1845 result = domain->domain_id == handles->domain_ids [slot];
1846 } else {
1847 MonoObject *obj;
1848 obj = (MonoObject *)handles->entries [slot];
1849 if (obj == NULL)
1850 result = TRUE;
1851 else
1852 result = domain == mono_object_domain (obj);
1854 } else {
1855 /* print a warning? */
1857 unlock_handles (handles);
1858 return result;
1862 * mono_gchandle_free:
1863 * @gchandle: a GCHandle's handle.
1865 * Frees the @gchandle handle. If there are no outstanding
1866 * references, the garbage collector can reclaim the memory of the
1867 * object wrapped.
1869 void
1870 mono_gchandle_free (guint32 gchandle)
1872 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1873 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1874 HandleData *handles = &gc_handles [type];
1875 if (type >= HANDLE_TYPE_MAX)
1876 return;
1878 lock_handles (handles);
1879 if (slot < handles->size && slot_occupied (handles, slot)) {
1880 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1881 if (handles->entries [slot])
1882 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1883 } else {
1884 handles->entries [slot] = NULL;
1886 vacate_slot (handles, slot);
1887 } else {
1888 /* print a warning? */
1890 #ifndef DISABLE_PERFCOUNTERS
1891 mono_perfcounters->gc_num_handles--;
1892 #endif
1893 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1894 unlock_handles (handles);
1895 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1899 * mono_gchandle_free_domain:
1900 * @domain: domain that is unloading
1902 * Function used internally to cleanup any GC handle for objects belonging
1903 * to the specified domain during appdomain unload.
1905 void
1906 mono_gchandle_free_domain (MonoDomain *domain)
1908 guint type;
1910 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1911 guint slot;
1912 HandleData *handles = &gc_handles [type];
1913 lock_handles (handles);
1914 for (slot = 0; slot < handles->size; ++slot) {
1915 if (!slot_occupied (handles, slot))
1916 continue;
1917 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1918 if (domain->domain_id == handles->domain_ids [slot]) {
1919 vacate_slot (handles, slot);
1920 if (handles->entries [slot])
1921 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1923 } else {
1924 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1925 vacate_slot (handles, slot);
1926 handles->entries [slot] = NULL;
1930 unlock_handles (handles);
1934 #else
1935 #ifdef _MSC_VER
1936 // Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty.
1937 void __mono_win32_boehm_gc_quiet_lnk4221(void) {}
1938 #endif
1939 #endif /* no Boehm GC */