/** * \file * Generic and internal operations on handles * * Author: * Dick Porter (dick@ximian.com) * Ludovic Henry (luhenry@microsoft.com) * * (C) 2002-2011 Novell, Inc. * Copyright 2011 Xamarin Inc * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include #include #include "w32handle.h" #include "utils/atomic.h" #include "utils/mono-logger-internals.h" #include "utils/mono-proclib.h" #include "utils/mono-threads.h" #include "utils/mono-time.h" #undef DEBUG_REFS #define SLOT_MAX (1024 * 32) /* must be a power of 2 */ #define HANDLE_PER_SLOT (256) static MonoW32HandleCapability handle_caps [MONO_W32TYPE_COUNT]; static MonoW32HandleOps *handle_ops [MONO_W32TYPE_COUNT]; /* * We can hold SLOT_MAX * HANDLE_PER_SLOT handles. * If 4M handles are not enough... Oh, well... we will crash. */ #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT) #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT) static MonoW32Handle **private_handles; static guint32 private_handles_size = 0; /* * This is an internal handle which is used for handling waiting for multiple handles. * Threads which wait for multiple handles wait on this one handle, and when a handle * is signalled, this handle is signalled too. */ static MonoCoopMutex global_signal_mutex; static MonoCoopCond global_signal_cond; static MonoCoopMutex scan_mutex; static gboolean shutting_down = FALSE; static const gchar* mono_w32handle_ops_typename (MonoW32Type type); const gchar* mono_w32handle_get_typename (MonoW32Type type) { return mono_w32handle_ops_typename (type); } void mono_w32handle_set_signal_state (MonoW32Handle *handle_data, gboolean state, gboolean broadcast) { #ifdef DEBUG g_message ("%s: setting state of %p to %s (broadcast %s)", __func__, handle_data, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE"); #endif if (state) { /* Tell everyone blocking on a single handle */ /* The condition the global signal cond is waiting on is the signalling of * _any_ handle. So lock it before setting the signalled state. */ mono_coop_mutex_lock (&global_signal_mutex); /* This function _must_ be called with * handle->signal_mutex locked */ handle_data->signalled = TRUE; if (broadcast) mono_coop_cond_broadcast (&handle_data->signal_cond); else mono_coop_cond_signal (&handle_data->signal_cond); /* Tell everyone blocking on multiple handles that something * was signalled */ mono_coop_cond_broadcast (&global_signal_cond); mono_coop_mutex_unlock (&global_signal_mutex); } else { handle_data->signalled = FALSE; } } gboolean mono_w32handle_issignalled (MonoW32Handle *handle_data) { return handle_data->signalled; } static void mono_w32handle_set_in_use (MonoW32Handle *handle_data, gboolean in_use) { handle_data->in_use = in_use; } static void mono_w32handle_lock_signal_mutex (void) { #ifdef DEBUG g_message ("%s: lock global signal mutex", __func__); #endif mono_coop_mutex_lock (&global_signal_mutex); } static void mono_w32handle_unlock_signal_mutex (void) { #ifdef DEBUG g_message ("%s: unlock global signal mutex", __func__); #endif mono_coop_mutex_unlock (&global_signal_mutex); } void mono_w32handle_lock (MonoW32Handle *handle_data) { mono_coop_mutex_lock (&handle_data->signal_mutex); } gboolean mono_w32handle_trylock (MonoW32Handle *handle_data) { return mono_coop_mutex_trylock (&handle_data->signal_mutex) == 0; } void mono_w32handle_unlock (MonoW32Handle *handle_data) { mono_coop_mutex_unlock (&handle_data->signal_mutex); } void mono_w32handle_init (void) { static gboolean initialized = FALSE; if (initialized) return; mono_coop_mutex_init (&scan_mutex); mono_coop_cond_init (&global_signal_cond); mono_coop_mutex_init (&global_signal_mutex); private_handles = g_new0 (MonoW32Handle*, SLOT_MAX); initialized = TRUE; } void mono_w32handle_cleanup (void) { int i; g_assert (!shutting_down); shutting_down = TRUE; for (i = 0; i < SLOT_MAX; ++i) g_free (private_handles [i]); g_free (private_handles); } static gsize mono_w32handle_ops_typesize (MonoW32Type type); /* * mono_w32handle_new_internal: * @type: Init handle to this type * * Search for a free handle and initialize it. Return the handle on * success and 0 on failure. This is only called from * mono_w32handle_new, and scan_mutex must be held. */ static MonoW32Handle* mono_w32handle_new_internal (MonoW32Type type, gpointer handle_specific) { guint32 i, k, count; static guint32 last = 0; gboolean retry = FALSE; /* A linear scan should be fast enough. Start from the last * allocation, assuming that handles are allocated more often * than they're freed. Leave the space reserved for file * descriptors */ 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_size; i++) { if (private_handles [i]) { for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) { MonoW32Handle *handle_data = &private_handles [i][k]; if (handle_data->type == MONO_W32TYPE_UNUSED) { last = count + 1; g_assert (handle_data->ref == 0); handle_data->type = type; handle_data->signalled = FALSE; handle_data->ref = 1; mono_coop_cond_init (&handle_data->signal_cond); mono_coop_mutex_init (&handle_data->signal_mutex); if (handle_specific) handle_data->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type)); return handle_data; } count++; } } } if (retry) { /* Try again from the beginning */ last = 1; retry = FALSE; goto again; } /* Will need to expand the array. The caller will sort it out */ return GINT_TO_POINTER (-1); } gpointer mono_w32handle_new (MonoW32Type type, gpointer handle_specific) { MonoW32Handle *handle_data; g_assert (!shutting_down); mono_coop_mutex_lock (&scan_mutex); while ((handle_data = mono_w32handle_new_internal (type, handle_specific)) == GINT_TO_POINTER (-1)) { /* Try and expand the array, and have another go */ if (private_handles_size >= SLOT_MAX) { mono_coop_mutex_unlock (&scan_mutex); /* We ran out of slots */ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type)); return INVALID_HANDLE_VALUE; } private_handles [private_handles_size ++] = g_new0 (MonoW32Handle, HANDLE_PER_SLOT); } mono_coop_mutex_unlock (&scan_mutex); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle_data); return (gpointer) handle_data; } static gboolean mono_w32handle_ref_core (MonoW32Handle *handle_data); static gboolean mono_w32handle_unref_core (MonoW32Handle *handle_data); static void w32handle_destroy (MonoW32Handle *handle_data); gpointer mono_w32handle_duplicate (MonoW32Handle *handle_data) { if (!mono_w32handle_ref_core (handle_data)) g_error ("%s: unknown handle %p", __func__, handle_data); return (gpointer) handle_data; } gboolean mono_w32handle_close (gpointer handle) { MonoW32Handle *handle_data; gboolean destroy; if (handle == INVALID_HANDLE_VALUE) return FALSE; handle_data = (MonoW32Handle*) handle; if (handle_data->type == MONO_W32TYPE_UNUSED) return FALSE; destroy = mono_w32handle_unref_core (handle_data); if (destroy) w32handle_destroy (handle_data); return TRUE; } gboolean mono_w32handle_lookup_and_ref (gpointer handle, MonoW32Handle **handle_data) { g_assert (handle_data); if (handle == INVALID_HANDLE_VALUE) return FALSE; *handle_data = (MonoW32Handle*) handle; if (!mono_w32handle_ref_core (*handle_data)) return FALSE; if ((*handle_data)->type == MONO_W32TYPE_UNUSED) { mono_w32handle_unref_core (*handle_data); return FALSE; } return TRUE; } void mono_w32handle_foreach (gboolean (*on_each)(MonoW32Handle *handle_data, gpointer user_data), gpointer user_data) { GPtrArray *handles_to_destroy; guint32 i, k; handles_to_destroy = NULL; mono_coop_mutex_lock (&scan_mutex); 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++) { MonoW32Handle *handle_data; gboolean destroy, finished; handle_data = &private_handles [i][k]; if (handle_data->type == MONO_W32TYPE_UNUSED) continue; if (!mono_w32handle_ref_core (handle_data)) { /* we are racing with mono_w32handle_unref: * the handle ref has been decremented, but it * hasn't yet been destroyed. */ continue; } finished = on_each (handle_data, user_data); /* we might have to destroy the handle here, as * it could have been unrefed in another thread */ destroy = mono_w32handle_unref_core (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, (gpointer) handle_data); } if (finished) goto done; } } done: mono_coop_mutex_unlock (&scan_mutex); if (handles_to_destroy) { for (i = 0; i < handles_to_destroy->len; ++i) w32handle_destroy ((MonoW32Handle*) handles_to_destroy->pdata [i]); g_ptr_array_free (handles_to_destroy, TRUE); } } static gboolean mono_w32handle_ref_core (MonoW32Handle *handle_data) { guint old, new; do { old = handle_data->ref; if (old == 0) return FALSE; new = old + 1; } while (mono_atomic_cas_i32 ((gint32*) &handle_data->ref, (gint32)new, (gint32)old) != (gint32)old); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: ref %s handle %p, ref: %d -> %d", __func__, mono_w32handle_ops_typename (handle_data->type), handle_data, old, new); return TRUE; } static gboolean mono_w32handle_unref_core (MonoW32Handle *handle_data) { MonoW32Type type; guint old, new; type = handle_data->type; do { old = handle_data->ref; if (!(old >= 1)) g_error ("%s: handle %p has ref %d, it should be >= 1", __func__, handle_data, old); new = old - 1; } while (mono_atomic_cas_i32 ((gint32*) &handle_data->ref, (gint32)new, (gint32)old) != (gint32)old); /* handle_data might contain invalid data from now on, if * another thread is unref'ing this handle at the same time */ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: unref %s handle %p, ref: %d -> %d destroy: %s", __func__, mono_w32handle_ops_typename (type), handle_data, old, new, new == 0 ? "true" : "false"); return new == 0; } static void (*_wapi_handle_ops_get_close_func (MonoW32Type type))(gpointer, gpointer); static void w32handle_destroy (MonoW32Handle *handle_data) { /* 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()) */ MonoW32Type type; gpointer handle_specific; void (*close_func)(gpointer, gpointer); g_assert (!handle_data->in_use); type = handle_data->type; handle_specific = handle_data->specific; mono_coop_mutex_lock (&scan_mutex); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle_data); mono_coop_mutex_destroy (&handle_data->signal_mutex); mono_coop_cond_destroy (&handle_data->signal_cond); memset (handle_data, 0, sizeof (MonoW32Handle)); mono_coop_mutex_unlock (&scan_mutex); close_func = _wapi_handle_ops_get_close_func (type); if (close_func != NULL) { close_func (handle_data, handle_specific); } memset (handle_specific, 0, mono_w32handle_ops_typesize (type)); g_free (handle_specific); } /* The handle must not be locked on entry to this function */ void mono_w32handle_unref (MonoW32Handle *handle_data) { gboolean destroy; destroy = mono_w32handle_unref_core (handle_data); if (destroy) w32handle_destroy (handle_data); } void mono_w32handle_register_ops (MonoW32Type type, MonoW32HandleOps *ops) { handle_ops [type] = ops; } void mono_w32handle_register_capabilities (MonoW32Type type, MonoW32HandleCapability caps) { handle_caps[type] = caps; } static gboolean mono_w32handle_test_capabilities (MonoW32Handle *handle_data, MonoW32HandleCapability caps) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__, handle_caps[handle_data->type], caps, handle_caps[handle_data->type] & caps); return (handle_caps [handle_data->type] & caps) != 0; } static void (*_wapi_handle_ops_get_close_func (MonoW32Type type))(gpointer, gpointer) { if (handle_ops[type] != NULL && handle_ops[type]->close != NULL) { return (handle_ops[type]->close); } return (NULL); } static void mono_w32handle_ops_details (MonoW32Handle *handle_data) { if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->details != NULL) handle_ops [handle_data->type]->details (handle_data); } static const gchar* mono_w32handle_ops_typename (MonoW32Type type) { g_assert (handle_ops [type]); g_assert (handle_ops [type]->typename); return handle_ops [type]->typename (); } static gsize mono_w32handle_ops_typesize (MonoW32Type type) { g_assert (handle_ops [type]); g_assert (handle_ops [type]->typesize); return handle_ops [type]->typesize (); } static void mono_w32handle_ops_signal (MonoW32Handle *handle_data) { if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->signal) handle_ops [handle_data->type]->signal (handle_data); } static gboolean mono_w32handle_ops_own (MonoW32Handle *handle_data, gboolean *abandoned) { if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->own_handle) return handle_ops [handle_data->type]->own_handle (handle_data, abandoned); return FALSE; } static gboolean mono_w32handle_ops_isowned (MonoW32Handle *handle_data) { if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->is_owned) return handle_ops [handle_data->type]->is_owned (handle_data); return FALSE; } static MonoW32HandleWaitRet mono_w32handle_ops_specialwait (MonoW32Handle *handle_data, guint32 timeout, gboolean *alerted) { if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->special_wait) return handle_ops [handle_data->type]->special_wait (handle_data, timeout, alerted); return MONO_W32HANDLE_WAIT_RET_FAILED; } static void mono_w32handle_ops_prewait (MonoW32Handle *handle_data) { if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->prewait) handle_ops [handle_data->type]->prewait (handle_data); } static void mono_w32handle_lock_handles (MonoW32Handle **handles_data, gsize nhandles) { gint i, j, iter = 0; #ifndef HOST_WIN32 struct timespec sleepytime; #endif /* Lock all the handles, with backoff */ again: for (i = 0; i < nhandles; i++) { if (!mono_w32handle_trylock (handles_data [i])) { /* Bummer */ for (j = i - 1; j >= 0; j--) mono_w32handle_unlock (handles_data [j]); iter += 10; if (iter == 1000) iter = 10; #ifdef HOST_WIN32 SleepEx (iter, TRUE); #else /* If iter ever reaches 1000 the nanosleep will * return EINVAL immediately, but we have a * design flaw if that happens. */ g_assert (iter < 1000); sleepytime.tv_sec = 0; sleepytime.tv_nsec = iter * 1000000; nanosleep (&sleepytime, NULL); #endif /* HOST_WIN32 */ goto again; } } mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: Locked all handles", __func__); } static void mono_w32handle_unlock_handles (MonoW32Handle **handles_data, gsize nhandles) { gint i; for (i = nhandles - 1; i >= 0; i--) mono_w32handle_unlock (handles_data [i]); } static int mono_w32handle_timedwait_signal_naked (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout, gboolean poll, gboolean *alerted) { int res; if (!poll) { res = mono_coop_cond_timedwait (cond, mutex, timeout); } else { /* This is needed when waiting for process handles */ if (!alerted) { /* * pthread_cond_(timed)wait() can return 0 even if the condition was not * signalled. This happens at least on Darwin. We surface this, i.e., we * get spurious wake-ups. * * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html */ res = mono_coop_cond_timedwait (cond, mutex, timeout); } else { if (timeout < 100) { /* Real timeout is less than 100ms time */ res = mono_coop_cond_timedwait (cond, mutex, timeout); } else { res = mono_coop_cond_timedwait (cond, mutex, 100); /* Mask the fake timeout, this will cause * another poll if the cond was not really signaled */ if (res == -1) res = 0; } } } return res; } static void signal_global (gpointer unused) { /* If we reach here, then interrupt token is set to the flag value, which * means that the target thread is either * - before the first CAS in timedwait, which means it won't enter the wait. * - it is after the first CAS, so it is already waiting, or it will enter * the wait, and it will be interrupted by the broadcast. */ mono_coop_mutex_lock (&global_signal_mutex); mono_coop_cond_broadcast (&global_signal_cond); mono_coop_mutex_unlock (&global_signal_mutex); } static int mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted) { int res; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for global", __func__); if (alerted) *alerted = FALSE; if (alerted) { mono_thread_info_install_interrupt (signal_global, NULL, alerted); if (*alerted) return 0; } res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted); if (alerted) mono_thread_info_uninstall_interrupt (alerted); return res; } static void signal_handle_and_unref (gpointer handle_duplicate) { MonoW32Handle *handle_data; MonoCoopCond *cond; MonoCoopMutex *mutex; if (!mono_w32handle_lookup_and_ref (handle_duplicate, &handle_data)) g_error ("%s: unknown handle %p", __func__, handle_duplicate); /* If we reach here, then interrupt token is set to the flag value, which * means that the target thread is either * - before the first CAS in timedwait, which means it won't enter the wait. * - it is after the first CAS, so it is already waiting, or it will enter * the wait, and it will be interrupted by the broadcast. */ cond = &handle_data->signal_cond; mutex = &handle_data->signal_mutex; mono_coop_mutex_lock (mutex); mono_coop_cond_broadcast (cond); mono_coop_mutex_unlock (mutex); mono_w32handle_unref (handle_data); mono_w32handle_close (handle_duplicate); } static int mono_w32handle_timedwait_signal_handle (MonoW32Handle *handle_data, guint32 timeout, gboolean poll, gboolean *alerted) { gpointer handle_duplicate; int res; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for %p (type %s)", __func__, handle_data, mono_w32handle_ops_typename (handle_data->type)); if (alerted) *alerted = FALSE; if (alerted) { mono_thread_info_install_interrupt (signal_handle_and_unref, handle_duplicate = mono_w32handle_duplicate (handle_data), alerted); if (*alerted) { mono_w32handle_close (handle_duplicate); return 0; } } res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted); if (alerted) { mono_thread_info_uninstall_interrupt (alerted); if (!*alerted) { /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */ mono_w32handle_close (handle_duplicate); } } return res; } static gboolean dump_callback (MonoW32Handle *handle_data, gpointer user_data) { g_print ("%p [%7s] signalled: %5s ref: %3d ", handle_data, 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); g_print ("\n"); return FALSE; } void mono_w32handle_dump (void) { mono_w32handle_foreach (dump_callback, NULL); } static gboolean own_if_signalled (MonoW32Handle *handle_data, gboolean *abandoned) { if (!mono_w32handle_issignalled (handle_data)) return FALSE; *abandoned = FALSE; mono_w32handle_ops_own (handle_data, abandoned); return TRUE; } static gboolean own_if_owned (MonoW32Handle *handle_data, gboolean *abandoned) { if (!mono_w32handle_ops_isowned (handle_data)) return FALSE; *abandoned = FALSE; mono_w32handle_ops_own (handle_data, abandoned); return TRUE; } MonoW32HandleWaitRet mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable) { MonoW32Handle *handle_data; MonoW32HandleWaitRet ret; gboolean alerted; gint64 start; gboolean abandoned = FALSE; alerted = FALSE; if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) return MONO_W32HANDLE_WAIT_RET_FAILED; if (mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p has special wait", __func__, handle_data); mono_w32handle_unref (handle_data); return mono_w32handle_ops_specialwait (handle_data, timeout, alertable ? &alerted : NULL); } if (!mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_WAIT)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p can't be waited for", __func__, handle_data); mono_w32handle_unref (handle_data); return MONO_W32HANDLE_WAIT_RET_FAILED; } mono_w32handle_lock (handle_data); if (mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_OWN)) { if (own_if_owned (handle_data, &abandoned)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p already owned", __func__, handle_data); ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0; goto done; } } if (timeout != MONO_INFINITE_WAIT) start = mono_msec_ticks (); mono_w32handle_set_in_use (handle_data, TRUE); for (;;) { gint waited; if (own_if_signalled (handle_data, &abandoned)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p signalled", __func__, handle_data); ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0; goto done; } mono_w32handle_ops_prewait (handle_data); if (timeout == MONO_INFINITE_WAIT) { waited = mono_w32handle_timedwait_signal_handle (handle_data, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL); } else { gint64 elapsed; elapsed = mono_msec_ticks () - start; if (elapsed > timeout) { ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; goto done; } waited = mono_w32handle_timedwait_signal_handle (handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL); } if (alerted) { ret = MONO_W32HANDLE_WAIT_RET_ALERTED; goto done; } if (waited != 0) { ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; goto done; } } done: mono_w32handle_set_in_use (handle_data, FALSE); mono_w32handle_unlock (handle_data); mono_w32handle_unref (handle_data); return ret; } MonoW32HandleWaitRet mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable) { MonoW32HandleWaitRet ret; gboolean alerted, poll; gint i; gint64 start; MonoW32Handle *handles_data [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS], *handles_data_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS]; gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0}; if (nhandles == 0) return MONO_W32HANDLE_WAIT_RET_FAILED; if (nhandles == 1) return mono_w32handle_wait_one (handles [0], timeout, alertable); alerted = FALSE; if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: too many handles: %zd", __func__, nhandles); return MONO_W32HANDLE_WAIT_RET_FAILED; } for (i = 0; i < nhandles; ++i) { if (!mono_w32handle_lookup_and_ref (handles [i], &handles_data [i])) { for (; i >= 0; --i) mono_w32handle_unref (handles_data [i]); return MONO_W32HANDLE_WAIT_RET_FAILED; } } for (i = 0; i < nhandles; ++i) { if (!mono_w32handle_test_capabilities (handles_data[i], MONO_W32HANDLE_CAP_WAIT) && !mono_w32handle_test_capabilities (handles_data[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p can't be waited for", __func__, handles_data [i]); for (i = nhandles - 1; i >= 0; --i) mono_w32handle_unref (handles_data [i]); return MONO_W32HANDLE_WAIT_RET_FAILED; } handles_data_sorted [i] = handles_data [i]; } qsort (handles_data_sorted, nhandles, sizeof (gpointer), g_direct_equal); for (i = 1; i < nhandles; ++i) { if (handles_data_sorted [i - 1] == handles_data_sorted [i]) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p is duplicated", __func__, handles_data_sorted [i]); for (i = nhandles - 1; i >= 0; --i) mono_w32handle_unref (handles_data [i]); return MONO_W32HANDLE_WAIT_RET_FAILED; } } poll = FALSE; for (i = 0; i < nhandles; ++i) { if (handles_data [i]->type == MONO_W32TYPE_PROCESS) { /* Can't wait for a process handle + another handle without polling */ poll = TRUE; } } if (timeout != MONO_INFINITE_WAIT) start = mono_msec_ticks (); for (;;) { gsize count, lowest; gboolean signalled; gint waited; count = 0; lowest = nhandles; mono_w32handle_lock_handles (handles_data, nhandles); for (i = 0; i < nhandles; i++) { if ((mono_w32handle_test_capabilities (handles_data [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles_data [i])) || mono_w32handle_issignalled (handles_data [i])) { count ++; if (i < lowest) lowest = i; } } signalled = (waitall && count == nhandles) || (!waitall && count > 0); if (signalled) { for (i = 0; i < nhandles; i++) { if (own_if_signalled (handles_data [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_data, nhandles); if (signalled) { ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest; for (i = lowest; i < nhandles; i++) { if (abandoned [i]) { ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest; break; } } goto done; } for (i = 0; i < nhandles; i++) { mono_w32handle_ops_prewait (handles_data [i]); if (mono_w32handle_test_capabilities (handles_data [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT) && !mono_w32handle_issignalled (handles_data [i])) { mono_w32handle_ops_specialwait (handles_data [i], 0, alertable ? &alerted : NULL); } } mono_w32handle_lock_signal_mutex (); if (waitall) { signalled = TRUE; for (i = 0; i < nhandles; ++i) { if (!mono_w32handle_issignalled (handles_data [i])) { signalled = FALSE; break; } } } else { signalled = FALSE; for (i = 0; i < nhandles; ++i) { if (mono_w32handle_issignalled (handles_data [i])) { signalled = TRUE; break; } } } waited = 0; if (!signalled) { if (timeout == MONO_INFINITE_WAIT) { waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL); } else { gint64 elapsed; elapsed = mono_msec_ticks () - start; if (elapsed > timeout) { ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; mono_w32handle_unlock_signal_mutex (); goto done; } waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL); } } mono_w32handle_unlock_signal_mutex (); if (alerted) { ret = MONO_W32HANDLE_WAIT_RET_ALERTED; goto done; } if (waited != 0) { ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; goto done; } } done: for (i = nhandles - 1; i >= 0; i--) { /* Unref everything we reffed above */ mono_w32handle_unref (handles_data [i]); } return ret; } MonoW32HandleWaitRet mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable) { MonoW32Handle *signal_handle_data, *wait_handle_data, *handles_data [2]; MonoW32HandleWaitRet ret; gint64 start; gboolean alerted; gboolean abandoned = FALSE; alerted = FALSE; if (!mono_w32handle_lookup_and_ref (signal_handle, &signal_handle_data)) { return MONO_W32HANDLE_WAIT_RET_FAILED; } if (!mono_w32handle_lookup_and_ref (wait_handle, &wait_handle_data)) { mono_w32handle_unref (signal_handle_data); return MONO_W32HANDLE_WAIT_RET_FAILED; } if (!mono_w32handle_test_capabilities (signal_handle_data, MONO_W32HANDLE_CAP_SIGNAL)) { mono_w32handle_unref (wait_handle_data); mono_w32handle_unref (signal_handle_data); return MONO_W32HANDLE_WAIT_RET_FAILED; } if (!mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_WAIT)) { mono_w32handle_unref (wait_handle_data); mono_w32handle_unref (signal_handle_data); return MONO_W32HANDLE_WAIT_RET_FAILED; } if (mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) { g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle_data); mono_w32handle_unref (wait_handle_data); mono_w32handle_unref (signal_handle_data); return MONO_W32HANDLE_WAIT_RET_FAILED; } handles_data [0] = wait_handle_data; handles_data [1] = signal_handle_data; mono_w32handle_lock_handles (handles_data, 2); mono_w32handle_ops_signal (signal_handle_data); mono_w32handle_unlock (signal_handle_data); if (mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_OWN)) { if (own_if_owned (wait_handle_data, &abandoned)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p already owned", __func__, wait_handle_data); ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0; goto done; } } if (timeout != MONO_INFINITE_WAIT) start = mono_msec_ticks (); for (;;) { gint waited; if (own_if_signalled (wait_handle_data, &abandoned)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p signalled", __func__, wait_handle_data); ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0; goto done; } mono_w32handle_ops_prewait (wait_handle_data); if (timeout == MONO_INFINITE_WAIT) { waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL); } else { gint64 elapsed; elapsed = mono_msec_ticks () - start; if (elapsed > timeout) { ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; goto done; } waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL); } if (alerted) { ret = MONO_W32HANDLE_WAIT_RET_ALERTED; goto done; } if (waited != 0) { ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; goto done; } } done: mono_w32handle_unlock (wait_handle_data); mono_w32handle_unref (wait_handle_data); mono_w32handle_unref (signal_handle_data); return ret; }