Merge pull request #350 from robwilkens/bug1089
[mono.git] / mono / metadata / threads.c
index 0728e6a05cea23cb552eb585466fa56a836d19df..e3b9e1a569dd3eccfe36042ab9c0aea4d3de9da0 100644 (file)
@@ -364,15 +364,6 @@ static void thread_cleanup (MonoInternalThread *thread)
                        mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
        }
 
-       /* if the thread is not in the hash it has been removed already */
-       if (!handle_remove (thread)) {
-               /* This needs to be called even if handle_remove () fails */
-               if (mono_thread_cleanup_fn)
-                       mono_thread_cleanup_fn (thread);
-               return;
-       }
-       mono_release_type_locks (thread);
-
        ensure_synch_cs_set (thread);
 
        EnterCriticalSection (thread->synch_cs);
@@ -381,7 +372,29 @@ static void thread_cleanup (MonoInternalThread *thread)
        thread->state &= ~ThreadState_Background;
 
        LeaveCriticalSection (thread->synch_cs);
+
+       /*
+       An interruption request has leaked to cleanup. Adjust the global counter.
+
+       This can happen is the abort source thread finds the abortee (this) thread
+       in unmanaged code. If this thread never trips back to managed code or check
+       the local flag it will be left set and positively unbalance the global counter.
        
+       Leaving the counter unbalanced will cause a performance degradation since all threads
+       will now keep checking their local flags all the time.
+       */
+       if (InterlockedExchange (&thread->interruption_requested, 0))
+               InterlockedDecrement (&thread_interruption_requested);
+
+       /* if the thread is not in the hash it has been removed already */
+       if (!handle_remove (thread)) {
+               /* This needs to be called even if handle_remove () fails */
+               if (mono_thread_cleanup_fn)
+                       mono_thread_cleanup_fn (thread);
+               return;
+       }
+       mono_release_type_locks (thread);
+
        mono_profiler_thread_end (thread->tid);
 
        if (thread == mono_thread_internal_current ())
@@ -4090,13 +4103,13 @@ mono_thread_request_interruption (gboolean running_managed)
        
        if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
                return NULL;
+       InterlockedIncrement (&thread_interruption_requested);
 
        if (!running_managed || is_running_protected_wrapper ()) {
                /* Can't stop while in unmanaged code. Increase the global interruption
                   request count. When exiting the unmanaged method the count will be
                   checked and the thread will be interrupted. */
                
-               InterlockedIncrement (&thread_interruption_requested);
 
                if (mono_thread_notify_pending_exc_fn && !running_managed)
                        /* The JIT will notify the thread about the interruption */
@@ -4481,6 +4494,7 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
                mono_thread_info_resume (mono_thread_info_get_tid (info));
                return;
        }
+       InterlockedIncrement (&thread_interruption_requested);
 
        ji = mono_thread_info_get_last_managed (info);
        protected_wrapper = ji && mono_threads_is_critical_method (ji->method);
@@ -4501,7 +4515,6 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
                 * functions in the io-layer until the signal handler calls QueueUserAPC which will
                 * make it return.
                 */
-               InterlockedIncrement (&thread_interruption_requested);
 #ifndef HOST_WIN32
                interrupt_handle = wapi_prepare_interrupt_thread (thread->handle);
 #endif