Merge pull request #2721 from ludovic-henry/fix-mono_ms_ticks
[mono.git] / mono / metadata / monitor.c
index d4dd2ee371c21f5d3fa3cec11df7c0cde47b9c03..510d1c18bcd31b3fcd86556f7ee0cea21b4fe0ef 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>
@@ -260,8 +261,8 @@ mono_monitor_cleanup (void)
        /*mono_os_mutex_destroy (&monitor_mutex);*/
 
        /* The monitors on the freelist don't have weak links - mark them */
-       for (mon = monitor_freelist; mon; mon = mon->data)
-               mon->wait_list = (gpointer)-1;
+       for (mon = monitor_freelist; mon; mon = (MonoThreadsSync *)mon->data)
+               mon->wait_list = (GSList *)-1;
 
        /*
         * FIXME: This still crashes with sgen (async_read.exe)
@@ -319,7 +320,7 @@ mono_locks_dump (gboolean include_untaken)
        int used = 0, on_freelist = 0, to_recycle = 0, total = 0, num_arrays = 0;
        MonoThreadsSync *mon;
        MonitorArray *marray;
-       for (mon = monitor_freelist; mon; mon = mon->data)
+       for (mon = monitor_freelist; mon; mon = (MonoThreadsSync *)mon->data)
                on_freelist++;
        for (marray = monitor_allocated; marray; marray = marray->next) {
                total += marray->num_monitors;
@@ -330,7 +331,7 @@ mono_locks_dump (gboolean include_untaken)
                                if (i < marray->num_monitors - 1)
                                        to_recycle++;
                        } else {
-                               if (!monitor_is_on_freelist (mon->data)) {
+                               if (!monitor_is_on_freelist ((MonoThreadsSync *)mon->data)) {
                                        MonoObject *holder = (MonoObject *)mono_gchandle_get_target ((guint32)mon->data);
                                        if (mon_status_get_owner (mon->status)) {
                                                g_print ("Lock %p in object %p held by thread %d, nest level: %d\n",
@@ -378,39 +379,39 @@ mon_finalize (MonoThreadsSync *mon)
 static MonoThreadsSync *
 mon_new (gsize id)
 {
-       MonoThreadsSync *new;
+       MonoThreadsSync *new_;
 
        if (!monitor_freelist) {
                MonitorArray *marray;
                int i;
                /* see if any sync block has been collected */
-               new = NULL;
+               new_ = NULL;
                for (marray = monitor_allocated; marray; marray = marray->next) {
                        for (i = 0; i < marray->num_monitors; ++i) {
                                if (mono_gchandle_get_target ((guint32)marray->monitors [i].data) == NULL) {
-                                       new = &marray->monitors [i];
-                                       if (new->wait_list) {
+                                       new_ = &marray->monitors [i];
+                                       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));
-                                                       CloseHandle (new->wait_list->data);
-                                                       new->wait_list = g_slist_remove (new->wait_list, new->wait_list->data);
+                                               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));
+                                                       CloseHandle (new_->wait_list->data);
+                                                       new_->wait_list = g_slist_remove (new_->wait_list, new_->wait_list->data);
                                                }
                                        }
-                                       mono_gchandle_free ((guint32)new->data);
-                                       new->data = monitor_freelist;
-                                       monitor_freelist = new;
+                                       mono_gchandle_free ((guint32)new_->data);
+                                       new_->data = monitor_freelist;
+                                       monitor_freelist = new_;
                                }
                        }
                        /* small perf tweak to avoid scanning all the blocks */
-                       if (new)
+                       if (new_)
                                break;
                }
                /* need to allocate a new array of monitors */
                if (!monitor_freelist) {
                        MonitorArray *last;
                        LOCK_DEBUG (g_message ("%s: allocating more monitors: %d", __func__, array_size));
-                       marray = g_malloc0 (MONO_SIZEOF_MONO_ARRAY + array_size * sizeof (MonoThreadsSync));
+                       marray = (MonitorArray *)g_malloc0 (MONO_SIZEOF_MONO_ARRAY + array_size * sizeof (MonoThreadsSync));
                        marray->num_monitors = array_size;
                        array_size *= 2;
                        /* link into the freelist */
@@ -433,18 +434,18 @@ mon_new (gsize id)
                }
        }
 
-       new = monitor_freelist;
-       monitor_freelist = new->data;
+       new_ = monitor_freelist;
+       monitor_freelist = (MonoThreadsSync *)new_->data;
 
-       new->status = mon_status_set_owner (0, id);
-       new->status = mon_status_init_entry_count (new->status);
-       new->nest = 1;
-       new->data = NULL;
+       new_->status = mon_status_set_owner (0, id);
+       new_->status = mon_status_init_entry_count (new_->status);
+       new_->nest = 1;
+       new_->data = NULL;
        
 #ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->gc_sync_blocks++;
 #endif
-       return new;
+       return new_;
 }
 
 static MonoThreadsSync*
@@ -493,7 +494,7 @@ mono_monitor_inflate_owned (MonoObject *obj, int id)
        nlw = lock_word_new_inflated (mon);
 
        mono_memory_write_barrier ();
-       tmp_lw.sync = InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, nlw.sync, old_lw.sync);
+       tmp_lw.sync = (MonoThreadsSync *)InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, nlw.sync, old_lw.sync);
        if (tmp_lw.sync != old_lw.sync) {
                /* Someone else inflated the lock in the meantime */
                discard_mon (mon);
@@ -536,7 +537,7 @@ mono_monitor_inflate (MonoObject *obj)
                        mon->nest = lock_word_get_nest (old_lw);
                }
                mono_memory_write_barrier ();
-               tmp_lw.sync = InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, nlw.sync, old_lw.sync);
+               tmp_lw.sync = (MonoThreadsSync *)InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, nlw.sync, old_lw.sync);
                if (tmp_lw.sync == old_lw.sync) {
                        /* Successfully inflated the lock */
                        return;
@@ -592,7 +593,7 @@ mono_object_hash (MonoObject* obj)
                LockWord old_lw;
                lw = lock_word_new_thin_hash (hash);
 
-               old_lw.sync = InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, NULL);
+               old_lw.sync = (MonoThreadsSync *)InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, NULL);
                if (old_lw.sync == NULL) {
                        return hash;
                }
@@ -707,7 +708,7 @@ mono_monitor_exit_flat (MonoObject *obj, LockWord old_lw)
        else
                new_lw.lock_word = 0;
 
-       tmp_lw.sync = InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, new_lw.sync, old_lw.sync);
+       tmp_lw.sync = (MonoThreadsSync *)InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, new_lw.sync, old_lw.sync);
        if (old_lw.sync != tmp_lw.sync) {
                /* Someone inflated the lock in the meantime */
                mono_monitor_exit_inflated (obj);
@@ -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;
@@ -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 (), (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) {
@@ -981,7 +977,7 @@ mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_int
                        } else {
                                LockWord nlw, old_lw;
                                nlw = lock_word_increment_nest (lw);
-                               old_lw.sync = InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, nlw.sync, lw.sync);
+                               old_lw.sync = (MonoThreadsSync *)InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, nlw.sync, lw.sync);
                                if (old_lw.sync != lw.sync) {
                                        /* Someone else inflated it in the meantime */
                                        g_assert (lock_word_is_inflated (old_lw));
@@ -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;
@@ -1076,8 +1078,13 @@ ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 m
 
        do {
                res = mono_monitor_try_enter_internal (obj, ms, TRUE);
-               if (res == -1)
-                       mono_thread_interruption_checkpoint ();
+               if (res == -1) {
+                       MonoException *exc = mono_thread_interruption_checkpoint ();
+                       if (exc) {
+                               mono_set_pending_exception (exc);
+                               return FALSE;
+                       }
+               }
        } while (res == -1);
        
        return res == 1;
@@ -1090,8 +1097,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 +1112,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 +1120,23 @@ 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;
+       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)
 {
@@ -1276,34 +1304,14 @@ ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms)
         * 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;