From: Rodrigo Kumpera Date: Thu, 9 Feb 2017 06:11:25 +0000 (-0800) Subject: Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=3b2913ca03f284906f55606c9b36540890b7572a;hp=-c;p=mono.git Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed Implement mono_gc_alloc_fixed/mono_gc_free_fixed on Boehm to match SGen --- 3b2913ca03f284906f55606c9b36540890b7572a diff --combined mono/metadata/domain.c index a498f9491f8,39f265d5f3d..810d4119791 --- a/mono/metadata/domain.c +++ b/mono/metadata/domain.c @@@ -34,6 -34,7 +34,6 @@@ #include #include #include -#include #include #include #include @@@ -43,10 -44,10 +43,10 @@@ #include #include #include +#include #include #include #include -#include //#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 (); @@@ -513,9 -508,6 +507,6 @@@ 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 diff --combined mono/metadata/gc-internals.h index 56b9cded8a0,a11a2f5b501..38aa44de6ca --- a/mono/metadata/gc-internals.h +++ b/mono/metadata/gc-internals.h @@@ -16,6 -16,7 +16,6 @@@ #include #include #include -#include #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); @@@ -25,16 -26,6 +25,6 @@@ #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 8162ea01f89,b1264232746..52aefb17693 --- a/mono/metadata/object.c +++ b/mono/metadata/object.c @@@ -31,12 -31,14 +31,12 @@@ #include #include #include "mono/metadata/debug-helpers.h" -#include "mono/metadata/marshal.h" #include #include #include #include "mono/metadata/profiler-private.h" #include "mono/metadata/security-manager.h" #include "mono/metadata/mono-debug-debugger.h" -#include #include #include #include @@@ -48,7 -50,7 +48,7 @@@ #include #include #include "cominterop.h" -#include +#include 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) { @@@ -424,9 -396,8 +424,9 @@@ 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; @@@ -439,7 -410,7 +439,7 @@@ 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; } @@@ -468,11 -439,9 +468,11 @@@ 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 @@@ -513,36 -482,30 +513,36 @@@ 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) @@@ -5634,51 -5585,62 +5627,51 @@@ * 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 fe4365c29ba,82590363f6f..27aa6e4c95e --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@@ -28,6 -28,7 +28,6 @@@ #include #include #include -#include #include #include #include @@@ -41,6 -42,7 +41,6 @@@ #include #include #include -#include #include #include #include @@@ -48,10 -50,9 +48,10 @@@ #include #include -#include #include #include +#include +#include #ifdef HAVE_SIGNAL_H #include @@@ -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); @@@ -955,7 -792,7 +953,7 @@@ 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."); @@@ -1588,7 -1423,8 +1586,7 @@@ 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; } diff --combined mono/mini/debugger-agent.c index 541b26e160a,98381c28f19..21779306d96 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@@ -59,11 -59,11 +59,11 @@@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@@ -75,7 -75,7 +75,7 @@@ #include "debugger-agent.h" #include "mini.h" #include "seq-points.h" -#include +#include /* * 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)) { /* @@@ -4604,7 -4543,7 +4600,7 @@@ 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); @@@ -4620,6 -4559,7 +4616,6 @@@ } 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); @@@ -4627,22 -4567,11 +4623,22 @@@ } } + 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) { @@@ -4839,45 -4692,10 +4835,45 @@@ 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]; @@@ -5447,9 -5239,6 +5443,9 @@@ 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)) @@@ -5458,46 -5247,10 +5454,46 @@@ } } + 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;