[System] Tweak socket test
[mono-project.git] / mono / metadata / boehm-gc.c
blobdcfd310f6490ebdd894c863b32e8c65d6ff52f6d
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/metadata/w32handle.h>
28 #include <mono/utils/atomic.h>
29 #include <mono/utils/mono-logger-internals.h>
30 #include <mono/utils/mono-memory-model.h>
31 #include <mono/utils/mono-time.h>
32 #include <mono/utils/mono-threads.h>
33 #include <mono/utils/dtrace.h>
34 #include <mono/utils/gc_wrapper.h>
35 #include <mono/utils/mono-os-mutex.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-compiler.h>
39 #if HAVE_BOEHM_GC
41 #undef TRUE
42 #undef FALSE
43 #define THREAD_LOCAL_ALLOC 1
44 #include "private/pthread_support.h"
46 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
47 void *pthread_get_stackaddr_np(pthread_t);
48 #endif
50 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
51 /*Boehm max heap cannot be smaller than 16MB*/
52 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
53 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
55 static gboolean gc_initialized = FALSE;
56 static mono_mutex_t mono_gc_lock;
58 static void*
59 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
60 static void
61 boehm_thread_unregister (MonoThreadInfo *p);
62 static void
63 boehm_thread_detach (MonoThreadInfo *p);
64 static void
65 register_test_toggleref_callback (void);
67 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
68 static MonoGCFinalizerCallbacks fin_callbacks;
70 /* GC Handles */
72 static mono_mutex_t handle_section;
73 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
74 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
76 typedef struct {
77 guint32 *bitmap;
78 gpointer *entries;
79 guint32 size;
80 guint8 type;
81 guint slot_hint : 24; /* starting slot for search in bitmap */
82 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
83 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
84 guint16 *domain_ids;
85 } HandleData;
87 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
89 /* weak and weak-track arrays will be allocated in malloc memory
91 static HandleData gc_handles [] = {
92 EMPTY_HANDLE_DATA (HANDLE_WEAK),
93 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
94 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
95 EMPTY_HANDLE_DATA (HANDLE_PINNED)
98 static void
99 mono_gc_warning (char *msg, GC_word arg)
101 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
104 static void on_gc_notification (GC_EventType event);
105 static void on_gc_heap_resize (size_t new_size);
107 void
108 mono_gc_base_init (void)
110 MonoThreadInfoCallbacks cb;
111 const char *env;
112 int dummy;
114 if (gc_initialized)
115 return;
117 mono_counters_init ();
119 #ifndef HOST_WIN32
120 mono_w32handle_init ();
121 #endif
124 * Handle the case when we are called from a thread different from the main thread,
125 * confusing libgc.
126 * FIXME: Move this to libgc where it belongs.
128 * we used to do this only when running on valgrind,
129 * but it happens also in other setups.
131 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
133 size_t size;
134 void *sstart;
135 pthread_attr_t attr;
136 pthread_getattr_np (pthread_self (), &attr);
137 pthread_attr_getstack (&attr, &sstart, &size);
138 pthread_attr_destroy (&attr);
139 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
140 #ifdef __ia64__
142 * The calculation above doesn't seem to work on ia64, also we need to set
143 * GC_register_stackbottom as well, but don't know how.
145 #else
146 /* apparently with some linuxthreads implementations sstart can be NULL,
147 * fallback to the more imprecise method (bug# 78096).
149 if (sstart) {
150 GC_stackbottom = (char*)sstart + size;
151 } else {
152 int dummy;
153 gsize stack_bottom = (gsize)&dummy;
154 stack_bottom += 4095;
155 stack_bottom &= ~4095;
156 GC_stackbottom = (char*)stack_bottom;
158 #endif
160 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
161 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
162 #elif defined(__OpenBSD__)
163 # include <pthread_np.h>
165 stack_t ss;
166 int rslt;
168 rslt = pthread_stackseg_np(pthread_self(), &ss);
169 g_assert (rslt == 0);
171 GC_stackbottom = (char*)ss.ss_sp;
173 #elif defined(__native_client__)
174 /* Do nothing, GC_stackbottom is set correctly in libgc */
175 #else
177 int dummy;
178 gsize stack_bottom = (gsize)&dummy;
179 stack_bottom += 4095;
180 stack_bottom &= ~4095;
181 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
182 GC_stackbottom = (char*)stack_bottom;
184 #endif
186 #if !defined(PLATFORM_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 log_finalizers = 1;
204 GC_init ();
206 GC_set_warn_proc (mono_gc_warning);
207 GC_finalize_on_demand = 1;
208 GC_finalizer_notifier = mono_gc_finalize_notify;
210 GC_init_gcj_malloc (5, NULL);
211 GC_allow_register_threads ();
213 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
214 char **ptr, **opts = g_strsplit (env, ",", -1);
215 for (ptr = opts; *ptr; ++ptr) {
216 char *opt = *ptr;
217 if (g_str_has_prefix (opt, "max-heap-size=")) {
218 size_t max_heap;
220 opt = strchr (opt, '=') + 1;
221 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
222 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
223 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
224 exit (1);
226 GC_set_max_heap_size (max_heap);
227 } else {
228 fprintf (stderr, "max-heap-size must be an integer.\n");
229 exit (1);
231 continue;
232 } else if (g_str_has_prefix (opt, "toggleref-test")) {
233 register_test_toggleref_callback ();
234 continue;
235 } else {
236 /* Could be a parameter for sgen */
238 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
239 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
240 exit (1);
244 g_strfreev (opts);
247 memset (&cb, 0, sizeof (cb));
248 cb.thread_register = boehm_thread_register;
249 cb.thread_unregister = boehm_thread_unregister;
250 cb.thread_detach = boehm_thread_detach;
251 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
253 mono_threads_init (&cb, sizeof (MonoThreadInfo));
254 mono_os_mutex_init (&mono_gc_lock);
255 mono_os_mutex_init_recursive (&handle_section);
257 mono_thread_info_attach (&dummy);
259 GC_set_on_collection_event (on_gc_notification);
260 GC_on_heap_resize = on_gc_heap_resize;
262 gc_initialized = TRUE;
265 void
266 mono_gc_base_cleanup (void)
268 GC_finalizer_notifier = NULL;
272 * mono_gc_collect:
273 * @generation: GC generation identifier
275 * Perform a garbage collection for the given generation, higher numbers
276 * mean usually older objects. Collecting a high-numbered generation
277 * implies collecting also the lower-numbered generations.
278 * The maximum value for @generation can be retrieved with a call to
279 * mono_gc_max_generation(), so this function is usually called as:
281 * mono_gc_collect (mono_gc_max_generation ());
283 void
284 mono_gc_collect (int generation)
286 #ifndef DISABLE_PERFCOUNTERS
287 mono_perfcounters->gc_induced++;
288 #endif
289 GC_gcollect ();
293 * mono_gc_max_generation:
295 * Get the maximum generation number used by the current garbage
296 * collector. The value will be 0 for the Boehm collector, 1 or more
297 * for the generational collectors.
299 * Returns: the maximum generation number.
302 mono_gc_max_generation (void)
304 return 0;
308 * mono_gc_get_generation:
309 * @object: a managed object
311 * Get the garbage collector's generation that @object belongs to.
312 * Use this has a hint only.
314 * Returns: a garbage collector generation number
317 mono_gc_get_generation (MonoObject *object)
319 return 0;
323 * mono_gc_collection_count:
324 * @generation: a GC generation number
326 * Get how many times a garbage collection has been performed
327 * for the given @generation number.
329 * Returns: the number of garbage collections
332 mono_gc_collection_count (int generation)
334 return GC_gc_no;
338 * mono_gc_add_memory_pressure:
339 * @value: amount of bytes
341 * Adjust the garbage collector's view of how many bytes of memory
342 * are indirectly referenced by managed objects (for example unmanaged
343 * memory holding image or other binary data).
344 * This is a hint only to the garbage collector algorithm.
345 * Note that negative amounts of @value will decrease the memory
346 * pressure.
348 void
349 mono_gc_add_memory_pressure (gint64 value)
354 * mono_gc_get_used_size:
356 * Get the approximate amount of memory used by managed objects.
358 * Returns: the amount of memory used in bytes
360 int64_t
361 mono_gc_get_used_size (void)
363 return GC_get_heap_size () - GC_get_free_bytes ();
367 * mono_gc_get_heap_size:
369 * Get the amount of memory used by the garbage collector.
371 * Returns: the size of the heap in bytes
373 int64_t
374 mono_gc_get_heap_size (void)
376 return GC_get_heap_size ();
379 gboolean
380 mono_gc_is_gc_thread (void)
382 return GC_thread_is_registered ();
385 gboolean
386 mono_gc_register_thread (void *baseptr)
388 return mono_thread_info_attach (baseptr) != NULL;
391 static void*
392 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
394 struct GC_stack_base sb;
395 int res;
397 /* TODO: use GC_get_stack_base instead of baseptr. */
398 sb.mem_base = baseptr;
399 res = GC_register_my_thread (&sb);
400 if (res == GC_UNIMPLEMENTED)
401 return NULL; /* Cannot happen with GC v7+. */
403 info->handle_stack = mono_handle_stack_alloc ();
405 return info;
408 static void
409 boehm_thread_unregister (MonoThreadInfo *p)
411 MonoNativeThreadId tid;
413 tid = mono_thread_info_get_tid (p);
415 if (p->runtime_thread)
416 mono_threads_add_joinable_thread ((gpointer)tid);
419 static void
420 boehm_thread_detach (MonoThreadInfo *p)
422 if (mono_thread_internal_current_is_attached ())
423 mono_thread_detach_internal (mono_thread_internal_current ());
426 gboolean
427 mono_object_is_alive (MonoObject* o)
429 return GC_is_marked ((ptr_t)o);
433 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
435 return 1;
438 static gint64 gc_start_time;
440 static void
441 on_gc_notification (GC_EventType event)
443 MonoGCEvent e = (MonoGCEvent)event;
445 switch (e) {
446 case MONO_GC_EVENT_PRE_STOP_WORLD:
447 MONO_GC_WORLD_STOP_BEGIN ();
448 break;
450 case MONO_GC_EVENT_POST_STOP_WORLD:
451 MONO_GC_WORLD_STOP_END ();
452 break;
454 case MONO_GC_EVENT_PRE_START_WORLD:
455 MONO_GC_WORLD_RESTART_BEGIN (1);
456 break;
458 case MONO_GC_EVENT_POST_START_WORLD:
459 MONO_GC_WORLD_RESTART_END (1);
460 break;
462 case MONO_GC_EVENT_START:
463 MONO_GC_BEGIN (1);
464 #ifndef DISABLE_PERFCOUNTERS
465 if (mono_perfcounters)
466 mono_perfcounters->gc_collections0++;
467 #endif
468 gc_stats.major_gc_count ++;
469 gc_start_time = mono_100ns_ticks ();
470 break;
472 case MONO_GC_EVENT_END:
473 MONO_GC_END (1);
474 #if defined(ENABLE_DTRACE) && defined(__sun__)
475 /* This works around a dtrace -G problem on Solaris.
476 Limit its actual use to when the probe is enabled. */
477 if (MONO_GC_END_ENABLED ())
478 sleep(0);
479 #endif
481 #ifndef DISABLE_PERFCOUNTERS
482 if (mono_perfcounters) {
483 guint64 heap_size = GC_get_heap_size ();
484 guint64 used_size = heap_size - GC_get_free_bytes ();
485 mono_perfcounters->gc_total_bytes = used_size;
486 mono_perfcounters->gc_committed_bytes = heap_size;
487 mono_perfcounters->gc_reserved_bytes = heap_size;
488 mono_perfcounters->gc_gen0size = heap_size;
490 #endif
491 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
492 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
493 break;
494 default:
495 break;
498 mono_profiler_gc_event (e, 0);
500 switch (e) {
501 case MONO_GC_EVENT_PRE_STOP_WORLD:
502 mono_thread_info_suspend_lock ();
503 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
504 break;
505 case MONO_GC_EVENT_POST_START_WORLD:
506 mono_thread_info_suspend_unlock ();
507 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
508 break;
509 default:
510 break;
515 static void
516 on_gc_heap_resize (size_t new_size)
518 guint64 heap_size = GC_get_heap_size ();
519 #ifndef DISABLE_PERFCOUNTERS
520 if (mono_perfcounters) {
521 mono_perfcounters->gc_committed_bytes = heap_size;
522 mono_perfcounters->gc_reserved_bytes = heap_size;
523 mono_perfcounters->gc_gen0size = heap_size;
525 #endif
526 mono_profiler_gc_heap_resize (new_size);
530 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
532 /* for some strange reason, they want one extra byte on the end */
533 GC_add_roots (start, start + size + 1);
535 return TRUE;
539 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
541 return mono_gc_register_root (start, size, descr, source, msg);
544 void
545 mono_gc_deregister_root (char* addr)
547 #ifndef HOST_WIN32
548 /* FIXME: libgc doesn't define this work win32 for some reason */
549 /* FIXME: No size info */
550 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
551 #endif
554 static void
555 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
557 /* libgc requires that we use HIDE_POINTER... */
558 *link_addr = (void*)HIDE_POINTER (obj);
559 if (track)
560 GC_REGISTER_LONG_LINK (link_addr, obj);
561 else
562 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
565 static void
566 mono_gc_weak_link_remove (void **link_addr, gboolean track)
568 if (track)
569 GC_unregister_long_link (link_addr);
570 else
571 GC_unregister_disappearing_link (link_addr);
572 *link_addr = NULL;
575 static gpointer
576 reveal_link (gpointer link_addr)
578 void **link_a = (void **)link_addr;
579 return REVEAL_POINTER (*link_a);
582 static MonoObject *
583 mono_gc_weak_link_get (void **link_addr)
585 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
586 if (obj == (MonoObject *) -1)
587 return NULL;
588 return obj;
591 void*
592 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
594 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
597 void*
598 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
600 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
603 void*
604 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
606 /* libgc has no usable support for arrays... */
607 return GC_NO_DESCRIPTOR;
610 void*
611 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
613 /* It seems there are issues when the bitmap doesn't fit: play it safe */
614 if (numbits >= 30)
615 return GC_NO_DESCRIPTOR;
616 else
617 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
620 void*
621 mono_gc_make_root_descr_all_refs (int numbits)
623 return NULL;
626 void*
627 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
629 return GC_MALLOC_UNCOLLECTABLE (size);
632 void
633 mono_gc_free_fixed (void* addr)
635 GC_FREE (addr);
638 void *
639 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
641 MonoObject *obj;
643 if (!vtable->klass->has_references) {
644 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
645 if (G_UNLIKELY (!obj))
646 return NULL;
648 obj->vtable = vtable;
649 obj->synchronisation = NULL;
651 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
652 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
653 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
654 if (G_UNLIKELY (!obj))
655 return NULL;
656 } else {
657 obj = (MonoObject *)GC_MALLOC (size);
658 if (G_UNLIKELY (!obj))
659 return NULL;
661 obj->vtable = vtable;
664 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
665 mono_profiler_allocation (obj);
667 return obj;
670 void *
671 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
673 MonoArray *obj;
675 if (!vtable->klass->has_references) {
676 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
677 if (G_UNLIKELY (!obj))
678 return NULL;
680 obj->obj.vtable = vtable;
681 obj->obj.synchronisation = NULL;
683 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
684 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
685 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
686 if (G_UNLIKELY (!obj))
687 return NULL;
688 } else {
689 obj = (MonoArray *)GC_MALLOC (size);
690 if (G_UNLIKELY (!obj))
691 return NULL;
693 obj->obj.vtable = vtable;
696 obj->max_length = max_length;
698 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
699 mono_profiler_allocation (&obj->obj);
701 return obj;
704 void *
705 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
707 MonoArray *obj;
709 if (!vtable->klass->has_references) {
710 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
711 if (G_UNLIKELY (!obj))
712 return NULL;
714 obj->obj.vtable = vtable;
715 obj->obj.synchronisation = NULL;
717 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
718 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
719 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
720 if (G_UNLIKELY (!obj))
721 return NULL;
722 } else {
723 obj = (MonoArray *)GC_MALLOC (size);
724 if (G_UNLIKELY (!obj))
725 return NULL;
727 obj->obj.vtable = vtable;
730 obj->max_length = max_length;
732 if (bounds_size)
733 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
735 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
736 mono_profiler_allocation (&obj->obj);
738 return obj;
741 void *
742 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
744 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
745 if (G_UNLIKELY (!obj))
746 return NULL;
748 obj->object.vtable = vtable;
749 obj->object.synchronisation = NULL;
750 obj->length = len;
751 obj->chars [len] = 0;
753 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
754 mono_profiler_allocation (&obj->object);
756 return obj;
759 void*
760 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
762 return mono_gc_alloc_obj (vtable, size);
765 void*
766 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
768 return mono_gc_alloc_obj (vtable, size);
772 mono_gc_invoke_finalizers (void)
774 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
775 * the 'mem_freed' variable is not initialized when there are no
776 * objects to finalize, which leads to strange behavior later on.
777 * The check is necessary to work around that bug.
779 if (GC_should_invoke_finalizers ())
780 return GC_invoke_finalizers ();
781 return 0;
784 MonoBoolean
785 mono_gc_pending_finalizers (void)
787 return GC_should_invoke_finalizers ();
790 void
791 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
793 *(void**)field_ptr = value;
796 void
797 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
799 *(void**)slot_ptr = value;
802 void
803 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
805 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
808 void
809 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
811 *(void**)ptr = value;
814 void
815 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
817 InterlockedWritePointer ((volatile gpointer *)ptr, value);
820 void
821 mono_gc_wbarrier_generic_nostore (gpointer ptr)
825 void
826 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
828 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
831 void
832 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
834 /* do not copy the sync state */
835 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
836 mono_object_class (obj)->instance_size - sizeof (MonoObject));
839 void
840 mono_gc_clear_domain (MonoDomain *domain)
844 void
845 mono_gc_suspend_finalizers (void)
850 mono_gc_get_suspend_signal (void)
852 return GC_get_suspend_signal ();
856 mono_gc_get_restart_signal (void)
858 return GC_get_thr_restart_signal ();
861 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
862 extern __thread void* GC_thread_tls;
863 #include "metadata-internals.h"
865 static int
866 shift_amount (int v)
868 int i = 0;
869 while (!(v & (1 << i)))
870 i++;
871 return i;
874 enum {
875 ATYPE_FREEPTR,
876 ATYPE_FREEPTR_FOR_BOX,
877 ATYPE_NORMAL,
878 ATYPE_GCJ,
879 ATYPE_STRING,
880 ATYPE_NUM
883 static MonoMethod*
884 create_allocator (int atype, int tls_key, gboolean slowpath)
886 int index_var, bytes_var, my_fl_var, my_entry_var;
887 guint32 no_freelist_branch, not_small_enough_branch = 0;
888 guint32 size_overflow_branch = 0;
889 MonoMethodBuilder *mb;
890 MonoMethod *res;
891 MonoMethodSignature *csig;
892 const char *name = NULL;
893 WrapperInfo *info;
895 g_assert_not_reached ();
897 if (atype == ATYPE_FREEPTR) {
898 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
899 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
900 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
901 } else if (atype == ATYPE_NORMAL) {
902 name = slowpath ? "SlowAlloc" : "Alloc";
903 } else if (atype == ATYPE_GCJ) {
904 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
905 } else if (atype == ATYPE_STRING) {
906 name = slowpath ? "SlowAllocString" : "AllocString";
907 } else {
908 g_assert_not_reached ();
911 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
913 if (atype == ATYPE_STRING) {
914 csig->ret = &mono_defaults.string_class->byval_arg;
915 csig->params [0] = &mono_defaults.int_class->byval_arg;
916 csig->params [1] = &mono_defaults.int32_class->byval_arg;
917 } else {
918 csig->ret = &mono_defaults.object_class->byval_arg;
919 csig->params [0] = &mono_defaults.int_class->byval_arg;
920 csig->params [1] = &mono_defaults.int32_class->byval_arg;
923 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
925 if (slowpath)
926 goto always_slowpath;
928 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
929 if (atype == ATYPE_STRING) {
930 /* a string alloator method takes the args: (vtable, len) */
931 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
932 mono_mb_emit_ldarg (mb, 1);
933 mono_mb_emit_icon (mb, 1);
934 mono_mb_emit_byte (mb, MONO_CEE_ADD);
935 mono_mb_emit_icon (mb, 1);
936 mono_mb_emit_byte (mb, MONO_CEE_SHL);
937 // sizeof (MonoString) might include padding
938 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
939 mono_mb_emit_byte (mb, MONO_CEE_ADD);
940 mono_mb_emit_stloc (mb, bytes_var);
941 } else {
942 mono_mb_emit_ldarg (mb, 1);
943 mono_mb_emit_stloc (mb, bytes_var);
946 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
947 if (atype == ATYPE_STRING) {
948 /* check for size */
949 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
950 mono_mb_emit_ldloc (mb, bytes_var);
951 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
952 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
953 /* check for overflow */
954 mono_mb_emit_ldloc (mb, bytes_var);
955 mono_mb_emit_icon (mb, sizeof (MonoString));
956 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
959 /* int index = INDEX_FROM_BYTES(bytes); */
960 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
962 mono_mb_emit_ldloc (mb, bytes_var);
963 mono_mb_emit_icon (mb, GRANULARITY - 1);
964 mono_mb_emit_byte (mb, MONO_CEE_ADD);
965 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
966 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
967 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
968 mono_mb_emit_byte (mb, MONO_CEE_SHL);
969 /* index var is already adjusted into bytes */
970 mono_mb_emit_stloc (mb, index_var);
972 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
973 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
974 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
975 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
976 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
977 mono_mb_emit_i4 (mb, tls_key);
978 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
979 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
980 + G_STRUCT_OFFSET (struct thread_local_freelists,
981 ptrfree_freelists));
982 else if (atype == ATYPE_NORMAL)
983 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
984 + G_STRUCT_OFFSET (struct thread_local_freelists,
985 normal_freelists));
986 else if (atype == ATYPE_GCJ)
987 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
988 + G_STRUCT_OFFSET (struct thread_local_freelists,
989 gcj_freelists));
990 else
991 g_assert_not_reached ();
992 mono_mb_emit_byte (mb, MONO_CEE_ADD);
993 mono_mb_emit_ldloc (mb, index_var);
994 mono_mb_emit_byte (mb, MONO_CEE_ADD);
995 mono_mb_emit_stloc (mb, my_fl_var);
997 /* my_entry = *my_fl; */
998 mono_mb_emit_ldloc (mb, my_fl_var);
999 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1000 mono_mb_emit_stloc (mb, my_entry_var);
1002 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1003 mono_mb_emit_ldloc (mb, my_entry_var);
1004 mono_mb_emit_icon (mb, HBLKSIZE);
1005 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1007 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1008 mono_mb_emit_ldloc (mb, my_fl_var);
1009 mono_mb_emit_ldloc (mb, my_entry_var);
1010 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1011 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1013 /* set the vtable and clear the words in the object */
1014 mono_mb_emit_ldloc (mb, my_entry_var);
1015 mono_mb_emit_ldarg (mb, 0);
1016 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1018 if (atype == ATYPE_FREEPTR) {
1019 int start_var, end_var, start_loop;
1020 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1022 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1023 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1024 mono_mb_emit_ldloc (mb, my_entry_var);
1025 mono_mb_emit_ldloc (mb, bytes_var);
1026 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1027 mono_mb_emit_stloc (mb, end_var);
1028 mono_mb_emit_ldloc (mb, my_entry_var);
1029 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1030 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1031 mono_mb_emit_stloc (mb, start_var);
1033 * do {
1034 * *start++ = NULL;
1035 * } while (start < end);
1037 start_loop = mono_mb_get_label (mb);
1038 mono_mb_emit_ldloc (mb, start_var);
1039 mono_mb_emit_icon (mb, 0);
1040 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1041 mono_mb_emit_ldloc (mb, start_var);
1042 mono_mb_emit_icon (mb, sizeof (gpointer));
1043 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1044 mono_mb_emit_stloc (mb, start_var);
1046 mono_mb_emit_ldloc (mb, start_var);
1047 mono_mb_emit_ldloc (mb, end_var);
1048 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1049 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1050 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1051 /* need to clear just the sync pointer */
1052 mono_mb_emit_ldloc (mb, my_entry_var);
1053 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1054 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1055 mono_mb_emit_icon (mb, 0);
1056 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1059 if (atype == ATYPE_STRING) {
1060 /* need to set length and clear the last char */
1061 /* s->length = len; */
1062 mono_mb_emit_ldloc (mb, my_entry_var);
1063 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1064 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1065 mono_mb_emit_ldarg (mb, 1);
1066 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1067 /* s->chars [len] = 0; */
1068 mono_mb_emit_ldloc (mb, my_entry_var);
1069 mono_mb_emit_ldloc (mb, bytes_var);
1070 mono_mb_emit_icon (mb, 2);
1071 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1072 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1073 mono_mb_emit_icon (mb, 0);
1074 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1077 /* return my_entry; */
1078 mono_mb_emit_ldloc (mb, my_entry_var);
1079 mono_mb_emit_byte (mb, MONO_CEE_RET);
1081 mono_mb_patch_short_branch (mb, no_freelist_branch);
1082 if (not_small_enough_branch > 0)
1083 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1084 if (size_overflow_branch > 0)
1085 mono_mb_patch_short_branch (mb, size_overflow_branch);
1087 /* the slow path: we just call back into the runtime */
1088 always_slowpath:
1089 if (atype == ATYPE_STRING) {
1090 mono_mb_emit_ldarg (mb, 1);
1091 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1092 } else {
1093 mono_mb_emit_ldarg (mb, 0);
1094 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1097 mono_mb_emit_byte (mb, MONO_CEE_RET);
1099 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1100 info->d.alloc.gc_name = "boehm";
1101 info->d.alloc.alloc_type = atype;
1102 mb->init_locals = FALSE;
1104 res = mono_mb_create (mb, csig, 8, info);
1105 mono_mb_free (mb);
1107 return res;
1110 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1111 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1113 gboolean
1114 mono_gc_is_critical_method (MonoMethod *method)
1116 int i;
1118 for (i = 0; i < ATYPE_NUM; ++i)
1119 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1120 return TRUE;
1122 return FALSE;
1126 * If possible, generate a managed method that can quickly allocate objects in class
1127 * @klass. The method will typically have an thread-local inline allocation sequence.
1128 * The signature of the called method is:
1129 * object allocate (MonoVTable *vtable)
1130 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1131 * keep in sync.
1132 * The thread local alloc logic is taken from libgc/pthread_support.c.
1135 MonoMethod*
1136 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1138 int atype;
1141 * Tls implementation changed, we jump to tls native getters/setters.
1142 * Is boehm managed allocator ok with this ? Do we even care ?
1144 return NULL;
1146 if (!SMALL_ENOUGH (klass->instance_size))
1147 return NULL;
1148 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1149 return NULL;
1150 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1151 return NULL;
1152 if (klass->rank)
1153 return NULL;
1154 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1155 return NULL;
1156 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1157 atype = ATYPE_STRING;
1158 } else if (!known_instance_size) {
1159 return NULL;
1160 } else if (!klass->has_references) {
1161 if (for_box)
1162 atype = ATYPE_FREEPTR_FOR_BOX;
1163 else
1164 atype = ATYPE_FREEPTR;
1165 } else {
1166 return NULL;
1168 * disabled because we currently do a runtime choice anyway, to
1169 * deal with multiple appdomains.
1170 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1171 atype = ATYPE_GCJ;
1172 else
1173 atype = ATYPE_NORMAL;
1176 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1179 MonoMethod*
1180 mono_gc_get_managed_array_allocator (MonoClass *klass)
1182 return NULL;
1186 * mono_gc_get_managed_allocator_by_type:
1188 * Return a managed allocator method corresponding to allocator type ATYPE.
1190 MonoMethod*
1191 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1193 MonoMethod *res;
1194 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1195 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1197 return NULL;
1199 res = cache [atype];
1200 if (res)
1201 return res;
1203 res = create_allocator (atype, -1, slowpath);
1204 mono_os_mutex_lock (&mono_gc_lock);
1205 if (cache [atype]) {
1206 mono_free_method (res);
1207 res = cache [atype];
1208 } else {
1209 mono_memory_barrier ();
1210 cache [atype] = res;
1212 mono_os_mutex_unlock (&mono_gc_lock);
1213 return res;
1216 guint32
1217 mono_gc_get_managed_allocator_types (void)
1219 return ATYPE_NUM;
1222 MonoMethod*
1223 mono_gc_get_write_barrier (void)
1225 g_assert_not_reached ();
1226 return NULL;
1229 #else
1231 gboolean
1232 mono_gc_is_critical_method (MonoMethod *method)
1234 return FALSE;
1237 MonoMethod*
1238 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1240 return NULL;
1243 MonoMethod*
1244 mono_gc_get_managed_array_allocator (MonoClass *klass)
1246 return NULL;
1249 MonoMethod*
1250 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1252 return NULL;
1255 guint32
1256 mono_gc_get_managed_allocator_types (void)
1258 return 0;
1261 MonoMethod*
1262 mono_gc_get_write_barrier (void)
1264 g_assert_not_reached ();
1265 return NULL;
1268 #endif
1270 MonoMethod*
1271 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1273 g_assert_not_reached ();
1274 return NULL;
1278 mono_gc_get_aligned_size_for_allocator (int size)
1280 return size;
1283 const char *
1284 mono_gc_get_gc_name (void)
1286 return "boehm";
1289 void*
1290 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1292 return GC_call_with_alloc_lock (func, data);
1295 char*
1296 mono_gc_get_description (void)
1298 return g_strdup (DEFAULT_GC_NAME);
1301 void
1302 mono_gc_set_desktop_mode (void)
1304 GC_dont_expand = 1;
1307 gboolean
1308 mono_gc_is_moving (void)
1310 return FALSE;
1313 gboolean
1314 mono_gc_is_disabled (void)
1316 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1317 return TRUE;
1318 else
1319 return FALSE;
1322 void
1323 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1325 g_assert_not_reached ();
1329 guint8*
1330 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1332 g_assert_not_reached ();
1333 return NULL;
1336 gboolean
1337 mono_gc_card_table_nursery_check (void)
1339 g_assert_not_reached ();
1340 return TRUE;
1343 void*
1344 mono_gc_get_nursery (int *shift_bits, size_t *size)
1346 return NULL;
1349 gboolean
1350 mono_gc_precise_stack_mark_enabled (void)
1352 return FALSE;
1355 FILE *
1356 mono_gc_get_logfile (void)
1358 return NULL;
1361 void
1362 mono_gc_params_set (const char* options)
1366 void
1367 mono_gc_debug_set (const char* options)
1371 void
1372 mono_gc_conservatively_scan_area (void *start, void *end)
1374 g_assert_not_reached ();
1377 void *
1378 mono_gc_scan_object (void *obj, void *gc_data)
1380 g_assert_not_reached ();
1381 return NULL;
1384 gsize*
1385 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1387 g_assert_not_reached ();
1388 return NULL;
1391 void
1392 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1396 void
1397 mono_gc_set_stack_end (void *stack_end)
1401 void mono_gc_set_skip_thread (gboolean value)
1405 void
1406 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1408 guint offset = 0;
1410 #ifndef GC_DEBUG
1411 /* This assertion is not valid when GC_DEBUG is defined */
1412 g_assert (GC_base (obj) == (char*)obj - offset);
1413 #endif
1415 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1418 #ifndef HOST_WIN32
1420 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1422 /* it is being replaced by GC_pthread_create on some
1423 * platforms, see libgc/include/gc_pthread_redirects.h */
1424 return pthread_create (new_thread, attr, start_routine, arg);
1426 #endif
1428 #ifdef HOST_WIN32
1429 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1431 return GC_DllMain (module_handle, reason, reserved);
1433 #endif
1435 guint
1436 mono_gc_get_vtable_bits (MonoClass *klass)
1438 if (fin_callbacks.is_class_finalization_aware) {
1439 if (fin_callbacks.is_class_finalization_aware (klass))
1440 return BOEHM_GC_BIT_FINALIZER_AWARE;
1442 return 0;
1446 * mono_gc_register_altstack:
1448 * Register the dimensions of the normal stack and altstack with the collector.
1449 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1451 void
1452 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1454 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1458 mono_gc_get_los_limit (void)
1460 return G_MAXINT;
1463 void
1464 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1466 mono_unichar2 *new_end = str->chars + new_length;
1468 /* zero the discarded string. This null-delimits the string and allows
1469 * the space to be reclaimed by SGen. */
1471 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1472 str->length = new_length;
1475 gboolean
1476 mono_gc_user_markers_supported (void)
1478 return FALSE;
1481 void *
1482 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1484 g_assert_not_reached ();
1485 return NULL;
1488 /* Toggleref support */
1490 void
1491 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1493 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1494 g_error ("GC_toggleref_add failed\n");
1497 void
1498 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1500 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1503 /* Test support code */
1505 static MonoToggleRefStatus
1506 test_toggleref_callback (MonoObject *obj)
1508 static MonoClassField *mono_toggleref_test_field;
1509 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1511 if (!mono_toggleref_test_field) {
1512 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1513 g_assert (mono_toggleref_test_field);
1516 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1517 printf ("toggleref-cb obj %d\n", status);
1518 return status;
1521 static void
1522 register_test_toggleref_callback (void)
1524 mono_gc_toggleref_register_callback (test_toggleref_callback);
1527 static gboolean
1528 is_finalization_aware (MonoObject *obj)
1530 MonoVTable *vt = obj->vtable;
1531 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1534 static void
1535 fin_notifier (MonoObject *obj)
1537 if (is_finalization_aware (obj))
1538 fin_callbacks.object_queued_for_finalization (obj);
1541 void
1542 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1544 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1545 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1547 fin_callbacks = *callbacks;
1549 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1552 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1554 static inline gboolean
1555 slot_occupied (HandleData *handles, guint slot) {
1556 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1559 static inline void
1560 vacate_slot (HandleData *handles, guint slot) {
1561 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1564 static inline void
1565 occupy_slot (HandleData *handles, guint slot) {
1566 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1569 static int
1570 find_first_unset (guint32 bitmap)
1572 int i;
1573 for (i = 0; i < 32; ++i) {
1574 if (!(bitmap & (1 << i)))
1575 return i;
1577 return -1;
1580 static void
1581 handle_data_alloc_entries (HandleData *handles)
1583 handles->size = 32;
1584 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1585 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1586 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1587 } else {
1588 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1590 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1593 static gint
1594 handle_data_next_unset (HandleData *handles)
1596 gint slot;
1597 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1598 if (handles->bitmap [slot] == 0xffffffff)
1599 continue;
1600 handles->slot_hint = slot;
1601 return find_first_unset (handles->bitmap [slot]);
1603 return -1;
1606 static gint
1607 handle_data_first_unset (HandleData *handles)
1609 gint slot;
1610 for (slot = 0; slot < handles->slot_hint; ++slot) {
1611 if (handles->bitmap [slot] == 0xffffffff)
1612 continue;
1613 handles->slot_hint = slot;
1614 return find_first_unset (handles->bitmap [slot]);
1616 return -1;
1619 /* Returns the index of the current slot in the bitmap. */
1620 static void
1621 handle_data_grow (HandleData *handles, gboolean track)
1623 guint32 *new_bitmap;
1624 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1626 /* resize and copy the bitmap */
1627 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1628 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1629 g_free (handles->bitmap);
1630 handles->bitmap = new_bitmap;
1632 /* resize and copy the entries */
1633 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1634 gpointer *entries;
1635 guint16 *domain_ids;
1636 gint i;
1637 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1638 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1639 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1640 for (i = 0; i < handles->size; ++i) {
1641 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1642 if (obj) {
1643 mono_gc_weak_link_add (&(entries [i]), obj, track);
1644 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1645 } else {
1646 g_assert (!handles->entries [i]);
1649 g_free (handles->entries);
1650 g_free (handles->domain_ids);
1651 handles->entries = entries;
1652 handles->domain_ids = domain_ids;
1653 } else {
1654 gpointer *entries;
1655 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1656 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1657 mono_gc_free_fixed (handles->entries);
1658 handles->entries = entries;
1660 handles->slot_hint = handles->size / BITMAP_SIZE;
1661 handles->size = new_size;
1664 static guint32
1665 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1667 gint slot, i;
1668 guint32 res;
1669 lock_handles (handles);
1670 if (!handles->size)
1671 handle_data_alloc_entries (handles);
1672 i = handle_data_next_unset (handles);
1673 if (i == -1 && handles->slot_hint != 0)
1674 i = handle_data_first_unset (handles);
1675 if (i == -1) {
1676 handle_data_grow (handles, track);
1677 i = 0;
1679 slot = handles->slot_hint * BITMAP_SIZE + i;
1680 occupy_slot (handles, slot);
1681 handles->entries [slot] = NULL;
1682 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1683 /*FIXME, what to use when obj == null?*/
1684 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1685 if (obj)
1686 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1687 } else {
1688 handles->entries [slot] = obj;
1691 #ifndef DISABLE_PERFCOUNTERS
1692 mono_perfcounters->gc_num_handles++;
1693 #endif
1694 unlock_handles (handles);
1695 res = MONO_GC_HANDLE (slot, handles->type);
1696 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1697 return res;
1701 * mono_gchandle_new:
1702 * @obj: managed object to get a handle for
1703 * @pinned: whether the object should be pinned
1705 * This returns a handle that wraps the object, this is used to keep a
1706 * reference to a managed object from the unmanaged world and preventing the
1707 * object from being disposed.
1709 * If @pinned is false the address of the object can not be obtained, if it is
1710 * true the address of the object can be obtained. This will also pin the
1711 * object so it will not be possible by a moving garbage collector to move the
1712 * object.
1714 * Returns: a handle that can be used to access the object from
1715 * unmanaged code.
1717 guint32
1718 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1720 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1724 * mono_gchandle_new_weakref:
1725 * @obj: managed object to get a handle for
1726 * @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.
1728 * This returns a weak handle that wraps the object, this is used to
1729 * keep a reference to a managed object from the unmanaged world.
1730 * Unlike the mono_gchandle_new the object can be reclaimed by the
1731 * garbage collector. In this case the value of the GCHandle will be
1732 * set to zero.
1734 * If @track_resurrection is TRUE the object will be tracked through
1735 * finalization and if the object is resurrected during the execution
1736 * of the finalizer, then the returned weakref will continue to hold
1737 * a reference to the object. If @track_resurrection is FALSE, then
1738 * the weak reference's target will become NULL as soon as the object
1739 * is passed on to the finalizer.
1741 * Returns: a handle that can be used to access the object from
1742 * unmanaged code.
1744 guint32
1745 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1747 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1751 * mono_gchandle_get_target:
1752 * @gchandle: a GCHandle's handle.
1754 * The handle was previously created by calling `mono_gchandle_new` or
1755 * `mono_gchandle_new_weakref`.
1757 * Returns: A pointer to the `MonoObject*` represented by the handle or
1758 * NULL for a collected object if using a weakref handle.
1760 MonoObject*
1761 mono_gchandle_get_target (guint32 gchandle)
1763 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1764 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1765 HandleData *handles = &gc_handles [type];
1766 MonoObject *obj = NULL;
1767 if (type >= HANDLE_TYPE_MAX)
1768 return NULL;
1770 lock_handles (handles);
1771 if (slot < handles->size && slot_occupied (handles, slot)) {
1772 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1773 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1774 } else {
1775 obj = (MonoObject *)handles->entries [slot];
1777 } else {
1778 /* print a warning? */
1780 unlock_handles (handles);
1781 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1782 return obj;
1785 void
1786 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1788 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1789 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1790 HandleData *handles = &gc_handles [type];
1791 MonoObject *old_obj = NULL;
1793 g_assert (type < HANDLE_TYPE_MAX);
1794 lock_handles (handles);
1795 if (slot < handles->size && slot_occupied (handles, slot)) {
1796 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1797 old_obj = (MonoObject *)handles->entries [slot];
1798 if (handles->entries [slot])
1799 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1800 if (obj)
1801 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1802 /*FIXME, what to use when obj == null?*/
1803 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1804 } else {
1805 handles->entries [slot] = obj;
1807 } else {
1808 /* print a warning? */
1810 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1811 unlock_handles (handles);
1814 gboolean
1815 mono_gc_is_null (void)
1817 return FALSE;
1821 * mono_gchandle_is_in_domain:
1822 * @gchandle: a GCHandle's handle.
1823 * @domain: An application domain.
1825 * Use this function to determine if the @gchandle points to an
1826 * object allocated in the specified @domain.
1828 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1830 gboolean
1831 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1833 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1834 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1835 HandleData *handles = &gc_handles [type];
1836 gboolean result = FALSE;
1838 if (type >= HANDLE_TYPE_MAX)
1839 return FALSE;
1841 lock_handles (handles);
1842 if (slot < handles->size && slot_occupied (handles, slot)) {
1843 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1844 result = domain->domain_id == handles->domain_ids [slot];
1845 } else {
1846 MonoObject *obj;
1847 obj = (MonoObject *)handles->entries [slot];
1848 if (obj == NULL)
1849 result = TRUE;
1850 else
1851 result = domain == mono_object_domain (obj);
1853 } else {
1854 /* print a warning? */
1856 unlock_handles (handles);
1857 return result;
1861 * mono_gchandle_free:
1862 * @gchandle: a GCHandle's handle.
1864 * Frees the @gchandle handle. If there are no outstanding
1865 * references, the garbage collector can reclaim the memory of the
1866 * object wrapped.
1868 void
1869 mono_gchandle_free (guint32 gchandle)
1871 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1872 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1873 HandleData *handles = &gc_handles [type];
1874 if (type >= HANDLE_TYPE_MAX)
1875 return;
1877 lock_handles (handles);
1878 if (slot < handles->size && slot_occupied (handles, slot)) {
1879 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1880 if (handles->entries [slot])
1881 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1882 } else {
1883 handles->entries [slot] = NULL;
1885 vacate_slot (handles, slot);
1886 } else {
1887 /* print a warning? */
1889 #ifndef DISABLE_PERFCOUNTERS
1890 mono_perfcounters->gc_num_handles--;
1891 #endif
1892 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1893 unlock_handles (handles);
1894 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1898 * mono_gchandle_free_domain:
1899 * @domain: domain that is unloading
1901 * Function used internally to cleanup any GC handle for objects belonging
1902 * to the specified domain during appdomain unload.
1904 void
1905 mono_gchandle_free_domain (MonoDomain *domain)
1907 guint type;
1909 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1910 guint slot;
1911 HandleData *handles = &gc_handles [type];
1912 lock_handles (handles);
1913 for (slot = 0; slot < handles->size; ++slot) {
1914 if (!slot_occupied (handles, slot))
1915 continue;
1916 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1917 if (domain->domain_id == handles->domain_ids [slot]) {
1918 vacate_slot (handles, slot);
1919 if (handles->entries [slot])
1920 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1922 } else {
1923 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1924 vacate_slot (handles, slot);
1925 handles->entries [slot] = NULL;
1929 unlock_handles (handles);
1933 #else
1935 MONO_EMPTY_SOURCE_FILE (boehm_gc);
1936 #endif /* no Boehm GC */