[threads] Make mono_thread_detach_internal static (#5241)
[mono.git] / mono / metadata / threads.c
index 69a00d0b6880b51fd2f4a92f6c52973468f77486..21c6e53c317c9d0be21d916b524ff4c9eb33e76b 100644 (file)
@@ -737,6 +737,149 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean
        return TRUE;
 }
 
+static void
+mono_thread_detach_internal (MonoInternalThread *thread)
+{
+       gboolean removed;
+
+       g_assert (thread != NULL);
+       SET_CURRENT_OBJECT (thread);
+
+       THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
+
+#ifndef HOST_WIN32
+       mono_w32mutex_abandon ();
+#endif
+
+       if (thread->abort_state_handle) {
+               mono_gchandle_free (thread->abort_state_handle);
+               thread->abort_state_handle = 0;
+       }
+
+       thread->abort_exc = NULL;
+       thread->current_appcontext = NULL;
+
+       /*
+        * thread->synch_cs can be NULL if this was called after
+        * ves_icall_System_Threading_InternalThread_Thread_free_internal.
+        * This can happen only during shutdown.
+        * The shutting_down flag is not always set, so we can't assert on it.
+        */
+       if (thread->synch_cs)
+               LOCK_THREAD (thread);
+
+       thread->state |= ThreadState_Stopped;
+       thread->state &= ~ThreadState_Background;
+
+       if (thread->synch_cs)
+               UNLOCK_THREAD (thread);
+
+       /*
+       An interruption request has leaked to cleanup. Adjust the global counter.
+
+       This can happen is the abort source thread finds the abortee (this) thread
+       in unmanaged code. If this thread never trips back to managed code or check
+       the local flag it will be left set and positively unbalance the global counter.
+
+       Leaving the counter unbalanced will cause a performance degradation since all threads
+       will now keep checking their local flags all the time.
+       */
+       mono_thread_clear_interruption_requested (thread);
+
+       mono_threads_lock ();
+
+       if (!threads) {
+               removed = FALSE;
+       } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
+               /* We have to check whether the thread object for the
+                * tid is still the same in the table because the
+                * thread might have been destroyed and the tid reused
+                * in the meantime, in which case the tid would be in
+                * the table, but with another thread object.
+                */
+               removed = FALSE;
+       } else {
+               mono_g_hash_table_remove (threads, (gpointer)thread->tid);
+               removed = TRUE;
+       }
+
+       mono_threads_unlock ();
+
+       /* Don't close the handle here, wait for the object finalizer
+        * to do it. Otherwise, the following race condition applies:
+        *
+        * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
+        *
+        * 2) Some other handle is reassigned the same slot
+        *
+        * 3) Another thread tries to join the first thread, and
+        * blocks waiting for the reassigned handle to be signalled
+        * (which might never happen).  This is possible, because the
+        * thread calling Join() still has a reference to the first
+        * thread's object.
+        */
+
+       /* if the thread is not in the hash it has been removed already */
+       if (!removed) {
+               mono_domain_unset ();
+               mono_memory_barrier ();
+
+               if (mono_thread_cleanup_fn)
+                       mono_thread_cleanup_fn (thread_get_tid (thread));
+
+               goto done;
+       }
+
+       mono_release_type_locks (thread);
+
+       /* Can happen when we attach the profiler helper thread in order to heapshot. */
+       if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
+               MONO_PROFILER_RAISE (thread_stopped, (thread->tid));
+
+       mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
+
+       /*
+        * This will signal async signal handlers that the thread has exited.
+        * The profiler callback needs this to be set, so it cannot be done earlier.
+        */
+       mono_domain_unset ();
+       mono_memory_barrier ();
+
+       if (thread == mono_thread_internal_current ())
+               mono_thread_pop_appdomain_ref ();
+
+       mono_free_static_data (thread->static_data);
+       thread->static_data = NULL;
+       ref_stack_destroy (thread->appdomain_refs);
+       thread->appdomain_refs = NULL;
+
+       g_assert (thread->suspended);
+       mono_os_event_destroy (thread->suspended);
+       g_free (thread->suspended);
+       thread->suspended = NULL;
+
+       if (mono_thread_cleanup_fn)
+               mono_thread_cleanup_fn (thread_get_tid (thread));
+
+       mono_memory_barrier ();
+
+       if (mono_gc_is_moving ()) {
+               MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
+               thread->thread_pinning_ref = NULL;
+       }
+
+done:
+       SET_CURRENT_OBJECT (NULL);
+       mono_domain_unset ();
+
+       mono_thread_info_unset_internal_thread_gchandle ((MonoThreadInfo*) thread->thread_info);
+
+       /* Don't need to close the handle to this thread, even though we took a
+        * reference in mono_thread_attach (), because the GC will do it
+        * when the Thread object is finalised.
+        */
+}
+
 typedef struct {
        gint32 ref;
        MonoThread *thread;
@@ -824,12 +967,12 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack
         * to lock the thread, and the lock is held by thread_start () which waits for
         * start_notify.
         */
-       mono_profiler_thread_start (tid);
+       MONO_PROFILER_RAISE (thread_started, (tid));
 
        /* if the name was set before starting, we didn't invoke the profiler callback */
        if (internal->name) {
                char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
-               mono_profiler_thread_name (internal->tid, tname);
+               MONO_PROFILER_RAISE (thread_name, (internal->tid, tname));
                mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
                g_free (tname);
        }
@@ -1124,8 +1267,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
 
        /* Can happen when we attach the profiler helper thread in order to heapshot. */
        if (!mono_thread_info_current ()->tools_thread)
-               // FIXME: Need a separate callback
-               mono_profiler_thread_start (MONO_NATIVE_THREAD_ID_TO_UINT (tid));
+               MONO_PROFILER_RAISE (thread_started, (MONO_NATIVE_THREAD_ID_TO_UINT (tid)));
 
        return thread;
 }
@@ -1139,149 +1281,6 @@ mono_thread_attach (MonoDomain *domain)
        return mono_thread_attach_full (domain, FALSE);
 }
 
-void
-mono_thread_detach_internal (MonoInternalThread *thread)
-{
-       gboolean removed;
-
-       g_assert (thread != NULL);
-       SET_CURRENT_OBJECT (thread);
-
-       THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
-
-#ifndef HOST_WIN32
-       mono_w32mutex_abandon ();
-#endif
-
-       if (thread->abort_state_handle) {
-               mono_gchandle_free (thread->abort_state_handle);
-               thread->abort_state_handle = 0;
-       }
-
-       thread->abort_exc = NULL;
-       thread->current_appcontext = NULL;
-
-       /*
-        * thread->synch_cs can be NULL if this was called after
-        * ves_icall_System_Threading_InternalThread_Thread_free_internal.
-        * This can happen only during shutdown.
-        * The shutting_down flag is not always set, so we can't assert on it.
-        */
-       if (thread->synch_cs)
-               LOCK_THREAD (thread);
-
-       thread->state |= ThreadState_Stopped;
-       thread->state &= ~ThreadState_Background;
-
-       if (thread->synch_cs)
-               UNLOCK_THREAD (thread);
-
-       /*
-       An interruption request has leaked to cleanup. Adjust the global counter.
-
-       This can happen is the abort source thread finds the abortee (this) thread
-       in unmanaged code. If this thread never trips back to managed code or check
-       the local flag it will be left set and positively unbalance the global counter.
-       
-       Leaving the counter unbalanced will cause a performance degradation since all threads
-       will now keep checking their local flags all the time.
-       */
-       mono_thread_clear_interruption_requested (thread);
-
-       mono_threads_lock ();
-
-       if (!threads) {
-               removed = FALSE;
-       } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
-               /* We have to check whether the thread object for the
-                * tid is still the same in the table because the
-                * thread might have been destroyed and the tid reused
-                * in the meantime, in which case the tid would be in
-                * the table, but with another thread object.
-                */
-               removed = FALSE;
-       } else {
-               mono_g_hash_table_remove (threads, (gpointer)thread->tid);
-               removed = TRUE;
-       }
-
-       mono_threads_unlock ();
-
-       /* Don't close the handle here, wait for the object finalizer
-        * to do it. Otherwise, the following race condition applies:
-        *
-        * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
-        *
-        * 2) Some other handle is reassigned the same slot
-        *
-        * 3) Another thread tries to join the first thread, and
-        * blocks waiting for the reassigned handle to be signalled
-        * (which might never happen).  This is possible, because the
-        * thread calling Join() still has a reference to the first
-        * thread's object.
-        */
-
-       /* if the thread is not in the hash it has been removed already */
-       if (!removed) {
-               mono_domain_unset ();
-               mono_memory_barrier ();
-
-               if (mono_thread_cleanup_fn)
-                       mono_thread_cleanup_fn (thread_get_tid (thread));
-
-               goto done;
-       }
-
-       mono_release_type_locks (thread);
-
-       /* Can happen when we attach the profiler helper thread in order to heapshot. */
-       if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
-               mono_profiler_thread_end (thread->tid);
-
-       mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
-
-       /*
-        * This will signal async signal handlers that the thread has exited.
-        * The profiler callback needs this to be set, so it cannot be done earlier.
-        */
-       mono_domain_unset ();
-       mono_memory_barrier ();
-
-       if (thread == mono_thread_internal_current ())
-               mono_thread_pop_appdomain_ref ();
-
-       mono_free_static_data (thread->static_data);
-       thread->static_data = NULL;
-       ref_stack_destroy (thread->appdomain_refs);
-       thread->appdomain_refs = NULL;
-
-       g_assert (thread->suspended);
-       mono_os_event_destroy (thread->suspended);
-       g_free (thread->suspended);
-       thread->suspended = NULL;
-
-       if (mono_thread_cleanup_fn)
-               mono_thread_cleanup_fn (thread_get_tid (thread));
-
-       mono_memory_barrier ();
-
-       if (mono_gc_is_moving ()) {
-               MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
-               thread->thread_pinning_ref = NULL;
-       }
-
-done:
-       SET_CURRENT_OBJECT (NULL);
-       mono_domain_unset ();
-
-       mono_thread_info_unset_internal_thread_gchandle ((MonoThreadInfo*) thread->thread_info);
-
-       /* Don't need to close the handle to this thread, even though we took a
-        * reference in mono_thread_attach (), because the GC will do it
-        * when the Thread object is finalised.
-        */
-}
-
 /**
  * mono_thread_detach:
  */
@@ -1625,7 +1624,7 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, g
        if (this_obj->name && this_obj->tid) {
                char *tname = mono_string_to_utf8_checked (name, error);
                return_if_nok (error);
-               mono_profiler_thread_name (this_obj->tid, tname);
+               MONO_PROFILER_RAISE (thread_name, (this_obj->tid, tname));
                mono_native_thread_set_name (thread_get_tid (this_obj), tname);
                mono_free (tname);
        }
@@ -2905,7 +2904,7 @@ mono_threads_register_app_context (MonoAppContext *ctx, MonoError *error)
 
        mono_threads_unlock ();
 
-       mono_profiler_context_loaded (ctx);
+       MONO_PROFILER_RAISE (context_loaded, (ctx));
 }
 
 void
@@ -2926,7 +2925,7 @@ mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
 
        //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
 
-       mono_profiler_context_unloaded (ctx);
+       MONO_PROFILER_RAISE (context_unloaded, (ctx));
 }
 
 void
@@ -5107,35 +5106,32 @@ ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoA
 /*
  * mono_threads_attach_coop: called by native->managed wrappers
  *
- * In non-coop mode:
- *  - @dummy: is NULL
+ *  - @dummy:
+ *    - blocking mode: contains gc unsafe transition cookie
+ *    - non-blocking mode: contains random data
  *  - @return: the original domain which needs to be restored, or NULL.
- *
- * In coop mode:
- *  - @dummy: contains the original domain
- *  - @return: a cookie containing current MonoThreadInfo*.
  */
 gpointer
 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
 {
        MonoDomain *orig;
-       gboolean fresh_thread = FALSE;
+       MonoThreadInfo *info;
+       gboolean external;
+
+       orig = mono_domain_get ();
 
        if (!domain) {
                /* Happens when called from AOTed code which is only used in the root domain. */
                domain = mono_get_root_domain ();
+               g_assert (domain);
        }
 
-       g_assert (domain);
-
        /* On coop, when we detached, we moved the thread from  RUNNING->BLOCKING.
         * If we try to reattach we do a BLOCKING->RUNNING transition.  If the thread
         * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
         * we're only responsible for making the cookie. */
-       if (mono_threads_is_coop_enabled ()) {
-               MonoThreadInfo *info = mono_thread_info_current_unchecked ();
-               fresh_thread = !info || !mono_thread_info_is_live (info);
-       }
+       if (mono_threads_is_blocking_transition_enabled ())
+               external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
 
        if (!mono_thread_internal_current ()) {
                mono_thread_attach_full (domain, FALSE);
@@ -5144,61 +5140,52 @@ mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
                mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
        }
 
-       orig = mono_domain_get ();
        if (orig != domain)
                mono_domain_set (domain, TRUE);
 
-       if (!mono_threads_is_coop_enabled ())
-               return orig != domain ? orig : NULL;
-
-       if (fresh_thread) {
-               *dummy = NULL;
-               /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
-                * return the right cookie. */
-               return mono_threads_enter_gc_unsafe_region_cookie ();
-       } else {
-               *dummy = orig;
-               /* thread state (BLOCKING|RUNNING) -> RUNNING */
-               return mono_threads_enter_gc_unsafe_region (dummy);
+       if (mono_threads_is_blocking_transition_enabled ()) {
+               if (external) {
+                       /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
+                        * return the right cookie. */
+                       *dummy = mono_threads_enter_gc_unsafe_region_cookie ();
+               } else {
+                       /* thread state (BLOCKING|RUNNING) -> RUNNING */
+                       *dummy = mono_threads_enter_gc_unsafe_region (dummy);
+               }
        }
+
+       return orig;
 }
 
 /*
  * mono_threads_detach_coop: called by native->managed wrappers
  *
- * In non-coop mode:
  *  - @cookie: the original domain which needs to be restored, or NULL.
- *  - @dummy: is NULL
- *
- * In coop mode:
- *  - @cookie: contains current MonoThreadInfo* if it was in BLOCKING mode, NULL otherwise
- *  - @dummy: contains the original domain
+ *  - @dummy:
+ *    - blocking mode: contains gc unsafe transition cookie
+ *    - non-blocking mode: contains random data
  */
 void
 mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
 {
        MonoDomain *domain, *orig;
 
-       if (!mono_threads_is_coop_enabled ()) {
-               orig = (MonoDomain*) cookie;
-               if (orig)
-                       mono_domain_set (orig, TRUE);
-       } else {
-               orig = (MonoDomain*) *dummy;
+       orig = (MonoDomain*) cookie;
 
-               domain = mono_domain_get ();
-               g_assert (domain);
+       domain = mono_domain_get ();
+       g_assert (domain);
 
+       if (mono_threads_is_blocking_transition_enabled ()) {
                /* it won't do anything if cookie is NULL
                 * thread state RUNNING -> (RUNNING|BLOCKING) */
-               mono_threads_exit_gc_unsafe_region (cookie, dummy);
+               mono_threads_exit_gc_unsafe_region (*dummy, dummy);
+       }
 
-               if (orig != domain) {
-                       if (!orig)
-                               mono_domain_unset ();
-                       else
-                               mono_domain_set (orig, TRUE);
-               }
+       if (orig != domain) {
+               if (!orig)
+                       mono_domain_unset ();
+               else
+                       mono_domain_set (orig, TRUE);
        }
 }