From: Niklas Therning Date: Thu, 15 Sep 2016 10:51:09 +0000 (+0200) Subject: Correctly handle abandoned mutexes on non-Windows platforms X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=5dcbbab04e970a139f8a5697a61686e3a4b1803e;p=mono.git Correctly handle abandoned mutexes on non-Windows platforms When a thread owning a mutex doesn't release it before it exits and a second thread tries to take it using e.g. Mutex.WaitOne() an AbandonedMutexException should be thrown. This is what .NET and Mono on Windows does. This commit adds the infrastructure needed in the Win32 emulation layer to coomunicate that a mutex has been abandoned. It builds on the work done in PR #2267 and updates it to the current HEAD. --- diff --git a/mono/io-layer/wait.c b/mono/io-layer/wait.c index 448232dff9c..aa58ea1614f 100644 --- a/mono/io-layer/wait.c +++ b/mono/io-layer/wait.c @@ -42,6 +42,8 @@ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout, gboolean alertab ret = mono_w32handle_wait_one (handle, timeout, alertable); if (ret == MONO_W32HANDLE_WAIT_RET_SUCCESS_0) return WAIT_OBJECT_0; + else if (ret == MONO_W32HANDLE_WAIT_RET_ABANDONED_0) + return WAIT_ABANDONED_0; else if (ret == MONO_W32HANDLE_WAIT_RET_ALERTED) return WAIT_IO_COMPLETION; else if (ret == MONO_W32HANDLE_WAIT_RET_TIMEOUT) @@ -96,6 +98,8 @@ guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait, ret = mono_w32handle_signal_and_wait (signal_handle, wait, timeout, alertable); if (ret == MONO_W32HANDLE_WAIT_RET_SUCCESS_0) return WAIT_OBJECT_0; + else if (ret == MONO_W32HANDLE_WAIT_RET_ABANDONED_0) + return WAIT_ABANDONED_0; else if (ret == MONO_W32HANDLE_WAIT_RET_ALERTED) return WAIT_IO_COMPLETION; else if (ret == MONO_W32HANDLE_WAIT_RET_TIMEOUT) @@ -143,8 +147,10 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles, MonoW32HandleWaitRet ret; ret = mono_w32handle_wait_multiple (handles, numobjects, waitall, timeout, alertable); - if (ret >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0) + if (ret >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && ret <= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects - 1) return WAIT_OBJECT_0 + (ret - MONO_W32HANDLE_WAIT_RET_SUCCESS_0); + else if (ret >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && ret <= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects - 1) + return WAIT_ABANDONED_0 + (ret - MONO_W32HANDLE_WAIT_RET_ABANDONED_0); else if (ret == MONO_W32HANDLE_WAIT_RET_ALERTED) return WAIT_IO_COMPLETION; else if (ret == MONO_W32HANDLE_WAIT_RET_TIMEOUT) diff --git a/mono/metadata/w32event-unix.c b/mono/metadata/w32event-unix.c index de995851327..801d0487d8e 100644 --- a/mono/metadata/w32event-unix.c +++ b/mono/metadata/w32event-unix.c @@ -24,11 +24,13 @@ struct MonoW32HandleNamedEvent { MonoW32HandleNamespace sharedns; }; -static gboolean event_handle_own (gpointer handle, MonoW32HandleType type) +static gboolean event_handle_own (gpointer handle, MonoW32HandleType type, guint32 *statuscode) { MonoW32HandleEvent *event_handle; gboolean ok; + *statuscode = WAIT_OBJECT_0; + ok = mono_w32handle_lookup (handle, type, (gpointer *)&event_handle); if (!ok) { g_warning ("%s: error looking up %s handle %p", @@ -55,9 +57,9 @@ static void event_signal(gpointer handle) ves_icall_System_Threading_Events_SetEvent_internal (handle); } -static gboolean event_own (gpointer handle) +static gboolean event_own (gpointer handle, guint32 *statuscode) { - return event_handle_own (handle, MONO_W32HANDLE_EVENT); + return event_handle_own (handle, MONO_W32HANDLE_EVENT, statuscode); } static void namedevent_signal (gpointer handle) @@ -66,9 +68,9 @@ static void namedevent_signal (gpointer handle) } /* NB, always called with the shared handle lock held */ -static gboolean namedevent_own (gpointer handle) +static gboolean namedevent_own (gpointer handle, guint32 *statuscode) { - return event_handle_own (handle, MONO_W32HANDLE_NAMEDEVENT); + return event_handle_own (handle, MONO_W32HANDLE_NAMEDEVENT, statuscode); } static void event_details (gpointer data) diff --git a/mono/metadata/w32mutex-unix.c b/mono/metadata/w32mutex-unix.c index d7a3c03dbd4..a9fdb56ede0 100644 --- a/mono/metadata/w32mutex-unix.c +++ b/mono/metadata/w32mutex-unix.c @@ -29,10 +29,12 @@ struct MonoW32HandleNamedMutex { }; static gboolean -mutex_handle_own (gpointer handle, MonoW32HandleType type) +mutex_handle_own (gpointer handle, MonoW32HandleType type, guint32 *statuscode) { MonoW32HandleMutex *mutex_handle; + *statuscode = WAIT_OBJECT_0; + if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) { g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle); return FALSE; @@ -80,9 +82,9 @@ static void mutex_signal(gpointer handle) ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle); } -static gboolean mutex_own (gpointer handle) +static gboolean mutex_own (gpointer handle, guint32 *statuscode) { - return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX); + return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, statuscode); } static gboolean mutex_is_owned (gpointer handle) @@ -97,9 +99,9 @@ static void namedmutex_signal (gpointer handle) } /* NB, always called with the shared handle lock held */ -static gboolean namedmutex_own (gpointer handle) +static gboolean namedmutex_own (gpointer handle, guint32 *statuscode) { - return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX); + return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, statuscode); } static gboolean namedmutex_is_owned (gpointer handle) @@ -223,6 +225,7 @@ static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32Ha { gpointer handle; int thr_ret; + guint32 statuscode; mutex_handle->tid = 0; mutex_handle->recursion = 0; @@ -239,7 +242,7 @@ static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32Ha g_assert (thr_ret == 0); if (owned) - mutex_handle_own (handle, type); + mutex_handle_own (handle, type, &statuscode); else mono_w32handle_set_signal_state (handle, TRUE, FALSE); diff --git a/mono/metadata/w32semaphore-unix.c b/mono/metadata/w32semaphore-unix.c index d21ef9743fa..3a96f27d4a1 100644 --- a/mono/metadata/w32semaphore-unix.c +++ b/mono/metadata/w32semaphore-unix.c @@ -24,10 +24,12 @@ struct MonoW32HandleNamedSemaphore { MonoW32HandleNamespace sharedns; }; -static gboolean sem_handle_own (gpointer handle, MonoW32HandleType type) +static gboolean sem_handle_own (gpointer handle, MonoW32HandleType type, guint32 *statuscode) { MonoW32HandleSemaphore *sem_handle; + *statuscode = WAIT_OBJECT_0; + if (!mono_w32handle_lookup (handle, type, (gpointer *)&sem_handle)) { g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle); @@ -50,9 +52,9 @@ static void sema_signal(gpointer handle) ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal(handle, 1, NULL); } -static gboolean sema_own (gpointer handle) +static gboolean sema_own (gpointer handle, guint32 *statuscode) { - return sem_handle_own (handle, MONO_W32HANDLE_SEM); + return sem_handle_own (handle, MONO_W32HANDLE_SEM, statuscode); } static void namedsema_signal (gpointer handle) @@ -61,9 +63,9 @@ static void namedsema_signal (gpointer handle) } /* NB, always called with the shared handle lock held */ -static gboolean namedsema_own (gpointer handle) +static gboolean namedsema_own (gpointer handle, guint32 *statuscode) { - return sem_handle_own (handle, MONO_W32HANDLE_NAMEDSEM); + return sem_handle_own (handle, MONO_W32HANDLE_NAMEDSEM, statuscode); } static void sema_details (gpointer data) diff --git a/mono/utils/w32handle.c b/mono/utils/w32handle.c index 5fd01a3db23..799c231d6ba 100644 --- a/mono/utils/w32handle.c +++ b/mono/utils/w32handle.c @@ -817,7 +817,7 @@ void mono_w32handle_ops_signal (gpointer handle) } } -gboolean mono_w32handle_ops_own (gpointer handle) +gboolean mono_w32handle_ops_own (gpointer handle, guint32 *statuscode) { MonoW32HandleBase *handle_data; MonoW32HandleType type; @@ -829,7 +829,7 @@ gboolean mono_w32handle_ops_own (gpointer handle) type = handle_data->type; if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) { - return(handle_ops[type]->own_handle (handle)); + return(handle_ops[type]->own_handle (handle, statuscode)); } else { return(FALSE); } @@ -1132,22 +1132,24 @@ void mono_w32handle_dump (void) } static gboolean -own_if_signalled (gpointer handle) +own_if_signalled (gpointer handle, guint32 *statuscode) { if (!mono_w32handle_issignalled (handle)) return FALSE; - mono_w32handle_ops_own (handle); + *statuscode = WAIT_OBJECT_0; + mono_w32handle_ops_own (handle, statuscode); return TRUE; } static gboolean -own_if_owned( gpointer handle) +own_if_owned( gpointer handle, guint32 *statuscode) { if (!mono_w32handle_ops_isowned (handle)) return FALSE; - mono_w32handle_ops_own (handle); + *statuscode = WAIT_OBJECT_0; + mono_w32handle_ops_own (handle, statuscode); return TRUE; } @@ -1158,6 +1160,7 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable) gboolean alerted; gint64 start; gint thr_ret; + guint32 statuscode = 0; alerted = FALSE; @@ -1169,6 +1172,9 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable) case WAIT_OBJECT_0: ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0; break; + case WAIT_ABANDONED_0: + ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0; + break; case WAIT_IO_COMPLETION: ret = MONO_W32HANDLE_WAIT_RET_ALERTED; break; @@ -1199,7 +1205,7 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable) g_assert (thr_ret == 0); if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) { - if (own_if_owned (handle)) { + if (own_if_owned (handle, &statuscode)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned", __func__, handle); @@ -1214,7 +1220,7 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable) for (;;) { gint waited; - if (own_if_signalled (handle)) { + if (own_if_signalled (handle, &statuscode)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled", __func__, handle); @@ -1264,6 +1270,7 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital gint i, thr_ret; gint64 start; gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS]; + guint32 statuscode = 0; if (nhandles == 0) return MONO_W32HANDLE_WAIT_RET_FAILED; @@ -1346,7 +1353,7 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital if (signalled) { for (i = 0; i < nhandles; i++) - own_if_signalled (handles [i]); + own_if_signalled (handles [i], &statuscode); } mono_w32handle_unlock_handles (handles, nhandles); @@ -1439,6 +1446,7 @@ mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, gu gint64 start; gboolean alerted; gint thr_ret; + guint32 statuscode = 0; alerted = FALSE; @@ -1458,7 +1466,7 @@ mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, gu mono_w32handle_ops_signal (signal_handle); if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) { - if (own_if_owned (wait_handle)) { + if (own_if_owned (wait_handle, &statuscode)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned", __func__, wait_handle); @@ -1473,7 +1481,7 @@ mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, gu for (;;) { gint waited; - if (own_if_signalled (wait_handle)) { + if (own_if_signalled (wait_handle, &statuscode)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled", __func__, wait_handle); diff --git a/mono/utils/w32handle.h b/mono/utils/w32handle.h index 27c403ce39d..de65da19e9f 100644 --- a/mono/utils/w32handle.h +++ b/mono/utils/w32handle.h @@ -39,8 +39,10 @@ typedef struct /* Called by WaitForSingleObject and WaitForMultipleObjects, * with the handle locked (shared handles aren't locked.) * Returns TRUE if ownership was established, false otherwise. + * If TRUE, *statuscode contains a status code such as + * WAIT_OBJECT_0 or WAIT_ABANDONED_0. */ - gboolean (*own_handle)(gpointer handle); + gboolean (*own_handle)(gpointer handle, guint32 *statuscode); /* Called by WaitForSingleObject and WaitForMultipleObjects, if the * handle in question is "ownable" (ie mutexes), to see if the current @@ -129,7 +131,7 @@ void mono_w32handle_ops_signal (gpointer handle); gboolean -mono_w32handle_ops_own (gpointer handle); +mono_w32handle_ops_own (gpointer handle, guint32 *statuscode); gboolean mono_w32handle_ops_isowned (gpointer handle); @@ -165,10 +167,11 @@ int mono_w32handle_unlock_handle (gpointer handle); typedef enum { - MONO_W32HANDLE_WAIT_RET_SUCCESS_0 = 0, - MONO_W32HANDLE_WAIT_RET_ALERTED = -1, - MONO_W32HANDLE_WAIT_RET_TIMEOUT = -2, - MONO_W32HANDLE_WAIT_RET_FAILED = -3, + MONO_W32HANDLE_WAIT_RET_SUCCESS_0 = 0, + MONO_W32HANDLE_WAIT_RET_ABANDONED_0 = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS, + MONO_W32HANDLE_WAIT_RET_ALERTED = -1, + MONO_W32HANDLE_WAIT_RET_TIMEOUT = -2, + MONO_W32HANDLE_WAIT_RET_FAILED = -3, } MonoW32HandleWaitRet; MonoW32HandleWaitRet