2009-07-22 Mark Probst <mark.probst@gmail.com>
[mono.git] / mono / metadata / monitor.c
index 78aa3961027ff5684b3666231e6d94ba3d417a0a..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)
@@ -1040,6 +1095,27 @@ mono_monitor_get_fast_path (MonoMethod *enter_or_exit)
        return NULL;
 }
 
+/*
+ * mono_monitor_threads_sync_member_offset:
+ * @owner_offset: returns size and offset of the "owner" member
+ * @nest_offset: returns size and offset of the "nest" member
+ * @entry_count_offset: returns size and offset of the "entry_count" member
+ *
+ * Returns the offsets and sizes of three members of the
+ * MonoThreadsSync struct.  The Monitor ASM fastpaths need this.
+ */
+void
+mono_monitor_threads_sync_members_offset (int *owner_offset, int *nest_offset, int *entry_count_offset)
+{
+       MonoThreadsSync ts;
+
+#define ENCODE_OFF_SIZE(o,s)   (((o) << 8) | ((s) & 0xff))
+
+       *owner_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, owner), sizeof (ts.owner));
+       *nest_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, nest), sizeof (ts.nest));
+       *entry_count_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, entry_count), sizeof (ts.entry_count));
+}
+
 gboolean 
 ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 ms)
 {