Merge pull request #1840 from ludovic-henry/iolayer-thread-interrupt
[mono.git] / mono / metadata / threads.c
index 5479f53c9ffbd61115a48e46204943fb2e84f71e..17246184a8c573982ac41504c16d66e5919b57f8 100644 (file)
@@ -20,7 +20,6 @@
 #include <mono/metadata/domain-internals.h>
 #include <mono/metadata/profiler-private.h>
 #include <mono/metadata/threads.h>
-#include <mono/metadata/threadpool.h>
 #include <mono/metadata/threads-types.h>
 #include <mono/metadata/exception.h>
 #include <mono/metadata/environment.h>
@@ -1095,28 +1094,28 @@ mono_thread_exit ()
 }
 
 void
-ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this)
+ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
 {
        MonoInternalThread *internal = create_internal_thread ();
 
        internal->state = ThreadState_Unstarted;
 
-       InterlockedCompareExchangePointer ((gpointer)&this->internal_thread, internal, NULL);
+       InterlockedCompareExchangePointer ((gpointer)&this_obj->internal_thread, internal, NULL);
 }
 
 HANDLE
-ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this,
+ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
                                                                                                   MonoObject *start)
 {
        StartInfo *start_info;
        MonoInternalThread *internal;
        gboolean res;
 
-       THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this, start));
+       THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
 
-       if (!this->internal_thread)
-               ves_icall_System_Threading_Thread_ConstructInternalThread (this);
-       internal = this->internal_thread;
+       if (!this_obj->internal_thread)
+               ves_icall_System_Threading_Thread_ConstructInternalThread (this_obj);
+       internal = this_obj->internal_thread;
 
        LOCK_THREAD (internal);
 
@@ -1128,17 +1127,17 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this,
 
        if ((internal->state & ThreadState_Aborted) != 0) {
                UNLOCK_THREAD (internal);
-               return this;
+               return this_obj;
        }
        /* This is freed in start_wrapper */
        start_info = g_new0 (StartInfo, 1);
        start_info->func = NULL;
-       start_info->start_arg = this->start_obj; /* FIXME: GC object stored in unmanaged memory */
+       start_info->start_arg = this_obj->start_obj; /* FIXME: GC object stored in unmanaged memory */
        start_info->delegate = start;
-       start_info->obj = this;
-       g_assert (this->obj.vtable->domain == mono_domain_get ());
+       start_info->obj = this_obj;
+       g_assert (this_obj->obj.vtable->domain == mono_domain_get ());
 
-       res = create_thread (this, internal, start_info, FALSE, 0, FALSE);
+       res = create_thread (this_obj, internal, start_info, FALSE, 0, FALSE);
        if (!res) {
                UNLOCK_THREAD (internal);
                return NULL;
@@ -1156,7 +1155,7 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this,
  * This is called from the finalizer of the internal thread object.
  */
 void
-ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this, HANDLE thread)
+ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj, HANDLE thread)
 {
        THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
 
@@ -1168,16 +1167,16 @@ ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThre
        if (thread)
                CloseHandle (thread);
 
-       if (this->synch_cs) {
-               mono_mutex_t *synch_cs = this->synch_cs;
-               this->synch_cs = NULL;
+       if (this_obj->synch_cs) {
+               mono_mutex_t *synch_cs = this_obj->synch_cs;
+               this_obj->synch_cs = NULL;
                mono_mutex_destroy (synch_cs);
                g_free (synch_cs);
        }
 
-       if (this->name) {
-               void *name = this->name;
-               this->name = NULL;
+       if (this_obj->name) {
+               void *name = this_obj->name;
+               this_obj->name = NULL;
                g_free (name);
        }
 }
@@ -1326,7 +1325,7 @@ ves_icall_System_Threading_Thread_GetPriority (MonoThread *this)
 }
 
 void
-ves_icall_System_Threading_Thread_SetPriority (MonoThread *this, int priority)
+ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority)
 {
 }
 
@@ -1386,35 +1385,35 @@ mono_thread_internal_current (void)
 }
 
 gboolean
-ves_icall_System_Threading_Thread_Join_internal(MonoThread *this, int ms)
+ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
 {
-       MonoInternalThread *this_obj = this->internal_thread;
-       HANDLE thread = this_obj->handle;
+       MonoInternalThread *thread = this_obj->internal_thread;
+       HANDLE handle = thread->handle;
        MonoInternalThread *cur_thread = mono_thread_internal_current ();
        gboolean ret;
 
        mono_thread_current_check_pending_interrupt ();
 
-       LOCK_THREAD (this_obj);
+       LOCK_THREAD (thread);
        
-       if ((this_obj->state & ThreadState_Unstarted) != 0) {
-               UNLOCK_THREAD (this_obj);
+       if ((thread->state & ThreadState_Unstarted) != 0) {
+               UNLOCK_THREAD (thread);
                
                mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started."));
                return FALSE;
        }
 
-       UNLOCK_THREAD (this_obj);
+       UNLOCK_THREAD (thread);
 
        if(ms== -1) {
                ms=INFINITE;
        }
-       THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, thread, ms));
+       THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
        
        mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
 
        MONO_PREPARE_BLOCKING
-       ret=WaitForSingleObjectEx (thread, ms, TRUE);
+       ret=WaitForSingleObjectEx (handle, ms, TRUE);
        MONO_FINISH_BLOCKING
 
        mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
@@ -1564,7 +1563,7 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha
 }
 
 /* FIXME: exitContext isnt documented */
-gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
+gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this_obj, HANDLE handle, gint32 ms, gboolean exitContext)
 {
        guint32 ret;
        MonoInternalThread *thread = mono_thread_internal_current ();
@@ -2023,23 +2022,23 @@ ves_icall_System_Threading_Thread_GetState (MonoInternalThread* this)
        return state;
 }
 
-void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this)
+void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
 {
        MonoInternalThread *current;
        gboolean throw;
-       MonoInternalThread *this_obj = this->internal_thread;
+       MonoInternalThread *thread = this_obj->internal_thread;
 
-       LOCK_THREAD (this_obj);
+       LOCK_THREAD (thread);
 
        current = mono_thread_internal_current ();
 
-       this_obj->thread_interrupt_requested = TRUE;
-       throw = current != this_obj && (this_obj->state & ThreadState_WaitSleepJoin);
+       thread->thread_interrupt_requested = TRUE;
+       throw = current != thread && (thread->state & ThreadState_WaitSleepJoin);
 
-       UNLOCK_THREAD (this_obj);
+       UNLOCK_THREAD (thread);
        
        if (throw) {
-               abort_thread_internal (this_obj, TRUE, FALSE);
+               abort_thread_internal (thread, TRUE, FALSE);
        }
 }
 
@@ -2105,7 +2104,7 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject
 }
 
 void
-ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this)
+ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
        gboolean was_aborting;
@@ -2150,9 +2149,9 @@ mono_thread_internal_reset_abort (MonoInternalThread *thread)
 }
 
 MonoObject*
-ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this)
+ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this_obj)
 {
-       MonoInternalThread *thread = this->internal_thread;
+       MonoInternalThread *thread = this_obj->internal_thread;
        MonoObject *state, *deserialized = NULL, *exc;
        MonoDomain *domain;
 
@@ -2209,9 +2208,9 @@ mono_thread_suspend (MonoInternalThread *thread)
 }
 
 void
-ves_icall_System_Threading_Thread_Suspend (MonoThread *this)
+ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj)
 {
-       if (!mono_thread_suspend (this->internal_thread)) {
+       if (!mono_thread_suspend (this_obj->internal_thread)) {
                mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
                return;
        }
@@ -2585,6 +2584,24 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppConte
        g_hash_table_insert (contexts, gch, gch);
 
        mono_threads_unlock ();
+
+       mono_profiler_context_loaded (ctx);
+}
+
+void
+ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
+{
+       /*
+        * NOTE: Since finalizers are unreliable for the purposes of ensuring
+        * cleanup in exceptional circumstances, we don't actually do any
+        * cleanup work here. We instead do this when we iterate the `contexts`
+        * hash table. The only purpose of this finalizer, at the moment, is to
+        * notify the profiler.
+        */
+
+       //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
+
+       mono_profiler_context_unloaded (ctx);
 }
 
 void
@@ -3163,7 +3180,7 @@ print_stack_frame_to_string (MonoStackFrameInfo *frame, MonoContext *ctx, gpoint
 {
        GString *p = (GString*)data;
        MonoMethod *method = NULL;
-       if (frame->ji)
+       if (frame->type == FRAME_TYPE_MANAGED)
                method = mono_jit_info_get_method (frame->ji);
 
        if (method) {
@@ -3564,7 +3581,7 @@ mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc
                        void **p = ptr + idx;
 
                        if (*p)
-                               mark_func (p, gc_data);
+                               mark_func ((MonoObject**)p, gc_data);
                });
        }
 }
@@ -3594,14 +3611,14 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean thr
 
        gpointer* static_data = *static_data_ptr;
        if (!static_data) {
-               static void *tls_desc = NULL;
-               static void *ctx_desc = NULL;
+               static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
+               static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
 
                if (mono_gc_user_markers_supported ()) {
-                       if (!tls_desc)
+                       if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
                                tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
 
-                       if (!ctx_desc)
+                       if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
                                ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
                }
 
@@ -3617,7 +3634,7 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean thr
                if (mono_gc_user_markers_supported ())
                        static_data [i] = g_malloc0 (static_data_size [i]);
                else
-                       static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL);
+                       static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL);
        }
 }
 
@@ -3703,17 +3720,16 @@ thread_adjust_static_data (MonoInternalThread *thread)
        mono_threads_unlock ();
 }
 
+/*
+ * LOCKING: requires that threads_mutex is held
+ */
 static void
 context_adjust_static_data (MonoAppContext *ctx)
 {
-       mono_threads_lock ();
-
        if (context_static_info.offset || context_static_info.idx > 0) {
                guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
                mono_alloc_static_data (&ctx->static_data, offset, FALSE);
        }
-
-       mono_threads_unlock ();
 }
 
 /*
@@ -3731,20 +3747,21 @@ alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
 /*
  * LOCKING: requires that threads_mutex is held
  */
-static void
+static gboolean
 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
 {
        uint32_t gch = GPOINTER_TO_INT (key);
        MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (gch);
 
        if (!ctx) {
-               g_hash_table_remove (contexts, key);
                mono_gchandle_free (gch);
-               return;
+               return TRUE; // Remove this key/value pair
        }
 
        guint32 offset = GPOINTER_TO_UINT (user);
        mono_alloc_static_data (&ctx->static_data, offset, FALSE);
+
+       return FALSE; // Don't remove it
 }
 
 static StaticDataFreeList*
@@ -3836,7 +3853,7 @@ mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align
                        mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
        } else {
                if (contexts != NULL)
-                       g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
+                       g_hash_table_foreach_remove (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
 
                ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
        }
@@ -3890,16 +3907,15 @@ free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
 /*
  * LOCKING: requires that threads_mutex is held
  */
-static void
+static gboolean
 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
 {
        uint32_t gch = GPOINTER_TO_INT (key);
        MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (gch);
 
        if (!ctx) {
-               g_hash_table_remove (contexts, key);
                mono_gchandle_free (gch);
-               return;
+               return TRUE; // Remove this key/value pair
        }
 
        OffsetSize *data = user;
@@ -3908,10 +3924,12 @@ free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
        char *ptr;
 
        if (!ctx->static_data || !ctx->static_data [idx])
-               return;
+               return FALSE; // Don't remove this key/value pair
 
        ptr = ((char*) ctx->static_data [idx]) + off;
        mono_gc_bzero_atomic (ptr, data->size);
+
+       return FALSE; // Don't remove this key/value pair
 }
 
 static void
@@ -3940,7 +3958,7 @@ do_free_special_slot (guint32 offset, guint32 size)
                        mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
        } else {
                if (contexts != NULL)
-                       g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
+                       g_hash_table_foreach_remove (contexts, free_context_static_data_helper, &data);
        }
 
        if (!mono_runtime_is_shutting_down ()) {
@@ -4010,8 +4028,9 @@ mono_thread_execute_interruption (MonoInternalThread *thread)
                WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
 #endif
                InterlockedDecrement (&thread_interruption_requested);
+
                /* Clear the interrupted flag of the thread so it can wait again */
-               mono_thread_info_clear_interruption ();
+               mono_thread_info_clear_self_interrupt ();
        }
 
        if ((thread->state & ThreadState_AbortRequested) != 0) {
@@ -4381,7 +4400,7 @@ mono_thread_info_get_last_managed (MonoThreadInfo *info)
 typedef struct {
        MonoInternalThread *thread;
        gboolean install_async_abort;
-       gpointer interrupt_handle;
+       MonoThreadInfoInterruptToken *interrupt_token;
 } AbortThreadData;
 
 static SuspendThreadResult
@@ -4403,7 +4422,7 @@ abort_thread_critical (MonoThreadInfo *info, gpointer ud)
        InterlockedIncrement (&thread_interruption_requested);
 
        ji = mono_thread_info_get_last_managed (info);
-       protected_wrapper = ji && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
+       protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
        running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
 
        if (!protected_wrapper && running_managed) {
@@ -4424,7 +4443,8 @@ abort_thread_critical (MonoThreadInfo *info, gpointer ud)
                 * functions in the io-layer until the signal handler calls QueueUserAPC which will
                 * make it return.
                 */
-               data->interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
+               data->interrupt_token = mono_thread_info_prepare_interrupt (info);
+
                return MonoResumeThread;
        }
 }
@@ -4445,20 +4465,22 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
                MonoException *exc = mono_thread_request_interruption (can_raise_exception); 
                if (exc)
                        mono_raise_exception (exc);
-               mono_thread_info_interrupt (thread->handle);
+
+               mono_thread_info_self_interrupt ();
+
                return;
        }
 
        mono_thread_info_safe_suspend_and_run ((MonoNativeThreadId)(gsize)thread->tid, TRUE, abort_thread_critical, &data);
-       if (data.interrupt_handle)
-               mono_thread_info_finish_interrupt (data.interrupt_handle);
+       if (data.interrupt_token)
+               mono_thread_info_finish_interrupt (data.interrupt_token);
        /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
 }
 
 typedef struct{
        MonoInternalThread *thread;
        gboolean interrupt;
-       gpointer interrupt_handle;
+       MonoThreadInfoInterruptToken *interrupt_token;
 } SuspendThreadData;
 
 static SuspendThreadResult
@@ -4471,7 +4493,7 @@ suspend_thread_critical (MonoThreadInfo *info, gpointer ud)
        gboolean running_managed;
 
        ji = mono_thread_info_get_last_managed (info);
-       protected_wrapper = ji && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
+       protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
        running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
 
        if (running_managed && !protected_wrapper) {
@@ -4482,7 +4504,7 @@ suspend_thread_critical (MonoThreadInfo *info, gpointer ud)
                if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
                        InterlockedIncrement (&thread_interruption_requested);
                if (data->interrupt)
-                       data->interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
+                       data->interrupt_token = mono_thread_info_prepare_interrupt (thread->thread_info);
                
                if (mono_thread_notify_pending_exc_fn && !running_managed)
                        /* The JIT will notify the thread about the interruption */
@@ -4508,8 +4530,8 @@ suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
                data.interrupt = interrupt;
 
                mono_thread_info_safe_suspend_and_run ((MonoNativeThreadId)(gsize)thread->tid, interrupt, suspend_thread_critical, &data);
-               if (data.interrupt_handle)
-                       mono_thread_info_finish_interrupt (data.interrupt_handle);
+               if (data.interrupt_token)
+                       mono_thread_info_finish_interrupt (data.interrupt_token);
                UNLOCK_THREAD (thread);
        }
 }
@@ -4653,3 +4675,43 @@ mono_thread_join (gpointer tid)
        pthread_join (thread, NULL);
 #endif
 }
+
+void
+mono_thread_internal_check_for_interruption_critical (MonoInternalThread *thread)
+{
+       if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
+               mono_thread_interruption_checkpoint ();
+}
+
+static inline gboolean
+is_appdomainunloaded_exception (MonoClass *klass)
+{
+       static MonoClass *app_domain_unloaded_exception_klass = NULL;
+
+       if (!app_domain_unloaded_exception_klass)
+               app_domain_unloaded_exception_klass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainUnloadedException");
+       g_assert (app_domain_unloaded_exception_klass);
+
+       return klass == app_domain_unloaded_exception_klass;
+}
+
+static inline gboolean
+is_threadabort_exception (MonoClass *klass)
+{
+       return klass == mono_defaults.threadabortexception_class;
+}
+
+void
+mono_thread_internal_unhandled_exception (MonoObject* exc)
+{
+       if (mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
+               MonoClass *klass = exc->vtable->klass;
+               if (is_threadabort_exception (klass)) {
+                       mono_thread_internal_reset_abort (mono_thread_internal_current ());
+               } else if (!is_appdomainunloaded_exception (klass)) {
+                       mono_unhandled_exception (exc);
+                       if (mono_environment_exitcode_get () == 1)
+                               exit (255);
+               }
+       }
+}