[io-layer] Extract error (#4279)
[mono.git] / mono / metadata / threadpool.c
index bef76e578159788083c0fbbc1fec9b2ea8b64258..dfd61ea3f589177a1aa7c7f9cd877e85488a6052 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
@@ -340,6 +346,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)
 {
@@ -413,7 +428,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 +566,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 +802,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 +825,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;