Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed
authorRodrigo Kumpera <kumpera@users.noreply.github.com>
Thu, 9 Feb 2017 06:11:25 +0000 (22:11 -0800)
committerGitHub <noreply@github.com>
Thu, 9 Feb 2017 06:11:25 +0000 (22:11 -0800)
Implement mono_gc_alloc_fixed/mono_gc_free_fixed on Boehm to match SGen

1  2 
mono/metadata/domain.c
mono/metadata/gc-internals.h
mono/metadata/object.c
mono/metadata/threads.c
mono/mini/debugger-agent.c

diff --combined mono/metadata/domain.c
index a498f9491f8412f9fa10bc0bf700d9c3826b0b14,39f265d5f3d9f32b4d3a78d936618cd61087bafe..810d41197919ce7d0700169d2d080fced5c6db26
@@@ -34,6 -34,7 +34,6 @@@
  #include <mono/metadata/assembly.h>
  #include <mono/metadata/exception.h>
  #include <mono/metadata/metadata-internals.h>
 -#include <mono/metadata/gc-internals.h>
  #include <mono/metadata/appdomain.h>
  #include <mono/metadata/mono-debug-debugger.h>
  #include <mono/metadata/mono-config.h>
  #include <mono/metadata/w32semaphore.h>
  #include <mono/metadata/w32event.h>
  #include <mono/metadata/w32process.h>
 +#include <mono/metadata/w32file.h>
  #include <metadata/threads.h>
  #include <metadata/profiler-private.h>
  #include <mono/metadata/coree.h>
 -#include <mono/io-layer/io-layer.h>
  
  //#define DEBUG_DOMAIN_UNLOAD 1
  
@@@ -382,13 -383,7 +382,7 @@@ mono_domain_create (void
        mono_appdomains_unlock ();
  
  #ifdef HAVE_BOEHM_GC
-       /*
-        * Boehm doesn't like roots inside GC allocated objects, and alloc_fixed returns
-        * a GC_MALLOC-ed object, contrary to the api docs. This causes random crashes when
-        * running the corlib test suite.
-        * To solve this, we pass a NULL descriptor, and don't register roots.
-        */
-       domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), NULL, MONO_ROOT_SOURCE_DOMAIN, "domain object");
+       domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "domain object");
  #else
        domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), domain_gc_desc, MONO_ROOT_SOURCE_DOMAIN, "domain object");
        mono_gc_register_root ((char*)&(domain->MONO_DOMAIN_FIRST_GC_TRACKED), G_STRUCT_OFFSET (MonoDomain, MONO_DOMAIN_LAST_GC_TRACKED) - G_STRUCT_OFFSET (MonoDomain, MONO_DOMAIN_FIRST_GC_TRACKED), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "misc domain fields");
@@@ -483,13 -478,13 +477,13 @@@ mono_init_internal (const char *filenam
  #ifndef HOST_WIN32
        mono_w32handle_init ();
        mono_w32handle_namespace_init ();
 -      wapi_init ();
  #endif
  
        mono_w32mutex_init ();
        mono_w32semaphore_init ();
        mono_w32event_init ();
        mono_w32process_init ();
 +      mono_w32file_init ();
  
  #ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters_init ();
        mono_reflection_init ();
        mono_runtime_init_tls ();
  
-       /* FIXME: When should we release this memory? */
-       MONO_GC_REGISTER_ROOT_FIXED (appdomains_list, MONO_ROOT_SOURCE_DOMAIN, "domains list");
        domain = mono_domain_create ();
        mono_root_domain = domain;
  
@@@ -851,7 -843,10 +842,7 @@@ mono_cleanup (void
        mono_coop_mutex_destroy (&appdomains_mutex);
  
        mono_w32process_cleanup ();
 -
 -#ifndef HOST_WIN32
 -      wapi_cleanup ();
 -#endif
 +      mono_w32file_cleanup ();
  }
  
  void
index 56b9cded8a05924a27889dce58e0e4ee8c7b7e48,a11a2f5b501f476f8b3bd621c766d02088c8e89e..38aa44de6caf0297dd75eb7ecc4432458312ce4c
@@@ -16,6 -16,7 +16,6 @@@
  #include <mono/metadata/object-internals.h>
  #include <mono/metadata/threads-types.h>
  #include <mono/sgen/gc-internal-agnostic.h>
 -#include <mono/utils/gc_wrapper.h>
  
  #define mono_domain_finalizers_lock(domain) mono_os_mutex_lock (&(domain)->finalizable_objects_hash_lock);
  #define mono_domain_finalizers_unlock(domain) mono_os_mutex_unlock (&(domain)->finalizable_objects_hash_lock);
  
  #define MONO_GC_UNREGISTER_ROOT(x) mono_gc_deregister_root ((char*)&(x))
  
- /*
-  * Register a memory location as a root pointing to memory allocated using
-  * mono_gc_alloc_fixed (). This includes MonoGHashTable.
-  */
- /* The result of alloc_fixed () is not GC tracked memory */
- #define MONO_GC_REGISTER_ROOT_FIXED(x,src,msg) do { \
-       if (!mono_gc_is_moving ())                              \
-               MONO_GC_REGISTER_ROOT_PINNING ((x),(src),(msg)); \
-       } while (0)
  /*
   * Return a GC descriptor for an array containing N pointers to memory allocated
   * by mono_gc_alloc_fixed ().
@@@ -130,8 -121,6 +120,6 @@@ gboolean mono_gc_user_markers_supporte
   * The memory is non-moving and it will be explicitly deallocated.
   * size bytes will be available from the returned address (ie, descr
   * must not be stored in the returned memory)
-  * NOTE: Under Boehm, this returns memory allocated using GC_malloc, so the result should
-  * be stored into a location registered using MONO_GC_REGISTER_ROOT_FIXED ().
   */
  void* mono_gc_alloc_fixed            (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg);
  void  mono_gc_free_fixed             (void* addr);
diff --combined mono/metadata/object.c
index 8162ea01f89f28adbdd9802a8852dcaf086fab16,b1264232746cc1052014c0c5ed8aa08f11cd380c..52aefb176930d89ada4cb3e3b451f133f5cf4ad5
  #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>
@@@ -48,7 -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);
@@@ -62,18 -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)
@@@ -153,9 -152,7 +153,9 @@@ typedef struc
        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 */
@@@ -179,13 -176,13 +179,13 @@@ mono_type_init_lock (TypeInitialization
  {
        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 */
@@@ -317,24 -314,6 +317,24 @@@ mono_runtime_class_init (MonoVTable *vt
        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
@@@ -391,13 -370,6 +391,13 @@@ mono_runtime_class_init_full (MonoVTabl
  
        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) {
                                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;
                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;
                }
        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
  
                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 ();
  
@@@ -573,11 -536,13 +573,11 @@@ gboolean release_type_locks (gpointer k
                 * 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;
  }
@@@ -783,18 -748,11 +783,11 @@@ compute_class_bitmap (MonoClass *klass
  
                        type = mono_type_get_underlying_type (field->type);
                        switch (type->type) {
+                       case MONO_TYPE_U:
                        case MONO_TYPE_I:
                        case MONO_TYPE_PTR:
                        case MONO_TYPE_FNPTR:
                                break;
-                       /* only UIntPtr is allowed to be GC-tracked and only in mscorlib */
-                       case MONO_TYPE_U:
- #ifdef HAVE_SGEN_GC
-                               break;
- #else
-                               if (klass->image != mono_defaults.corlib)
-                                       break;
- #endif
                        case MONO_TYPE_STRING:
                        case MONO_TYPE_SZARRAY:
                        case MONO_TYPE_CLASS:
@@@ -5604,13 -5562,6 +5597,13 @@@ mono_array_full_copy (MonoArray *src, M
        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)
   * 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;
  }
  
  /**
@@@ -5707,15 -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 */
@@@ -6052,21 -6010,6 +6045,21 @@@ mono_string_new_utf16_checked (MonoDoma
        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
@@@ -7335,11 -7278,11 +7328,11 @@@ mono_string_to_utf8_internal (MonoMemPo
   * 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 */
  }
  
  /**
@@@ -8444,21 -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*
diff --combined mono/metadata/threads.c
index fe4365c29ba9a33999ae99de23761190997aa13b,82590363f6fc7b0d528c9445465b46d3cf646709..27aa6e4c95e663158a565ad3c0f18d33e0d7e7fc
@@@ -28,6 -28,7 +28,6 @@@
  #include <mono/metadata/gc-internals.h>
  #include <mono/metadata/marshal.h>
  #include <mono/metadata/runtime.h>
 -#include <mono/io-layer/io-layer.h>
  #include <mono/metadata/object-internals.h>
  #include <mono/metadata/mono-debug-debugger.h>
  #include <mono/utils/monobitset.h>
@@@ -41,6 -42,7 +41,6 @@@
  #include <mono/utils/mono-tls.h>
  #include <mono/utils/atomic.h>
  #include <mono/utils/mono-memory-model.h>
 -#include <mono/utils/mono-threads-coop.h>
  #include <mono/utils/mono-error-internals.h>
  #include <mono/utils/os-event.h>
  #include <mono/utils/mono-threads-debug.h>
  #include <mono/metadata/w32event.h>
  #include <mono/metadata/w32mutex.h>
  
 -#include <mono/metadata/gc-internals.h>
  #include <mono/metadata/reflection-internals.h>
  #include <mono/metadata/abi-details.h>
 +#include <mono/metadata/w32error.h>
 +#include <mono/utils/w32api.h>
  
  #ifdef HAVE_SIGNAL_H
  #include <signal.h>
@@@ -206,7 -207,7 +206,7 @@@ static gboolean shutting_down = FALSE
  static gint32 managed_thread_id_counter = 0;
  
  /* Class lazy loading functions */
 -static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, System, AppDomainUnloadedException)
 +static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
  
  static void
  mono_threads_lock (void)
@@@ -227,167 -228,6 +227,167 @@@ get_next_managed_thread_id (void
        return InterlockedIncrement (&managed_thread_id_counter);
  }
  
 +enum {
 +      INTERRUPT_REQUESTED_BIT = 0x1,
 +      INTERRUPT_REQUEST_DEFERRED_BIT = 0x2,
 +      ABORT_PROT_BLOCK_SHIFT = 2,
 +      ABORT_PROT_BLOCK_BITS = 8,
 +      ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
 +};
 +
 +static int
 +mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
 +{
 +      gsize state = thread->thread_state;
 +      return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
 +}
 +
 +static void
 +verify_thread_state (gsize state)
 +{
 +      //can't have both INTERRUPT_REQUESTED_BIT and INTERRUPT_REQUEST_DEFERRED_BIT set at the same time
 +      g_assert ((state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) != (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT));
 +
 +      //XXX This would be nice to be true, but can happen due to self-aborts (and possibly set-pending-exception)
 +      //if prot_count > 0, INTERRUPT_REQUESTED_BIT must never be set
 +      // int prot_count = (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
 +      // g_assert (!(prot_count > 0 && (state & INTERRUPT_REQUESTED_BIT)));
 +}
 +
 +void
 +mono_threads_begin_abort_protected_block (void)
 +{
 +      MonoInternalThread *thread = mono_thread_internal_current ();
 +      gsize old_state, new_state;
 +      do {
 +              old_state = thread->thread_state;
 +              verify_thread_state (old_state);
 +
 +              int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
 +
 +              new_state = 0;
 +              if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) {
 +                      if (old_state & INTERRUPT_REQUESTED_BIT)
 +                              printf ("begin prot happy as it demoted interrupt to deferred interrupt\n");
 +                      new_state |= INTERRUPT_REQUEST_DEFERRED_BIT;
 +              }
 +
 +              //bounds check abort_prot_count
 +              g_assert (new_val > 0);
 +              g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
 +              new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
 +
 +      } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 +}
 +
 +gboolean
 +mono_threads_end_abort_protected_block (void)
 +{
 +      MonoInternalThread *thread = mono_thread_internal_current ();
 +      gsize old_state, new_state;
 +      do {
 +              old_state = thread->thread_state;
 +              verify_thread_state (old_state);
 +
 +              int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
 +              new_state = 0;
 +
 +              if ((old_state & INTERRUPT_REQUEST_DEFERRED_BIT) && new_val == 0) {
 +                      printf ("end abort on alert, promoted deferred to pront interrupt\n");
 +                      new_state |= INTERRUPT_REQUESTED_BIT;
 +              }
 +
 +              //bounds check abort_prot_count
 +              g_assert (new_val >= 0);
 +              g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
 +              new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
 +
 +      } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 +      return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
 +}
 +
 +
 +//Don't use this function, use inc/dec below
 +static void
 +mono_thread_abort_prot_block_count_add (MonoInternalThread *thread, int val)
 +{
 +      gsize old_state, new_state;
 +      do {
 +              old_state = thread->thread_state;
 +              verify_thread_state (old_state);
 +
 +              int new_val = val + ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
 +              //bounds check abort_prot_count
 +              g_assert (new_val >= 0);
 +              g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
 +              new_state = (old_state & ~ABORT_PROT_BLOCK_MASK) | (new_val << ABORT_PROT_BLOCK_SHIFT);
 +
 +      } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 +}
 +
 +static void
 +mono_thread_inc_abort_prot_block_count (MonoInternalThread *thread)
 +{
 +      mono_thread_abort_prot_block_count_add (thread, 1);
 +}
 +
 +static void
 +mono_thread_dec_abort_prot_block_count (MonoInternalThread *thread)
 +{
 +      mono_thread_abort_prot_block_count_add (thread, -1);
 +}
 +
 +static gboolean
 +mono_thread_get_interruption_requested (MonoInternalThread *thread)
 +{
 +      gsize state = thread->thread_state;
 +      return (state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
 +}
 +
 +/* Returns TRUE is there was a state change */
 +static gboolean
 +mono_thread_clear_interruption_requested (MonoInternalThread *thread)
 +{
 +      gsize old_state, new_state;
 +      do {
 +              old_state = thread->thread_state;
 +              verify_thread_state (old_state);
 +
 +              //Already cleared
 +              if (!(old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)))
 +                      return FALSE;
 +              new_state = old_state & ~(INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT);
 +      } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 +      return TRUE;
 +}
 +
 +/* Returns TRUE is there was a state change */
 +static gboolean
 +mono_thread_set_interruption_requested (MonoInternalThread *thread)
 +{
 +      //always force when the current thread is doing it to itself.
 +      gboolean force_interrupt = thread == mono_thread_internal_current ();
 +      gsize old_state, new_state;
 +      do {
 +              old_state = thread->thread_state;
 +              verify_thread_state (old_state);
 +
 +              int prot_count = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
 +              //Already set
 +              if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT))
 +                      return FALSE;
 +
 +              //If there's an outstanding prot block, we queue it
 +              if (prot_count && !force_interrupt) {
 +                      printf ("set interrupt unhappy, as it's only putting a deferred req %d\n", force_interrupt);
 +                      new_state = old_state | INTERRUPT_REQUEST_DEFERRED_BIT;
 +              } else
 +                      new_state = old_state | INTERRUPT_REQUESTED_BIT;
 +      } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 +
 +      return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
 +}
 +
  static inline MonoNativeThreadId
  thread_get_tid (MonoInternalThread *thread)
  {
@@@ -636,8 -476,7 +636,8 @@@ mono_thread_internal_set_priority (Mono
                        param.sched_priority = 0;
                        break;
                default:
 -                      g_error ("%s: unknown policy %d", __func__, policy);
 +                      g_warning ("%s: unknown policy %d", __func__, policy);
 +                      return;
                }
        }
  
@@@ -699,7 -538,6 +699,6 @@@ mono_thread_attach_internal (MonoThrea
        }
  
        if (!threads) {
-               MONO_GC_REGISTER_ROOT_FIXED (threads, MONO_ROOT_SOURCE_THREADING, "threads table");
                threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table");
        }
  
@@@ -923,7 -761,6 +922,6 @@@ create_thread (MonoThread *thread, Mono
                return FALSE;
        }
        if (threads_starting_up == NULL) {
-               MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up, MONO_ROOT_SOURCE_THREADING, "starting threads table");
                threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "starting threads table");
        }
        mono_g_hash_table_insert (threads_starting_up, thread, thread);
                mono_threads_lock ();
                mono_g_hash_table_remove (threads_starting_up, thread);
                mono_threads_unlock ();
 -              mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", GetLastError());
 +              mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
                /* ref is not going to be decremented in start_wrapper_internal */
                InterlockedDecrement (&start_info->ref);
                ret = FALSE;
@@@ -1167,7 -1004,7 +1165,7 @@@ mono_thread_detach_internal (MonoIntern
        Leaving the counter unbalanced will cause a performance degradation since all threads
        will now keep checking their local flags all the time.
        */
 -      if (InterlockedExchange (&thread->interruption_requested, 0) != 0)
 +      if (mono_thread_clear_interruption_requested (thread))
                InterlockedDecrement (&thread_interruption_requested);
  
        mono_threads_lock ();
@@@ -1569,15 -1406,13 +1567,15 @@@ ves_icall_System_Threading_Thread_GetNa
  }
  
  void 
 -mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, MonoError *error)
 +mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
  {
        LOCK_THREAD (this_obj);
  
        mono_error_init (error);
  
 -      if ((this_obj->flags & MONO_THREAD_FLAG_NAME_SET)) {
 +      if (reset) {
 +              this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
 +      } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
                UNLOCK_THREAD (this_obj);
                
                mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
                this_obj->name_len = 0;
        }
        if (name) {
 -              this_obj->name = g_new (gunichar2, mono_string_length (name));
 -              memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2);
 +              this_obj->name = g_memdup (mono_string_chars (name), mono_string_length (name) * sizeof (gunichar2));
                this_obj->name_len = mono_string_length (name);
  
                if (permanent)
@@@ -1613,7 -1449,7 +1611,7 @@@ voi
  ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
  {
        MonoError error;
 -      mono_thread_set_name_internal (this_obj, name, TRUE, &error);
 +      mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, &error);
        mono_error_set_pending_exception (&error);
  }
  
@@@ -4467,7 -4303,7 +4465,7 @@@ mono_thread_execute_interruption (void
        LOCK_THREAD (thread);
  
        /* MonoThread::interruption_requested can only be changed with atomics */
 -      if (InterlockedCompareExchange (&thread->interruption_requested, FALSE, TRUE)) {
 +      if (mono_thread_clear_interruption_requested (thread)) {
                /* this will consume pending APC calls */
  #ifdef HOST_WIN32
                WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
@@@ -4547,7 -4383,7 +4545,7 @@@ mono_thread_request_interruption (gbool
                thread->state & ThreadState_Background)
                ExitThread (1);
  #endif
 -      if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
 +      if (!mono_thread_set_interruption_requested (thread))
                return NULL;
        InterlockedIncrement (&thread_interruption_requested);
  
@@@ -4591,7 -4427,7 +4589,7 @@@ mono_thread_resume_interruption (void
        if (!still_aborting)
                return FALSE;
  
 -      if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
 +      if (!mono_thread_set_interruption_requested (thread))
                return NULL;
        InterlockedIncrement (&thread_interruption_requested);
  
@@@ -4606,7 -4442,7 +4604,7 @@@ gboolean mono_thread_interruption_reque
                MonoInternalThread *thread = mono_thread_internal_current ();
                /* The thread may already be stopping */
                if (thread != NULL) 
 -                      return (thread->interruption_requested);
 +                      return mono_thread_get_interruption_requested (thread);
        }
        return FALSE;
  }
@@@ -4619,7 -4455,7 +4617,7 @@@ mono_thread_interruption_checkpoint_req
        /* The thread may already be stopping */
        if (!thread)
                return NULL;
 -      if (!thread->interruption_requested)
 +      if (!mono_thread_get_interruption_requested (thread))
                return NULL;
        if (!bypass_abort_protection && is_running_protected_wrapper ())
                return NULL;
@@@ -4850,11 -4686,11 +4848,11 @@@ async_abort_critical (MonoThreadInfo *i
        The target thread is running at least one protected block, which must not be interrupted, so we give up.
        The protected block code will give them a chance when appropriate.
        */
 -      if (thread->abort_protected_block_count)
 +      if (mono_thread_get_abort_prot_block_count (thread) > 0)
                return MonoResumeThread;
  
        /*someone is already interrupting it*/
 -      if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
 +      if (!mono_thread_set_interruption_requested (thread))
                return MonoResumeThread;
  
        InterlockedIncrement (&thread_interruption_requested);
@@@ -4949,7 -4785,7 +4947,7 @@@ async_suspend_critical (MonoThreadInfo 
                        return KeepSuspended;
                }
        } else {
 -              if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
 +              if (mono_thread_set_interruption_requested (thread))
                        InterlockedIncrement (&thread_interruption_requested);
                if (data->interrupt)
                        data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
@@@ -5086,9 -4922,7 +5084,9 @@@ mono_threads_join_threads (void
                        if (thread != pthread_self ()) {
                                MONO_ENTER_GC_SAFE;
                                /* This shouldn't block */
 +                              mono_threads_join_lock ();
                                mono_native_thread_join (thread);
 +                              mono_threads_join_unlock ();
                                MONO_EXIT_GC_SAFE;
                        }
                } else {
@@@ -5259,15 -5093,34 +5257,15 @@@ mono_threads_detach_coop (gpointer cook
        }
  }
  
 -void
 -mono_threads_begin_abort_protected_block (void)
 -{
 -      MonoInternalThread *thread;
 -
 -      thread = mono_thread_internal_current ();
 -      ++thread->abort_protected_block_count;
 -      mono_memory_barrier ();
 -}
 -
 -void
 -mono_threads_end_abort_protected_block (void)
 -{
 -      MonoInternalThread *thread;
 -
 -      thread = mono_thread_internal_current ();
 -
 -      mono_memory_barrier ();
 -      --thread->abort_protected_block_count;
 -}
 -
  MonoException*
  mono_thread_try_resume_interruption (void)
  {
        MonoInternalThread *thread;
  
        thread = mono_thread_internal_current ();
 -      if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
 +      if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
 +              return NULL;
 +      if (mono_thread_get_abort_prot_block_count (thread) > 0 || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
                return NULL;
  
        return mono_thread_resume_interruption ();
@@@ -5287,7 -5140,7 +5285,7 @@@ mono_threads_is_ready_to_be_interrupte
                return FALSE;
        }
  
 -      if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
 +      if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
                UNLOCK_THREAD (thread);
                return FALSE;
        }
index 541b26e160a60bcffcdd4db5f8beb030f26a40fb,98381c28f19073d5cd037a10bbff5b98279ca717..21779306d9684b992216e9403965b5ca04a82473
  #include <mono/metadata/environment.h>
  #include <mono/metadata/threads-types.h>
  #include <mono/metadata/threadpool.h>
 -#include <mono/metadata/socket-io.h>
  #include <mono/metadata/assembly.h>
  #include <mono/metadata/runtime.h>
  #include <mono/metadata/verify-internals.h>
  #include <mono/metadata/reflection-internals.h>
 +#include <mono/metadata/w32socket.h>
  #include <mono/utils/mono-coop-mutex.h>
  #include <mono/utils/mono-coop-semaphore.h>
  #include <mono/utils/mono-error-internals.h>
@@@ -75,7 -75,7 +75,7 @@@
  #include "debugger-agent.h"
  #include "mini.h"
  #include "seq-points.h"
 -#include <mono/io-layer/io-layer.h>
 +#include <mono/utils/w32api.h>
  
  /*
   * On iOS we can't use System.Environment.Exit () as it will do the wrong
@@@ -273,7 -273,7 +273,7 @@@ typedef struct 
  #define HEADER_LENGTH 11
  
  #define MAJOR_VERSION 2
 -#define MINOR_VERSION 44
 +#define MINOR_VERSION 45
  
  typedef enum {
        CMD_SET_VM = 1,
@@@ -450,8 -450,7 +450,8 @@@ typedef enum 
        CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3,
        CMD_ASSEMBLY_GET_OBJECT = 4,
        CMD_ASSEMBLY_GET_TYPE = 5,
 -      CMD_ASSEMBLY_GET_NAME = 6
 +      CMD_ASSEMBLY_GET_NAME = 6,
 +      CMD_ASSEMBLY_GET_DOMAIN = 7
  } CmdAssembly;
  
  typedef enum {
@@@ -572,10 -571,6 +572,10 @@@ typedef struct 
        int nframes;
        /* If set, don't stop in methods that are not part of user assemblies */
        MonoAssembly** user_assemblies;
 +      /* Used to distinguish stepping breakpoint hits in parallel tasks executions */
 +      int async_id;
 +      /* Used to know if we are in process of async step-out and distishing from exception breakpoints */
 +      MonoMethod* async_stepout_method;
  } SingleStepReq;
  
  /*
@@@ -830,10 -825,9 +830,10 @@@ parse_address (char *address, char **ho
        if (pos == NULL || pos == address)
                return 1;
  
 -      *host = (char *)g_malloc (pos - address + 1);
 -      strncpy (*host, address, pos - address);
 -      (*host) [pos - address] = '\0';
 +      size_t len = pos - address;
 +      *host = (char *)g_malloc (len + 1);
 +      memcpy (*host, address, len);
 +      (*host) [len] = '\0';
  
        *port = atoi (pos + 1);
  
@@@ -999,13 -993,10 +999,10 @@@ mono_debugger_agent_init (void
        mono_gc_base_init ();
  
        thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table");
-       MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table");
  
        tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread table");
-       MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread table");
  
        tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread object table");
-       MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread object table");
  
        pending_assembly_loads = g_ptr_array_new ();
        domains = g_hash_table_new (mono_aligned_addr_hash, NULL);
@@@ -1943,7 -1934,6 +1940,6 @@@ objrefs_init (void
        objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref);
        obj_to_objref = g_hash_table_new (NULL, NULL);
        suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table");
-       MONO_GC_REGISTER_ROOT_FIXED (suspended_objs, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table");
  }
  
  static void
@@@ -4021,41 -4011,14 +4017,41 @@@ send_type_load (MonoClass *klass
  static void
  send_types_for_domain (MonoDomain *domain, void *user_data)
  {
 +      MonoDomain* old_domain;
        AgentDomainInfo *info = NULL;
  
        info = get_agent_domain_info (domain);
        g_assert (info);
 +
 +      old_domain = mono_domain_get ();
 +
 +      mono_domain_set (domain, TRUE);
        
        mono_loader_lock ();
        g_hash_table_foreach (info->loaded_classes, emit_type_load, NULL);
        mono_loader_unlock ();
 +
 +      mono_domain_set (old_domain, TRUE);
 +}
 +
 +static void
 +send_assemblies_for_domain (MonoDomain *domain, void *user_data)
 +{
 +      GSList *tmp;
 +      MonoDomain* old_domain;
 +
 +      old_domain = mono_domain_get ();
 +
 +      mono_domain_set (domain, TRUE);
 +
 +      mono_domain_assemblies_lock (domain);
 +      for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
 +              MonoAssembly* ass = (MonoAssembly *)tmp->data;
 +              emit_assembly_load (ass, NULL);
 +      }
 +      mono_domain_assemblies_unlock (domain);
 +
 +      mono_domain_set (old_domain, TRUE);
  }
  
  static void
@@@ -4559,42 -4522,18 +4555,42 @@@ static void ss_calculate_framecount (De
        compute_frame_info (tls->thread, tls);
  }
  
 +static gboolean
 +ensure_jit (StackFrame* frame)
 +{
 +      if (!frame->jit) {
 +              frame->jit = mono_debug_find_method (frame->api_method, frame->domain);
 +              if (!frame->jit && frame->api_method->is_inflated)
 +                      frame->jit = mono_debug_find_method(mono_method_get_declaring_generic_method (frame->api_method), frame->domain);
 +              if (!frame->jit) {
 +                      char *s;
 +
 +                      /* This could happen for aot images with no jit debug info */
 +                      s = mono_method_full_name (frame->api_method, TRUE);
 +                      DEBUG_PRINTF(1, "[dbg] No debug information found for '%s'.\n", s);
 +                      g_free (s);
 +                      return FALSE;
 +              }
 +      }
 +      return TRUE;
 +}
 +
  /*
   * ss_update:
   *
   * Return FALSE if single stepping needs to continue.
   */
  static gboolean
 -ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx)
 +ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx, MonoMethod* method)
  {
        MonoDebugMethodInfo *minfo;
        MonoDebugSourceLocation *loc = NULL;
        gboolean hit = TRUE;
 -      MonoMethod *method;
 +
 +      if (req->async_stepout_method == method) {
 +              DEBUG_PRINTF (1, "[%p] Breakpoint hit during async step-out at %s hit, continuing stepping out.\n", (gpointer)(gsize)mono_native_thread_id_get (), method->name);
 +              return FALSE;
 +      }
  
        if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK)) {
                /*
                return FALSE;
        }
  
 -      if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit) {
 +      if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit && !req->async_stepout_method) {
                gboolean is_step_out = req->depth == STEP_DEPTH_OUT;
  
                ss_calculate_framecount (tls, ctx);
        }
  
        if (req->depth == STEP_DEPTH_INTO && req->size == STEP_SIZE_MIN && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && ss_req->start_method){
 -              method = jinfo_get_method (ji);
                ss_calculate_framecount (tls, ctx);
                if (ss_req->start_method == method && req->nframes && tls->frame_count == req->nframes) {//Check also frame count(could be recursion)
                        DEBUG_PRINTF (1, "[%p] Seq point at nonempty stack %x while stepping in, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
                }
        }
  
 +      MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
 +      if (asyncMethod) {
 +              for (int i = 0; i < asyncMethod->num_awaits; i++)
 +              {
 +                      if (asyncMethod->yield_offsets[i] == sp->il_offset || asyncMethod->resume_offsets[i] == sp->il_offset) {
 +                              mono_debug_free_method_async_debug_info (asyncMethod);
 +                              return FALSE;
 +                      }
 +              }
 +              mono_debug_free_method_async_debug_info (asyncMethod);
 +      }
 +
        if (req->size != STEP_SIZE_LINE)
                return TRUE;
  
        /* Have to check whenever a different source line was reached */
 -      method = jinfo_get_method (ji);
        minfo = mono_debug_lookup_method (method);
  
        if (minfo)
@@@ -4675,82 -4604,6 +4671,82 @@@ breakpoint_matches_assembly (MonoBreakp
        return bp->method && bp->method->klass->image->assembly == assembly;
  }
  
 +static MonoObject*
 +get_this (StackFrame *frame)
 +{
 +      //Logic inspiered by "add_var" method and took out path that happens in async method for getting this
 +      MonoDebugVarInfo *var = frame->jit->this_var;
 +      if ((var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) != MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET)
 +              return NULL;
 +
 +      guint8 * addr = (guint8 *)mono_arch_context_get_int_reg (&frame->ctx, var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS);
 +      addr += (gint32)var->offset;
 +      return *(MonoObject**)addr;
 +}
 +
 +//This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not
 +//since thread probably changed...
 +static int
 +get_this_async_id (StackFrame *frame)
 +{
 +      return get_objid (get_this (frame));
 +}
 +
 +static MonoMethod* set_notification_method_cache = NULL;
 +
 +static MonoMethod*
 +get_set_notification_method ()
 +{
 +      if(set_notification_method_cache != NULL)
 +              return set_notification_method_cache;
 +      MonoError error;
 +      MonoClass* async_builder_class = mono_class_load_from_name (mono_defaults.corlib, "System.Runtime.CompilerServices", "AsyncTaskMethodBuilder");
 +      GPtrArray* array = mono_class_get_methods_by_name (async_builder_class, "SetNotificationForWaitCompletion", 0x24, FALSE, FALSE, &error);
 +      mono_error_assert_ok (&error);
 +      g_assert (array->len == 1);
 +      set_notification_method_cache = (MonoMethod *)g_ptr_array_index (array, 0);
 +      g_ptr_array_free (array, TRUE);
 +      return set_notification_method_cache;
 +}
 +
 +static void
 +set_set_notification_for_wait_completion_flag (StackFrame *frame)
 +{
 +      MonoObject* obj = get_this (frame);
 +      g_assert (obj);
 +      MonoClassField *builder_field = mono_class_get_field_from_name (obj->vtable->klass, "<>t__builder");
 +      g_assert (builder_field);
 +      MonoObject* builder;
 +      MonoError error;
 +      builder = mono_field_get_value_object_checked (frame->domain, builder_field, obj, &error);
 +      mono_error_assert_ok (&error);
 +      g_assert (builder);
 +
 +      void* args [1];
 +      gboolean arg = TRUE;
 +      args [0] = &arg;
 +      mono_runtime_invoke_checked (get_set_notification_method(), mono_object_unbox (builder), args, &error);
 +      mono_error_assert_ok (&error);
 +      mono_field_set_value (obj, builder_field, mono_object_unbox (builder));
 +}
 +
 +static MonoMethod* notify_debugger_of_wait_completion_method_cache = NULL;
 +
 +static MonoMethod*
 +get_notify_debugger_of_wait_completion_method ()
 +{
 +      if (notify_debugger_of_wait_completion_method_cache != NULL)
 +              return notify_debugger_of_wait_completion_method_cache;
 +      MonoError error;
 +      MonoClass* task_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading.Tasks", "Task");
 +      GPtrArray* array = mono_class_get_methods_by_name (task_class, "NotifyDebuggerOfWaitCompletion", 0x24, FALSE, FALSE, &error);
 +      mono_error_assert_ok (&error);
 +      g_assert (array->len == 1);
 +      notify_debugger_of_wait_completion_method_cache = (MonoMethod *)g_ptr_array_index (array, 0);
 +      g_ptr_array_free (array, TRUE);
 +      return notify_debugger_of_wait_completion_method_cache;
 +}
 +
  static void
  process_breakpoint_inner (DebuggerTlsData *tls, gboolean from_signal)
  {
                SingleStepReq *ss_req = (SingleStepReq *)req->info;
                gboolean hit;
  
 -              if (mono_thread_internal_current () != ss_req->thread)
 -                      continue;
 +              //if we hit async_stepout_method, it's our no matter which thread
 +              if ((ss_req->async_stepout_method != method) && (ss_req->async_id || mono_thread_internal_current () != ss_req->thread)) {
 +                      //We have different thread and we don't have async stepping in progress
 +                      //it's breakpoint in parallel thread, ignore it
 +                      if (ss_req->async_id == 0)
 +                              continue;
 +
 +                      tls->context.valid = FALSE;
 +                      tls->async_state.valid = FALSE;
 +                      invalidate_frames (tls);
 +                      ss_calculate_framecount(tls, ctx);
 +                      //make sure we have enough data to get current async method instance id
 +                      if (tls->frame_count == 0 || !ensure_jit (tls->frames [0]))
 +                              continue;
  
 -              hit = ss_update (ss_req, ji, &sp, tls, ctx);
 +                      //Check method is async before calling get_this_async_id
 +                      MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
 +                      if (!asyncMethod)
 +                              continue;
 +                      else
 +                              mono_debug_free_method_async_debug_info (asyncMethod);
 +
 +                      //breakpoint was hit in parallelly executing async method, ignore it
 +                      if (ss_req->async_id != get_this_async_id (tls->frames [0]))
 +                              continue;
 +              }
 +
 +              //Update stepping request to new thread/frame_count that we are continuing on
 +              //so continuing with normal stepping works as expected
 +              if (ss_req->async_stepout_method || ss_req->async_id) {
 +                      tls->context.valid = FALSE;
 +                      tls->async_state.valid = FALSE;
 +                      invalidate_frames (tls);
 +                      ss_calculate_framecount (tls, ctx);
 +                      ss_req->thread = mono_thread_internal_current ();
 +                      ss_req->nframes = tls->frame_count;
 +              }
 +
 +              hit = ss_update (ss_req, ji, &sp, tls, ctx, method);
                if (hit)
                        g_ptr_array_add (ss_reqs, req);
  
@@@ -5115,7 -4933,7 +5111,7 @@@ process_single_step_inner (DebuggerTlsD
  
        il_offset = sp.il_offset;
  
 -      if (!ss_update (ss_req, ji, &sp, tls, ctx))
 +      if (!ss_update (ss_req, ji, &sp, tls, ctx, method))
                return;
  
        /* Start single stepping again from the current sequence point */
@@@ -5284,8 -5102,6 +5280,8 @@@ ss_stop (SingleStepReq *ss_req
                ss_req->bps = NULL;
        }
  
 +      ss_req->async_id = 0;
 +      ss_req->async_stepout_method = NULL;
        if (ss_req->global) {
                stop_single_stepping ();
                ss_req->global = FALSE;
@@@ -5371,28 -5187,6 +5367,28 @@@ ss_bp_add_one (SingleStepReq *ss_req, i
        }
  }
  
 +static gboolean
 +is_last_non_empty (SeqPoint* sp, MonoSeqPointInfo *info)
 +{
 +      if (!sp->next_len)
 +              return TRUE;
 +      SeqPoint* next = g_new (SeqPoint, sp->next_len);
 +      mono_seq_point_init_next (info, *sp, next);
 +      for (int i = 0; i < sp->next_len; i++) {
 +              if (next [i].flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) {
 +                      if (!is_last_non_empty (&next [i], info)) {
 +                              g_free (next);
 +                              return FALSE;
 +                      }
 +              } else {
 +                      g_free (next);
 +                      return FALSE;
 +              }
 +      }
 +      g_free (next);
 +      return TRUE;
 +}
 +
  /*
   * ss_start:
   *
@@@ -5438,8 -5232,6 +5434,8 @@@ ss_start (SingleStepReq *ss_req, MonoMe
                        nframes = tls->frame_count;
                }
  
 +              MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
 +
                /* Need to stop in catch clauses as well */
                for (i = ss_req->depth == STEP_DEPTH_OUT ? 1 : 0; i < nframes; ++i) {
                        StackFrame *frame = frames [i];
                        if (frame->ji) {
                                MonoJitInfo *jinfo = frame->ji;
                                for (j = 0; j < jinfo->num_clauses; ++j) {
 +                                      // In case of async method we don't want to place breakpoint on last catch handler(which state machine added for whole method)
 +                                      if (asyncMethod && asyncMethod->num_awaits && i == 0 && j + 1 == jinfo->num_clauses)
 +                                              break;
                                        MonoJitExceptionInfo *ei = &jinfo->clauses [j];
  
                                        if (mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp))
                        }
                }
  
 +              if (asyncMethod && asyncMethod->num_awaits && nframes && ensure_jit (frames [0])) {
 +                      //asyncMethod has value and num_awaits > 0, this means we are inside async method with awaits
 +
 +                      // Check if we hit yield_offset during normal stepping, because if we did...
 +                      // Go into special async stepping mode which places breakpoint on resumeOffset
 +                      // of this await call and sets async_id so we can distinguish it from parallel executions
 +                      for (i = 0; i < asyncMethod->num_awaits; i++) {
 +                              if (sp->il_offset == asyncMethod->yield_offsets [i]) {
 +                                      ss_req->async_id = get_this_async_id (frames [0]);
 +                                      ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, asyncMethod->resume_offsets [i]);
 +                                      if (ss_req_bp_cache)
 +                                              g_hash_table_destroy (ss_req_bp_cache);
 +                                      mono_debug_free_method_async_debug_info (asyncMethod);
 +                                      return;
 +                              }
 +                      }
 +                      //If we are at end of async method and doing step-in or step-over...
 +                      //Switch to step-out, so whole NotifyDebuggerOfWaitCompletion magic happens...
 +                      if (is_last_non_empty (sp, info)) {
 +                              ss_req->depth = STEP_DEPTH_OUT;//setting depth to step-out is important, don't inline IF, because code later depends on this
 +                      }
 +                      if (ss_req->depth == STEP_DEPTH_OUT) {
 +                              set_set_notification_for_wait_completion_flag (frames [0]);
 +                              ss_req->async_id = get_this_async_id (frames [0]);
 +                              ss_req->async_stepout_method = get_notify_debugger_of_wait_completion_method ();
 +                              ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0);
 +                              if (ss_req_bp_cache)
 +                                      g_hash_table_destroy (ss_req_bp_cache);
 +                              mono_debug_free_method_async_debug_info (asyncMethod);
 +                              return;
 +                      }
 +              }
 +
 +              if (asyncMethod)
 +                      mono_debug_free_method_async_debug_info (asyncMethod);
 +
                /*
 -               * Find the first sequence point in the current or in a previous frame which
 -               * is not the last in its method.
 -               */
 +              * Find the first sequence point in the current or in a previous frame which
 +              * is not the last in its method.
 +              */
                if (ss_req->depth == STEP_DEPTH_OUT) {
                        /* Ignore seq points in current method */
                        while (frame_index < nframes) {
@@@ -7850,7 -7603,7 +7846,7 @@@ event_commands (int command, guint8 *p
                                break;
                        case EVENT_KIND_ASSEMBLY_LOAD:
                                /* Emit load events for currently loaded assemblies */
 -                              mono_assembly_foreach (emit_assembly_load, NULL);
 +                              mono_domain_foreach (send_assemblies_for_domain, NULL);
                                break;
                        case EVENT_KIND_THREAD_START:
                                /* Emit start events for currently started threads */
@@@ -8070,10 -7823,6 +8066,10 @@@ assembly_commands (int command, guint8 
                mono_error_cleanup (&error);
                return err;
        }
 +      case CMD_ASSEMBLY_GET_DOMAIN: {
 +              buffer_add_domainid (buf, domain);
 +              break;
 +      }
        case CMD_ASSEMBLY_GET_TYPE: {
                MonoError error;
                char *s = decode_string (p, &p, end);
@@@ -9425,9 -9174,20 +9421,9 @@@ frame_commands (int command, guint8 *p
        if (!frame->has_ctx)
                return ERR_ABSENT_INFORMATION;
  
 -      if (!frame->jit) {
 -              frame->jit = mono_debug_find_method (frame->api_method, frame->domain);
 -              if (!frame->jit && frame->api_method->is_inflated)
 -                      frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->api_method), frame->domain);
 -              if (!frame->jit) {
 -                      char *s;
 +      if (!ensure_jit (frame))
 +              return ERR_ABSENT_INFORMATION;
  
 -                      /* This could happen for aot images with no jit debug info */
 -                      s = mono_method_full_name (frame->api_method, TRUE);
 -                      DEBUG_PRINTF (1, "[dbg] No debug information found for '%s'.\n", s);
 -                      g_free (s);
 -                      return ERR_ABSENT_INFORMATION;
 -              }
 -      }
        jit = frame->jit;
  
        sig = mono_method_signature (frame->actual_method);
@@@ -9934,8 -9694,7 +9930,8 @@@ static const char* assembly_cmds_str[] 
        "GET_MANIFEST_MODULE",
        "GET_OBJECT",
        "GET_TYPE",
 -      "GET_NAME"
 +      "GET_NAME",
 +      "GET_DOMAIN"
  };
  
  static const char* module_cmds_str[] = {
@@@ -10137,7 -9896,7 +10133,7 @@@ debugger_thread (void *arg
        debugger_thread_id = mono_native_thread_id_get ();
  
        MonoThread *thread = mono_thread_attach (mono_get_root_domain ());
 -      mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Debugger agent"), TRUE, &error);
 +      mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Debugger agent"), TRUE, FALSE, &error);
        mono_error_assert_ok (&error);
  
        thread->internal_thread->state |= ThreadState_Background;