#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/tabledefs.h>
#include <mono/metadata/marshal.h>
+#include <mono/metadata/w32event.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/profiler-private.h>
#include <mono/utils/mono-time.h>
LOCK_DEBUG (g_message ("%s: Finalizing sync %p", __func__, mon));
if (mon->entry_sem != NULL) {
- CloseHandle (mon->entry_sem);
+ mono_coop_sem_destroy (mon->entry_sem);
+ g_free (mon->entry_sem);
mon->entry_sem = NULL;
}
/* If this isn't empty then something is seriously broken - it
#endif
}
-static void
+static gboolean
mono_monitor_ensure_owned (LockWord lw, guint32 id)
{
if (lock_word_is_flat (lw)) {
if (lock_word_get_owner (lw) == id)
- return;
+ return TRUE;
} else if (lock_word_is_inflated (lw)) {
if (mon_status_get_owner (lock_word_get_inflated_lock (lw)->status) == id)
- return;
+ return TRUE;
}
mono_set_pending_exception (mono_get_exception_synchronization_lock ("Object synchronization method was called from an unsynchronized block of code."));
+ return FALSE;
}
/*
tmp_status = InterlockedCompareExchange ((gint32*)&mon->status, new_status, old_status);
if (tmp_status == old_status) {
if (have_waiters)
- ReleaseSemaphore (mon->entry_sem, 1, NULL);
+ mono_coop_sem_post (mon->entry_sem);
break;
}
old_status = tmp_status;
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;
+ MonoSemTimedwaitRet wait_ret;
MonoInternalThread *thread;
gboolean interrupted = FALSE;
*/
if (mon->entry_sem == NULL) {
/* Create the semaphore */
- sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
- g_assert (sem != NULL);
+ sem = g_new0 (MonoCoopSem, 1);
+ mono_coop_sem_init (sem, 0);
if (InterlockedCompareExchangePointer ((gpointer*)&mon->entry_sem, sem, NULL) != NULL) {
/* Someone else just put a handle here */
- CloseHandle (sem);
+ mono_coop_sem_destroy (sem);
+ g_free (sem);
}
}
#endif
thread = mono_thread_internal_current ();
- mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
+ /*
+ * If we allow interruption, we check the test state for an abort request before going into sleep.
+ * This is a workaround to the fact that Thread.Abort does non-sticky interruption of semaphores.
+ *
+ * Semaphores don't support the sticky interruption with mono_thread_info_install_interrupt.
+ *
+ * A better fix would be to switch to wait with something that allows sticky interrupts together
+ * with wrapping it with abort_protected_block_count for the non-alertable cases.
+ * And somehow make this whole dance atomic and not crazy expensive. Good luck.
+ *
+ */
+ if (allow_interruption) {
+ if (!mono_thread_test_and_set_state (thread, (MonoThreadState)(ThreadState_StopRequested | ThreadState_AbortRequested), ThreadState_WaitSleepJoin)) {
+ wait_ret = MONO_SEM_TIMEDWAIT_RET_ALERTED;
+ goto done_waiting;
+ }
+ } else {
+ mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
+ }
/*
- * We pass TRUE instead of allow_interruption since we have to check for the
+ * We pass ALERTABLE instead of allow_interruption since we have to check for the
* StopRequested case below.
*/
- MONO_PREPARE_BLOCKING;
- ret = WaitForSingleObjectEx (mon->entry_sem, waitms, TRUE);
- MONO_FINISH_BLOCKING;
+ wait_ret = mono_coop_sem_timedwait (mon->entry_sem, waitms, MONO_SEM_FLAGS_ALERTABLE);
mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
-
+
+done_waiting:
#ifndef DISABLE_PERFCOUNTERS
mono_perfcounters->thread_queue_len--;
#endif
- if (ret == WAIT_IO_COMPLETION && !allow_interruption) {
+ if (wait_ret == MONO_SEM_TIMEDWAIT_RET_ALERTED && !allow_interruption) {
interrupted = TRUE;
/*
* We have to obey a stop/suspend request even if
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) {
/* retry from the top */
goto retry_contended;
}
- } else if (ret == WAIT_OBJECT_0) {
+ } else if (wait_ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
interrupted = FALSE;
/* retry from the top */
goto retry_contended;
- } else if (ret == WAIT_TIMEOUT) {
+ } else if (wait_ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
/* we're done */
}
mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_FAIL);
- if (ret == WAIT_IO_COMPLETION) {
+ if (wait_ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
LOCK_DEBUG (g_message ("%s: (%d) interrupted waiting, returning -1", __func__, id));
return -1;
- } else if (ret == WAIT_TIMEOUT) {
+ } else if (wait_ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
LOCK_DEBUG (g_message ("%s: (%d) timed out waiting, returning FALSE", __func__, id));
return 0;
} else {
LOCK_DEBUG (g_message("%s: (%d) Trying to lock object %p (%d ms)", __func__, id, obj, ms));
- if (G_UNLIKELY (!obj)) {
- mono_set_pending_exception (mono_get_exception_argument_null ("obj"));
- return FALSE;
- }
-
lw.sync = obj->synchronisation;
if (G_LIKELY (lock_word_is_free (lw))) {
gboolean
mono_monitor_enter (MonoObject *obj)
{
- return mono_monitor_try_enter_internal (obj, INFINITE, FALSE) == 1;
+ gint32 res;
+ gboolean allow_interruption = TRUE;
+ if (G_UNLIKELY (!obj)) {
+ mono_set_pending_exception (mono_get_exception_argument_null ("obj"));
+ return FALSE;
+ }
+
+ /*
+ * An inquisitive mind could ask what's the deal with this loop.
+ * It exists to deal with interrupting a monitor enter that happened within an abort-protected block, like a .cctor.
+ *
+ * The thread will be set with a pending abort and the wait might even be interrupted. Either way, once we call mono_thread_interruption_checkpoint,
+ * it will return NULL meaning we can't be aborted right now. Once that happens we switch to non-alertable.
+ */
+ do {
+ res = mono_monitor_try_enter_internal (obj, INFINITE, allow_interruption);
+ /*This means we got interrupted during the wait and didn't got the monitor.*/
+ if (res == -1) {
+ MonoException *exc = mono_thread_interruption_checkpoint ();
+ if (exc) {
+ mono_set_pending_exception (exc);
+ return FALSE;
+ } else {
+ //we detected a pending interruption but it turned out to be a false positive, we ignore it from now on (this feels like a hack, right?, threads.c should give us less confusing directions)
+ allow_interruption = FALSE;
+ }
+ }
+ } while (res == -1);
+ return TRUE;
}
gboolean
mono_monitor_enter_fast (MonoObject *obj)
{
+ if (G_UNLIKELY (!obj)) {
+ /* don't set pending exn on the fast path, just return
+ * FALSE and let the slow path take care of it. */
+ return FALSE;
+ }
return mono_monitor_try_enter_internal (obj, 0, FALSE) == 1;
}
gboolean
mono_monitor_try_enter (MonoObject *obj, guint32 ms)
{
+ if (G_UNLIKELY (!obj)) {
+ mono_set_pending_exception (mono_get_exception_argument_null ("obj"));
+ return FALSE;
+ }
return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1;
}
lw.sync = obj->synchronisation;
- mono_monitor_ensure_owned (lw, mono_thread_info_get_small_id ());
+ if (!mono_monitor_ensure_owned (lw, mono_thread_info_get_small_id ()))
+ return;
if (G_UNLIKELY (lock_word_is_inflated (lw)))
mono_monitor_exit_inflated (obj);
*nest_offset = ENCODE_OFF_SIZE (MONO_STRUCT_OFFSET (MonoThreadsSync, nest), sizeof (ts.nest));
}
-gboolean
-ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 ms)
-{
- gint32 res;
-
- do {
- res = mono_monitor_try_enter_internal (obj, ms, TRUE);
- if (res == -1) {
- MonoException *exc = mono_thread_interruption_checkpoint ();
- if (exc) {
- mono_set_pending_exception (exc);
- return FALSE;
- }
- }
- } while (res == -1);
-
- return res == 1;
-}
-
void
ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (MonoObject *obj, guint32 ms, char *lockTaken)
{
gint32 res;
+ gboolean allow_interruption = TRUE;
+ if (G_UNLIKELY (!obj)) {
+ mono_set_pending_exception (mono_get_exception_argument_null ("obj"));
+ return;
+ }
do {
- res = mono_monitor_try_enter_internal (obj, ms, TRUE);
+ res = mono_monitor_try_enter_internal (obj, ms, allow_interruption);
/*This means we got interrupted during the wait and didn't got the monitor.*/
if (res == -1) {
MonoException *exc = mono_thread_interruption_checkpoint ();
if (exc) {
mono_set_pending_exception (exc);
return;
+ } else {
+ //we detected a pending interruption but it turned out to be a false positive, we ignore it from now on (this feels like a hack, right?, threads.c should give us less confusing directions)
+ allow_interruption = FALSE;
}
}
} while (res == -1);
{
if (*lock_taken == 1)
return FALSE;
+ if (G_UNLIKELY (!obj))
+ return FALSE;
gint32 res = mono_monitor_try_enter_internal (obj, 0, TRUE);
*lock_taken = res == 1;
return res == 1;
id = mono_thread_info_get_small_id ();
lw.sync = obj->synchronisation;
- mono_monitor_ensure_owned (lw, id);
+ if (!mono_monitor_ensure_owned (lw, id))
+ return;
if (!lock_word_is_inflated (lw)) {
/* No threads waiting. A wait would have inflated the lock */
if (mon->wait_list != NULL) {
LOCK_DEBUG (g_message ("%s: (%d) signalling and dequeuing handle %p", __func__, mono_thread_info_get_small_id (), mon->wait_list->data));
- SetEvent (mon->wait_list->data);
+ mono_w32event_set (mon->wait_list->data);
mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
}
}
id = mono_thread_info_get_small_id ();
lw.sync = obj->synchronisation;
- mono_monitor_ensure_owned (lw, id);
+ if (!mono_monitor_ensure_owned (lw, id))
+ return;
if (!lock_word_is_inflated (lw)) {
/* No threads waiting. A wait would have inflated the lock */
while (mon->wait_list != NULL) {
LOCK_DEBUG (g_message ("%s: (%d) signalling and dequeuing handle %p", __func__, mono_thread_info_get_small_id (), mon->wait_list->data));
- SetEvent (mon->wait_list->data);
+ mono_w32event_set (mon->wait_list->data);
mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
}
}
lw.sync = obj->synchronisation;
- mono_monitor_ensure_owned (lw, id);
+ if (!mono_monitor_ensure_owned (lw, id))
+ return FALSE;
if (!lock_word_is_inflated (lw)) {
mono_monitor_inflate_owned (obj, id);
mon = lock_word_get_inflated_lock (lw);
/* Do this WaitSleepJoin check before creating the event handle */
- mono_thread_current_check_pending_interrupt ();
+ if (mono_thread_current_check_pending_interrupt ())
+ return FALSE;
- event = CreateEvent (NULL, FALSE, FALSE, NULL);
+ event = mono_w32event_create (FALSE, FALSE);
if (event == NULL) {
mono_set_pending_exception (mono_get_exception_synchronization_lock ("Failed to set up wait event"));
return FALSE;
LOCK_DEBUG (g_message ("%s: (%d) queuing handle %p", __func__, mono_thread_info_get_small_id (), event));
- mono_thread_current_check_pending_interrupt ();
+ /* This looks superfluous */
+ if (mono_thread_current_check_pending_interrupt ()) {
+ CloseHandle (event);
+ return FALSE;
+ }
mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
* is private to this thread. Therefore even if the event was
* signalled before we wait, we still succeed.
*/
- MONO_PREPARE_BLOCKING;
+ MONO_ENTER_GC_SAFE;
ret = WaitForSingleObjectEx (event, ms, TRUE);
- MONO_FINISH_BLOCKING;
+ MONO_EXIT_GC_SAFE;
/* Reset the thread state fairly early, so we don't have to worry
* about the monitor error checking
/* Poll the event again, just in case it was signalled
* while we were trying to regain the monitor lock
*/
- MONO_PREPARE_BLOCKING;
+ MONO_ENTER_GC_SAFE;
ret = WaitForSingleObjectEx (event, 0, FALSE);
- MONO_FINISH_BLOCKING;
+ MONO_EXIT_GC_SAFE;
}
/* Pulse will have popped our event from the queue if it signalled