2009-01-03 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / threads.c
index 400f1b9a06cf683fc72f1a007a762f13cae15dc0..17f56971ac329912fdc2db775c08bd5d491409d4 100644 (file)
@@ -419,9 +419,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
@@ -468,9 +467,14 @@ mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func
 void
 mono_thread_hazardous_try_free_all (void)
 {
-       int len = delayed_free_table->len;
+       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);
 }
@@ -523,6 +527,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);
@@ -556,6 +562,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);
        
@@ -584,7 +592,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;
        }
@@ -724,6 +732,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);
@@ -738,8 +748,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)
@@ -747,6 +757,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)
@@ -755,28 +766,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 *
@@ -834,6 +848,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) {
@@ -1039,16 +1055,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
@@ -1919,6 +1927,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);
        
@@ -1940,6 +1951,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);
@@ -2049,9 +2062,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);
 }
@@ -2313,6 +2328,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
@@ -2341,6 +2358,7 @@ void mono_thread_cleanup (void)
 #endif
 
        g_array_free (delayed_free_table, TRUE);
+       delayed_free_table = NULL;
 
        TlsFree (current_object_key);
 }
@@ -3011,6 +3029,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__));
 
@@ -3034,7 +3053,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);
@@ -3491,6 +3510,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 ());
@@ -3509,7 +3529,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 ();
 
@@ -3517,16 +3538,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
@@ -3534,9 +3547,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 */
@@ -3550,8 +3560,6 @@ MonoException* mono_thread_request_interruption (gboolean running_managed)
                return NULL;
        }
        else {
-               LeaveCriticalSection (thread->synch_cs);
-               
                return mono_thread_execute_interruption (thread);
        }
 }
@@ -3575,6 +3583,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);
@@ -3615,8 +3625,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);
+       }
 }
 
 /**