2009-01-12 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mono / metadata / threads.c
index bd06079be762cf7cbc619925d736f08272d43872..96bdefc0bdce864fdc243e8a0fa6e131dcc9be0a 100644 (file)
@@ -27,6 +27,9 @@
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/marshal.h>
 #include <mono/io-layer/io-layer.h>
+#ifndef PLATFORM_WIN32
+#include <mono/io-layer/threads.h>
+#endif
 #include <mono/metadata/object-internals.h>
 #include <mono/metadata/mono-debug-debugger.h>
 #include <mono/utils/mono-compiler.h>
@@ -419,9 +422,8 @@ static void
 try_free_delayed_free_item (int index)
 {
        if (delayed_free_table->len > index) {
-               DelayedFreeItem item;
+               DelayedFreeItem item = { NULL, NULL };
 
-               item.p = NULL;
                EnterCriticalSection (&delayed_free_table_mutex);
                /* We have to check the length again because another
                   thread might have freed an item before we acquired
@@ -528,6 +530,8 @@ static void thread_cleanup (MonoThread *thread)
        if (thread->serialized_culture_info)
                g_free (thread->serialized_culture_info);
 
+       g_free (thread->name);
+
        thread->cached_culture_info = NULL;
 
        mono_gc_free_fixed (thread->static_data);
@@ -591,7 +595,7 @@ static guint32 WINAPI start_wrapper(void *data)
 
        /* On 2.0 profile (and higher), set explicitly since state might have been
           Unknown */
-       if (mono_get_runtime_info ()->framework_version [0] != '1') {
+       if (mono_framework_version () != 1) {
                if (thread->apartment_state == ThreadApartmentState_Unknown)
                        thread->apartment_state = ThreadApartmentState_MTA;
        }
@@ -731,6 +735,8 @@ void mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer ar
        InitializeCriticalSection (thread->synch_cs);
 
        thread->threadpool_thread = threadpool_thread;
+       if (threadpool_thread)
+               mono_thread_set_state (thread, ThreadState_Background);
 
        if (handle_store (thread))
                ResumeThread (thread_handle);
@@ -754,6 +760,7 @@ mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize)
 #if defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
        *staddr = (guint8*)pthread_get_stackaddr_np (pthread_self ());
        *stsize = pthread_get_stacksize_np (pthread_self ());
+       *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
        return;
        /* FIXME: simplify the mess below */
 #elif !defined(PLATFORM_WIN32)
@@ -762,31 +769,31 @@ mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize)
 
        pthread_attr_init (&attr);
 #ifdef HAVE_PTHREAD_GETATTR_NP
-               pthread_getattr_np (pthread_self(), &attr);
+       pthread_getattr_np (pthread_self(), &attr);
 #else
 #ifdef HAVE_PTHREAD_ATTR_GET_NP
-               pthread_attr_get_np (pthread_self(), &attr);
+       pthread_attr_get_np (pthread_self(), &attr);
 #elif defined(sun)
-               *staddr = NULL;
-               pthread_attr_getstacksize (&attr, &stsize);
+       *staddr = NULL;
+       pthread_attr_getstacksize (&attr, &stsize);
 #else
-               *staddr = NULL;
-               *stsize = 0;
-               return;
+       *staddr = NULL;
+       *stsize = 0;
+       return;
 #endif
 #endif
 
 #ifndef sun
-               pthread_attr_getstack (&attr, (void**)staddr, stsize);
-               if (*staddr)
-                       g_assert ((current > *staddr) && (current < *staddr + *stsize));
+       pthread_attr_getstack (&attr, (void**)staddr, stsize);
+       if (*staddr)
+               g_assert ((current > *staddr) && (current < *staddr + *stsize));
 #endif
 
-               pthread_attr_destroy (&attr); 
+       pthread_attr_destroy (&attr); 
 #endif
 
-               /* When running under emacs, sometimes staddr is not aligned to a page size */
-               *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
+       /* When running under emacs, sometimes staddr is not aligned to a page size */
+       *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
 }      
 
 MonoThread *
@@ -1051,16 +1058,8 @@ void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
 }
 
-void ves_icall_System_Threading_Thread_SpinWait_internal (gint32 iterations)
+void ves_icall_System_Threading_Thread_SpinWait_nop (void)
 {
-       gint32 i;
-       
-       for(i = 0; i < iterations; i++) {
-               /* We're busy waiting, but at least we can tell the
-                * scheduler to let someone else have a go...
-                */
-               Sleep (0);
-       }
 }
 
 gint32
@@ -1274,8 +1273,9 @@ ves_icall_System_Threading_Thread_SetSerializedCurrentUICulture (MonoThread *thi
 MonoThread *
 mono_thread_current (void)
 {
-       THREAD_DEBUG (g_message ("%s: returning %p", __func__, GET_CURRENT_OBJECT ()));
-       return GET_CURRENT_OBJECT ();
+       MonoThread *res = GET_CURRENT_OBJECT ()
+       THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
+       return res;
 }
 
 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
@@ -1931,6 +1931,9 @@ void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this)
        gboolean throw = FALSE;
        
        ensure_synch_cs_set (this);
+
+       if (this == mono_thread_current ())
+               return;
        
        EnterCriticalSection (this->synch_cs);
        
@@ -1952,6 +1955,8 @@ void mono_thread_current_check_pending_interrupt ()
        MonoThread *thread = mono_thread_current ();
        gboolean throw = FALSE;
 
+       mono_debugger_check_interruption ();
+
        ensure_synch_cs_set (thread);
        
        EnterCriticalSection (thread->synch_cs);
@@ -2028,6 +2033,15 @@ static void signal_thread_state_change (MonoThread *thread)
 #else
        pthread_kill (thread->tid, mono_thread_get_abort_signal ());
 #endif
+
+       /* 
+        * This will cause waits to be broken.
+        * It will also prevent the thread from entering a wait, so if the thread returns
+        * from the wait before it receives the abort signal, it will just spin in the wait
+        * functions in the io-layer until the signal handler calls QueueUserAPC which will
+        * make it return.
+        */
+       wapi_interrupt_thread (thread->handle);
 #endif /* PLATFORM_WIN32 */
 }
 
@@ -2061,9 +2075,11 @@ ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
        LeaveCriticalSection (thread->synch_cs);
 
        THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Abort requested for %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid));
-       
-       /* Make sure the thread is awake */
-       mono_thread_resume (thread);
+
+       /* During shutdown, we can't wait for other threads */
+       if (!shutting_down)
+               /* Make sure the thread is awake */
+               mono_thread_resume (thread);
        
        signal_thread_state_change (thread);
 }
@@ -3026,6 +3042,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
 {
        abort_appdomain_data user_data;
        guint32 start_time;
+       int orig_timeout = timeout;
 
        THREAD_DEBUG (g_message ("%s: starting abort", __func__));
 
@@ -3049,7 +3066,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
                timeout -= mono_msec_ticks () - start_time;
                start_time = mono_msec_ticks ();
 
-               if (timeout < 0)
+               if (orig_timeout != -1 && timeout < 0)
                        return FALSE;
        }
        while (user_data.wait.num > 0);
@@ -3453,6 +3470,10 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread)
                WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
                InterlockedDecrement (&thread_interruption_requested);
                thread->interruption_requested = FALSE;
+#ifndef PLATFORM_WIN32
+               /* Clear the interrupted flag of the thread so it can wait again */
+               wapi_clear_interruption ();
+#endif
        }
 
        if ((thread->state & ThreadState_AbortRequested) != 0) {
@@ -3506,6 +3527,7 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread)
                return NULL;
        } else if (thread->thread_interrupt_requested) {
 
+               thread->thread_interrupt_requested = FALSE;
                LeaveCriticalSection (thread->synch_cs);
                
                return(mono_get_exception_thread_interrupted ());
@@ -3524,7 +3546,8 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread)
  * the thread. If the result is an exception that needs to be throw, it is 
  * provided as return value.
  */
-MonoException* mono_thread_request_interruption (gboolean running_managed)
+MonoException*
+mono_thread_request_interruption (gboolean running_managed)
 {
        MonoThread *thread = mono_thread_current ();
 
@@ -3532,16 +3555,8 @@ MonoException* mono_thread_request_interruption (gboolean running_managed)
        if (thread == NULL) 
                return NULL;
        
-       ensure_synch_cs_set (thread);
-
-       /* FIXME: This is NOT signal safe */
-       EnterCriticalSection (thread->synch_cs);
-       
-       if (thread->interruption_requested) {
-               LeaveCriticalSection (thread->synch_cs);
-               
+       if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
                return NULL;
-       }
 
        if (!running_managed || is_running_protected_wrapper ()) {
                /* Can't stop while in unmanaged code. Increase the global interruption
@@ -3549,9 +3564,6 @@ MonoException* mono_thread_request_interruption (gboolean running_managed)
                   checked and the thread will be interrupted. */
                
                InterlockedIncrement (&thread_interruption_requested);
-               thread->interruption_requested = TRUE;
-
-               LeaveCriticalSection (thread->synch_cs);
 
                if (mono_thread_notify_pending_exc_fn && !running_managed)
                        /* The JIT will notify the thread about the interruption */
@@ -3565,8 +3577,6 @@ MonoException* mono_thread_request_interruption (gboolean running_managed)
                return NULL;
        }
        else {
-               LeaveCriticalSection (thread->synch_cs);
-               
                return mono_thread_execute_interruption (thread);
        }
 }
@@ -3590,6 +3600,8 @@ static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_p
        if (thread == NULL)
                return;
 
+       mono_debugger_check_interruption ();
+
        if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
                MonoException* exc = mono_thread_execute_interruption (thread);
                if (exc) mono_raise_exception (exc);