[sgen] Allocate job data dynamically.
[mono.git] / mono / utils / mono-threads.c
index 200e1ba2512ad5c8eeca7c761751cd4d98802d18..40cd9b17dc4c1cc3dc4d337a8e2747d198b71929 100644 (file)
@@ -4,7 +4,8 @@
  * 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>
@@ -12,8 +13,8 @@
 #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>
 
@@ -38,8 +39,10 @@ static CRITICAL_SECTION global_suspend_lock;
 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)
@@ -86,8 +89,7 @@ mono_thread_info_insert (MonoThreadInfo *info)
 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);
@@ -102,19 +104,35 @@ free_thread_info (gpointer mem)
        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);
 
@@ -127,7 +145,6 @@ register_thread (MonoThreadInfo *info, gpointer baseptr)
        }
 
        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);
@@ -138,20 +155,45 @@ register_thread (MonoThreadInfo *info, gpointer baseptr)
 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*
@@ -160,6 +202,15 @@ mono_thread_info_current (void)
        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)
 {
@@ -169,7 +220,15 @@ 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);
@@ -184,7 +243,15 @@ mono_thread_info_attach (void *baseptr)
 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);
@@ -198,17 +265,23 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
        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));
 }
 
@@ -218,6 +291,12 @@ mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
        runtime_callbacks = *callbacks;
 }
 
+MonoThreadInfoCallbacks *
+mono_threads_get_callbacks (void)
+{
+       return &threads_callbacks;
+}
+
 MonoThreadInfoRuntimeCallbacks *
 mono_threads_get_runtime_callbacks (void)
 {
@@ -238,7 +317,7 @@ mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel
        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;
        }
@@ -262,6 +341,7 @@ mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel
                mono_threads_core_interrupt (info);
 
        ++info->suspend_count;
+       info->thread_state |= STATE_SUSPENDED;
        LeaveCriticalSection (&info->suspend_lock);
        mono_hazard_pointer_clear (hp, 1);
 
@@ -269,8 +349,9 @@ mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel
 }
 
 void
-mono_thread_info_self_suspend ()
+mono_thread_info_self_suspend (void)
 {
+       gboolean ret;
        MonoThreadInfo *info = mono_thread_info_current ();
        if (!info)
                return;
@@ -282,12 +363,36 @@ mono_thread_info_self_suspend ()
        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
@@ -316,7 +421,7 @@ mono_thread_info_resume (MonoNativeThreadId tid)
        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);
@@ -341,7 +446,7 @@ is_thread_in_critical_region (MonoThreadInfo *info)
 
        method = ji->method;
 
-       return mono_gc_is_critical_method (method);
+       return threads_callbacks.mono_method_is_critical (method);
 }
 
 /*
@@ -430,6 +535,49 @@ mono_thread_info_suspend_unlock (void)
        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
@@ -438,5 +586,16 @@ which means mono-context and setup_async_callback, and we need a mono-threads ba
 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;
 }