Correctly handle abandoned mutexes on non-Windows platforms
authorNiklas Therning <niklas@therning.org>
Thu, 15 Sep 2016 10:51:09 +0000 (12:51 +0200)
committerNiklas Therning <niklas@therning.org>
Fri, 16 Sep 2016 09:06:38 +0000 (11:06 +0200)
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.

mono/io-layer/wait.c
mono/metadata/w32event-unix.c
mono/metadata/w32mutex-unix.c
mono/metadata/w32semaphore-unix.c
mono/utils/w32handle.c
mono/utils/w32handle.h

index 448232dff9ca550e7c0f81a70e5b9b23dc2b1d55..aa58ea1614f76dde7cc51478b3cc0a73b7991b40 100644 (file)
@@ -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)
index de9958513273ab83ae3f191e7f6de10b34d9a2d7..801d0487d8eba022c2d917aa5e05b38537c9adc2 100644 (file)
@@ -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)
index d7a3c03dbd48c9b67aefe00deff653925eaaf192..a9fdb56ede0dd459b53440ac453e08325373a5a3 100644 (file)
@@ -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);
 
index d21ef9743faf77fc4c542b77dae548350425d19a..3a96f27d4a17056523e336481d7372b869394ec9 100644 (file)
@@ -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)
index 5fd01a3db23a1edd6ca2804ec731a61c409919bb..799c231d6ba01fc3d60e1353a416bb944d8dd742 100644 (file)
@@ -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);
 
index 27c403ce39dde47cb9563be14b0d5d58ecc5bdb0..de65da19e9fb9c106f45097e5a1ff1ebf6042cf3 100644 (file)
@@ -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