#define GC_I_HIDE_POINTERS
#include <mono/metadata/gc-internal.h>
#include <mono/metadata/mono-gc.h>
-#include <mono/metadata/gc-internal.h>
#include <mono/metadata/profiler-private.h>
#include <mono/metadata/class-internals.h>
#include <mono/metadata/method-builder.h>
#include <mono/metadata/metadata-internals.h>
#include <mono/metadata/marshal.h>
#include <mono/metadata/runtime.h>
+#include <mono/metadata/sgen-toggleref.h>
+#include <mono/utils/atomic.h>
#include <mono/utils/mono-logger-internal.h>
+#include <mono/utils/mono-memory-model.h>
#include <mono/utils/mono-time.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/dtrace.h>
#include <mono/utils/gc_wrapper.h>
+#include <mono/utils/mono-mutex.h>
#if HAVE_BOEHM_GC
#define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
static gboolean gc_initialized = FALSE;
+static mono_mutex_t mono_gc_lock;
static void*
boehm_thread_register (MonoThreadInfo* info, void *baseptr);
+static void
+boehm_thread_unregister (MonoThreadInfo *p);
+static void
+register_test_toggleref_callback (void);
+
+#define BOEHM_GC_BIT_FINALIZER_AWARE 1
+static MonoGCFinalizerCallbacks fin_callbacks;
static void
mono_gc_warning (char *msg, GC_word arg)
mono_gc_base_init (void)
{
MonoThreadInfoCallbacks cb;
- char *env;
+ const char *env;
+ int dummy;
if (gc_initialized)
return;
* we used to do this only when running on valgrind,
* but it happens also in other setups.
*/
-#if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
+#if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
{
size_t size;
void *sstart;
GC_allow_register_threads();
#endif
- if ((env = getenv ("MONO_GC_PARAMS"))) {
+ if ((env = g_getenv ("MONO_GC_PARAMS"))) {
char **ptr, **opts = g_strsplit (env, ",", -1);
for (ptr = opts; *ptr; ++ptr) {
char *opt = *ptr;
if (g_str_has_prefix (opt, "max-heap-size=")) {
- glong max_heap;
+ size_t max_heap;
opt = strchr (opt, '=') + 1;
if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
exit (1);
}
continue;
+ } else if (g_str_has_prefix (opt, "toggleref-test")) {
+ register_test_toggleref_callback ();
+ continue;
} else {
/* Could be a parameter for sgen */
/*
memset (&cb, 0, sizeof (cb));
cb.thread_register = boehm_thread_register;
+ cb.thread_unregister = boehm_thread_unregister;
cb.mono_method_is_critical = (gpointer)mono_runtime_is_critical_method;
#ifndef HOST_WIN32
+ cb.thread_exit = mono_gc_pthread_exit;
cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
#endif
mono_threads_init (&cb, sizeof (MonoThreadInfo));
+ mono_mutex_init (&mono_gc_lock);
+
+ mono_thread_info_attach (&dummy);
mono_gc_enable_events ();
gc_initialized = TRUE;
#endif
}
+static void
+boehm_thread_unregister (MonoThreadInfo *p)
+{
+ MonoNativeThreadId tid;
+
+ tid = mono_thread_info_get_tid (p);
+
+ if (p->runtime_thread)
+ mono_threads_add_joinable_thread ((gpointer)tid);
+}
+
gboolean
mono_object_is_alive (MonoObject* o)
{
mono_perfcounters->gc_gen0size = heap_size;
}
#endif
- gc_stats.major_gc_time_usecs += (mono_100ns_ticks () - gc_start_time) / 10;
+ gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
break;
}
/*
static int count;
count ++;
- if (count == atoi (getenv ("COUNT2")))
+ if (count == atoi (g_getenv ("COUNT2")))
printf ("HIT!\n");
- if (count > atoi (getenv ("COUNT2")))
+ if (count > atoi (g_getenv ("COUNT2")))
return GC_MALLOC (size);
*/
void
mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
{
- mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
+ mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
}
void
*(void**)ptr = value;
}
+void
+mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
+{
+ InterlockedWritePointer (ptr, value);
+}
+
void
mono_gc_wbarrier_generic_nostore (gpointer ptr)
{
void
mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
{
- mono_gc_memmove (dest, src, count * mono_class_value_size (klass, NULL));
+ mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
}
void
mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
{
/* do not copy the sync state */
- mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
+ mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
mono_object_class (obj)->instance_size - sizeof (MonoObject));
}
#endif
}
+int
+mono_gc_get_restart_signal (void)
+{
+#ifdef USE_INCLUDED_GC
+ return GC_get_restart_signal ();
+#else
+ return -1;
+#endif
+}
+
#if defined(USE_INCLUDED_LIBGC) && defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
extern __thread MONO_TLS_FAST void* GC_thread_tls;
#include "metadata-internals.h"
};
static MonoMethod*
-create_allocator (int atype, int offset)
+create_allocator (int atype, int tls_key)
{
int index_var, bytes_var, my_fl_var, my_entry_var;
guint32 no_freelist_branch, not_small_enough_branch = 0;
/* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
- mono_mb_emit_i4 (mb, offset);
+ mono_mb_emit_i4 (mb, tls_key);
if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
else if (atype == ATYPE_NORMAL)
*/
MonoMethod*
-mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
+mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box)
{
int offset = -1;
int atype;
- MonoClass *klass = vtable->klass;
MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
/*g_print ("thread tls: %d\n", offset);*/
return NULL;
if (!SMALL_ENOUGH (klass->instance_size))
return NULL;
- if (mono_class_has_finalizer (klass) || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
+ if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
return NULL;
if (klass->rank)
return NULL;
+ if (mono_class_is_open_constructed_type (&klass->byval_arg))
+ return NULL;
if (klass->byval_arg.type == MONO_TYPE_STRING) {
atype = ATYPE_STRING;
} else if (!klass->has_references) {
}
MonoMethod*
-mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
+mono_gc_get_managed_array_allocator (MonoClass *klass)
{
return NULL;
}
MonoMethod *res;
MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
- mono_loader_lock ();
+ mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
+
res = alloc_method_cache [atype];
- if (!res)
- res = alloc_method_cache [atype] = create_allocator (atype, offset);
- mono_loader_unlock ();
+ if (res)
+ return res;
+
+ res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD);
+ mono_mutex_lock (&mono_gc_lock);
+ if (alloc_method_cache [atype]) {
+ mono_free_method (res);
+ res = alloc_method_cache [atype];
+ } else {
+ mono_memory_barrier ();
+ alloc_method_cache [atype] = res;
+ }
+ mono_mutex_unlock (&mono_gc_lock);
return res;
}
}
MonoMethod*
-mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
+mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box)
{
return NULL;
}
MonoMethod*
-mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
+mono_gc_get_managed_array_allocator (MonoClass *klass)
{
return NULL;
}
}
void *
-mono_gc_scan_object (void *obj)
+mono_gc_scan_object (void *obj, void *gc_data)
{
g_assert_not_reached ();
return NULL;
{
}
+void
+mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
+{
+ guint offset = 0;
+
+#ifndef GC_DEBUG
+ /* This assertion is not valid when GC_DEBUG is defined */
+ g_assert (GC_base (obj) == (char*)obj - offset);
+#endif
+
+ GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, user_data, GUINT_TO_POINTER (offset), NULL, NULL);
+}
+
/*
* These will call the redefined versions in libgc.
*/
mono_gc_pthread_exit (void *retval)
{
pthread_exit (retval);
+ g_assert_not_reached ();
}
#endif
guint
mono_gc_get_vtable_bits (MonoClass *class)
{
+ if (fin_callbacks.is_class_finalization_aware) {
+ if (fin_callbacks.is_class_finalization_aware (class))
+ return BOEHM_GC_BIT_FINALIZER_AWARE;
+ }
return 0;
}
{
return flag;
}
+/* Toggleref support */
+
+void
+mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
+{
+ GC_toggleref_add ((GC_PTR)object, (int)strong_ref);
+}
+
+void
+mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
+{
+ GC_toggleref_register_callback ((int (*) (GC_PTR obj)) proccess_toggleref);
+}
+
+/* Test support code */
+
+static MonoToggleRefStatus
+test_toggleref_callback (MonoObject *obj)
+{
+ static MonoClassField *mono_toggleref_test_field;
+ int status = MONO_TOGGLE_REF_DROP;
+
+ if (!mono_toggleref_test_field) {
+ mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
+ g_assert (mono_toggleref_test_field);
+ }
+
+ mono_field_get_value (obj, mono_toggleref_test_field, &status);
+ printf ("toggleref-cb obj %d\n", status);
+ return status;
+}
+
+static void
+register_test_toggleref_callback (void)
+{
+ mono_gc_toggleref_register_callback (test_toggleref_callback);
+}
+
+static gboolean
+is_finalization_aware (MonoObject *obj)
+{
+ MonoVTable *vt = obj->vtable;
+ return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
+}
+
+static void
+fin_notifier (MonoObject *obj)
+{
+ if (is_finalization_aware (obj))
+ fin_callbacks.object_queued_for_finalization (obj);
+}
+
+void
+mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
+{
+ if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
+ g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
+
+ fin_callbacks = *callbacks;
+
+ GC_set_finalizer_notify_proc ((void (*) (GC_PTR))fin_notifier);
+}
#endif /* no Boehm GC */