[io-layer] Extract file (#4255)
[mono.git] / mono / metadata / w32handle.c
index ef3b24838e1729459f9da4c99ddc7a834ce51aa7..248bf1b316f00a7920a2f7618d15fe152b5af99b 100644 (file)
@@ -29,8 +29,6 @@
 /* must be a power of 2 */
 #define HANDLE_PER_SLOT        (256)
 
-#define INFINITE 0xFFFFFFFF
-
 typedef struct {
        MonoW32HandleType type;
        guint ref;
@@ -252,7 +250,7 @@ mono_w32handle_unlock_handle (gpointer handle)
 }
 
 /*
- * wapi_init:
+ * mono_w32handle_init:
  *
  *   Initialize the io-layer.
  */
@@ -509,13 +507,19 @@ static gboolean
 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);
+
+static void
+w32handle_destroy (gpointer handle);
 
 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++) {
@@ -541,10 +545,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;
@@ -553,6 +565,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
@@ -575,7 +594,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;
@@ -584,8 +603,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);
@@ -614,53 +633,61 @@ 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);
+       type = handle_data->type;
+       handle_specific = handle_data->specific;
 
-       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);
+       mono_os_mutex_lock (&scan_mutex);
 
-               type = handle_data->type;
-               handle_specific = handle_data->specific;
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
 
-               mono_os_mutex_lock (&scan_mutex);
+       mono_os_mutex_destroy (&handle_data->signal_mutex);
+       mono_os_cond_destroy (&handle_data->signal_cond);
 
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
+       memset (handle_data, 0, sizeof (MonoW32HandleBase));
 
-               mono_os_mutex_destroy (&handle_data->signal_mutex);
-               mono_os_cond_destroy (&handle_data->signal_cond);
+       mono_os_mutex_unlock (&scan_mutex);
 
-               memset (handle_data, 0, sizeof (MonoW32HandleBase));
+       close_func = _wapi_handle_ops_get_close_func (type);
+       if (close_func != NULL) {
+               close_func (handle, handle_specific);
+       }
 
-               mono_os_mutex_unlock (&scan_mutex);
+       g_free (handle_specific);
+}
 
-               close_func = _wapi_handle_ops_get_close_func (type);
-               if (close_func != NULL) {
-                       close_func (handle, handle_specific);
-               }
+/* The handle must not be locked on entry to this function */
+void
+mono_w32handle_unref (gpointer handle)
+{
+       MonoW32HandleBase *handle_data;
+       gboolean destroy;
 
-               g_free (handle_specific);
+       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;
        }
+
+       destroy = mono_w32handle_unref_core (handle, handle_data);
+       if (destroy)
+               w32handle_destroy (handle);
 }
 
 static void
@@ -773,7 +800,7 @@ mono_w32handle_ops_signal (gpointer handle)
 }
 
 static gboolean
-mono_w32handle_ops_own (gpointer handle, guint32 *statuscode)
+mono_w32handle_ops_own (gpointer handle, gboolean *abandoned)
 {
        MonoW32HandleBase *handle_data;
        MonoW32HandleType type;
@@ -785,7 +812,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);
        }
@@ -817,7 +844,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;
@@ -826,7 +853,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;
        }
 }
 
@@ -879,7 +906,7 @@ again:
                if (!mono_w32handle_trylock_handle (handle)) {
                        /* Bummer */
 
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p: %s", __func__,
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p.", __func__,
                                   handle);
 
                        while (i--) {
@@ -1080,24 +1107,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;
 }
 
@@ -1107,7 +1134,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;
 
@@ -1128,33 +1155,33 @@ 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;
                }
        }
 
-       if (timeout != INFINITE)
+       if (timeout != MONO_INFINITE_WAIT)
                start = mono_msec_ticks ();
 
        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;
                }
 
                mono_w32handle_ops_prewait (handle);
 
-               if (timeout == INFINITE) {
-                       waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &alerted : NULL);
+               if (timeout == MONO_INFINITE_WAIT) {
+                       waited = mono_w32handle_timedwait_signal_handle (handle, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
                } else {
                        gint64 elapsed;
 
@@ -1192,7 +1219,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;
@@ -1240,7 +1267,7 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital
                }
        }
 
-       if (timeout != INFINITE)
+       if (timeout != MONO_INFINITE_WAIT)
                start = mono_msec_ticks ();
 
        for (i = 0; i < nhandles; ++i) {
@@ -1275,7 +1302,7 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital
 
                if (signalled) {
                        for (i = 0; i < nhandles; i++)
-                               own_if_signalled (handles [i], &statuscodes [i]);
+                               own_if_signalled (handles [i], &abandoned [i]);
                }
 
                mono_w32handle_unlock_handles (handles, nhandles);
@@ -1283,7 +1310,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;
                                }
@@ -1324,8 +1351,8 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital
                waited = 0;
 
                if (!signalled) {
-                       if (timeout == INFINITE) {
-                               waited = mono_w32handle_timedwait_signal (INFINITE, poll, alertable ? &alerted : NULL);
+                       if (timeout == MONO_INFINITE_WAIT) {
+                               waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL);
                        } else {
                                gint64 elapsed;
 
@@ -1370,7 +1397,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;
@@ -1395,33 +1422,33 @@ 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;
                }
        }
 
-       if (timeout != INFINITE)
+       if (timeout != MONO_INFINITE_WAIT)
                start = mono_msec_ticks ();
 
        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;
                }
 
                mono_w32handle_ops_prewait (wait_handle);
 
-               if (timeout == INFINITE) {
-                       waited = mono_w32handle_timedwait_signal_handle (wait_handle, INFINITE, FALSE, alertable ? &alerted : NULL);
+               if (timeout == MONO_INFINITE_WAIT) {
+                       waited = mono_w32handle_timedwait_signal_handle (wait_handle, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
                } else {
                        gint64 elapsed;