Fix sporadic hang in Mono.Debugger.Soft test suite on Windows.
[mono.git] / mono / metadata / threads.c
index 8eb4fe5f24e474e619a68bb2e17a00cb1f52f043..cc4705c813336a8dfb7d1936d43f1324267aba4c 100644 (file)
@@ -45,6 +45,7 @@
 #include <mono/utils/mono-error-internals.h>
 #include <mono/utils/os-event.h>
 #include <mono/utils/mono-threads-debug.h>
+#include <mono/utils/unlocked.h>
 #include <mono/metadata/w32handle.h>
 #include <mono/metadata/w32event.h>
 #include <mono/metadata/w32mutex.h>
@@ -53,6 +54,7 @@
 #include <mono/metadata/abi-details.h>
 #include <mono/metadata/w32error.h>
 #include <mono/utils/w32api.h>
+#include <mono/utils/mono-os-wait.h>
 
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
@@ -152,7 +154,7 @@ static MonoGHashTable *threads_starting_up = NULL;
 /* Contains tids */
 /* Protected by the threads lock */
 static GHashTable *joinable_threads;
-static int joinable_thread_count;
+static gint32 joinable_thread_count;
 
 #define SET_CURRENT_OBJECT(x) mono_tls_set_thread (x)
 #define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_tls_get_thread ()
@@ -198,9 +200,6 @@ 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)
 {
@@ -1022,8 +1021,6 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack
 
        mono_thread_detach_internal (internal);
 
-       internal->tid = 0;
-
        return(0);
 }
 
@@ -1801,7 +1798,7 @@ mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError
 }
 
 gboolean
-ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
+ves_icall_System_Threading_Thread_Join_internal (MonoThread *this_obj, int ms)
 {
        MonoInternalThread *thread = this_obj->internal_thread;
        MonoThreadHandle *handle = thread->handle;
@@ -1821,30 +1818,42 @@ ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
                return FALSE;
        }
 
+       MonoNativeThreadId tid = thread_get_tid (thread);
+
        UNLOCK_THREAD (thread);
 
-       if(ms== -1) {
-               ms=MONO_INFINITE_WAIT;
-       }
+       if (ms == -1)
+               ms = MONO_INFINITE_WAIT;
        THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
-       
+
        mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
 
-       ret=mono_join_uninterrupted (handle, ms, &error);
+       ret = mono_join_uninterrupted (handle, ms, &error);
 
        mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
 
        mono_error_set_pending_exception (&error);
 
-       if(ret==MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
+       if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
                THREAD_DEBUG (g_message ("%s: join successful", __func__));
 
-               return(TRUE);
+#ifdef HOST_WIN32
+               /* TODO: Do this on Unix platforms as well. See PR #5454 for context.  */
+               /* Wait for the thread to really exit */
+               MONO_ENTER_GC_SAFE;
+               /* This shouldn't block */
+               mono_threads_join_lock ();
+               mono_native_thread_join (tid);
+               mono_threads_join_unlock ();
+               MONO_EXIT_GC_SAFE;
+#endif
+
+               return TRUE;
        }
        
        THREAD_DEBUG (g_message ("%s: join failed", __func__));
 
-       return(FALSE);
+       return FALSE;
 }
 
 #define MANAGED_WAIT_FAILED 0x7fffffff
@@ -1896,9 +1905,9 @@ ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 n
                MONO_ENTER_GC_SAFE;
 #ifdef HOST_WIN32
                if (numhandles != 1)
-                       ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
+                       ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_multiple_objects_ex(numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
                else
-                       ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], timeoutLeft, TRUE), 1);
+                       ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (handles [0], timeoutLeft, TRUE), 1);
 #else
                /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
                ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE);
@@ -1948,7 +1957,7 @@ ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal,
        
        MONO_ENTER_GC_SAFE;
 #ifdef HOST_WIN32
-       ret = mono_w32handle_convert_wait_ret (SignalObjectAndWait (toSignal, toWait, ms, TRUE), 1);
+       ret = mono_w32handle_convert_wait_ret (mono_win32_signal_object_and_wait (toSignal, toWait, ms, TRUE), 1);
 #else
        ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
 #endif
@@ -2281,7 +2290,7 @@ mono_thread_current_check_pending_interrupt (void)
 }
 
 static gboolean
-request_thread_abort (MonoInternalThread *thread, MonoObject *state)
+request_thread_abort (MonoInternalThread *thread, MonoObject *state, gboolean appdomain_unload)
 {
        LOCK_THREAD (thread);
        
@@ -2298,6 +2307,11 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state)
        }
 
        thread->state |= ThreadState_AbortRequested;
+       if (appdomain_unload)
+               thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT;
+       else
+               thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT;
+
        if (thread->abort_state_handle)
                mono_gchandle_free (thread->abort_state_handle);
        if (state) {
@@ -2322,7 +2336,7 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state)
 void
 ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
 {
-       if (!request_thread_abort (thread, state))
+       if (!request_thread_abort (thread, state, FALSE))
                return;
 
        if (thread == mono_thread_internal_current ()) {
@@ -2340,11 +2354,11 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject
  * \p thread MUST NOT be the current thread.
  */
 void
-mono_thread_internal_abort (MonoInternalThread *thread)
+mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload)
 {
        g_assert (thread != mono_thread_internal_current ());
 
-       if (!request_thread_abort (thread, NULL))
+       if (!request_thread_abort (thread, NULL, appdomain_unload))
                return;
        async_abort_internal (thread, TRUE);
 }
@@ -2353,17 +2367,23 @@ void
 ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
-       gboolean was_aborting;
+       gboolean was_aborting, is_domain_abort;
 
        LOCK_THREAD (thread);
        was_aborting = thread->state & ThreadState_AbortRequested;
-       thread->state &= ~ThreadState_AbortRequested;
+       is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT; 
+
+       if (was_aborting && !is_domain_abort)
+               thread->state &= ~ThreadState_AbortRequested;
        UNLOCK_THREAD (thread);
 
        if (!was_aborting) {
                const char *msg = "Unable to reset abort because no abort was requested";
                mono_set_pending_exception (mono_get_exception_thread_state (msg));
                return;
+       } else if (is_domain_abort) {
+               /* Silently ignore abort resets in unloading appdomains */
+               return;
        }
 
        mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
@@ -2563,7 +2583,7 @@ mono_thread_stop (MonoThread *thread)
 {
        MonoInternalThread *internal = thread->internal_thread;
 
-       if (!request_thread_abort (internal, NULL))
+       if (!request_thread_abort (internal, NULL, FALSE))
                return;
 
        if (internal == mono_thread_internal_current ()) {
@@ -3036,6 +3056,8 @@ mono_thread_callbacks_init (void)
 void
 mono_thread_cleanup (void)
 {
+       mono_threads_join_threads ();
+
 #if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
        /* The main thread must abandon any held mutexes (particularly
         * important for named mutexes as they are shared across
@@ -3209,7 +3231,7 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
                wait->num++;
 
                THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
-               mono_thread_internal_abort (thread);
+               mono_thread_internal_abort (thread, FALSE);
        }
 
        return TRUE;
@@ -3924,7 +3946,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
                if (user_data.wait.num > 0) {
                        /* Abort the threads outside the threads lock */
                        for (i = 0; i < user_data.wait.num; ++i)
-                               mono_thread_internal_abort (user_data.wait.threads [i]);
+                               mono_thread_internal_abort (user_data.wait.threads [i], TRUE);
 
                        /*
                         * We should wait for the threads either to abort, or to leave the
@@ -4433,9 +4455,10 @@ mono_thread_execute_interruption (void)
        }
 
        /* this will consume pending APC calls */
-#ifdef HOST_WIN32
-       WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
+#ifdef USE_WINDOWS_BACKEND
+       mono_win32_wait_for_single_object_ex (GetCurrentThread (), 0, TRUE);
 #endif
+
        /* Clear the interrupted flag of the thread so it can wait again */
        mono_thread_info_clear_self_interrupt ();
 
@@ -4503,9 +4526,8 @@ mono_thread_request_interruption (gboolean running_managed)
 
                /* this will awake the thread if it is in WaitForSingleObject 
                   or similar */
-               /* Our implementation of this function ignores the func argument */
-#ifdef HOST_WIN32
-               QueueUserAPC ((PAPCFUNC)dummy_apc, thread->native_handle, (ULONG_PTR)NULL);
+#ifdef USE_WINDOWS_BACKEND
+               mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid);
 #else
                mono_thread_info_self_interrupt ();
 #endif
@@ -5000,7 +5022,7 @@ mono_threads_add_joinable_thread (gpointer tid)
        if (!joinable_threads)
                joinable_threads = g_hash_table_new (NULL, NULL);
        g_hash_table_insert (joinable_threads, tid, tid);
-       joinable_thread_count ++;
+       UnlockedIncrement (&joinable_thread_count);
        joinable_threads_unlock ();
 
        mono_gc_finalize_notify ();
@@ -5024,7 +5046,7 @@ mono_threads_join_threads (void)
        gboolean found;
 
        /* Fastpath */
-       if (!joinable_thread_count)
+       if (!UnlockedRead (&joinable_thread_count))
                return;
 
        while (TRUE) {
@@ -5035,7 +5057,7 @@ mono_threads_join_threads (void)
                        g_hash_table_iter_next (&iter, &key, (void**)&tid);
                        thread = (pthread_t)tid;
                        g_hash_table_remove (joinable_threads, key);
-                       joinable_thread_count --;
+                       UnlockedDecrement (&joinable_thread_count);
                        found = TRUE;
                }
                joinable_threads_unlock ();
@@ -5073,7 +5095,7 @@ mono_thread_join (gpointer tid)
                joinable_threads = g_hash_table_new (NULL, NULL);
        if (g_hash_table_lookup (joinable_threads, tid)) {
                g_hash_table_remove (joinable_threads, tid);
-               joinable_thread_count --;
+               UnlockedDecrement (&joinable_thread_count);
                found = TRUE;
        }
        joinable_threads_unlock ();