X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fmonitor.c;h=1d9ff1b8acc251ee6fd5524232aa1edf35204b71;hb=22577feecfa38fc15a01887f27aa5687b0c5504c;hp=b9a305121641975286467b84392ceba8856a4111;hpb=7ebae4c7f57a6c1b4d0a38e69f270a016b6f3143;p=mono.git diff --git a/mono/metadata/monitor.c b/mono/metadata/monitor.c index b9a30512164..1d9ff1b8acc 100644 --- a/mono/metadata/monitor.c +++ b/mono/metadata/monitor.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -357,7 +358,8 @@ mon_finalize (MonoThreadsSync *mon) 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 @@ -629,18 +631,19 @@ mono_object_hash (MonoObject* obj) #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; } /* @@ -679,7 +682,7 @@ mono_monitor_exit_inflated (MonoObject *obj) 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; @@ -745,8 +748,8 @@ mono_monitor_try_enter_inflated (MonoObject *obj, guint32 ms, gboolean allow_int HANDLE sem; gint64 then = 0, now, delta; guint32 waitms; - guint32 ret; guint32 new_status, old_status, tmp_status; + MonoSemTimedwaitRet wait_ret; MonoInternalThread *thread; gboolean interrupted = FALSE; @@ -838,11 +841,12 @@ retry_contended: */ 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); } } @@ -875,23 +879,40 @@ retry_contended: #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_ENTER_GC_SAFE; - ret = WaitForSingleObjectEx (mon->entry_sem, waitms, TRUE); - MONO_EXIT_GC_SAFE; + 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 @@ -914,11 +935,11 @@ retry_contended: /* 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 */ } @@ -927,10 +948,10 @@ retry_contended: 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 { @@ -951,11 +972,6 @@ mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_int 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))) { @@ -1001,18 +1017,55 @@ mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_int 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; } @@ -1030,7 +1083,8 @@ mono_monitor_exit (MonoObject *obj) 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); @@ -1075,14 +1129,22 @@ 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); @@ -1173,7 +1235,8 @@ ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj) 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 */ @@ -1187,7 +1250,7 @@ ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj) 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); } } @@ -1204,7 +1267,8 @@ ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj) 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 */ @@ -1218,7 +1282,7 @@ ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj) 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); } } @@ -1240,7 +1304,8 @@ ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms) 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); @@ -1253,7 +1318,7 @@ ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms) 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;