Merge pull request #3008 from lateralusX/jlorenss/win-x64-shutdown-crash
[mono.git] / mono / metadata / monitor.c
index 0c1d28130aff76741a2bc76e03d5cd84ea9dfdd8..6cf9691777ea783631f39c7cac24c239cd180a7f 100644 (file)
@@ -6,6 +6,7 @@
  *
  * Copyright 2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include <config.h>
@@ -392,7 +393,7 @@ mon_new (gsize id)
                                        if (new_->wait_list) {
                                                /* Orphaned events left by aborted threads */
                                                while (new_->wait_list) {
-                                                       LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d): Closing orphaned event %d", mono_thread_info_get_small_id (), new->wait_list->data));
+                                                       LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d): Closing orphaned event %d", mono_thread_info_get_small_id (), new_->wait_list->data));
                                                        CloseHandle (new_->wait_list->data);
                                                        new_->wait_list = g_slist_remove (new_->wait_list, new_->wait_list->data);
                                                }
@@ -742,7 +743,7 @@ mono_monitor_try_enter_inflated (MonoObject *obj, guint32 ms, gboolean allow_int
        LockWord lw;
        MonoThreadsSync *mon;
        HANDLE sem;
-       guint32 then = 0, now, delta;
+       gint64 then = 0, now, delta;
        guint32 waitms;
        guint32 ret;
        guint32 new_status, old_status, tmp_status;
@@ -880,9 +881,9 @@ retry_contended:
         * We pass TRUE instead of allow_interruption since we have to check for the
         * StopRequested case below.
         */
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
        ret = WaitForSingleObjectEx (mon->entry_sem, waitms, TRUE);
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
        
@@ -896,17 +897,12 @@ retry_contended:
                 * We have to obey a stop/suspend request even if 
                 * allow_interruption is FALSE to avoid hangs at shutdown.
                 */
-               if (!mono_thread_test_state (mono_thread_internal_current (), (MonoThreadState)(ThreadState_StopRequested | ThreadState_SuspendRequested))) {
+               if (!mono_thread_test_state (mono_thread_internal_current (), (MonoThreadState)(ThreadState_StopRequested | ThreadState_SuspendRequested | ThreadState_AbortRequested))) {
                        if (ms != INFINITE) {
                                now = mono_msec_ticks ();
-                               if (now < then) {
-                                       LOCK_DEBUG (g_message ("%s: wrapped around! now=0x%x then=0x%x", __func__, now, then));
 
-                                       now += (0xffffffff - then);
-                                       then = 0;
-
-                                       LOCK_DEBUG (g_message ("%s: wrap rejig: now=0x%x then=0x%x delta=0x%x", __func__, now, then, now-then));
-                               }
+                               /* it should not overflow before ~30k years */
+                               g_assert (now >= then);
 
                                delta = now - then;
                                if (delta >= ms) {
@@ -1009,6 +1005,12 @@ mono_monitor_enter (MonoObject *obj)
 }
 
 gboolean 
+mono_monitor_enter_fast (MonoObject *obj)
+{
+       return mono_monitor_try_enter_internal (obj, 0, FALSE) == 1;
+}
+
+gboolean
 mono_monitor_try_enter (MonoObject *obj, guint32 ms)
 {
        return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1;
@@ -1069,20 +1071,6 @@ mono_monitor_threads_sync_members_offset (int *status_offset, int *nest_offset)
        *nest_offset = ENCODE_OFF_SIZE (MONO_STRUCT_OFFSET (MonoThreadsSync, nest), sizeof (ts.nest));
 }
 
-gboolean 
-ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 ms)
-{
-       gint32 res;
-
-       do {
-               res = mono_monitor_try_enter_internal (obj, ms, TRUE);
-               if (res == -1)
-                       mono_thread_interruption_checkpoint ();
-       } while (res == -1);
-       
-       return res == 1;
-}
-
 void
 ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (MonoObject *obj, guint32 ms, char *lockTaken)
 {
@@ -1090,8 +1078,13 @@ ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (MonoObject
        do {
                res = mono_monitor_try_enter_internal (obj, ms, TRUE);
                /*This means we got interrupted during the wait and didn't got the monitor.*/
-               if (res == -1)
-                       mono_thread_interruption_checkpoint ();
+               if (res == -1) {
+                       MonoException *exc = mono_thread_interruption_checkpoint ();
+                       if (exc) {
+                               mono_set_pending_exception (exc);
+                               return;
+                       }
+               }
        } while (res == -1);
        /*It's safe to do it from here since interruption would happen only on the wrapper.*/
        *lockTaken = res == 1;
@@ -1100,7 +1093,6 @@ ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (MonoObject
 void
 mono_monitor_enter_v4 (MonoObject *obj, char *lock_taken)
 {
-
        if (*lock_taken == 1) {
                mono_set_pending_exception (mono_get_exception_argument ("lockTaken", "lockTaken is already true"));
                return;
@@ -1109,6 +1101,25 @@ mono_monitor_enter_v4 (MonoObject *obj, char *lock_taken)
        ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (obj, INFINITE, lock_taken);
 }
 
+/*
+ * mono_monitor_enter_v4_fast:
+ *
+ *   Same as mono_monitor_enter_v4, but return immediately if the
+ * monitor cannot be acquired.
+ * Returns TRUE if the lock was acquired, FALSE otherwise.
+ */
+gboolean
+mono_monitor_enter_v4_fast (MonoObject *obj, char *lock_taken)
+{
+       if (*lock_taken == 1)
+               return FALSE;
+       if (G_UNLIKELY (!obj))
+               return FALSE;
+       gint32 res = mono_monitor_try_enter_internal (obj, 0, TRUE);
+       *lock_taken = res == 1;
+       return res == 1;
+}
+
 gboolean 
 ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj)
 {
@@ -1268,42 +1279,22 @@ ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms)
         * is private to this thread.  Therefore even if the event was
         * signalled before we wait, we still succeed.
         */
-       MONO_PREPARE_BLOCKING;
+       MONO_ENTER_GC_SAFE;
        ret = WaitForSingleObjectEx (event, ms, TRUE);
-       MONO_FINISH_BLOCKING;
+       MONO_EXIT_GC_SAFE;
 
        /* Reset the thread state fairly early, so we don't have to worry
         * about the monitor error checking
         */
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
-       
-       if (mono_thread_interruption_requested ()) {
-               /* 
-                * Can't remove the event from wait_list, since the monitor is not locked by
-                * us. So leave it there, mon_new () will delete it when the mon structure
-                * is placed on the free list.
-                * FIXME: The caller expects to hold the lock after the wait returns, but it
-                * doesn't happen in this case:
-                * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=97268
-                */
-               return FALSE;
-       }
 
        /* Regain the lock with the previous nest count */
        do {
                regain = mono_monitor_try_enter_inflated (obj, INFINITE, TRUE, id);
-               if (regain == -1) 
-                       mono_thread_interruption_checkpoint ();
+               /* We must regain the lock before handling interruption requests */
        } while (regain == -1);
 
-       if (regain == 0) {
-               /* Something went wrong, so throw a
-                * SynchronizationLockException
-                */
-               CloseHandle (event);
-               mono_set_pending_exception (mono_get_exception_synchronization_lock ("Failed to regain lock"));
-               return FALSE;
-       }
+       g_assert (regain == 1);
 
        mon->nest = nest;
 
@@ -1313,9 +1304,9 @@ ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms)
                /* Poll the event again, just in case it was signalled
                 * while we were trying to regain the monitor lock
                 */
-               MONO_PREPARE_BLOCKING;
+               MONO_ENTER_GC_SAFE;
                ret = WaitForSingleObjectEx (event, 0, FALSE);
-               MONO_FINISH_BLOCKING;
+               MONO_EXIT_GC_SAFE;
        }
 
        /* Pulse will have popped our event from the queue if it signalled