Merge pull request #654 from alesliehughes/master
[mono.git] / mono / metadata / monitor.c
index bd588fc851a823a4720b4976f4800757a690ddb0..9933f1dd48730ba5f4d5ae377a67752543063a85 100644 (file)
@@ -26,7 +26,7 @@
 #include <mono/metadata/marshal.h>
 #include <mono/metadata/profiler-private.h>
 #include <mono/utils/mono-time.h>
-#include <mono/utils/mono-semaphore.h>
+#include <mono/utils/atomic.h>
 
 /*
  * Pull the list of opcodes
@@ -81,7 +81,7 @@ struct _MonoThreadsSync
        gint32 hash_code;
 #endif
        volatile gint32 entry_count;
-       MonoSemType *entry_sem;
+       HANDLE entry_sem;
        GSList *wait_list;
        void *data;
 };
@@ -229,8 +229,7 @@ mon_finalize (MonoThreadsSync *mon)
        LOCK_DEBUG (g_message ("%s: Finalizing sync %p", __func__, mon));
 
        if (mon->entry_sem != NULL) {
-               MONO_SEM_DESTROY (mon->entry_sem);
-               g_free (mon->entry_sem);
+               CloseHandle (mon->entry_sem);
                mon->entry_sem = NULL;
        }
        /* If this isn't empty then something is seriously broken - it
@@ -244,7 +243,9 @@ mon_finalize (MonoThreadsSync *mon)
 
        mon->data = monitor_freelist;
        monitor_freelist = mon;
+#ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->gc_sync_blocks--;
+#endif
 }
 
 /* LOCKING: this is called with monitor_mutex held */
@@ -270,7 +271,7 @@ mon_new (gsize id)
                                                        new->wait_list = g_slist_remove (new->wait_list, new->wait_list->data);
                                                }
                                        }
-                                       mono_gc_weak_link_remove (&new->data);
+                                       mono_gc_weak_link_remove (&new->data, FALSE);
                                        new->data = monitor_freelist;
                                        monitor_freelist = new;
                                }
@@ -311,8 +312,11 @@ mon_new (gsize id)
 
        new->owner = id;
        new->nest = 1;
+       new->data = NULL;
        
+#ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->gc_sync_blocks++;
+#endif
        return new;
 }
 
@@ -414,7 +418,7 @@ mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_int
 {
        MonoThreadsSync *mon;
        gsize id = GetCurrentThreadId ();
-       MonoSemType *sem;
+       HANDLE sem;
        guint32 then = 0, now, delta;
        guint32 waitms;
        guint32 ret;
@@ -538,7 +542,9 @@ retry:
        }
 
        /* The object must be locked by someone else... */
+#ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->thread_contentions++;
+#endif
 
        /* If ms is 0 we don't block, but just fail straight away */
        if (ms == 0) {
@@ -583,13 +589,11 @@ retry_contended:
         */
        if (mon->entry_sem == NULL) {
                /* Create the semaphore */
-               sem = g_new0 (MonoSemType, 1);
-               MONO_SEM_INIT (sem, 0);
+               sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
                g_assert (sem != NULL);
                if (InterlockedCompareExchangePointer ((gpointer*)&mon->entry_sem, sem, NULL) != NULL) {
                        /* Someone else just put a handle here */
-                       MONO_SEM_DESTROY (sem);
-                       g_free (sem);
+                       CloseHandle (sem);
                }
        }
        
@@ -616,8 +620,10 @@ retry_contended:
        
        InterlockedIncrement (&mon->entry_count);
 
+#ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->thread_queue_len++;
        mono_perfcounters->thread_queue_max++;
+#endif
        thread = mono_thread_internal_current ();
 
        mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
@@ -626,12 +632,14 @@ retry_contended:
         * We pass TRUE instead of allow_interruption since we have to check for the
         * StopRequested case below.
         */
-       ret = MONO_SEM_TIMEDWAIT_ALERTABLE(mon->entry_sem, waitms, TRUE);
+       ret = WaitForSingleObjectEx (mon->entry_sem, waitms, TRUE);
 
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
        
        InterlockedDecrement (&mon->entry_count);
+#ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->thread_queue_len--;
+#endif
 
        if (ms != INFINITE) {
                now = mono_msec_ticks ();
@@ -750,7 +758,7 @@ mono_monitor_exit (MonoObject *obj)
                 * struct.
                 */
                if (mon->entry_count > 0) {
-                       MONO_SEM_POST (mon->entry_sem);
+                       ReleaseSemaphore (mon->entry_sem, 1, NULL);
                }
        } else {
                LOCK_DEBUG (g_message ("%s: (%d) Object %p is now locked %d times", __func__, GetCurrentThreadId (), obj, nest));
@@ -777,9 +785,11 @@ mono_monitor_get_object_monitor_weak_link (MonoObject *object)
        return NULL;
 }
 
+#ifndef DISABLE_JIT
+
 static void
-emit_obj_syncp_check (MonoMethodBuilder *mb, int syncp_loc, int *obj_null_branch, int *syncp_true_false_branch,
-       gboolean branch_on_true)
+emit_obj_syncp_check (MonoMethodBuilder *mb, int syncp_loc, int *obj_null_branch, int *true_locktaken_branch, int *syncp_true_false_branch,
+       int *thin_hash_branch, gboolean branch_on_true)
 {
        /*
          ldarg         0                                                       obj
@@ -789,6 +799,17 @@ emit_obj_syncp_check (MonoMethodBuilder *mb, int syncp_loc, int *obj_null_branch
        mono_mb_emit_byte (mb, CEE_LDARG_0);
        *obj_null_branch = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S);
 
+       /*
+         ldarg.1
+         ldind.i1
+         brtrue.s true_locktaken
+       */
+       if (true_locktaken_branch) {
+               mono_mb_emit_byte (mb, CEE_LDARG_1);
+               mono_mb_emit_byte (mb, CEE_LDIND_I1);
+               *true_locktaken_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+       }
+
        /*
          ldarg         0                                                       obj
          conv.i                                                                objp
@@ -806,31 +827,82 @@ emit_obj_syncp_check (MonoMethodBuilder *mb, int syncp_loc, int *obj_null_branch
        mono_mb_emit_byte (mb, CEE_ADD);
        mono_mb_emit_byte (mb, CEE_LDIND_I);
        mono_mb_emit_stloc (mb, syncp_loc);
+
+       
+       if (mono_gc_is_moving ()) {
+               /*check for a thin hash*/
+               mono_mb_emit_ldloc (mb, syncp_loc);
+               mono_mb_emit_icon (mb, 0x01);
+               mono_mb_emit_byte (mb, CEE_CONV_I);
+               mono_mb_emit_byte (mb, CEE_AND);
+               *thin_hash_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+               /*clear gc bits*/
+               mono_mb_emit_ldloc (mb, syncp_loc);
+               mono_mb_emit_icon (mb, ~0x3);
+               mono_mb_emit_byte (mb, CEE_CONV_I);
+               mono_mb_emit_byte (mb, CEE_AND);
+               mono_mb_emit_stloc (mb, syncp_loc);
+       } else {
+               *thin_hash_branch = 0;
+       }
+
        mono_mb_emit_ldloc (mb, syncp_loc);
        *syncp_true_false_branch = mono_mb_emit_short_branch (mb, branch_on_true ? CEE_BRTRUE_S : CEE_BRFALSE_S);
 }
 
+#endif
+
+static MonoMethod* monitor_il_fastpaths[3];
+
+gboolean
+mono_monitor_is_il_fastpath_wrapper (MonoMethod *method)
+{
+       int i;
+       for (i = 0; i < 3; ++i) {
+               if (monitor_il_fastpaths [i] == method)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+enum {
+       FASTPATH_ENTER,
+       FASTPATH_ENTERV4,
+       FASTPATH_EXIT
+};
+
+
+static MonoMethod*
+register_fastpath (MonoMethod *method, int idx)
+{
+       mono_memory_barrier ();
+       monitor_il_fastpaths [idx] = method;
+       return method;
+}
+
 static MonoMethod*
 mono_monitor_get_fast_enter_method (MonoMethod *monitor_enter_method)
 {
-       static MonoMethod *fast_monitor_enter;
-       static MonoMethod *compare_exchange_method;
-
        MonoMethodBuilder *mb;
-       int obj_null_branch, syncp_null_branch, has_owner_branch, other_owner_branch, tid_branch;
+       MonoMethod *res;
+       static MonoMethod *compare_exchange_method;
+       int obj_null_branch, true_locktaken_branch = 0, syncp_null_branch, has_owner_branch, other_owner_branch, tid_branch, thin_hash_branch;
        int tid_loc, syncp_loc, owner_loc;
        int thread_tls_offset;
+       gboolean is_v4 = mono_method_signature (monitor_enter_method)->param_count == 2;
+       int fast_path_idx = is_v4 ? FASTPATH_ENTERV4 : FASTPATH_ENTER;
+       WrapperInfo *info;
 
-#ifdef HAVE_MOVING_COLLECTOR
-       return NULL;
-#endif
+       /* The !is_v4 version is not used/tested */
+       g_assert (is_v4);
 
        thread_tls_offset = mono_thread_get_tls_offset ();
        if (thread_tls_offset == -1)
                return NULL;
 
-       if (fast_monitor_enter)
-               return fast_monitor_enter;
+       if (monitor_il_fastpaths [fast_path_idx])
+               return monitor_il_fastpaths [fast_path_idx];
 
        if (!compare_exchange_method) {
                MonoMethodDesc *desc;
@@ -845,17 +917,18 @@ mono_monitor_get_fast_enter_method (MonoMethod *monitor_enter_method)
                        return NULL;
        }
 
-       mb = mono_mb_new (mono_defaults.monitor_class, "FastMonitorEnter", MONO_WRAPPER_UNKNOWN);
+       mb = mono_mb_new (mono_defaults.monitor_class, is_v4 ? "FastMonitorEnterV4" : "FastMonitorEnter", MONO_WRAPPER_UNKNOWN);
 
        mb->method->slot = -1;
        mb->method->flags = METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_STATIC |
                METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_FINAL;
 
+#ifndef DISABLE_JIT
        tid_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
        syncp_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
        owner_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
 
-       emit_obj_syncp_check (mb, syncp_loc, &obj_null_branch, &syncp_null_branch, FALSE);
+       emit_obj_syncp_check (mb, syncp_loc, &obj_null_branch, is_v4 ? &true_locktaken_branch : NULL, &syncp_null_branch, &thin_hash_branch, FALSE);
 
        /*
          mono. tls     thread_tls_offset                                       threadp
@@ -905,6 +978,12 @@ mono_monitor_get_fast_enter_method (MonoMethod *monitor_enter_method)
        mono_mb_emit_byte (mb, CEE_LDC_I4_0);
        mono_mb_emit_managed_call (mb, compare_exchange_method, NULL);
        has_owner_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+       if (is_v4) {
+               mono_mb_emit_byte (mb, CEE_LDARG_1);
+               mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+               mono_mb_emit_byte (mb, CEE_STIND_I1);
+       }
        mono_mb_emit_byte (mb, CEE_RET);
 
        /*
@@ -935,6 +1014,13 @@ mono_monitor_get_fast_enter_method (MonoMethod *monitor_enter_method)
        mono_mb_emit_byte (mb, CEE_LDC_I4_1);
        mono_mb_emit_byte (mb, CEE_ADD);
        mono_mb_emit_byte (mb, CEE_STIND_I4);
+
+       if (is_v4) {
+               mono_mb_emit_byte (mb, CEE_LDARG_1);
+               mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+               mono_mb_emit_byte (mb, CEE_STIND_I1);
+       }
+
        mono_mb_emit_byte (mb, CEE_RET);
 
        /*
@@ -944,40 +1030,47 @@ mono_monitor_get_fast_enter_method (MonoMethod *monitor_enter_method)
          ret
        */
 
+       if (thin_hash_branch)
+               mono_mb_patch_short_branch (mb, thin_hash_branch);
        mono_mb_patch_short_branch (mb, obj_null_branch);
        mono_mb_patch_short_branch (mb, syncp_null_branch);
        mono_mb_patch_short_branch (mb, has_owner_branch);
        mono_mb_patch_short_branch (mb, other_owner_branch);
+       if (true_locktaken_branch)
+               mono_mb_patch_short_branch (mb, true_locktaken_branch);
        mono_mb_emit_byte (mb, CEE_LDARG_0);
+       if (is_v4)
+               mono_mb_emit_byte (mb, CEE_LDARG_1);
        mono_mb_emit_managed_call (mb, monitor_enter_method, NULL);
        mono_mb_emit_byte (mb, CEE_RET);
+#endif
 
-       fast_monitor_enter = mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_enter_method), 5);
-       mono_mb_free (mb);
+       res = register_fastpath (mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_enter_method), 5), fast_path_idx);
+
+       info = mono_image_alloc0 (mono_defaults.corlib, sizeof (WrapperInfo));
+       info->subtype = is_v4 ? WRAPPER_SUBTYPE_FAST_MONITOR_ENTER_V4 : WRAPPER_SUBTYPE_FAST_MONITOR_ENTER;
+       mono_marshal_set_wrapper_info (res, info);
 
-       return fast_monitor_enter;
+       mono_mb_free (mb);
+       return res;
 }
 
 static MonoMethod*
 mono_monitor_get_fast_exit_method (MonoMethod *monitor_exit_method)
 {
-       static MonoMethod *fast_monitor_exit;
-
        MonoMethodBuilder *mb;
-       int obj_null_branch, has_waiting_branch, has_syncp_branch, owned_branch, nested_branch;
+       MonoMethod *res;
+       int obj_null_branch, has_waiting_branch, has_syncp_branch, owned_branch, nested_branch, thin_hash_branch;
        int thread_tls_offset;
        int syncp_loc;
-
-#ifdef HAVE_MOVING_COLLECTOR
-       return NULL;
-#endif
+       WrapperInfo *info;
 
        thread_tls_offset = mono_thread_get_tls_offset ();
        if (thread_tls_offset == -1)
                return NULL;
 
-       if (fast_monitor_exit)
-               return fast_monitor_exit;
+       if (monitor_il_fastpaths [FASTPATH_EXIT])
+               return monitor_il_fastpaths [FASTPATH_EXIT];
 
        mb = mono_mb_new (mono_defaults.monitor_class, "FastMonitorExit", MONO_WRAPPER_UNKNOWN);
 
@@ -985,9 +1078,10 @@ mono_monitor_get_fast_exit_method (MonoMethod *monitor_exit_method)
        mb->method->flags = METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_STATIC |
                METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_FINAL;
 
+#ifndef DISABLE_JIT
        syncp_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
 
-       emit_obj_syncp_check (mb, syncp_loc, &obj_null_branch, &has_syncp_branch, TRUE);
+       emit_obj_syncp_check (mb, syncp_loc, &obj_null_branch, NULL, &has_syncp_branch, &thin_hash_branch, TRUE);
 
        /*
          ret
@@ -1104,16 +1198,23 @@ mono_monitor_get_fast_exit_method (MonoMethod *monitor_exit_method)
          ret
         */
 
+       if (thin_hash_branch)
+               mono_mb_patch_short_branch (mb, thin_hash_branch);
        mono_mb_patch_short_branch (mb, obj_null_branch);
        mono_mb_patch_short_branch (mb, has_waiting_branch);
        mono_mb_emit_byte (mb, CEE_LDARG_0);
        mono_mb_emit_managed_call (mb, monitor_exit_method, NULL);
        mono_mb_emit_byte (mb, CEE_RET);
+#endif
 
-       fast_monitor_exit = mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_exit_method), 5);
+       res = register_fastpath (mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_exit_method), 5), FASTPATH_EXIT);
        mono_mb_free (mb);
 
-       return fast_monitor_exit;
+       info = mono_image_alloc0 (mono_defaults.corlib, sizeof (WrapperInfo));
+       info->subtype = WRAPPER_SUBTYPE_FAST_MONITOR_EXIT;
+       mono_marshal_set_wrapper_info (res, info);
+
+       return res;
 }
 
 MonoMethod*