2009-07-22 Mark Probst <mark.probst@gmail.com>
[mono.git] / mono / metadata / monitor.c
index 022495a3b14c9dbdb51b1cca8bcdc8bcc7237950..56981a59518faec6b7c43a55aded1d663a310d7c 100644 (file)
@@ -4,7 +4,8 @@
  * Author:
  *     Dick Porter (dick@ximian.com)
  *
- * (C) 2003 Ximian, Inc.
+ * Copyright 2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  */
 
 #include <config.h>
@@ -23,6 +24,7 @@
 #include <mono/metadata/debug-helpers.h>
 #include <mono/metadata/tabledefs.h>
 #include <mono/metadata/marshal.h>
+#include <mono/metadata/profiler-private.h>
 #include <mono/utils/mono-time.h>
 
 /*
@@ -400,7 +402,7 @@ retry:
                mono_monitor_allocator_lock ();
                mon = mon_new (id);
                if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL) == NULL) {
-                       mono_gc_weak_link_add (&mon->data, obj);
+                       mono_gc_weak_link_add (&mon->data, obj, FALSE);
                        mono_monitor_allocator_unlock ();
                        /* Successfully locked */
                        return 1;
@@ -415,7 +417,7 @@ retry:
                                lw.sync = mon;
                                lw.lock_word |= LOCK_WORD_FAT_HASH;
                                if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) {
-                                       mono_gc_weak_link_add (&mon->data, obj);
+                                       mono_gc_weak_link_add (&mon->data, obj, FALSE);
                                        mono_monitor_allocator_unlock ();
                                        /* Successfully locked */
                                        return 1;
@@ -454,7 +456,7 @@ retry:
                        lw.sync = mon;
                        lw.lock_word |= LOCK_WORD_FAT_HASH;
                        if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) {
-                               mono_gc_weak_link_add (&mon->data, obj);
+                               mono_gc_weak_link_add (&mon->data, obj, TRUE);
                                mono_monitor_allocator_unlock ();
                                /* Successfully locked */
                                return 1;
@@ -511,9 +513,40 @@ retry:
                return 0;
        }
 
-       /* The slow path begins here.  We need to make sure theres a
-        * semaphore handle (creating it if necessary), and block on
-        * it
+       mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_CONTENTION);
+
+       /* The slow path begins here. */
+retry_contended:
+       /* a small amount of duplicated code, but it allows us to insert the profiler
+        * callbacks without impacting the fast path: from here on we don't need to go back to the
+        * retry label, but to retry_contended. At this point mon is already installed in the object
+        * header.
+        */
+       /* This case differs from Dice's case 3 because we don't
+        * deflate locks or cache unused lock records
+        */
+       if (G_LIKELY (mon->owner == 0)) {
+               /* Try to install our ID in the owner field, nest
+               * should have been left at 1 by the previous unlock
+               * operation
+               */
+               if (G_LIKELY (InterlockedCompareExchangePointer ((gpointer *)&mon->owner, (gpointer)id, 0) == 0)) {
+                       /* Success */
+                       g_assert (mon->nest == 1);
+                       mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_DONE);
+                       return 1;
+               }
+       }
+
+       /* If the object is currently locked by this thread... */
+       if (mon->owner == id) {
+               mon->nest++;
+               mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_DONE);
+               return 1;
+       }
+
+       /* We need to make sure there's a semaphore handle (creating it if
+        * necessary), and block on it
         */
        if (mon->entry_sem == NULL) {
                /* Create the semaphore */
@@ -588,30 +621,33 @@ retry:
 
                if ((ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) && ms > 0) {
                        /* More time left */
-                       goto retry;
+                       goto retry_contended;
                }
        } else {
                if (ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) {
-                       if (ret == WAIT_IO_COMPLETION && mono_thread_test_state (mono_thread_current (), ThreadState_StopRequested)) {
+                       if (ret == WAIT_IO_COMPLETION && (mono_thread_test_state (mono_thread_current (), (ThreadState_StopRequested|ThreadState_SuspendRequested)))) {
                                /* 
-                                * We have to obey a stop request even if allow_interruption is
-                                * FALSE to avoid hangs at shutdown.
+                                * We have to obey a stop/suspend request even if 
+                                * allow_interruption is FALSE to avoid hangs at shutdown.
                                 */
+                               mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_FAIL);
                                return -1;
                        }
                        /* Infinite wait, so just try again */
-                       goto retry;
+                       goto retry_contended;
                }
        }
        
        if (ret == WAIT_OBJECT_0) {
                /* retry from the top */
-               goto retry;
+               goto retry_contended;
        }
-       
+
        /* We must have timed out */
        LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out waiting, returning FALSE", id));
 
+       mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_FAIL);
+
        if (ret == WAIT_IO_COMPLETION)
                return -1;
        else 
@@ -690,6 +726,25 @@ mono_monitor_exit (MonoObject *obj)
        }
 }
 
+void**
+mono_monitor_get_object_monitor_weak_link (MonoObject *object)
+{
+       LockWord lw;
+       MonoThreadsSync *sync = NULL;
+
+       lw.sync = object->synchronisation;
+       if (lw.lock_word & LOCK_WORD_FAT_HASH) {
+               lw.lock_word &= ~LOCK_WORD_BITS_MASK;
+               sync = lw.sync;
+       } else if (!(lw.lock_word & LOCK_WORD_THIN_HASH)) {
+               sync = lw.sync;
+       }
+
+       if (sync && sync->data)
+               return &sync->data;
+       return NULL;
+}
+
 static void
 emit_obj_syncp_check (MonoMethodBuilder *mb, int syncp_loc, int *obj_null_branch, int *syncp_true_false_branch,
        gboolean branch_on_true)