Add a hack to allow us to wait for the finalizer thread to exit using pthread_join...
authorZoltan Varga <vargaz@gmail.com>
Mon, 21 Jan 2013 03:52:01 +0000 (04:52 +0100)
committerZoltan Varga <vargaz@gmail.com>
Mon, 21 Jan 2013 14:36:09 +0000 (15:36 +0100)
mono/io-layer/processes.h
mono/io-layer/wthreads.c
mono/metadata/gc.c
mono/metadata/threadpool.c
mono/metadata/threads-types.h
mono/metadata/threads.c

index 486d0005c3c0bac459e4b9379e9b482edfbf57ba..b0a9d805908307ca1ba5399a8fa95280fcd20661 100644 (file)
@@ -146,6 +146,10 @@ struct _WapiShellExecuteInfo
 #define CREATE_DEFAULT_ERROR_MODE 0x04000000
 #define CREATE_NO_WINDOW 0x08000000
 
+#ifndef HOST_WIN32
+#define CREATE_NO_DETACH 0x10000000
+#endif
+
 #ifdef NEW_STUFF
 #define CREATE_PRESERVE_CODE_AUTHZ_LEVEL find out the value for this one...
 #endif
index 0a42235637809a483740b933501bc01ff08ba8ff..b5d6d6c551181d3a322bf3f8f49a783c6fec3b83 100644 (file)
@@ -247,9 +247,11 @@ static void *thread_start_routine (gpointer args)
 {
        struct _WapiHandle_thread *thread = (struct _WapiHandle_thread *)args;
        int thr_ret;
-       
-       thr_ret = mono_gc_pthread_detach (pthread_self ());
-       g_assert (thr_ret == 0);
+
+       if (!(thread->create_flags & CREATE_NO_DETACH)) {
+               thr_ret = mono_gc_pthread_detach (pthread_self ());
+               g_assert (thr_ret == 0);
+       }
 
        thr_ret = pthread_setspecific (thread_hash_key,
                                       (void *)thread->handle);
index bdf58170ae3e3c27745dd9d8785b7b897683deff..796ff3698cb2fc67cfd67218ee755703622514c4 100644 (file)
@@ -1118,7 +1118,7 @@ static
 void
 mono_gc_init_finalizer_thread (void)
 {
-       gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
+       gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, TRUE, 0);
        ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
 }
 
@@ -1207,6 +1207,15 @@ mono_gc_cleanup (void)
                                /* Wait for the thread to actually exit */
                                ret = WaitForSingleObjectEx (gc_thread->handle, INFINITE, TRUE);
                                g_assert (ret == WAIT_OBJECT_0);
+
+#ifndef HOST_WIN32
+                               /*
+                                * The above wait only waits for the exited event to be signalled, the thread might still be running. To fix this race, we
+                                * create the finalizer thread without calling pthread_detach () on it, so we can wait for it manually.
+                                */
+                               ret = pthread_join ((gpointer)(gsize)gc_thread->tid, NULL);
+                               g_assert (ret == 0);
+#endif
                        }
                }
                gc_thread = NULL;
index 85ae229e37afba5ba83006b23beeae50dbd2dc92..e8a2f1a45741b3248d33e1a1ed238c1d91c81d17 100644 (file)
@@ -515,7 +515,7 @@ socket_io_init (SocketIOData *data)
                data->event_system = POLL_BACKEND;
 
        init_event_system (data);
-       mono_thread_create_internal (mono_get_root_domain (), data->wait, data, TRUE, SMALL_STACK);
+       mono_thread_create_internal (mono_get_root_domain (), data->wait, data, TRUE, FALSE, SMALL_STACK);
        LeaveCriticalSection (&data->io_lock);
        data->inited = 2;
        threadpool_start_thread (&async_io_tp);
@@ -667,7 +667,7 @@ threadpool_start_idle_threads (ThreadPool *tp)
 #ifndef DISABLE_PERFCOUNTERS
                mono_perfcounter_update_value (tp->pc_nthreads, TRUE, 1);
 #endif
-               mono_thread_create_internal (mono_get_root_domain (), tp->async_invoke, tp, TRUE, stack_size);
+               mono_thread_create_internal (mono_get_root_domain (), tp->async_invoke, tp, TRUE, FALSE, stack_size);
                SleepEx (100, TRUE);
        } while (1);
 }
@@ -1009,7 +1009,7 @@ threadpool_start_thread (ThreadPool *tp)
 #ifndef DISABLE_PERFCOUNTERS
                        mono_perfcounter_update_value (tp->pc_nthreads, TRUE, 1);
 #endif
-                       mono_thread_create_internal (mono_get_root_domain (), tp->async_invoke, tp, TRUE, stack_size);
+                       mono_thread_create_internal (mono_get_root_domain (), tp->async_invoke, tp, TRUE, FALSE, stack_size);
                        return TRUE;
                }
        }
@@ -1048,12 +1048,12 @@ threadpool_append_jobs (ThreadPool *tp, MonoObject **jobs, gint njobs)
 
        if (tp->pool_status == 0 && InterlockedCompareExchange (&tp->pool_status, 1, 0) == 0) {
                if (!tp->is_io) {
-                       mono_thread_create_internal (mono_get_root_domain (), monitor_thread, NULL, TRUE, SMALL_STACK);
+                       mono_thread_create_internal (mono_get_root_domain (), monitor_thread, NULL, TRUE, FALSE, SMALL_STACK);
                        threadpool_start_thread (tp);
                }
                /* Create on demand up to min_threads to avoid startup penalty for apps that don't use
                 * the threadpool that much
-               * mono_thread_create_internal (mono_get_root_domain (), threadpool_start_idle_threads, tp, TRUE, SMALL_STACK);
+               * mono_thread_create_internal (mono_get_root_domain (), threadpool_start_idle_threads, tp, TRUE, FALSE, SMALL_STACK);
                */
        }
 
@@ -1594,9 +1594,9 @@ ves_icall_System_Threading_ThreadPool_SetMinThreads (gint workerThreads, gint co
        InterlockedExchange (&async_tp.min_threads, workerThreads);
        InterlockedExchange (&async_io_tp.min_threads, completionPortThreads);
        if (workerThreads > async_tp.nthreads)
-               mono_thread_create_internal (mono_get_root_domain (), threadpool_start_idle_threads, &async_tp, TRUE, SMALL_STACK);
+               mono_thread_create_internal (mono_get_root_domain (), threadpool_start_idle_threads, &async_tp, TRUE, FALSE, SMALL_STACK);
        if (completionPortThreads > async_io_tp.nthreads)
-               mono_thread_create_internal (mono_get_root_domain (), threadpool_start_idle_threads, &async_io_tp, TRUE, SMALL_STACK);
+               mono_thread_create_internal (mono_get_root_domain (), threadpool_start_idle_threads, &async_io_tp, TRUE, FALSE, SMALL_STACK);
        return TRUE;
 }
 
index 9027c119591e43487b0417ac3362ae43c81df0b5..7e9da9a0165ca017579a66a57e9f939ba7e2b673 100644 (file)
@@ -58,7 +58,7 @@ gpointer mono_create_thread (WapiSecurityAttributes *security,
                                                         guint32 stacksize, WapiThreadStart start,
                                                         gpointer param, guint32 create, gsize *tid) MONO_INTERNAL;
 
-MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size) MONO_INTERNAL;
+MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, gboolean no_detach, guint32 stack_size) MONO_INTERNAL;
 
 void mono_threads_install_cleanup (MonoThreadCleanupFunc func) MONO_INTERNAL;
 
index 5b9ca001b1536fb255d9815fa37e2e5d120b45a0..3f407ec3cb8be90336dfc57614a80a65e08fc875 100644 (file)
@@ -695,13 +695,22 @@ register_thread_start_argument (MonoThread *thread, struct StartInfo *start_info
        mono_g_hash_table_insert (thread_start_args, thread, start_info->start_arg);
 }
 
-MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size)
+/*
+ * mono_thread_create_internal:
+ * 
+ * If NO_DETACH is TRUE, then the thread is not detached using pthread_detach (). This is needed to fix the race condition where waiting for a thred to exit only waits for its exit event to be
+ * signalled, which can cause shutdown crashes if the thread shutdown code accesses data already freed by the runtime shutdown.
+ * Currently, this is only used for the finalizer thread.
+ */
+MonoInternalThread*
+mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, gboolean no_detach, guint32 stack_size)
 {
        MonoThread *thread;
        MonoInternalThread *internal;
        HANDLE thread_handle;
        struct StartInfo *start_info;
        gsize tid;
+       guint32 create_flags;
 
        thread = create_thread_object (domain);
        internal = create_internal_thread_object ();
@@ -733,8 +742,13 @@ MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer fu
        /* Create suspended, so we can do some housekeeping before the thread
         * starts
         */
+       create_flags = CREATE_SUSPENDED;
+#ifndef HOST_WIN32
+       if (no_detach)
+               create_flags |= CREATE_NO_DETACH;
+#endif
        thread_handle = mono_create_thread (NULL, stack_size, (LPTHREAD_START_ROUTINE)start_wrapper, start_info,
-                                    CREATE_SUSPENDED, &tid);
+                                    create_flags, &tid);
        THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
        if (thread_handle == NULL) {
                /* The thread couldn't be created, so throw an exception */
@@ -773,7 +787,7 @@ MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer fu
 void
 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
 {
-       mono_thread_create_internal (domain, func, arg, FALSE, 0);
+       mono_thread_create_internal (domain, func, arg, FALSE, FALSE, 0);
 }
 
 /*