2009-01-12 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mono / metadata / threads.c
index 6805e21f643bfca423c8c3ae347e2e7b1dae864c..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>
@@ -415,6 +418,31 @@ mono_hazard_pointer_get (void)
        return &hazard_table [current_thread->small_id];
 }
 
+static void
+try_free_delayed_free_item (int index)
+{
+       if (delayed_free_table->len > index) {
+               DelayedFreeItem item = { NULL, NULL };
+
+               EnterCriticalSection (&delayed_free_table_mutex);
+               /* We have to check the length again because another
+                  thread might have freed an item before we acquired
+                  the lock. */
+               if (delayed_free_table->len > index) {
+                       item = g_array_index (delayed_free_table, DelayedFreeItem, index);
+
+                       if (!is_pointer_hazardous (item.p))
+                               g_array_remove_index_fast (delayed_free_table, index);
+                       else
+                               item.p = NULL;
+               }
+               LeaveCriticalSection (&delayed_free_table_mutex);
+
+               if (item.p != NULL)
+                       item.free_func (item.p);
+       }
+}
+
 void
 mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func)
 {
@@ -422,29 +450,8 @@ mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func
 
        /* First try to free a few entries in the delayed free
           table. */
-       for (i = 2; i >= 0; --i) {
-               if (delayed_free_table->len > i) {
-                       DelayedFreeItem item;
-
-                       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
-                          the lock. */
-                       if (delayed_free_table->len > i) {
-                               item = g_array_index (delayed_free_table, DelayedFreeItem, i);
-
-                               if (!is_pointer_hazardous (item.p))
-                                       g_array_remove_index_fast (delayed_free_table, i);
-                               else
-                                       item.p = NULL;
-                       }
-                       LeaveCriticalSection (&delayed_free_table_mutex);
-
-                       if (item.p != NULL)
-                               item.free_func (item.p);
-               }
-       }
+       for (i = 2; i >= 0; --i)
+               try_free_delayed_free_item (i);
 
        /* Now see if the pointer we're freeing is hazardous.  If it
           isn't, free it.  Otherwise put it in the delay list. */
@@ -460,6 +467,21 @@ mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func
                free_func (p);
 }
 
+void
+mono_thread_hazardous_try_free_all (void)
+{
+       int len;
+       int i;
+
+       if (!delayed_free_table)
+               return;
+
+       len = delayed_free_table->len;
+
+       for (i = len - 1; i >= 0; --i)
+               try_free_delayed_free_item (i);
+}
+
 static void ensure_synch_cs_set (MonoThread *thread)
 {
        CRITICAL_SECTION *synch_cs;
@@ -508,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);
@@ -541,6 +565,8 @@ static guint32 WINAPI start_wrapper(void *data)
 
        SET_CURRENT_OBJECT (thread);
 
+       mono_monitor_init_tls ();
+
        /* Every thread references the appdomain which created it */
        mono_thread_push_appdomain_ref (start_info->domain);
        
@@ -569,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;
        }
@@ -709,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);
@@ -723,8 +751,8 @@ mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
 /*
  * mono_thread_get_stack_bounds:
  *
- *   Return the address and size of the current threads stack. Return NULL as the stack
- * address if the stack address cannot be determined.
+ *   Return the address and size of the current threads stack. Return NULL as the 
+ * stack address if the stack address cannot be determined.
  */
 void
 mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize)
@@ -732,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)
@@ -740,28 +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));
 }      
 
 MonoThread *
@@ -819,6 +851,8 @@ mono_thread_attach (MonoDomain *domain)
        SET_CURRENT_OBJECT (thread);
        mono_domain_set (domain, TRUE);
 
+       mono_monitor_init_tls ();
+
        thread_adjust_static_data (thread);
 
        if (mono_thread_attach_cb) {
@@ -1024,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
@@ -1247,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,
@@ -1904,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);
        
@@ -1925,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);
@@ -2001,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 */
 }
 
@@ -2034,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);
 }
@@ -2298,6 +2341,8 @@ void mono_thread_init (MonoThreadStartCB start_cb,
 
 void mono_thread_cleanup (void)
 {
+       mono_thread_hazardous_try_free_all ();
+
 #if !defined(PLATFORM_WIN32) && !defined(RUN_IN_SUBTHREAD)
        /* The main thread must abandon any held mutexes (particularly
         * important for named mutexes as they are shared across
@@ -2326,6 +2371,7 @@ void mono_thread_cleanup (void)
 #endif
 
        g_array_free (delayed_free_table, TRUE);
+       delayed_free_table = NULL;
 
        TlsFree (current_object_key);
 }
@@ -2996,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__));
 
@@ -3019,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);
@@ -3423,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) {
@@ -3476,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 ());
@@ -3494,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 ();
 
@@ -3502,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
@@ -3519,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 */
@@ -3530,13 +3572,11 @@ MonoException* mono_thread_request_interruption (gboolean running_managed)
 
                /* this will awake the thread if it is in WaitForSingleObject 
                   or similar */
-               /* FIXME: This is NOT signal-safe, since it allocates memory and uses locking */
+               /* Our implementation of this function ignores the func argument */
                QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL);
                return NULL;
        }
        else {
-               LeaveCriticalSection (thread->synch_cs);
-               
                return mono_thread_execute_interruption (thread);
        }
 }
@@ -3560,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);
@@ -3600,8 +3642,44 @@ mono_thread_get_and_clear_pending_exception (void)
        if (thread->interruption_requested && !is_running_protected_wrapper ()) {
                return mono_thread_execute_interruption (thread);
        }
-       else
-               return NULL;
+       
+       if (thread->pending_exception) {
+               MonoException *exc = thread->pending_exception;
+
+               thread->pending_exception = NULL;
+               return exc;
+       }
+
+       return NULL;
+}
+
+/*
+ * mono_set_pending_exception:
+ *
+ *   Set the pending exception of the current thread to EXC. On platforms which 
+ * support it, the exception will be thrown when execution returns to managed code. 
+ * On other platforms, this function is equivalent to mono_raise_exception (). 
+ * Internal calls which report exceptions using this function instead of 
+ * raise_exception () might be called by JITted code using a more efficient calling 
+ * convention.
+ */
+void
+mono_set_pending_exception (MonoException *exc)
+{
+       MonoThread *thread = mono_thread_current ();
+
+       /* The thread may already be stopping */
+       if (thread == NULL)
+               return;
+
+       if (mono_thread_notify_pending_exc_fn) {
+               MONO_OBJECT_SETREF (thread, pending_exception, exc);
+
+               mono_thread_notify_pending_exc_fn ();
+       } else {
+               /* No way to notify the JIT about the exception, have to throw it now */
+               mono_raise_exception (exc);
+       }
 }
 
 /**