* 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>
#include <glib.h>
+#include <string.h>
#include <mono/metadata/monitor.h>
#include <mono/metadata/threads-types.h>
#include <mono/metadata/threads.h>
#include <mono/io-layer/io-layer.h>
#include <mono/metadata/object-internals.h>
+#include <mono/metadata/class-internals.h>
#include <mono/metadata/gc-internal.h>
+#include <mono/metadata/method-builder.h>
+#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>
+
+/*
+ * Pull the list of opcodes
+ */
+#define OPDEF(a,b,c,d,e,f,g,h,i,j) \
+ a = i,
+
+enum {
+#include "mono/cil/opcode.def"
+ LAST = 0xff
+};
+#undef OPDEF
/*#define LOCK_DEBUG(a) do { a; } while (0)*/
#define LOCK_DEBUG(a)
static MonitorArray *monitor_allocated;
static int array_size = 16;
+#ifdef HAVE_KW_THREAD
+static __thread gsize tls_pthread_self MONO_TLS_FAST;
+#endif
+
+#ifndef PLATFORM_WIN32
+#ifdef HAVE_KW_THREAD
+#define GetCurrentThreadId() tls_pthread_self
+#else
+/*
+ * The usual problem: we can't replace GetCurrentThreadId () with a macro because
+ * it is in a public header.
+ */
+#define GetCurrentThreadId() ((gsize)pthread_self ())
+#endif
+#endif
+
void
mono_monitor_init (void)
{
/*DeleteCriticalSection (&monitor_mutex);*/
}
+/*
+ * mono_monitor_init_tls:
+ *
+ * Setup TLS variables used by the monitor code for the current thread.
+ */
+void
+mono_monitor_init_tls (void)
+{
+#if !defined(PLATFORM_WIN32) && defined(HAVE_KW_THREAD)
+ tls_pthread_self = pthread_self ();
+#endif
+}
+
static int
monitor_is_on_freelist (MonoThreadsSync *mon)
{
mon->data = monitor_freelist;
monitor_freelist = mon;
+ mono_perfcounters->gc_sync_blocks--;
}
/* LOCKING: this is called with monitor_mutex held */
new->owner = id;
new->nest = 1;
+ mono_perfcounters->gc_sync_blocks++;
return new;
}
/* If allow_interruption==TRUE, the method will be interrumped if abort or suspend
* is requested. In this case it returns -1.
*/
-static gint32
+static inline gint32
mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_interruption)
{
MonoThreadsSync *mon;
guint32 then = 0, now, delta;
guint32 waitms;
guint32 ret;
-
+ MonoThread *thread;
+
LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
": (%d) Trying to lock object %p (%d ms)", id, obj, ms));
+ if (G_UNLIKELY (!obj)) {
+ mono_raise_exception (mono_get_exception_argument_null ("obj"));
+ return FALSE;
+ }
+
retry:
mon = obj->synchronisation;
/* If the object has never been locked... */
- if (mon == NULL) {
+ if (G_UNLIKELY (mon == NULL)) {
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;
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;
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;
}
#endif
- /* If the object is currently locked by this thread... */
- if (mon->owner == id) {
- mon->nest++;
- return 1;
- }
-
/* If the object has previously been locked but isn't now... */
/* This case differs from Dice's case 3 because we don't
* deflate locks or cache unused lock records
*/
- if (mon->owner == 0) {
+ 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 (InterlockedCompareExchangePointer ((gpointer *)&mon->owner, (gpointer)id, 0) == 0) {
+ if (G_LIKELY (InterlockedCompareExchangePointer ((gpointer *)&mon->owner, (gpointer)id, 0) == 0)) {
/* Success */
g_assert (mon->nest == 1);
return 1;
}
}
+ /* If the object is currently locked by this thread... */
+ if (mon->owner == id) {
+ mon->nest++;
+ return 1;
+ }
+
/* The object must be locked by someone else... */
+ mono_perfcounters->thread_contentions++;
/* If ms is 0 we don't block, but just fail straight away */
if (ms == 0) {
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 */
CloseHandle (sem);
}
}
-
+
/* If we need to time out, record a timestamp and adjust ms,
* because WaitForSingleObject doesn't tell us how long it
* waited for.
* lock struct and therefore slowing down the fast path.
*/
if (ms != INFINITE) {
- then = GetTickCount ();
+ then = mono_msec_ticks ();
if (ms < 100) {
waitms = ms;
} else {
}
InterlockedIncrement (&mon->entry_count);
- ret = WaitForSingleObjectEx (mon->entry_sem, waitms, allow_interruption);
+
+ mono_perfcounters->thread_queue_len++;
+ mono_perfcounters->thread_queue_max++;
+ thread = mono_thread_current ();
+
+ mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
+
+ /*
+ * We pass TRUE instead of allow_interruption since we have to check for the
+ * StopRequested case below.
+ */
+ ret = WaitForSingleObjectEx (mon->entry_sem, waitms, TRUE);
+
+ mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
+
InterlockedDecrement (&mon->entry_count);
+ mono_perfcounters->thread_queue_len--;
if (ms != INFINITE) {
- now = GetTickCount ();
+ now = mono_msec_ticks ();
if (now < then) {
/* The counter must have wrapped around */
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|ThreadState_SuspendRequested)))) {
+ /*
+ * 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
return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1;
}
-void
+void
mono_monitor_exit (MonoObject *obj)
{
MonoThreadsSync *mon;
LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p", GetCurrentThreadId (), obj));
+ if (G_UNLIKELY (!obj)) {
+ mono_raise_exception (mono_get_exception_argument_null ("obj"));
+ return;
+ }
+
mon = obj->synchronisation;
#ifdef HAVE_MOVING_COLLECTOR
mon = lw.sync;
}
#endif
- if (mon == NULL) {
+ if (G_UNLIKELY (mon == NULL)) {
/* No one ever used Enter. Just ignore the Exit request as MS does */
return;
}
- if (mon->owner != GetCurrentThreadId ()) {
+ if (G_UNLIKELY (mon->owner != GetCurrentThreadId ())) {
return;
}
}
}
+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)
+{
+ /*
+ ldarg 0 obj
+ brfalse.s obj_null
+ */
+
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ *obj_null_branch = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S);
+
+ /*
+ ldarg 0 obj
+ conv.i objp
+ ldc.i4 G_STRUCT_OFFSET(MonoObject, synchronisation) objp off
+ add &syncp
+ ldind.i syncp
+ stloc syncp
+ ldloc syncp syncp
+ brtrue/false.s syncp_true_false
+ */
+
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, syncp_loc);
+ 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);
+}
+
+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;
+ int tid_loc, syncp_loc, owner_loc;
+ int thread_tls_offset;
+
+#ifdef HAVE_MOVING_COLLECTOR
+ return NULL;
+#endif
+
+ thread_tls_offset = mono_thread_get_tls_offset ();
+ if (thread_tls_offset == -1)
+ return NULL;
+
+ if (fast_monitor_enter)
+ return fast_monitor_enter;
+
+ if (!compare_exchange_method) {
+ MonoMethodDesc *desc;
+ MonoClass *class;
+
+ desc = mono_method_desc_new ("Interlocked:CompareExchange(intptr&,intptr,intptr)", FALSE);
+ class = mono_class_from_name (mono_defaults.corlib, "System.Threading", "Interlocked");
+ compare_exchange_method = mono_method_desc_search_in_class (desc, class);
+ mono_method_desc_free (desc);
+
+ if (!compare_exchange_method)
+ return NULL;
+ }
+
+ mb = mono_mb_new (mono_defaults.monitor_class, "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;
+
+ 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);
+
+ /*
+ mono. tls thread_tls_offset threadp
+ ldc.i4 G_STRUCT_OFFSET(MonoThread, tid) threadp off
+ add &tid
+ ldind.i tid
+ stloc tid
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, owner) syncp off
+ add &owner
+ ldind.i owner
+ stloc owner
+ ldloc owner owner
+ brtrue.s tid
+ */
+
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_TLS);
+ mono_mb_emit_i4 (mb, thread_tls_offset);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThread, tid));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, tid_loc);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, owner_loc);
+ mono_mb_emit_ldloc (mb, owner_loc);
+ tid_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+ /*
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, owner) syncp off
+ add &owner
+ ldloc tid &owner tid
+ ldc.i4 0 &owner tid 0
+ call System.Threading.Interlocked.CompareExchange oldowner
+ brtrue.s has_owner
+ ret
+ */
+
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_ldloc (mb, tid_loc);
+ 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);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ tid:
+ ldloc owner owner
+ ldloc tid owner tid
+ brne.s other_owner
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, nest) syncp off
+ add &nest
+ dup &nest &nest
+ ldind.i4 &nest nest
+ ldc.i4 1 &nest nest 1
+ add &nest nest+
+ stind.i4
+ ret
+ */
+
+ mono_mb_patch_short_branch (mb, tid_branch);
+ mono_mb_emit_ldloc (mb, owner_loc);
+ mono_mb_emit_ldloc (mb, tid_loc);
+ other_owner_branch = mono_mb_emit_short_branch (mb, CEE_BNE_UN_S);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, nest));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_DUP);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_STIND_I4);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ obj_null, syncp_null, has_owner, other_owner:
+ ldarg 0 obj
+ call System.Threading.Monitor.Enter
+ ret
+ */
+
+ 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);
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_managed_call (mb, monitor_enter_method, NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ fast_monitor_enter = mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_enter_method), 5);
+ mono_mb_free (mb);
+
+ return fast_monitor_enter;
+}
+
+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;
+ int thread_tls_offset;
+ int syncp_loc;
+
+#ifdef HAVE_MOVING_COLLECTOR
+ return NULL;
+#endif
+
+ thread_tls_offset = mono_thread_get_tls_offset ();
+ if (thread_tls_offset == -1)
+ return NULL;
+
+ if (fast_monitor_exit)
+ return fast_monitor_exit;
+
+ mb = mono_mb_new (mono_defaults.monitor_class, "FastMonitorExit", MONO_WRAPPER_UNKNOWN);
+
+ mb->method->slot = -1;
+ mb->method->flags = METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_STATIC |
+ METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_FINAL;
+
+ 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);
+
+ /*
+ ret
+ */
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ has_syncp:
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, owner) syncp off
+ add &owner
+ ldind.i owner
+ mono. tls thread_tls_offset owner threadp
+ ldc.i4 G_STRUCT_OFFSET(MonoThread, tid) owner threadp off
+ add owner &tid
+ ldind.i owner tid
+ beq.s owned
+ */
+
+ mono_mb_patch_short_branch (mb, has_syncp_branch);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_TLS);
+ mono_mb_emit_i4 (mb, thread_tls_offset);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThread, tid));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ owned_branch = mono_mb_emit_short_branch (mb, CEE_BEQ_S);
+
+ /*
+ ret
+ */
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ owned:
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, nest) syncp off
+ add &nest
+ dup &nest &nest
+ ldind.i4 &nest nest
+ dup &nest nest nest
+ ldc.i4 1 &nest nest nest 1
+ bgt.un.s nested &nest nest
+ */
+
+ mono_mb_patch_short_branch (mb, owned_branch);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, nest));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_DUP);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ mono_mb_emit_byte (mb, CEE_DUP);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ nested_branch = mono_mb_emit_short_branch (mb, CEE_BGT_UN_S);
+
+ /*
+ pop &nest
+ pop
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, entry_count) syncp off
+ add &count
+ ldind.i4 count
+ brtrue.s has_waiting
+ */
+
+ mono_mb_emit_byte (mb, CEE_POP);
+ mono_mb_emit_byte (mb, CEE_POP);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, entry_count));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ has_waiting_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+ /*
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, owner) syncp off
+ add &owner
+ ldnull &owner 0
+ stind.i
+ ret
+ */
+
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_byte (mb, CEE_STIND_I);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ nested:
+ ldc.i4 1 &nest nest 1
+ sub &nest nest-
+ stind.i4
+ ret
+ */
+
+ mono_mb_patch_short_branch (mb, nested_branch);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_SUB);
+ mono_mb_emit_byte (mb, CEE_STIND_I4);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ obj_null, has_waiting:
+ ldarg 0 obj
+ call System.Threading.Monitor.Exit
+ ret
+ */
+
+ 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);
+
+ fast_monitor_exit = mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_exit_method), 5);
+ mono_mb_free (mb);
+
+ return fast_monitor_exit;
+}
+
+MonoMethod*
+mono_monitor_get_fast_path (MonoMethod *enter_or_exit)
+{
+ if (strcmp (enter_or_exit->name, "Enter") == 0)
+ return mono_monitor_get_fast_enter_method (enter_or_exit);
+ if (strcmp (enter_or_exit->name, "Exit") == 0)
+ return mono_monitor_get_fast_exit_method (enter_or_exit);
+ g_assert_not_reached ();
+ 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)
{
return res == 1;
}
-void
-ves_icall_System_Threading_Monitor_Monitor_exit (MonoObject *obj)
-{
- mono_monitor_exit (obj);
-}
-
gboolean
ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj)
{
LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) queuing handle %p",
GetCurrentThreadId (), event));
- mono_monitor_enter (thread->synch_lock);
- thread->state |= ThreadState_WaitSleepJoin;
- mono_monitor_exit (thread->synch_lock);
+ mono_thread_current_check_pending_interrupt ();
+
+ mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
mon->wait_list = g_slist_append (mon->wait_list, event);
/* Reset the thread state fairly early, so we don't have to worry
* about the monitor error checking
*/
- mono_monitor_enter (thread->synch_lock);
- thread->state &= ~ThreadState_WaitSleepJoin;
- mono_monitor_exit (thread->synch_lock);
+ mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
if (mono_thread_interruption_requested ()) {
CloseHandle (event);