[sgen] Don't pass redundant `scan_mod_union` to `major_finish_collection()`.
[mono.git] / mono / metadata / threads.c
index e4cf9b8904247b6ba0cef1e19145df004a4cf4e7..68a4d9b9b3aaa8c72346546ec284958f09e659b3 100644 (file)
@@ -14,7 +14,6 @@
 #include <config.h>
 
 #include <glib.h>
-#include <signal.h>
 #include <string.h>
 
 #include <mono/metadata/object.h>
 
 #include <mono/metadata/gc-internal.h>
 
-#if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64)
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
 #define USE_TKILL_ON_ANDROID 1
 #endif
 
@@ -1053,7 +1056,7 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this,
 
        if ((internal->state & ThreadState_Unstarted) == 0) {
                UNLOCK_THREAD (internal);
-               mono_raise_exception (mono_get_exception_thread_state ("Thread has already been started."));
+               mono_set_pending_exception (mono_get_exception_thread_state ("Thread has already been started."));
                return NULL;
        }
 
@@ -1113,7 +1116,8 @@ ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThre
        }
 }
 
-void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
+void
+ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
 {
        guint32 res;
        MonoInternalThread *thread = mono_thread_internal_current ();
@@ -1247,6 +1251,17 @@ ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj
        mono_thread_set_name_internal (this_obj, name, TRUE);
 }
 
+int
+ves_icall_System_Threading_Thread_GetPriority (MonoInternalThread *thread)
+{
+       return ThreadPriority_Lowest;
+}
+
+void
+ves_icall_System_Threading_Thread_SetPriority (MonoInternalThread *thread, int priority)
+{
+}
+
 /* If the array is already in the requested domain, we just return it,
    otherwise we return a copy in that domain. */
 static MonoArray*
@@ -1302,8 +1317,9 @@ mono_thread_internal_current (void)
        return res;
 }
 
-gboolean ves_icall_System_Threading_Thread_Join_internal(MonoInternalThread *this,
-                                                        int ms, HANDLE thread)
+gboolean
+ves_icall_System_Threading_Thread_Join_internal(MonoInternalThread *this,
+                                                                                               int ms, HANDLE thread)
 {
        MonoInternalThread *cur_thread = mono_thread_internal_current ();
        gboolean ret;
@@ -1315,7 +1331,7 @@ gboolean ves_icall_System_Threading_Thread_Join_internal(MonoInternalThread *thi
        if ((this->state & ThreadState_Unstarted) != 0) {
                UNLOCK_THREAD (this);
                
-               mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started."));
+               mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started."));
                return FALSE;
        }
 
@@ -1757,6 +1773,13 @@ gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *locati
        return InterlockedCompareExchange(location, value, comparand);
 }
 
+gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
+{
+       gint32 r = InterlockedCompareExchange(location, value, comparand);
+       *success = r == comparand;
+       return r;
+}
+
 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
 {
        MonoObject *res;
@@ -1966,7 +1989,7 @@ void mono_thread_current_check_pending_interrupt ()
 int  
 mono_thread_get_abort_signal (void)
 {
-#ifdef HOST_WIN32
+#if defined (HOST_WIN32) || !defined (HAVE_SIGACTION)
        return -1;
 #elif defined(PLATFORM_ANDROID)
        return SIGUNUSED;
@@ -2104,7 +2127,8 @@ ves_icall_System_Threading_Thread_ResetAbort (void)
 
        if (!was_aborting) {
                const char *msg = "Unable to reset abort because no abort was requested";
-               mono_raise_exception (mono_get_exception_thread_state (msg));
+               mono_set_pending_exception (mono_get_exception_thread_state (msg));
+               return;
        }
        thread->abort_exc = NULL;
        if (thread->abort_state_handle) {
@@ -2158,7 +2182,8 @@ ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this)
                MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
                if (exc)
                        MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
-               mono_raise_exception (invalid_op_exc);
+               mono_set_pending_exception (invalid_op_exc);
+               return NULL;
        }
 
        return deserialized;
@@ -2196,8 +2221,10 @@ mono_thread_suspend (MonoInternalThread *thread)
 void
 ves_icall_System_Threading_Thread_Suspend (MonoInternalThread *thread)
 {
-       if (!mono_thread_suspend (thread))
-               mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
+       if (!mono_thread_suspend (thread)) {
+               mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
+               return;
+       }
 }
 
 static gboolean
@@ -2226,8 +2253,10 @@ mono_thread_resume (MonoInternalThread *thread)
 void
 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
 {
-       if (!thread->internal_thread || !mono_thread_resume (thread->internal_thread))
-               mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
+       if (!thread->internal_thread || !mono_thread_resume (thread->internal_thread)) {
+               mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
+               return;
+       }
 }
 
 static gboolean
@@ -3195,9 +3224,10 @@ print_stack_frame_to_string (MonoStackFrameInfo *frame, MonoContext *ctx, gpoint
        return FALSE;
 }
 
-static void
-print_thread_dump (MonoInternalThread *thread, MonoThreadInfo *info)
+static SuspendThreadResult
+print_thread_dump (MonoThreadInfo *info, gpointer ud)
 {
+       MonoInternalThread *thread = ud;
        GString* text = g_string_new (0);
        char *name;
        GError *error = NULL;
@@ -3222,8 +3252,7 @@ print_thread_dump (MonoInternalThread *thread, MonoThreadInfo *info)
 #endif
 #endif
 
-       mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, text);
-       mono_thread_info_finish_suspend_and_resume (info);
+       mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, text);
 
        fprintf (stdout, "%s", text->str);
 
@@ -3233,13 +3262,13 @@ print_thread_dump (MonoInternalThread *thread, MonoThreadInfo *info)
 
        g_string_free (text, TRUE);
        fflush (stdout);
+       return MonoResumeThread;
 }
 
 static void
 dump_thread (gpointer key, gpointer value, gpointer user)
 {
        MonoInternalThread *thread = (MonoInternalThread *)value;
-       MonoThreadInfo *info;
 
        if (thread == mono_thread_internal_current ())
                return;
@@ -3251,12 +3280,7 @@ dump_thread (gpointer key, gpointer value, gpointer user)
        We probably should loop a bit around trying to get it to either managed code
        or WSJ state.
        */
-       info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gpointer)(gsize)thread->tid, FALSE);
-
-       if (!info)
-               return;
-
-       print_thread_dump (thread, info);
+       mono_thread_info_safe_suspend_and_run ((MonoNativeThreadId)(gsize)thread->tid, FALSE, print_thread_dump, thread);
 }
 
 void
@@ -3993,13 +4017,13 @@ mono_thread_alloc_tls (MonoReflectionType *type)
        return tls_offset;
 }
 
-void
-mono_thread_destroy_tls (uint32_t tls_offset)
+static void
+destroy_tls (MonoDomain *domain, uint32_t tls_offset)
 {
        MonoTlsDataRecord *prev = NULL;
        MonoTlsDataRecord *cur;
        guint32 size = 0;
-       MonoDomain *domain = mono_domain_get ();
+
        mono_domain_lock (domain);
        cur = domain->tlsrec_list;
        while (cur) {
@@ -4020,6 +4044,12 @@ mono_thread_destroy_tls (uint32_t tls_offset)
                mono_special_static_data_free_slot (tls_offset, size);
 }
 
+void
+mono_thread_destroy_tls (uint32_t tls_offset)
+{
+       destroy_tls (mono_domain_get (), tls_offset);
+}
+
 /*
  * This is just to ensure cleanup: the finalizers should have taken care, so this is not perf-critical.
  */
@@ -4027,7 +4057,7 @@ void
 mono_thread_destroy_domain_tls (MonoDomain *domain)
 {
        while (domain->tlsrec_list)
-               mono_thread_destroy_tls (domain->tlsrec_list->tls_offset);
+               destroy_tls (domain, domain->tlsrec_list->tls_offset);
 }
 
 static MonoClassField *local_slots = NULL;
@@ -4106,7 +4136,8 @@ static void CALLBACK dummy_apc (ULONG_PTR param)
  * Performs the operation that the requested thread state requires (abort,
  * suspend or stop)
  */
-static MonoException* mono_thread_execute_interruption (MonoInternalThread *thread)
+static MonoException*
+mono_thread_execute_interruption (MonoInternalThread *thread)
 {
        LOCK_THREAD (thread);
 
@@ -4257,35 +4288,56 @@ gboolean mono_thread_interruption_requested ()
        return FALSE;
 }
 
-static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
+static MonoException*
+mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
 
        /* The thread may already be stopping */
        if (thread == NULL)
-               return;
+               return NULL;
 
        if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
                MonoException* exc = mono_thread_execute_interruption (thread);
-               if (exc) mono_raise_exception (exc);
+               if (exc)
+                       return exc;
        }
+       return NULL;
 }
 
 /*
  * Performs the interruption of the current thread, if one has been requested,
  * and the thread is not running a protected wrapper.
+ * Return the exception which needs to be thrown, if any.
  */
-void mono_thread_interruption_checkpoint ()
+MonoException*
+mono_thread_interruption_checkpoint (void)
 {
-       mono_thread_interruption_checkpoint_request (FALSE);
+       return mono_thread_interruption_checkpoint_request (FALSE);
 }
 
 /*
  * Performs the interruption of the current thread, if one has been requested.
+ * Return the exception which needs to be thrown, if any.
  */
-void mono_thread_force_interruption_checkpoint ()
+MonoException*
+mono_thread_force_interruption_checkpoint_noraise (void)
 {
-       mono_thread_interruption_checkpoint_request (TRUE);
+       return mono_thread_interruption_checkpoint_request (TRUE);
+}
+
+/*
+ * Performs the interruption of the current thread, if one has been requested.
+ * Throw the exception which needs to be thrown, if any.
+ */
+void
+mono_thread_force_interruption_checkpoint (void)
+{
+       MonoException *ex;
+
+       ex = mono_thread_interruption_checkpoint_request (TRUE);
+       if (ex)
+               mono_raise_exception (ex);
 }
 
 /*
@@ -4472,7 +4524,7 @@ mono_thread_kill (MonoInternalThread *thread, int signal)
        /* Workaround pthread_kill abort() in NaCl glibc. */
        return -1;
 #endif
-#ifdef HOST_WIN32
+#if defined (HOST_WIN32) || !defined (HAVE_SIGACTION)
        /* Win32 uses QueueUserAPC and callers of this are guarded */
        g_assert_not_reached ();
 #else
@@ -4507,7 +4559,7 @@ self_interrupt_thread (void *_unused)
        MonoThreadInfo *info = mono_thread_info_current ();
        MonoException *exc = mono_thread_execute_interruption (mono_thread_internal_current ()); 
        if (exc) /*We must use _with_context since we didn't trampoline into the runtime*/
-               mono_raise_exception_with_context (exc, &info->suspend_state.ctx);
+               mono_raise_exception_with_context (exc, &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx); /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
        g_assert_not_reached (); /*this MUST not happen since we can't resume from an async call*/
 }
 
@@ -4533,64 +4585,49 @@ mono_thread_info_get_last_managed (MonoThreadInfo *info)
        MonoJitInfo *ji = NULL;
        if (!info)
                return NULL;
-       mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, &ji);
+       mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
        return ji;
 }
 
-static void
-abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception, gboolean install_async_abort)
+typedef struct {
+       MonoInternalThread *thread;
+       gboolean install_async_abort;
+       gpointer interrupt_handle;
+} AbortThreadData;
+
+static SuspendThreadResult
+abort_thread_critical (MonoThreadInfo *info, gpointer ud)
 {
-       MonoJitInfo *ji;
-       MonoThreadInfo *info = NULL;
+       AbortThreadData *data = ud;
+       MonoInternalThread *thread = data->thread;
+       MonoJitInfo *ji = NULL;
        gboolean protected_wrapper;
        gboolean running_managed;
 
-       if (!mono_thread_info_new_interrupt_enabled ()) {
-               signal_thread_state_change (thread);
-               return;
-       }
-
-       /*
-       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_interrupt (thread->handle);
-               return;
-       }
-
-       /*FIXME we need to check 2 conditions here, request to interrupt this thread or if the target died*/
-       if (!(info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gsize)thread->tid, TRUE))) {
-               return;
-       }
-
-       if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (&info->suspend_state)) {
-               mono_thread_info_finish_suspend_and_resume (info);
-               return;
-       }
+       if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
+               return MonoResumeThread;
 
        /*someone is already interrupting it*/
-       if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1) {
-               mono_thread_info_finish_suspend_and_resume (info);
-               return;
-       }
+       if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+               return MonoResumeThread;
+
        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));
-       running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
+       running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
 
        if (!protected_wrapper && running_managed) {
                /*We are in managed code*/
                /*Set the thread to call */
-               if (install_async_abort)
+               if (data->install_async_abort)
                        mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
-               mono_thread_info_finish_suspend_and_resume (info);
+               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
@@ -4598,36 +4635,78 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
                 * functions in the io-layer until the signal handler calls QueueUserAPC which will
                 * make it return.
                 */
-               gpointer interrupt_handle;
+               data->interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
+               return MonoResumeThread;
+       }
+}
 
-               if (mono_thread_notify_pending_exc_fn)
-                       /* The JIT will notify the thread about the interruption */
-                       mono_thread_notify_pending_exc_fn (info);
+static void
+abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception, gboolean install_async_abort)
+{
+       AbortThreadData data = { 0 };
+       data.thread = thread;
+       data.install_async_abort = install_async_abort;
+
+       if (!mono_thread_info_new_interrupt_enabled ()) {
+               signal_thread_state_change (thread);
+               return;
+       }
 
-               interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
-               mono_thread_info_finish_suspend_and_resume (info);
-               mono_thread_info_finish_interrupt (interrupt_handle);
+       /*
+       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_interrupt (thread->handle);
+               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);
        /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
 }
 
-static void
-transition_to_suspended (MonoInternalThread *thread, MonoThreadInfo *info)
+typedef struct{
+       MonoInternalThread *thread;
+       gboolean interrupt;
+       gpointer interrupt_handle;
+} SuspendThreadData;
+
+static SuspendThreadResult
+suspend_thread_critical (MonoThreadInfo *info, gpointer ud)
 {
-       if ((thread->state & ThreadState_SuspendRequested) == 0) {
-               g_assert (0); /*FIXME we should not reach this */
-               /*Make sure we balance the suspend count.*/
-               if (info)
-                       mono_thread_info_finish_suspend_and_resume (info);
-       } else {
+       SuspendThreadData *data = ud;
+       MonoInternalThread *thread = data->thread;
+       MonoJitInfo *ji = NULL;
+       gboolean protected_wrapper;
+       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));
+       running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
+
+       if (running_managed && !protected_wrapper) {
                thread->state &= ~ThreadState_SuspendRequested;
                thread->state |= ThreadState_Suspended;
-               if (info)
-                       mono_thread_info_finish_suspend (info);
+               return KeepSuspended;
+       } else {
+               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);
+               
+               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;
        }
-       UNLOCK_THREAD (thread);
 }
-
+       
 static void
 suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
 {
@@ -4638,41 +4717,21 @@ suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
 
        LOCK_THREAD (thread);
        if (thread == mono_thread_internal_current ()) {
-               transition_to_suspended (thread, NULL);
-               mono_thread_info_self_suspend ();
+               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 {
-               MonoThreadInfo *info;
-               MonoJitInfo *ji;
-               gboolean protected_wrapper;
-               gboolean running_managed;
-
-               /*A null info usually means the thread is already dead. */
-               if (!(info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gsize)thread->tid, interrupt))) {
-                       UNLOCK_THREAD (thread);
-                       return;
-               }
+               SuspendThreadData data = { 0 };
+               data.thread = thread;
+               data.interrupt = interrupt;
 
-               ji = mono_thread_info_get_last_managed (info);
-               protected_wrapper = ji && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
-               running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
-
-               if (running_managed && !protected_wrapper) {
-                       transition_to_suspended (thread, info);
-               } else {
-                       gpointer interrupt_handle;
-
-                       if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
-                               InterlockedIncrement (&thread_interruption_requested);
-                       if (interrupt)
-                               interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
-                       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);
-                       mono_thread_info_finish_suspend_and_resume (info);
-                       if (interrupt)
-                               mono_thread_info_finish_interrupt (interrupt_handle);
-                       UNLOCK_THREAD (thread);
-               }
+               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);
+               UNLOCK_THREAD (thread);
        }
 }
 
@@ -4716,8 +4775,11 @@ self_suspend_internal (MonoInternalThread *thread)
                return;
        }
 
-       transition_to_suspended (thread, NULL);
-       mono_thread_info_self_suspend ();
+       mono_thread_info_begin_self_suspend ();
+       thread->state &= ~ThreadState_SuspendRequested;
+       thread->state |= ThreadState_Suspended;
+       UNLOCK_THREAD (thread);
+       mono_thread_info_end_self_suspend ();
 }
 
 /*This is called with @thread synch_cs held and it must release it*/