Merge pull request #2816 from xmcclure/profile-clean-0
[mono.git] / mono / metadata / threads.c
index 7ae43c5cd631434e05ae96e9766134715559cfdb..5884d0270e58ee9ce7dd577fc37d3d16a8a4fced 100644 (file)
@@ -9,6 +9,7 @@
  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include <config.h>
@@ -42,6 +43,7 @@
 #include <mono/utils/mono-memory-model.h>
 
 #include <mono/metadata/gc-internals.h>
+#include <mono/metadata/reflection-internals.h>
 
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
@@ -191,9 +193,6 @@ static MonoThreadAttachCB mono_thread_attach_cb = NULL;
 /* function called at thread cleanup */
 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
 
-/* function called to notify the runtime about a pending exception on the current thread */
-static MonoThreadNotifyPendingExcFunc mono_thread_notify_pending_exc_fn = NULL;
-
 /* The default stack size for each thread */
 static guint32 default_stacksize = 0;
 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
@@ -204,9 +203,10 @@ static void mono_free_static_data (gpointer* static_data);
 static void mono_init_static_data_info (StaticDataInfo *static_data);
 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
 static gboolean mono_thread_resume (MonoInternalThread* thread);
-static void abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception, gboolean install_async_abort);
-static void suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt);
-static void self_suspend_internal (MonoInternalThread *thread);
+static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort);
+static void self_abort_internal (void);
+static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt);
+static void self_suspend_internal (void);
 
 static MonoException* mono_thread_execute_interruption (void);
 static void ref_stack_destroy (gpointer rs);
@@ -226,6 +226,9 @@ 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 void
 mono_threads_lock (void)
 {
@@ -1322,16 +1325,21 @@ mono_thread_get_managed_id (MonoThread *thread)
 MonoString* 
 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj)
 {
+       MonoError error;
        MonoString* str;
 
+       mono_error_init (&error);
+
        LOCK_THREAD (this_obj);
        
        if (!this_obj->name)
                str = NULL;
        else
-               str = mono_string_new_utf16 (mono_domain_get (), this_obj->name, this_obj->name_len);
+               str = mono_string_new_utf16_checked (mono_domain_get (), this_obj->name, this_obj->name_len, &error);
        
        UNLOCK_THREAD (this_obj);
+
+       mono_error_raise_exception (&error);
        
        return str;
 }
@@ -1576,7 +1584,7 @@ mono_wait_uninterrupted (MonoInternalThread *thread, gboolean multiple, guint32
 }
 
 /* FIXME: exitContext isnt documented */
-gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
+gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
 {
        HANDLE *handles;
        guint32 numhandles;
@@ -1609,15 +1617,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_
 
        g_free(handles);
 
-       if(ret==WAIT_FAILED) {
-               THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait failed", __func__, mono_native_thread_id_get ()));
-               return(FALSE);
-       } else if(ret==WAIT_TIMEOUT) {
-               THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, mono_native_thread_id_get ()));
-               return(FALSE);
-       }
-       
-       return(TRUE);
+       return ret;
 }
 
 /* FIXME: exitContext isnt documented */
@@ -1669,7 +1669,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_obj, HANDLE handle, gint32 ms, gboolean exitContext)
+gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms, gboolean exitContext)
 {
        guint32 ret;
        MonoInternalThread *thread = mono_thread_internal_current ();
@@ -1688,18 +1688,10 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this
        
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
        
-       if(ret==WAIT_FAILED) {
-               THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait failed", __func__, mono_native_thread_id_get ()));
-               return(FALSE);
-       } else if(ret==WAIT_TIMEOUT) {
-               THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, mono_native_thread_id_get ()));
-               return(FALSE);
-       }
-       
-       return(TRUE);
+       return ret;
 }
 
-gboolean
+gint32
 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms, gboolean exitContext)
 {
        guint32 ret;
@@ -1718,7 +1710,7 @@ ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, H
        
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
 
-       return  (!(ret == WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION || ret == WAIT_FAILED));
+       return ret;
 }
 
 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
@@ -2038,6 +2030,7 @@ MonoObject*
 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
 {
        MonoObject *res;
+       MONO_CHECK_NULL (location, NULL);
        res = (MonoObject *)InterlockedExchangePointer ((volatile gpointer *)location, value);
        mono_gc_wbarrier_generic_nostore (location);
        return res;
@@ -2142,9 +2135,9 @@ void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
        throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
 
        UNLOCK_THREAD (thread);
-       
+
        if (throw_) {
-               abort_thread_internal (thread, TRUE, FALSE);
+               async_abort_internal (thread, FALSE);
        }
 }
 
@@ -2206,7 +2199,10 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject
 
        UNLOCK_THREAD (thread);
 
-       abort_thread_internal (thread, TRUE, TRUE);
+       if (thread == mono_thread_internal_current ())
+               self_abort_internal ();
+       else
+               async_abort_internal (thread, TRUE);
 }
 
 void
@@ -2307,9 +2303,14 @@ mono_thread_suspend (MonoInternalThread *thread)
        
        thread->state |= ThreadState_SuspendRequested;
 
-       UNLOCK_THREAD (thread);
+       if (thread == mono_thread_internal_current ()) {
+               /* calls UNLOCK_THREAD (thread) */
+               self_suspend_internal ();
+       } else {
+               /* calls UNLOCK_THREAD (thread) */
+               async_suspend_internal (thread, FALSE);
+       }
 
-       suspend_thread_internal (thread, FALSE);
        return TRUE;
 }
 
@@ -2417,7 +2418,10 @@ void mono_thread_internal_stop (MonoInternalThread *thread)
        
        UNLOCK_THREAD (thread);
        
-       abort_thread_internal (thread, TRUE, TRUE);
+       if (thread == mono_thread_internal_current ())
+               self_abort_internal ();
+       else
+               async_abort_internal (thread, TRUE);
 }
 
 void mono_thread_stop (MonoThread *thread)
@@ -2842,11 +2846,6 @@ mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback fu
        thread->internal_thread->manage_callback = func;
 }
 
-void mono_threads_install_notify_pending_exc (MonoThreadNotifyPendingExcFunc func)
-{
-       mono_thread_notify_pending_exc_fn = func;
-}
-
 G_GNUC_UNUSED
 static void print_tids (gpointer key, gpointer value, gpointer user)
 {
@@ -3291,10 +3290,8 @@ void mono_thread_suspend_all_other_threads (void)
                        
                        thread->state |= ThreadState_SuspendRequested;
 
-                       UNLOCK_THREAD (thread);
-
-                       /* Signal the thread to suspend */
-                       suspend_thread_internal (thread, TRUE);
+                       /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
+                       async_suspend_internal (thread, TRUE);
                }
                if (eventidx <= 0) {
                        /* 
@@ -3493,6 +3490,7 @@ static void
 mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames)
 {
        MonoError error;
+
        ThreadDumpUserData ud;
        MonoInternalThread *thread_array [128];
        MonoDomain *domain = mono_domain_get ();
@@ -3549,7 +3547,9 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra
                        if (method) {
                                sf->method_address = (gsize) frame->ji->code_start;
 
-                               MONO_OBJECT_SETREF (sf, method, mono_method_get_object (domain, method, NULL));
+                               MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, &error);
+                               mono_error_raise_exception (&error); /* FIXME don't raise here */
+                               MONO_OBJECT_SETREF (sf, method, rm);
 
                                location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
                                if (location) {
@@ -4314,6 +4314,7 @@ static MonoException*
 mono_thread_execute_interruption (void)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
+       MonoThread *sys_thread = mono_thread_current ();
 
        LOCK_THREAD (thread);
 
@@ -4341,7 +4342,8 @@ mono_thread_execute_interruption (void)
                return thread->abort_exc;
        }
        else if ((thread->state & ThreadState_SuspendRequested) != 0) {
-               self_suspend_internal (thread);         
+               /* calls UNLOCK_THREAD (thread) */
+               self_suspend_internal ();
                return NULL;
        }
        else if ((thread->state & ThreadState_StopRequested) != 0) {
@@ -4351,11 +4353,11 @@ mono_thread_execute_interruption (void)
                
                mono_thread_exit ();
                return NULL;
-       } else if (thread->pending_exception) {
+       } else if (sys_thread->pending_exception) {
                MonoException *exc;
 
-               exc = thread->pending_exception;
-               thread->pending_exception = NULL;
+               exc = sys_thread->pending_exception;
+               sys_thread->pending_exception = NULL;
 
         UNLOCK_THREAD (thread);
         return exc;
@@ -4405,11 +4407,6 @@ mono_thread_request_interruption (gboolean running_managed)
                   request count. When exiting the unmanaged method the count will be
                   checked and the thread will be interrupted. */
 
-               if (mono_thread_notify_pending_exc_fn && !running_managed)
-                       /* The JIT will notify the thread about the interruption */
-                       /* This shouldn't take any locks */
-                       mono_thread_notify_pending_exc_fn (NULL);
-
                /* this will awake the thread if it is in WaitForSingleObject 
                   or similar */
                /* Our implementation of this function ignores the func argument */
@@ -4512,6 +4509,7 @@ MonoException*
 mono_thread_get_and_clear_pending_exception (void)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
+       MonoThread *sys_thread = mono_thread_current ();
 
        /* The thread may already be stopping */
        if (thread == NULL)
@@ -4521,10 +4519,10 @@ mono_thread_get_and_clear_pending_exception (void)
                return mono_thread_execute_interruption ();
        }
        
-       if (thread->pending_exception) {
-               MonoException *exc = thread->pending_exception;
+       if (sys_thread->pending_exception) {
+               MonoException *exc = sys_thread->pending_exception;
 
-               thread->pending_exception = NULL;
+               sys_thread->pending_exception = NULL;
                return exc;
        }
 
@@ -4540,7 +4538,7 @@ mono_thread_get_and_clear_pending_exception (void)
 void
 mono_set_pending_exception (MonoException *exc)
 {
-       MonoInternalThread *thread = mono_thread_internal_current ();
+       MonoThread *thread = mono_thread_current ();
 
        /* The thread may already be stopping */
        if (thread == NULL)
@@ -4675,7 +4673,17 @@ mono_thread_info_get_last_managed (MonoThreadInfo *info)
        MonoJitInfo *ji = NULL;
        if (!info)
                return NULL;
+
+       /*
+        * The suspended thread might be holding runtime locks. Make sure we don't try taking
+        * any runtime locks while unwinding. In coop case we shouldn't safepoint in regions
+        * where we hold runtime locks.
+        */
+       if (!mono_threads_is_coop_enabled ())
+               mono_thread_info_set_is_async_context (TRUE);
        mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
+       if (!mono_threads_is_coop_enabled ())
+               mono_thread_info_set_is_async_context (FALSE);
        return ji;
 }
 
@@ -4686,7 +4694,7 @@ typedef struct {
 } AbortThreadData;
 
 static SuspendThreadResult
-abort_thread_critical (MonoThreadInfo *info, gpointer ud)
+async_abort_critical (MonoThreadInfo *info, gpointer ud)
 {
        AbortThreadData *data = (AbortThreadData *)ud;
        MonoInternalThread *thread = data->thread;
@@ -4714,10 +4722,6 @@ abort_thread_critical (MonoThreadInfo *info, gpointer ud)
                        mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
                return MonoResumeThread;
        } else {
-               if (mono_thread_notify_pending_exc_fn)
-                       /* The JIT will notify the thread about the interruption */
-                       mono_thread_notify_pending_exc_fn (info);
-
                /* 
                 * This will cause waits to be broken.
                 * It will also prevent the thread from entering a wait, so if the thread returns
@@ -4732,41 +4736,45 @@ abort_thread_critical (MonoThreadInfo *info, gpointer ud)
 }
 
 static void
-abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception, gboolean install_async_abort)
+async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
 {
-       AbortThreadData data = { 0 };
-       data.thread = thread;
-       data.install_async_abort = install_async_abort;
+       AbortThreadData data;
 
-       /*
-       FIXME this is insanely broken, it doesn't cause interruption to happen
-       synchronously since passing FALSE to mono_thread_request_interruption makes sure it returns NULL
-       */
-       if (thread == mono_thread_internal_current ()) {
-               /* Do it synchronously */
-               MonoException *exc = mono_thread_request_interruption (can_raise_exception); 
-               if (exc)
-                       mono_raise_exception (exc);
-
-               mono_thread_info_self_interrupt ();
+       g_assert (thread != mono_thread_internal_current ());
 
-               return;
-       }
+       data.thread = thread;
+       data.install_async_abort = install_async_abort;
+       data.interrupt_token = NULL;
 
-       mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, abort_thread_critical, &data);
+       mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
        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{
+static void
+self_abort_internal (void)
+{
+       MonoException *exc;
+
+       /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
+        * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
+
+       exc = mono_thread_request_interruption (TRUE);
+       if (exc)
+               mono_raise_exception (exc);
+
+       mono_thread_info_self_interrupt ();
+}
+
+typedef struct {
        MonoInternalThread *thread;
        gboolean interrupt;
        MonoThreadInfoInterruptToken *interrupt_token;
 } SuspendThreadData;
 
 static SuspendThreadResult
-suspend_thread_critical (MonoThreadInfo *info, gpointer ud)
+async_suspend_critical (MonoThreadInfo *info, gpointer ud)
 {
        SuspendThreadData *data = (SuspendThreadData *)ud;
        MonoInternalThread *thread = data->thread;
@@ -4787,49 +4795,47 @@ suspend_thread_critical (MonoThreadInfo *info, gpointer ud)
                        InterlockedIncrement (&thread_interruption_requested);
                if (data->interrupt)
                        data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
-               
-               if (mono_thread_notify_pending_exc_fn && !running_managed)
-                       /* The JIT will notify the thread about the interruption */
-                       mono_thread_notify_pending_exc_fn (info);
+
                return MonoResumeThread;
        }
 }
-       
+
+/* LOCKING: called with @thread synch_cs held, and releases it */
 static void
-suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
+async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
 {
-       LOCK_THREAD (thread);
-       if (thread == mono_thread_internal_current ()) {
-               mono_thread_info_begin_self_suspend ();
-               //XXX replace this with better named functions
-               thread->state &= ~ThreadState_SuspendRequested;
-               thread->state |= ThreadState_Suspended;
-               UNLOCK_THREAD (thread);
-               mono_thread_info_end_self_suspend ();
-       } else {
-               SuspendThreadData data = { 0 };
-               data.thread = thread;
-               data.interrupt = interrupt;
+       SuspendThreadData data;
 
-               mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, suspend_thread_critical, &data);
-               if (data.interrupt_token)
-                       mono_thread_info_finish_interrupt (data.interrupt_token);
-               UNLOCK_THREAD (thread);
-       }
+       g_assert (thread != mono_thread_internal_current ());
+
+       data.thread = thread;
+       data.interrupt = interrupt;
+       data.interrupt_token = NULL;
+
+       mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
+       if (data.interrupt_token)
+               mono_thread_info_finish_interrupt (data.interrupt_token);
+
+       UNLOCK_THREAD (thread);
 }
 
-/*This is called with @thread synch_cs held and it must release it*/
+/* LOCKING: called with @thread synch_cs held, and releases it */
 static void
-self_suspend_internal (MonoInternalThread *thread)
+self_suspend_internal (void)
 {
+       MonoInternalThread *thread;
+
+       thread = mono_thread_internal_current ();
+
        mono_thread_info_begin_self_suspend ();
        thread->state &= ~ThreadState_SuspendRequested;
        thread->state |= ThreadState_Suspended;
+
        UNLOCK_THREAD (thread);
+
        mono_thread_info_end_self_suspend ();
 }
 
-
 /*
  * mono_thread_is_foreign:
  * @thread: the thread to query
@@ -4837,7 +4843,7 @@ self_suspend_internal (MonoInternalThread *thread)
  * This function allows one to determine if a thread was created by the mono runtime and has
  * a well defined lifecycle or it's a foreigh one, created by the native environment.
  *
- * Returns: true if @thread was not created by the runtime.
+ * Returns: TRUE if @thread was not created by the runtime.
  */
 mono_bool
 mono_thread_is_foreign (MonoThread *thread)
@@ -4954,13 +4960,7 @@ mono_thread_internal_check_for_interruption_critical (MonoInternalThread *thread
 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;
+       return klass == mono_class_get_appdomain_unloaded_exception_class ();
 }
 
 static inline gboolean