[threads] Allow resetting the name of a threadpool thread (#4350)
[mono.git] / mono / metadata / threadpool.c
index bef76e578159788083c0fbbc1fec9b2ea8b64258..c1f0c1a12a9f233f0dc41f902b60d7d2c6570abf 100644 (file)
@@ -131,7 +131,13 @@ destroy (gpointer unused)
        mono_coop_mutex_destroy (&threadpool->threads_lock);
        mono_coop_cond_destroy (&threadpool->threads_exit_cond);
 
-       g_free (threadpool);
+       /* We cannot free the threadpool, because there is a race
+        * on shutdown where a managed thread may request a new
+        * threadpool thread, but we already destroyed the
+        * threadpool. So to avoid a use-after-free, we simply do
+        * not free the threadpool, as we won't be able to access
+        * the threadpool anyway because the ref count will be 0 */
+       // g_free (threadpool);
 }
 
 static void
@@ -181,6 +187,7 @@ cleanup (void)
 
        mono_coop_mutex_unlock (&threadpool->threads_lock);
 
+#if 0
        /* give a chance to the other threads to exit */
        mono_thread_info_yield ();
 
@@ -199,6 +206,7 @@ cleanup (void)
        }
 
        mono_coop_mutex_unlock (&threadpool->threads_lock);
+#endif
 
        mono_threadpool_worker_cleanup (threadpool->worker);
 
@@ -340,6 +348,15 @@ tpdomain_get_next (ThreadPoolDomain *current)
        return tpdomain;
 }
 
+static MonoObject*
+try_invoke_perform_wait_callback (MonoObject** exc, MonoError *error)
+{
+       HANDLE_FUNCTION_ENTER ();
+       mono_error_init (error);
+       MonoObject *res = mono_runtime_try_invoke (mono_defaults.threadpool_perform_wait_callback_method, NULL, NULL, exc, error);
+       HANDLE_FUNCTION_RETURN_VAL (res);
+}
+
 static void
 worker_callback (gpointer unused)
 {
@@ -405,6 +422,9 @@ worker_callback (gpointer unused)
 
                domains_unlock ();
 
+               mono_thread_set_name_internal (thread, mono_string_new (mono_get_root_domain (), "Threadpool worker"), FALSE, TRUE, &error);
+               mono_error_assert_ok (&error);
+
                mono_thread_clr_state (thread, (MonoThreadState)~ThreadState_Background);
                if (!mono_thread_test_state (thread , ThreadState_Background))
                        ves_icall_System_Threading_Thread_SetState (thread, ThreadState_Background);
@@ -413,7 +433,7 @@ worker_callback (gpointer unused)
                if (mono_domain_set (tpdomain->domain, FALSE)) {
                        MonoObject *exc = NULL, *res;
 
-                       res = mono_runtime_try_invoke (mono_defaults.threadpool_perform_wait_callback_method, NULL, NULL, &exc, &error);
+                       res = try_invoke_perform_wait_callback (&exc, &error);
                        if (exc || !mono_error_ok(&error)) {
                                if (exc == NULL)
                                        exc = (MonoObject *) mono_error_convert_to_exception (&error);
@@ -551,7 +571,7 @@ mono_threadpool_end_invoke (MonoAsyncResult *ares, MonoArray **out_args, MonoObj
                        g_assert(wait_event);
                        MonoWaitHandle *wait_handle = mono_wait_handle_new (mono_object_domain (ares), wait_event, error);
                        if (!is_ok (error)) {
-                               CloseHandle (wait_event);
+                               mono_w32event_close (wait_event);
                                return NULL;
                        }
                        MONO_OBJECT_SETREF (ares, handle, (MonoObject*) wait_handle);
@@ -787,11 +807,17 @@ ves_icall_System_Threading_ThreadPool_RequestWorkerThread (void)
        if (mono_domain_is_unloading (domain))
                return FALSE;
 
+       if (!mono_refcount_tryinc (threadpool)) {
+               /* threadpool has been destroyed, we are shutting down */
+               return FALSE;
+       }
+
        domains_lock ();
 
        /* synchronize with mono_threadpool_remove_domain_jobs */
        if (mono_domain_is_unloading (domain)) {
                domains_unlock ();
+               mono_refcount_dec (threadpool);
                return FALSE;
        }
 
@@ -804,14 +830,14 @@ ves_icall_System_Threading_ThreadPool_RequestWorkerThread (void)
        domains_unlock ();
 
        COUNTER_ATOMIC (threadpool, counter, {
-               if (counter._.starting == 16)
+               if (counter._.starting == 16) {
+                       mono_refcount_dec (threadpool);
                        return TRUE;
+               }
 
                counter._.starting ++;
        });
 
-       mono_refcount_inc (threadpool);
-
        mono_threadpool_worker_enqueue (threadpool->worker, worker_callback, NULL);
 
        return TRUE;