* Author:
* Rodrigo Kumpera (kumpera@gmail.com)
*
- * (C) 2011 Novell, Inc
+ * Copyright 2011 Novell, Inc (http://www.novell.com)
+ * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
*/
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/mono-tls.h>
#include <mono/utils/hazard-pointer.h>
-#include <mono/metadata/gc-internal.h>
#include <mono/metadata/appdomain.h>
+#include <mono/metadata/domain-internals.h>
#include <errno.h>
static int thread_info_size;
static MonoThreadInfoCallbacks threads_callbacks;
static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
-static MonoNativeTlsKey thread_info_key;
+static MonoNativeTlsKey thread_info_key, small_id_key;
static MonoLinkedListSet thread_list;
+static gboolean disable_new_interrupt = FALSE;
+static gboolean mono_threads_inited = FALSE;
static inline void
mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
static gboolean
mono_thread_info_remove (MonoThreadInfo *info)
{
- /*TLS is gone by now, so we can't rely on it to retrieve hp*/
- MonoThreadHazardPointers *hp = mono_hazard_pointer_get_by_id (info->small_id);
+ MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
gboolean res;
THREADS_DEBUG ("removing info %p\n", info);
MonoThreadInfo *info = mem;
DeleteCriticalSection (&info->suspend_lock);
+ MONO_SEM_DESTROY (&info->resume_semaphore);
+ MONO_SEM_DESTROY (&info->finish_resume_semaphore);
mono_threads_platform_free (info);
g_free (info);
}
+int
+mono_thread_info_register_small_id (void)
+{
+ int small_id = mono_thread_small_id_alloc ();
+ mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
+ return small_id;
+}
+
static void*
register_thread (MonoThreadInfo *info, gpointer baseptr)
{
+ int small_id = mono_thread_info_register_small_id ();
gboolean result;
mono_thread_info_set_tid (info, mono_native_thread_id_get ());
- info->small_id = mono_thread_small_id_alloc ();
+ info->small_id = small_id;
InitializeCriticalSection (&info->suspend_lock);
+ MONO_SEM_INIT (&info->resume_semaphore, 0);
+ MONO_SEM_INIT (&info->finish_resume_semaphore, 0);
+
+ /*set TLS early so SMR works */
+ mono_native_tls_set_value (thread_info_key, info);
THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
}
mono_threads_platform_register (info);
- mono_native_tls_set_value (thread_info_key, info);
/*If this fail it means a given thread has been registered twice, which doesn't make sense. */
result = mono_thread_info_insert (info);
static void
unregister_thread (void *arg)
{
- gboolean result;
MonoThreadInfo *info = arg;
int small_id = info->small_id;
g_assert (info);
THREADS_DEBUG ("unregistering info %p\n", info);
+ /*
+ * TLS destruction order is not reliable so small_id might be cleaned up
+ * before us.
+ */
+ mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
+
+ /*
+ The unregister callback is reposible for calling mono_threads_unregister_current_thread
+ since it usually needs to be done in sync with the GC does a stop-the-world.
+ */
if (threads_callbacks.thread_unregister)
threads_callbacks.thread_unregister (info);
+ else
+ mono_threads_unregister_current_thread (info);
+
+ /*now it's safe to free the thread info.*/
+ mono_thread_hazardous_free_or_queue (info, free_thread_info);
+ mono_thread_small_id_free (small_id);
+}
+/**
+ * Removes the current thread from the thread list.
+ * This must be called from the thread unregister callback and nowhere else.
+ * The current thread must be passed as TLS might have already been cleaned up.
+*/
+void
+mono_threads_unregister_current_thread (MonoThreadInfo *info)
+{
+ gboolean result;
+ g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
result = mono_thread_info_remove (info);
g_assert (result);
- mono_thread_small_id_free (small_id);
}
MonoThreadInfo*
return mono_native_tls_get_value (thread_info_key);
}
+int
+mono_thread_info_get_small_id (void)
+{
+ gpointer val = mono_native_tls_get_value (small_id_key);
+ if (!val)
+ return -1;
+ return GPOINTER_TO_INT (val) - 1;
+}
+
MonoLinkedListSet*
mono_thread_info_list_head (void)
{
MonoThreadInfo*
mono_thread_info_attach (void *baseptr)
{
- MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
+ MonoThreadInfo *info;
+ if (!mono_threads_inited)
+ {
+ /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
+ * thread is created before an embedding API user initialized Mono. */
+ THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
+ return NULL;
+ }
+ info = mono_native_tls_get_value (thread_info_key);
if (!info) {
info = g_malloc0 (thread_info_size);
THREADS_DEBUG ("attaching %p\n", info);
void
mono_thread_info_dettach (void)
{
- MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
+ MonoThreadInfo *info;
+ if (!mono_threads_inited)
+ {
+ /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
+ * is created before an embedding API user initialized Mono. */
+ THREADS_DEBUG ("mono_thread_info_dettach called before mono_threads_init\n");
+ return;
+ }
+ info = mono_native_tls_get_value (thread_info_key);
if (info) {
THREADS_DEBUG ("detaching %p\n", info);
unregister_thread (info);
threads_callbacks = *callbacks;
thread_info_size = info_size;
#ifdef HOST_WIN32
- res = mono_native_tls_alloc (thread_info_key, NULL);
+ res = mono_native_tls_alloc (&thread_info_key, NULL);
#else
- res = mono_native_tls_alloc (thread_info_key, unregister_thread);
+ res = mono_native_tls_alloc (&thread_info_key, unregister_thread);
#endif
+ g_assert (res);
+
+ res = mono_native_tls_alloc (&small_id_key, NULL);
+ g_assert (res);
+
InitializeCriticalSection (&global_suspend_lock);
- mono_lls_init (&thread_list, free_thread_info);
+ mono_lls_init (&thread_list, NULL);
mono_thread_smr_init ();
mono_threads_init_platform ();
- g_assert (res);
+ mono_threads_inited = TRUE;
+
g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
}
runtime_callbacks = *callbacks;
}
+MonoThreadInfoCallbacks *
+mono_threads_get_callbacks (void)
+{
+ return &threads_callbacks;
+}
+
MonoThreadInfoRuntimeCallbacks *
mono_threads_get_runtime_callbacks (void)
{
EnterCriticalSection (&info->suspend_lock);
/*thread is on the process of detaching*/
- if (info->thread_state > STATE_RUNNING) {
+ if (mono_thread_info_run_state (info) > STATE_RUNNING) {
mono_hazard_pointer_clear (hp, 1);
return NULL;
}
mono_threads_core_interrupt (info);
++info->suspend_count;
+ info->thread_state |= STATE_SUSPENDED;
LeaveCriticalSection (&info->suspend_lock);
mono_hazard_pointer_clear (hp, 1);
}
void
-mono_thread_info_self_suspend ()
+mono_thread_info_self_suspend (void)
{
+ gboolean ret;
MonoThreadInfo *info = mono_thread_info_current ();
if (!info)
return;
g_assert (info->suspend_count == 0);
++info->suspend_count;
- /*
- The internal API contract with this function is a bit out of the ordinary.
- mono_threads_core_self_suspend executes with suspend_lock taken and must
- release it after capturing the current context.
- */
- mono_threads_core_self_suspend (info);
+ info->thread_state |= STATE_SELF_SUSPENDED;
+
+ ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->suspend_state, NULL);
+ g_assert (ret);
+
+ LeaveCriticalSection (&info->suspend_lock);
+
+ while (MONO_SEM_WAIT (&info->resume_semaphore) != 0) {
+ /*if (EINTR != errno) ABORT("sem_wait failed"); */
+ }
+
+ g_assert (!info->async_target); /*FIXME this should happen normally for suspend. */
+ MONO_SEM_POST (&info->finish_resume_semaphore);
+}
+
+static gboolean
+mono_thread_info_resume_internal (MonoThreadInfo *info)
+{
+ gboolean result;
+ if (mono_thread_info_suspend_state (info) == STATE_SELF_SUSPENDED) {
+ MONO_SEM_POST (&info->resume_semaphore);
+ while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
+ /* g_assert (errno == EINTR); */
+ }
+ result = TRUE;
+ } else {
+ result = mono_threads_core_resume (info);
+ }
+ info->thread_state &= ~SUSPEND_STATE_MASK;
+ return result;
}
gboolean
g_assert (mono_thread_info_get_tid (info));
if (--info->suspend_count == 0)
- result = mono_threads_core_resume (info);
+ result = mono_thread_info_resume_internal (info);
LeaveCriticalSection (&info->suspend_lock);
mono_hazard_pointer_clear (hp, 1);
method = ji->method;
- return mono_gc_is_critical_method (method);
+ return threads_callbacks.mono_method_is_critical (method);
}
/*
LeaveCriticalSection (&global_suspend_lock);
}
+void
+mono_thread_info_disable_new_interrupt (gboolean disable)
+{
+ disable_new_interrupt = disable;
+}
+
+/*
+ * This is a very specific function whose only purpose is to
+ * break a given thread from socket syscalls.
+ *
+ * This only exists because linux won't fail a call to connect
+ * if the underlying is closed.
+ *
+ * TODO We should cleanup and unify this with the other syscall abort
+ * facility.
+ */
+void
+mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
+{
+ MonoThreadHazardPointers *hp;
+ MonoThreadInfo *info;
+
+ if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
+ return;
+
+ hp = mono_hazard_pointer_get ();
+ info = mono_thread_info_lookup (tid); /*info on HP1*/
+ if (!info)
+ return;
+
+ if (mono_thread_info_run_state (info) > STATE_RUNNING) {
+ mono_hazard_pointer_clear (hp, 1);
+ return;
+ }
+
+ mono_thread_info_suspend_lock ();
+
+ mono_threads_core_abort_syscall (info);
+
+ mono_hazard_pointer_clear (hp, 1);
+ mono_thread_info_suspend_unlock ();
+}
+
/*
Disabled by default for now.
To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
gboolean
mono_thread_info_new_interrupt_enabled (void)
{
+ /*We need STW gc events to work correctly*/
+#if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
+ return FALSE;
+#endif
+ /*port not done*/
+#if defined(HOST_WIN32)
+ return FALSE;
+#endif
+#if defined (__i386__)
+ return !disable_new_interrupt;
+#endif
return FALSE;
}