#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
{
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);
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"));
}
/* 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;
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);
#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);
}
#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;
}
}
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);
*/
}
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;
}
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;
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 ();
/* 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 */
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);
}
/*