Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / w32handle.c
index 88895e7fb5e3624bdd1db59faaf69f1a3895f2ab..654b215538d800de195db57afeca5ccaaa9176e9 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * w32handle.c:  Generic and internal operations on handles
+/**
+ * \file
+ * Generic and internal operations on handles
  *
  * Author:
  *     Dick Porter (dick@ximian.com)
@@ -24,7 +25,7 @@
 
 #undef DEBUG_REFS
 
-#define SLOT_MAX               (1024 * 16)
+#define SLOT_MAX               (1024 * 32)
 
 /* must be a power of 2 */
 #define HANDLE_PER_SLOT        (256)
@@ -33,6 +34,7 @@ typedef struct {
        MonoW32HandleType type;
        guint ref;
        gboolean signalled;
+       gboolean in_use;
        mono_mutex_t signal_mutex;
        mono_cond_t signal_cond;
        gpointer specific;
@@ -49,10 +51,7 @@ static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
 
 static MonoW32HandleBase *private_handles [SLOT_MAX];
-static guint32 private_handles_count = 0;
-static guint32 private_handles_slots_count = 0;
-
-guint32 mono_w32handle_fd_reserve;
+static guint32 private_handles_size = 0;
 
 /*
  * This is an internal handle which is used for handling waiting for multiple handles.
@@ -66,20 +65,6 @@ static mono_mutex_t scan_mutex;
 
 static gboolean shutting_down = FALSE;
 
-static gboolean
-type_is_fd (MonoW32HandleType type)
-{
-       switch (type) {
-       case MONO_W32HANDLE_FILE:
-       case MONO_W32HANDLE_CONSOLE:
-       case MONO_W32HANDLE_SOCKET:
-       case MONO_W32HANDLE_PIPE:
-               return TRUE;
-       default:
-               return FALSE;
-       }
-}
-
 static gboolean
 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
 {
@@ -177,6 +162,17 @@ mono_w32handle_issignalled (gpointer handle)
        return handle_data->signalled;
 }
 
+static void
+mono_w32handle_set_in_use (gpointer handle, gboolean in_use)
+{
+       MonoW32HandleBase *handle_data;
+
+       if (!mono_w32handle_lookup_data (handle, &handle_data))
+               g_assert_not_reached ();
+
+       handle_data->in_use = in_use;
+}
+
 static void
 mono_w32handle_lock_signal_mutex (void)
 {
@@ -197,6 +193,12 @@ mono_w32handle_unlock_signal_mutex (void)
        mono_os_mutex_unlock (&global_signal_mutex);
 }
 
+static void
+mono_w32handle_ref (gpointer handle);
+
+static void
+mono_w32handle_unref (gpointer handle);
+
 void
 mono_w32handle_lock_handle (gpointer handle)
 {
@@ -249,11 +251,6 @@ mono_w32handle_unlock_handle (gpointer handle)
        mono_w32handle_unref (handle);
 }
 
-/*
- * wapi_init:
- *
- *   Initialize the io-layer.
- */
 void
 mono_w32handle_init (void)
 {
@@ -265,19 +262,6 @@ mono_w32handle_init (void)
        g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
                  == MONO_W32HANDLE_COUNT);
 
-       /* This is needed by the code in mono_w32handle_new_internal */
-       mono_w32handle_fd_reserve = (eg_getdtablesize () + (HANDLE_PER_SLOT - 1)) & ~(HANDLE_PER_SLOT - 1);
-
-       do {
-               /*
-                * The entries in private_handles reserved for fds are allocated lazily to
-                * save memory.
-                */
-
-               private_handles_count += HANDLE_PER_SLOT;
-               private_handles_slots_count ++;
-       } while(mono_w32handle_fd_reserve > private_handles_count);
-
        mono_os_mutex_init (&scan_mutex);
 
        mono_os_cond_init (&global_signal_cond);
@@ -289,29 +273,11 @@ mono_w32handle_init (void)
 void
 mono_w32handle_cleanup (void)
 {
-       int i, j, k;
+       int i;
 
        g_assert (!shutting_down);
        shutting_down = TRUE;
 
-       /* Every shared handle we were using ought really to be closed
-        * by now, but to make sure just blow them all away.  The
-        * exiting finalizer thread in particular races us to the
-        * program exit and doesn't always win, so it can be left
-        * cluttering up the shared file.  Anything else left over is
-        * really a bug.
-        */
-       for(i = SLOT_INDEX (0); private_handles[i] != NULL; i++) {
-               for(j = SLOT_OFFSET (0); j < HANDLE_PER_SLOT; j++) {
-                       MonoW32HandleBase *handle_data = &private_handles[i][j];
-                       gpointer handle = GINT_TO_POINTER (i*HANDLE_PER_SLOT+j);
-
-                       for(k = handle_data->ref; k > 0; k--) {
-                               mono_w32handle_unref (handle);
-                       }
-               }
-       }
-
        for (i = 0; i < SLOT_MAX; ++i)
                g_free (private_handles [i]);
 }
@@ -319,22 +285,6 @@ mono_w32handle_cleanup (void)
 static gsize
 mono_w32handle_ops_typesize (MonoW32HandleType type);
 
-static void mono_w32handle_init_handle (MonoW32HandleBase *handle,
-                              MonoW32HandleType type, gpointer handle_specific)
-{
-       g_assert (handle->ref == 0);
-
-       handle->type = type;
-       handle->signalled = FALSE;
-       handle->ref = 1;
-
-       mono_os_cond_init (&handle->signal_cond);
-       mono_os_mutex_init (&handle->signal_mutex);
-
-       if (handle_specific)
-               handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
-}
-
 /*
  * mono_w32handle_new_internal:
  * @type: Init handle to this type
@@ -356,15 +306,16 @@ static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
         * descriptors
         */
 
-       if (last < mono_w32handle_fd_reserve) {
-               last = mono_w32handle_fd_reserve;
+       if (last == 0) {
+               /* We need to go from 1 since a handle of value 0 can be considered invalid in managed code */
+               last = 1;
        } else {
                retry = TRUE;
        }
 
 again:
        count = last;
-       for(i = SLOT_INDEX (count); i < private_handles_slots_count; i++) {
+       for(i = SLOT_INDEX (count); i < private_handles_size; i++) {
                if (private_handles [i]) {
                        for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
                                MonoW32HandleBase *handle = &private_handles [i][k];
@@ -372,7 +323,18 @@ again:
                                if(handle->type == MONO_W32HANDLE_UNUSED) {
                                        last = count + 1;
 
-                                       mono_w32handle_init_handle (handle, type, handle_specific);
+                                       g_assert (handle->ref == 0);
+
+                                       handle->type = type;
+                                       handle->signalled = FALSE;
+                                       handle->ref = 1;
+
+                                       mono_os_cond_init (&handle->signal_cond);
+                                       mono_os_mutex_init (&handle->signal_mutex);
+
+                                       if (handle_specific)
+                                               handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
+
                                        return (count);
                                }
                                count++;
@@ -380,106 +342,91 @@ again:
                }
        }
 
-       if(retry && last > mono_w32handle_fd_reserve) {
+       if (retry) {
                /* Try again from the beginning */
-               last = mono_w32handle_fd_reserve;
+               last = 1;
+               retry = FALSE;
                goto again;
        }
 
        /* Will need to expand the array.  The caller will sort it out */
 
-       return(0);
+       return G_MAXUINT32;
 }
 
 gpointer
 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
 {
-       guint32 handle_idx = 0;
+       guint32 handle_idx;
        gpointer handle;
 
        g_assert (!shutting_down);
 
-       g_assert(!type_is_fd(type));
-
        mono_os_mutex_lock (&scan_mutex);
 
-       while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == 0) {
+       while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == G_MAXUINT32) {
                /* Try and expand the array, and have another go */
-               int idx = SLOT_INDEX (private_handles_count);
-               if (idx >= SLOT_MAX) {
-                       break;
-               }
+               if (private_handles_size >= SLOT_MAX) {
+                       mono_os_mutex_unlock (&scan_mutex);
 
-               private_handles [idx] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
+                       /* We ran out of slots */
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type));
+                       return INVALID_HANDLE_VALUE;
+               }
 
-               private_handles_count += HANDLE_PER_SLOT;
-               private_handles_slots_count ++;
+               private_handles [private_handles_size ++] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
        }
 
        mono_os_mutex_unlock (&scan_mutex);
 
-       if (handle_idx == 0) {
-               /* We ran out of slots */
-               handle = INVALID_HANDLE_VALUE;
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type));
-               goto done;
-       }
-
-       /* Make sure we left the space for fd mappings */
-       g_assert (handle_idx >= mono_w32handle_fd_reserve);
-
        handle = GUINT_TO_POINTER (handle_idx);
 
        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
 
-done:
        return(handle);
 }
 
-gpointer mono_w32handle_new_fd (MonoW32HandleType type, int fd,
-                             gpointer handle_specific)
-{
-       MonoW32HandleBase *handle_data;
-       int fd_index, fd_offset;
-
-       g_assert (!shutting_down);
-
-       g_assert(type_is_fd(type));
-
-       if (fd >= mono_w32handle_fd_reserve) {
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle, fd is too big", __func__, mono_w32handle_ops_typename (type));
+static gboolean
+mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data);
 
-               return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
-       }
+static gboolean
+mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data);
 
-       fd_index = SLOT_INDEX (fd);
-       fd_offset = SLOT_OFFSET (fd);
+static void
+w32handle_destroy (gpointer handle);
 
-       /* Initialize the array entries on demand */
-       if (!private_handles [fd_index]) {
-               mono_os_mutex_lock (&scan_mutex);
+gpointer
+mono_w32handle_duplicate (gpointer handle)
+{
+       MonoW32HandleBase *handle_data;
 
-               if (!private_handles [fd_index])
-                       private_handles [fd_index] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
+       if (handle == INVALID_HANDLE_VALUE)
+               return handle;
+       if (!mono_w32handle_lookup_data (handle, &handle_data))
+               return INVALID_HANDLE_VALUE;
 
-               mono_os_mutex_unlock (&scan_mutex);
-       }
+       if (!mono_w32handle_ref_core (handle, handle_data))
+               g_error ("%s: failed to ref handle %p", __func__, handle);
 
-       handle_data = &private_handles [fd_index][fd_offset];
+       return handle;
+}
 
-       if (handle_data->type != MONO_W32HANDLE_UNUSED) {
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle, fd is already in use", __func__, mono_w32handle_ops_typename (type));
-               /* FIXME: clean up this handle?  We can't do anything
-                * with the fd, cos thats the new one
-                */
-               return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
-       }
+gboolean
+mono_w32handle_close (gpointer handle)
+{
+       MonoW32HandleBase *handle_data;
+       gboolean destroy;
 
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), GUINT_TO_POINTER(fd));
+       if (handle == INVALID_HANDLE_VALUE)
+               return FALSE;
+       if (!mono_w32handle_lookup_data (handle, &handle_data))
+               return FALSE;
 
-       mono_w32handle_init_handle (handle_data, type, handle_specific);
+       destroy = mono_w32handle_unref_core (handle, handle_data);
+       if (destroy)
+               w32handle_destroy (handle);
 
-       return(GUINT_TO_POINTER(fd));
+       return TRUE;
 }
 
 gboolean
@@ -503,20 +450,17 @@ mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
        return(TRUE);
 }
 
-static gboolean
-mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data);
-
-static gboolean
-mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guint minimum);
-
 void
 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
 {
+       GPtrArray *handles_to_destroy;
        guint32 i, k;
 
+       handles_to_destroy = NULL;
+
        mono_os_mutex_lock (&scan_mutex);
 
-       for (i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
+       for (i = SLOT_INDEX (0); i < private_handles_size; i++) {
                if (!private_handles [i])
                        continue;
                for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
@@ -539,10 +483,18 @@ mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpoi
 
                        finished = on_each (handle, handle_data->specific, user_data);
 
-                       /* we do not want to have to destroy the handle here,
-                        * as it would means the ref/unref are unbalanced */
-                       destroy = mono_w32handle_unref_core (handle, handle_data, 2);
-                       g_assert (!destroy);
+                       /* we might have to destroy the handle here, as
+                        * it could have been unrefed in another thread */
+                       destroy = mono_w32handle_unref_core (handle, handle_data);
+                       if (destroy) {
+                               /* we do not destroy it while holding the scan_mutex
+                                * lock, because w32handle_destroy also needs to take
+                                * the lock, and it calls user code which might lead
+                                * to a deadlock */
+                               if (!handles_to_destroy)
+                                       handles_to_destroy = g_ptr_array_sized_new (4);
+                               g_ptr_array_add (handles_to_destroy, handle);
+                       }
 
                        if (finished)
                                goto done;
@@ -551,6 +503,13 @@ mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpoi
 
 done:
        mono_os_mutex_unlock (&scan_mutex);
+
+       if (handles_to_destroy) {
+               for (i = 0; i < handles_to_destroy->len; ++i)
+                       w32handle_destroy (handles_to_destroy->pdata [i]);
+
+               g_ptr_array_free (handles_to_destroy, TRUE);
+       }
 }
 
 static gboolean
@@ -573,7 +532,7 @@ mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data)
 }
 
 static gboolean
-mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guint minimum)
+mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data)
 {
        MonoW32HandleType type;
        guint old, new;
@@ -582,8 +541,8 @@ mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guin
 
        do {
                old = handle_data->ref;
-               if (!(old >= minimum))
-                       g_error ("%s: handle %p has ref %d, it should be >= %d", __func__, handle, old, minimum);
+               if (!(old >= 1))
+                       g_error ("%s: handle %p has ref %d, it should be >= 1", __func__, handle, old);
 
                new = old - 1;
        } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
@@ -597,14 +556,13 @@ mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guin
        return new == 0;
 }
 
-void mono_w32handle_ref (gpointer handle)
+static void
+mono_w32handle_ref (gpointer handle)
 {
        MonoW32HandleBase *handle_data;
 
-       if (!mono_w32handle_lookup_data (handle, &handle_data)) {
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to ref handle %p, unknown handle", __func__, handle);
-               return;
-       }
+       if (!mono_w32handle_lookup_data (handle, &handle_data))
+               g_error ("%s: failed to ref handle %p, unknown handle", __func__, handle);
 
        if (!mono_w32handle_ref_core (handle, handle_data))
                g_error ("%s: failed to ref handle %p", __func__, handle);
@@ -612,62 +570,62 @@ void mono_w32handle_ref (gpointer handle)
 
 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer);
 
-/* The handle must not be locked on entry to this function */
-void
-mono_w32handle_unref (gpointer handle)
+static void
+w32handle_destroy (gpointer handle)
 {
+       /* Need to copy the handle info, reset the slot in the
+        * array, and _only then_ call the close function to
+        * avoid race conditions (eg file descriptors being
+        * closed, and another file being opened getting the
+        * same fd racing the memset())
+        */
        MonoW32HandleBase *handle_data;
-       gboolean destroy;
+       MonoW32HandleType type;
+       gpointer handle_specific;
+       void (*close_func)(gpointer, gpointer);
 
-       if (!mono_w32handle_lookup_data (handle, &handle_data)) {
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to unref handle %p, unknown handle",
-                       __func__, handle);
-               return;
-       }
+       if (!mono_w32handle_lookup_data (handle, &handle_data))
+               g_error ("%s: unknown handle %p", __func__, handle);
 
-       destroy = mono_w32handle_unref_core (handle, handle_data, 1);
+       g_assert (!handle_data->in_use);
 
-       if (destroy) {
-               /* Need to copy the handle info, reset the slot in the
-                * array, and _only then_ call the close function to
-                * avoid race conditions (eg file descriptors being
-                * closed, and another file being opened getting the
-                * same fd racing the memset())
-                */
-               MonoW32HandleType type;
-               gpointer handle_specific;
-               void (*close_func)(gpointer, gpointer);
+       type = handle_data->type;
+       handle_specific = handle_data->specific;
 
-               type = handle_data->type;
-               handle_specific = handle_data->specific;
+       mono_os_mutex_lock (&scan_mutex);
 
-               mono_os_mutex_lock (&scan_mutex);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
 
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
+       mono_os_mutex_destroy (&handle_data->signal_mutex);
+       mono_os_cond_destroy (&handle_data->signal_cond);
 
-               mono_os_mutex_destroy (&handle_data->signal_mutex);
-               mono_os_cond_destroy (&handle_data->signal_cond);
+       memset (handle_data, 0, sizeof (MonoW32HandleBase));
 
-               memset (handle_data, 0, sizeof (MonoW32HandleBase));
+       mono_os_mutex_unlock (&scan_mutex);
 
-               mono_os_mutex_unlock (&scan_mutex);
+       close_func = _wapi_handle_ops_get_close_func (type);
+       if (close_func != NULL) {
+               close_func (handle, handle_specific);
+       }
 
-               close_func = _wapi_handle_ops_get_close_func (type);
-               if (close_func != NULL) {
-                       close_func (handle, handle_specific);
-               }
+       memset (handle_specific, 0, mono_w32handle_ops_typesize (type));
 
-               g_free (handle_specific);
-       }
+       g_free (handle_specific);
 }
 
+/* The handle must not be locked on entry to this function */
 static void
-mono_w32handle_ops_close (gpointer handle, gpointer data);
-
-void
-mono_w32handle_force_close (gpointer handle, gpointer data)
+mono_w32handle_unref (gpointer handle)
 {
-       mono_w32handle_ops_close (handle, data);
+       MonoW32HandleBase *handle_data;
+       gboolean destroy;
+
+       if (!mono_w32handle_lookup_data (handle, &handle_data))
+               g_error ("%s: failed to unref handle %p, unknown handle", __func__, handle);
+
+       destroy = mono_w32handle_unref_core (handle, handle_data);
+       if (destroy)
+               w32handle_destroy (handle);
 }
 
 void
@@ -710,24 +668,6 @@ static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer
        return (NULL);
 }
 
-static void
-mono_w32handle_ops_close (gpointer handle, gpointer data)
-{
-       MonoW32HandleBase *handle_data;
-       MonoW32HandleType type;
-
-       if (!mono_w32handle_lookup_data (handle, &handle_data)) {
-               return;
-       }
-
-       type = handle_data->type;
-
-       if (handle_ops[type] != NULL &&
-           handle_ops[type]->close != NULL) {
-               handle_ops[type]->close (handle, data);
-       }
-}
-
 static void
 mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
 {
@@ -766,12 +706,12 @@ mono_w32handle_ops_signal (gpointer handle)
        type = handle_data->type;
 
        if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
-               handle_ops[type]->signal (handle);
+               handle_ops[type]->signal (handle, handle_data->specific);
        }
 }
 
 static gboolean
-mono_w32handle_ops_own (gpointer handle, guint32 *statuscode)
+mono_w32handle_ops_own (gpointer handle, gboolean *abandoned)
 {
        MonoW32HandleBase *handle_data;
        MonoW32HandleType type;
@@ -783,7 +723,7 @@ mono_w32handle_ops_own (gpointer handle, guint32 *statuscode)
        type = handle_data->type;
 
        if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
-               return(handle_ops[type]->own_handle (handle, statuscode));
+               return(handle_ops[type]->own_handle (handle, abandoned));
        } else {
                return(FALSE);
        }
@@ -815,7 +755,7 @@ mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *aler
        MonoW32HandleType type;
 
        if (!mono_w32handle_lookup_data (handle, &handle_data)) {
-               return(WAIT_FAILED);
+               return MONO_W32HANDLE_WAIT_RET_FAILED;
        }
 
        type = handle_data->type;
@@ -824,7 +764,7 @@ mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *aler
            handle_ops[type]->special_wait != NULL) {
                return(handle_ops[type]->special_wait (handle, timeout, alerted));
        } else {
-               return(WAIT_FAILED);
+               return MONO_W32HANDLE_WAIT_RET_FAILED;
        }
 }
 
@@ -1018,13 +958,14 @@ signal_handle_and_unref (gpointer handle)
        mono_os_cond_broadcast (cond);
        mono_os_mutex_unlock (mutex);
 
-       mono_w32handle_unref (handle);
+       mono_w32handle_close (handle);
 }
 
 static int
 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
 {
        MonoW32HandleBase *handle_data;
+       gpointer handle_duplicate;
        int res;
 
        if (!mono_w32handle_lookup_data (handle, &handle_data))
@@ -1037,10 +978,11 @@ mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboole
                *alerted = FALSE;
 
        if (alerted) {
-               mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
-               if (*alerted)
+               mono_thread_info_install_interrupt (signal_handle_and_unref, handle_duplicate = mono_w32handle_duplicate (handle), alerted);
+               if (*alerted) {
+                       mono_w32handle_close (handle_duplicate);
                        return 0;
-               mono_w32handle_ref (handle);
+               }
        }
 
        res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
@@ -1048,8 +990,8 @@ mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboole
        if (alerted) {
                mono_thread_info_uninstall_interrupt (alerted);
                if (!*alerted) {
-                       /* if it is alerted, then the handle is unref in the interrupt callback */
-                       mono_w32handle_unref (handle);
+                       /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
+                       mono_w32handle_close (handle_duplicate);
                }
        }
 
@@ -1065,7 +1007,7 @@ dump_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
                g_error ("cannot dump unknown handle %p", handle);
 
        g_print ("%p [%7s] signalled: %5s ref: %3d ",
-               handle, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref);
+               handle, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref - 1 /* foreach increase ref by 1 */);
        mono_w32handle_ops_details (handle_data->type, handle_data->specific);
        g_print ("\n");
 
@@ -1078,24 +1020,24 @@ void mono_w32handle_dump (void)
 }
 
 static gboolean
-own_if_signalled (gpointer handle, guint32 *statuscode)
+own_if_signalled (gpointer handle, gboolean *abandoned)
 {
        if (!mono_w32handle_issignalled (handle))
                return FALSE;
 
-       *statuscode = WAIT_OBJECT_0;
-       mono_w32handle_ops_own (handle, statuscode);
+       *abandoned = FALSE;
+       mono_w32handle_ops_own (handle, abandoned);
        return TRUE;
 }
 
 static gboolean
-own_if_owned( gpointer handle, guint32 *statuscode)
+own_if_owned( gpointer handle, gboolean *abandoned)
 {
        if (!mono_w32handle_ops_isowned (handle))
                return FALSE;
 
-       *statuscode = WAIT_OBJECT_0;
-       mono_w32handle_ops_own (handle, statuscode);
+       *abandoned = FALSE;
+       mono_w32handle_ops_own (handle, abandoned);
        return TRUE;
 }
 
@@ -1105,7 +1047,7 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
        MonoW32HandleWaitRet ret;
        gboolean alerted;
        gint64 start;
-       guint32 statuscode = 0;
+       gboolean abandoned = FALSE;
 
        alerted = FALSE;
 
@@ -1126,11 +1068,11 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
        mono_w32handle_lock_handle (handle);
 
        if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) {
-               if (own_if_owned (handle, &statuscode)) {
+               if (own_if_owned (handle, &abandoned)) {
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
                                __func__, handle);
 
-                       ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
+                       ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
                        goto done;
                }
        }
@@ -1138,14 +1080,16 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
        if (timeout != MONO_INFINITE_WAIT)
                start = mono_msec_ticks ();
 
+       mono_w32handle_set_in_use (handle, TRUE);
+
        for (;;) {
                gint waited;
 
-               if (own_if_signalled (handle, &statuscode)) {
+               if (own_if_signalled (handle, &abandoned)) {
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
                                __func__, handle);
 
-                       ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
+                       ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
                        goto done;
                }
 
@@ -1177,6 +1121,8 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
        }
 
 done:
+       mono_w32handle_set_in_use (handle, FALSE);
+
        mono_w32handle_unlock_handle (handle);
 
        return ret;
@@ -1190,7 +1136,7 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital
        gint i;
        gint64 start;
        gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
-       guint32 statuscodes [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
+       gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
 
        if (nhandles == 0)
                return MONO_W32HANDLE_WAIT_RET_FAILED;
@@ -1272,8 +1218,13 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital
                signalled = (waitall && count == nhandles) || (!waitall && count > 0);
 
                if (signalled) {
-                       for (i = 0; i < nhandles; i++)
-                               own_if_signalled (handles [i], &statuscodes [i]);
+                       for (i = 0; i < nhandles; i++) {
+                               if (own_if_signalled (handles [i], &abandoned [i]) && !waitall) {
+                                       /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
+                                        * throw AbandonedMutexException in case we owned it but didn't release it */
+                                       break;
+                               }
+                       }
                }
 
                mono_w32handle_unlock_handles (handles, nhandles);
@@ -1281,7 +1232,7 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital
                if (signalled) {
                        ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
                        for (i = lowest; i < nhandles; i++) {
-                               if (statuscodes [i] == WAIT_ABANDONED_0) {
+                               if (abandoned [i]) {
                                        ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest;
                                        break;
                                }
@@ -1368,7 +1319,7 @@ mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, gu
        MonoW32HandleWaitRet ret;
        gint64 start;
        gboolean alerted;
-       guint32 statuscode = 0;
+       gboolean abandoned = FALSE;
        gpointer handles [2];
 
        alerted = FALSE;
@@ -1393,11 +1344,11 @@ mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, gu
        mono_w32handle_unlock_handle (signal_handle);
 
        if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) {
-               if (own_if_owned (wait_handle, &statuscode)) {
+               if (own_if_owned (wait_handle, &abandoned)) {
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
                                __func__, wait_handle);
 
-                       ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
+                       ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
                        goto done;
                }
        }
@@ -1408,11 +1359,11 @@ mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, gu
        for (;;) {
                gint waited;
 
-               if (own_if_signalled (wait_handle, &statuscode)) {
+               if (own_if_signalled (wait_handle, &abandoned)) {
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
                                __func__, wait_handle);
 
-                       ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
+                       ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
                        goto done;
                }