Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed
[mono.git] / mono / metadata / object.c
index b1264232746cc1052014c0c5ed8aa08f11cd380c..52aefb176930d89ada4cb3e3b451f133f5cf4ad5 100644 (file)
 #include <mono/metadata/assembly.h>
 #include <mono/metadata/marshal.h>
 #include "mono/metadata/debug-helpers.h"
-#include "mono/metadata/marshal.h"
 #include <mono/metadata/threads.h>
 #include <mono/metadata/threads-types.h>
 #include <mono/metadata/environment.h>
 #include "mono/metadata/profiler-private.h"
 #include "mono/metadata/security-manager.h"
 #include "mono/metadata/mono-debug-debugger.h"
-#include <mono/metadata/gc-internals.h>
 #include <mono/metadata/verify-internals.h>
 #include <mono/metadata/reflection-internals.h>
 #include <mono/metadata/w32event.h>
@@ -50,7 +48,7 @@
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/mono-threads-coop.h>
 #include "cominterop.h"
-#include <mono/io-layer/io-layer.h>
+#include <mono/utils/w32api.h>
 
 static void
 get_default_field_value (MonoDomain* domain, MonoClassField *field, void *value, MonoError *error);
@@ -64,15 +62,18 @@ free_main_args (void);
 static char *
 mono_string_to_utf8_internal (MonoMemPool *mp, MonoImage *image, MonoString *s, gboolean ignore_error, MonoError *error);
 
+static void
+array_full_copy_unchecked_size (MonoArray *src, MonoArray *dest, MonoClass *klass, uintptr_t size);
+
 static MonoMethod*
 class_get_virtual_method (MonoClass *klass, MonoMethod *method, gboolean is_proxy, MonoError *error);
 
 /* Class lazy loading functions */
-static GENERATE_GET_CLASS_WITH_CACHE (pointer, System.Reflection, Pointer)
-static GENERATE_GET_CLASS_WITH_CACHE (remoting_services, System.Runtime.Remoting, RemotingServices)
-static GENERATE_GET_CLASS_WITH_CACHE (unhandled_exception_event_args, System, UnhandledExceptionEventArgs)
-static GENERATE_GET_CLASS_WITH_CACHE (sta_thread_attribute, System, STAThreadAttribute)
-static GENERATE_GET_CLASS_WITH_CACHE (activation_services, System.Runtime.Remoting.Activation, ActivationServices)
+static GENERATE_GET_CLASS_WITH_CACHE (pointer, "System.Reflection", "Pointer")
+static GENERATE_GET_CLASS_WITH_CACHE (remoting_services, "System.Runtime.Remoting", "RemotingServices")
+static GENERATE_GET_CLASS_WITH_CACHE (unhandled_exception_event_args, "System", "UnhandledExceptionEventArgs")
+static GENERATE_GET_CLASS_WITH_CACHE (sta_thread_attribute, "System", "STAThreadAttribute")
+static GENERATE_GET_CLASS_WITH_CACHE (activation_services, "System.Runtime.Remoting.Activation", "ActivationServices")
 
 
 #define ldstr_lock() mono_os_mutex_lock (&ldstr_section)
@@ -152,7 +153,9 @@ typedef struct
        MonoNativeThreadId initializing_tid;
        guint32 waiting_count;
        gboolean done;
-       MonoCoopMutex initialization_section;
+       MonoCoopMutex mutex;
+       /* condvar used to wait for 'done' becoming TRUE */
+       MonoCoopCond cond;
 } TypeInitializationLock;
 
 /* for locking access to type_initialization_hash and blocked_thread_hash */
@@ -176,13 +179,13 @@ mono_type_init_lock (TypeInitializationLock *lock)
 {
        MONO_REQ_GC_NEUTRAL_MODE;
 
-       mono_coop_mutex_lock (&lock->initialization_section);
+       mono_coop_mutex_lock (&lock->mutex);
 }
 
 static void
 mono_type_init_unlock (TypeInitializationLock *lock)
 {
-       mono_coop_mutex_unlock (&lock->initialization_section);
+       mono_coop_mutex_unlock (&lock->mutex);
 }
 
 /* from vtable to lock */
@@ -314,6 +317,24 @@ mono_runtime_class_init (MonoVTable *vtable)
        mono_error_assert_ok (&error);
 }
 
+/*
+ * Returns TRUE if the lock was freed.
+ * LOCKING: Caller should hold type_initialization_lock.
+ */
+static gboolean
+unref_type_lock (TypeInitializationLock *lock)
+{
+       --lock->waiting_count;
+       if (lock->waiting_count == 0) {
+               mono_coop_mutex_destroy (&lock->mutex);
+               mono_coop_cond_destroy (&lock->cond);
+               g_free (lock);
+               return TRUE;
+       } else {
+               return FALSE;
+       }
+}
+
 /**
  * mono_runtime_class_init_full:
  * @vtable that neeeds to be initialized
@@ -370,6 +391,13 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
 
        tid = mono_native_thread_id_get ();
 
+       /*
+        * Due some preprocessing inside a global lock. If we are the first thread
+        * trying to initialize this class, create a separate lock+cond var, and
+        * acquire it before leaving the global lock. The other threads will wait
+        * on this cond var.
+        */
+
        mono_type_initialization_lock ();
        /* double check... */
        if (vtable->initialized) {
@@ -396,8 +424,9 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                                return FALSE;
                        }
                }
-               lock = (TypeInitializationLock *)g_malloc (sizeof (TypeInitializationLock));
-               mono_coop_mutex_init_recursive (&lock->initialization_section);
+               lock = (TypeInitializationLock *)g_malloc0 (sizeof (TypeInitializationLock));
+               mono_coop_mutex_init_recursive (&lock->mutex);
+               mono_coop_cond_init (&lock->cond);
                lock->initializing_tid = tid;
                lock->waiting_count = 1;
                lock->done = FALSE;
@@ -410,7 +439,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                gpointer blocked;
                TypeInitializationLock *pending_lock;
 
-               if (mono_native_thread_id_equals (lock->initializing_tid, tid) || lock->done) {
+               if (mono_native_thread_id_equals (lock->initializing_tid, tid)) {
                        mono_type_initialization_unlock ();
                        return TRUE;
                }
@@ -439,9 +468,11 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
        if (do_initialization) {
                MonoException *exc = NULL;
 
+               /* We are holding the per-vtable lock, do the actual initialization */
+
                mono_threads_begin_abort_protected_block ();
                mono_runtime_try_invoke (method, NULL, NULL, (MonoObject**) &exc, error);
-               mono_threads_end_abort_protected_block ();
+               gboolean got_pending_interrupt = mono_threads_end_abort_protected_block ();
 
                //exception extracted, error will be set to the right value later
                if (exc == NULL && !mono_error_ok (error))//invoking failed but exc was not set
@@ -482,30 +513,36 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
 
                if (last_domain)
                        mono_domain_set (last_domain, TRUE);
+               /* Signal to the other threads that we are done */
                lock->done = TRUE;
+               mono_coop_cond_broadcast (&lock->cond);
+
                mono_type_init_unlock (lock);
+
+               //This can happen if the cctor self-aborts
                if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class)
                        pending_tae = exc;
+
                //TAEs are blocked around .cctors, they must escape as soon as no cctor is left to run.
-               if (!pending_tae && mono_get_eh_callbacks ()->mono_above_abort_threshold ())
+               if (!pending_tae && got_pending_interrupt)
                        pending_tae = mono_thread_try_resume_interruption ();
        } else {
                /* this just blocks until the initializing thread is done */
                mono_type_init_lock (lock);
+               while (!lock->done)
+                       mono_coop_cond_wait (&lock->cond, &lock->mutex);
                mono_type_init_unlock (lock);
        }
 
+       /* Do cleanup and setting vtable->initialized inside the global lock again */
        mono_type_initialization_lock ();
-       if (!mono_native_thread_id_equals (lock->initializing_tid, tid))
+       if (!do_initialization)
                g_hash_table_remove (blocked_thread_hash, GUINT_TO_POINTER (tid));
-       --lock->waiting_count;
-       if (lock->waiting_count == 0) {
-               mono_coop_mutex_destroy (&lock->initialization_section);
+       gboolean deleted = unref_type_lock (lock);
+       if (deleted)
                g_hash_table_remove (type_initialization_hash, vtable);
-               g_free (lock);
-       }
-       mono_memory_barrier ();
-       if (!vtable->init_failed)
+       /* Have to set this here since we check it inside the global lock */
+       if (do_initialization && !vtable->init_failed)
                vtable->initialized = 1;
        mono_type_initialization_unlock ();
 
@@ -536,13 +573,11 @@ gboolean release_type_locks (gpointer key, gpointer value, gpointer user)
                 * and get_type_init_exception_for_class () needs to be aware of this.
                 */
                vtable->init_failed = 1;
+               mono_coop_cond_broadcast (&lock->cond);
                mono_type_init_unlock (lock);
-               --lock->waiting_count;
-               if (lock->waiting_count == 0) {
-                       mono_coop_mutex_destroy (&lock->initialization_section);
-                       g_free (lock);
+               gboolean deleted = unref_type_lock (lock);
+               if (deleted)
                        return TRUE;
-               }
        }
        return FALSE;
 }
@@ -5562,6 +5597,13 @@ mono_array_full_copy (MonoArray *src, MonoArray *dest)
        size = mono_array_length (src);
        g_assert (size == mono_array_length (dest));
        size *= mono_array_element_size (klass);
+
+       array_full_copy_unchecked_size (src, dest, klass, size);
+}
+
+static void
+array_full_copy_unchecked_size (MonoArray *src, MonoArray *dest, MonoClass *klass, uintptr_t size)
+{
 #ifdef HAVE_SGEN_GC
        if (klass->element_class->valuetype) {
                if (klass->element_class->has_references)
@@ -5585,62 +5627,51 @@ mono_array_full_copy (MonoArray *src, MonoArray *dest)
  * This routine returns a copy of the array that is hosted on the
  * specified MonoDomain.  On failure returns NULL and sets @error.
  */
-MonoArray*
-mono_array_clone_in_domain (MonoDomain *domain, MonoArray *array, MonoError *error)
+MonoArrayHandle
+mono_array_clone_in_domain (MonoDomain *domain, MonoArrayHandle array_handle, MonoError *error)
 {
        MONO_REQ_GC_UNSAFE_MODE;
 
-       MonoArray *o;
-       uintptr_t size, i;
-       uintptr_t *sizes;
-       MonoClass *klass = array->obj.vtable->klass;
+       MonoArrayHandle result = MONO_HANDLE_NEW (MonoArray, NULL);
+       uintptr_t size = 0;
+       MonoClass *klass = mono_handle_class (array_handle);
 
        mono_error_init (error);
 
-       if (array->bounds == NULL) {
-               size = mono_array_length (array);
-               o = mono_array_new_full_checked (domain, klass, &size, NULL, error);
-               return_val_if_nok (error, NULL);
-
-               size *= mono_array_element_size (klass);
-#ifdef HAVE_SGEN_GC
-               if (klass->element_class->valuetype) {
-                       if (klass->element_class->has_references)
-                               mono_value_copy_array (o, 0, mono_array_addr_with_size_fast (array, 0, 0), mono_array_length (array));
-                       else
-                               mono_gc_memmove_atomic (&o->vector, &array->vector, size);
-               } else {
-                       mono_array_memcpy_refs (o, 0, array, 0, mono_array_length (array));
-               }
-#else
-               mono_gc_memmove_atomic (&o->vector, &array->vector, size);
-#endif
-               return o;
-       }
+       /* Pin source array here - if bounds is non-NULL, it's a pointer into the object data */
+       uint32_t src_handle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, array_handle), TRUE);
        
-       sizes = (uintptr_t *)alloca (klass->rank * sizeof(intptr_t) * 2);
-       size = mono_array_element_size (klass);
-       for (i = 0; i < klass->rank; ++i) {
-               sizes [i] = array->bounds [i].length;
-               size *= array->bounds [i].length;
-               sizes [i + klass->rank] = array->bounds [i].lower_bound;
-       }
-       o = mono_array_new_full_checked (domain, klass, sizes, (intptr_t*)sizes + klass->rank, error);
-       return_val_if_nok (error, NULL);
-#ifdef HAVE_SGEN_GC
-       if (klass->element_class->valuetype) {
-               if (klass->element_class->has_references)
-                       mono_value_copy_array (o, 0, mono_array_addr_with_size_fast (array, 0, 0), mono_array_length (array));
-               else
-                       mono_gc_memmove_atomic (&o->vector, &array->vector, size);
+       MonoArrayBounds *array_bounds = MONO_HANDLE_GETVAL (array_handle, bounds);
+       MonoArrayHandle o;
+       if (array_bounds == NULL) {
+               size = mono_array_handle_length (array_handle);
+               o = mono_array_new_full_handle (domain, klass, &size, NULL, error);
+               if (!is_ok (error))
+                       goto leave;
+               size *= mono_array_element_size (klass);
        } else {
-               mono_array_memcpy_refs (o, 0, array, 0, mono_array_length (array));
+               uintptr_t *sizes = (uintptr_t *)alloca (klass->rank * sizeof (uintptr_t));
+               intptr_t *lower_bounds = (intptr_t *)alloca (klass->rank * sizeof (intptr_t));
+               size = mono_array_element_size (klass);
+               for (int i = 0; i < klass->rank; ++i) {
+                       sizes [i] = array_bounds [i].length;
+                       size *= array_bounds [i].length;
+                       lower_bounds [i] = array_bounds [i].lower_bound;
+               }
+               o = mono_array_new_full_handle (domain, klass, sizes, lower_bounds, error);
+               if (!is_ok (error))
+                       goto leave;
        }
-#else
-       mono_gc_memmove_atomic (&o->vector, &array->vector, size);
-#endif
 
-       return o;
+       uint32_t dst_handle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, o), TRUE);
+       array_full_copy_unchecked_size (MONO_HANDLE_RAW (array_handle), MONO_HANDLE_RAW (o), klass, size);
+       mono_gchandle_free (dst_handle);
+
+       MONO_HANDLE_ASSIGN (result, o);
+
+leave:
+       mono_gchandle_free (src_handle);
+       return result;
 }
 
 /**
@@ -5669,11 +5700,15 @@ mono_array_clone (MonoArray *array)
  * failure returns NULL and sets @error.
  */
 MonoArray*
-mono_array_clone_checked (MonoArray *array, MonoError *error)
+mono_array_clone_checked (MonoArray *array_raw, MonoError *error)
 {
-
        MONO_REQ_GC_UNSAFE_MODE;
-       return mono_array_clone_in_domain (((MonoObject *)array)->vtable->domain, array, error);
+       HANDLE_FUNCTION_ENTER ();
+       /* FIXME: callers of mono_array_clone_checked should use handles */
+       mono_error_init (error);
+       MONO_HANDLE_DCL (MonoArray, array);
+       MonoArrayHandle result = mono_array_clone_in_domain (MONO_HANDLE_DOMAIN (array), array, error);
+       HANDLE_FUNCTION_RETURN_OBJ (result);
 }
 
 /* helper macros to check for overflow when calculating the size of arrays */
@@ -6010,6 +6045,21 @@ mono_string_new_utf16_checked (MonoDomain *domain, const guint16 *text, gint32 l
        return s;
 }
 
+/**
+ * mono_string_new_utf16_handle:
+ * @text: a pointer to an utf16 string
+ * @len: the length of the string
+ * @error: written on error.
+ *
+ * Returns: A newly created string object which contains @text.
+ * On error, returns NULL and sets @error.
+ */
+MonoStringHandle
+mono_string_new_utf16_handle (MonoDomain *domain, const guint16 *text, gint32 len, MonoError *error)
+{
+       return MONO_HANDLE_NEW (MonoString, mono_string_new_utf16_checked (domain, text, len, error));
+}
+
 /**
  * mono_string_new_utf32:
  * @text: a pointer to an utf32 string
@@ -7278,11 +7328,11 @@ mono_string_to_utf8_internal (MonoMemPool *mp, MonoImage *image, MonoString *s,
  * Same as mono_string_to_utf8, but allocate the string from the image mempool.
  */
 char *
-mono_string_to_utf8_image (MonoImage *image, MonoString *s, MonoError *error)
+mono_string_to_utf8_image (MonoImage *image, MonoStringHandle s, MonoError *error)
 {
        MONO_REQ_GC_UNSAFE_MODE;
 
-       return mono_string_to_utf8_internal (NULL, image, s, FALSE, error);
+       return mono_string_to_utf8_internal (NULL, image, MONO_HANDLE_RAW (s), FALSE, error); /* FIXME pin the string */
 }
 
 /**
@@ -8387,6 +8437,21 @@ mono_string_length (MonoString *s)
        return s->length;
 }
 
+/**
+ * mono_string_handle_length:
+ * @s: MonoString
+ *
+ * Returns the lenght in characters of the string
+ */
+int
+mono_string_handle_length (MonoStringHandle s)
+{
+       MONO_REQ_GC_UNSAFE_MODE;
+
+       return MONO_HANDLE_GETVAL (s, length);
+}
+
+
 /**
  * mono_array_length:
  * @array: a MonoArray*