2 * w32handle.c: Generic and internal operations on handles
5 * Dick Porter (dick@ximian.com)
6 * Ludovic Henry (luhenry@microsoft.com)
8 * (C) 2002-2011 Novell, Inc.
9 * Copyright 2011 Xamarin Inc
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #if !defined(HOST_WIN32)
25 #include <sys/types.h>
26 #ifdef HAVE_SYS_SOCKET_H
27 # include <sys/socket.h>
32 #ifdef HAVE_SYS_MMAN_H
33 # include <sys/mman.h>
39 #ifdef HAVE_SYS_RESOURCE_H
40 # include <sys/resource.h>
43 #include "w32handle.h"
45 #include "utils/atomic.h"
46 #include "utils/mono-logger-internals.h"
47 #include "utils/mono-os-mutex.h"
48 #include "utils/mono-proclib.h"
49 #include "utils/mono-threads.h"
50 #include "utils/mono-time.h"
54 #define SLOT_MAX (1024 * 16)
56 /* must be a power of 2 */
57 #define HANDLE_PER_SLOT (256)
59 #define INFINITE 0xFFFFFFFF
62 MonoW32HandleType type;
65 mono_mutex_t signal_mutex;
66 mono_cond_t signal_cond;
70 static MonoW32HandleCapability handle_caps [MONO_W32HANDLE_COUNT];
71 static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
74 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
75 * If 4M handles are not enough... Oh, well... we will crash.
77 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
78 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
80 static MonoW32HandleBase *private_handles [SLOT_MAX];
81 static guint32 private_handles_count = 0;
82 static guint32 private_handles_slots_count = 0;
84 guint32 mono_w32handle_fd_reserve;
87 * This is an internal handle which is used for handling waiting for multiple handles.
88 * Threads which wait for multiple handles wait on this one handle, and when a handle
89 * is signalled, this handle is signalled too.
91 static mono_mutex_t global_signal_mutex;
92 static mono_cond_t global_signal_cond;
94 static mono_mutex_t scan_mutex;
96 static gboolean shutting_down = FALSE;
99 type_is_fd (MonoW32HandleType type)
102 case MONO_W32HANDLE_FILE:
103 case MONO_W32HANDLE_CONSOLE:
104 case MONO_W32HANDLE_SOCKET:
105 case MONO_W32HANDLE_PIPE:
113 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
117 g_assert (handle_data);
119 index = SLOT_INDEX ((gsize) handle);
120 if (index >= SLOT_MAX)
122 if (!private_handles [index])
125 offset = SLOT_OFFSET ((gsize) handle);
126 if (private_handles [index][offset].type == MONO_W32HANDLE_UNUSED)
129 *handle_data = &private_handles [index][offset];
134 mono_w32handle_get_type (gpointer handle)
136 MonoW32HandleBase *handle_data;
138 if (!mono_w32handle_lookup_data (handle, &handle_data))
139 return MONO_W32HANDLE_UNUSED; /* An impossible type */
141 return handle_data->type;
145 mono_w32handle_set_signal_state (gpointer handle, gboolean state, gboolean broadcast)
147 MonoW32HandleBase *handle_data;
149 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
154 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
155 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
159 /* Tell everyone blocking on a single handle */
161 /* The condition the global signal cond is waiting on is the signalling of
162 * _any_ handle. So lock it before setting the signalled state.
164 mono_os_mutex_lock (&global_signal_mutex);
166 /* This function _must_ be called with
167 * handle->signal_mutex locked
169 handle_data->signalled=state;
171 if (broadcast == TRUE) {
172 mono_os_cond_broadcast (&handle_data->signal_cond);
174 mono_os_cond_signal (&handle_data->signal_cond);
177 /* Tell everyone blocking on multiple handles that something
180 mono_os_cond_broadcast (&global_signal_cond);
182 mono_os_mutex_unlock (&global_signal_mutex);
184 handle_data->signalled=state;
189 mono_w32handle_issignalled (gpointer handle)
191 MonoW32HandleBase *handle_data;
193 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
197 return handle_data->signalled;
201 mono_w32handle_lock_signal_mutex (void)
204 g_message ("%s: lock global signal mutex", __func__);
207 mono_os_mutex_lock (&global_signal_mutex);
211 mono_w32handle_unlock_signal_mutex (void)
214 g_message ("%s: unlock global signal mutex", __func__);
217 mono_os_mutex_unlock (&global_signal_mutex);
221 mono_w32handle_lock_handle (gpointer handle)
223 MonoW32HandleBase *handle_data;
225 if (!mono_w32handle_lookup_data (handle, &handle_data))
226 g_error ("%s: failed to lookup handle %p", __func__, handle);
228 mono_w32handle_ref (handle);
230 mono_os_mutex_lock (&handle_data->signal_mutex);
232 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: lock handle %p", __func__, handle);
236 mono_w32handle_trylock_handle (gpointer handle)
238 MonoW32HandleBase *handle_data;
241 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p", __func__, handle);
243 if (!mono_w32handle_lookup_data (handle, &handle_data))
244 g_error ("%s: failed to lookup handle %p", __func__, handle);
246 mono_w32handle_ref (handle);
248 locked = mono_os_mutex_trylock (&handle_data->signal_mutex) == 0;
250 mono_w32handle_unref (handle);
252 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p, locked: %s", __func__, handle, locked ? "true" : "false");
258 mono_w32handle_unlock_handle (gpointer handle)
260 MonoW32HandleBase *handle_data;
262 if (!mono_w32handle_lookup_data (handle, &handle_data))
263 g_error ("%s: failed to lookup handle %p", __func__, handle);
265 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlock handle %p", __func__, handle);
267 mono_os_mutex_unlock (&handle_data->signal_mutex);
269 mono_w32handle_unref (handle);
275 * Initialize the io-layer.
278 mono_w32handle_init (void)
280 static gboolean initialized = FALSE;
285 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
286 == MONO_W32HANDLE_COUNT);
288 /* This is needed by the code in mono_w32handle_new_internal */
289 mono_w32handle_fd_reserve = (eg_getdtablesize () + (HANDLE_PER_SLOT - 1)) & ~(HANDLE_PER_SLOT - 1);
293 * The entries in private_handles reserved for fds are allocated lazily to
297 private_handles_count += HANDLE_PER_SLOT;
298 private_handles_slots_count ++;
299 } while(mono_w32handle_fd_reserve > private_handles_count);
301 mono_os_mutex_init (&scan_mutex);
303 mono_os_cond_init (&global_signal_cond);
304 mono_os_mutex_init (&global_signal_mutex);
310 mono_w32handle_cleanup (void)
314 g_assert (!shutting_down);
315 shutting_down = TRUE;
317 /* Every shared handle we were using ought really to be closed
318 * by now, but to make sure just blow them all away. The
319 * exiting finalizer thread in particular races us to the
320 * program exit and doesn't always win, so it can be left
321 * cluttering up the shared file. Anything else left over is
324 for(i = SLOT_INDEX (0); private_handles[i] != NULL; i++) {
325 for(j = SLOT_OFFSET (0); j < HANDLE_PER_SLOT; j++) {
326 MonoW32HandleBase *handle_data = &private_handles[i][j];
327 gpointer handle = GINT_TO_POINTER (i*HANDLE_PER_SLOT+j);
329 for(k = handle_data->ref; k > 0; k--) {
330 mono_w32handle_unref (handle);
335 for (i = 0; i < SLOT_MAX; ++i)
336 g_free (private_handles [i]);
339 static void mono_w32handle_init_handle (MonoW32HandleBase *handle,
340 MonoW32HandleType type, gpointer handle_specific)
342 g_assert (handle->ref == 0);
345 handle->signalled = FALSE;
348 mono_os_cond_init (&handle->signal_cond);
349 mono_os_mutex_init (&handle->signal_mutex);
352 handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
356 * mono_w32handle_new_internal:
357 * @type: Init handle to this type
359 * Search for a free handle and initialize it. Return the handle on
360 * success and 0 on failure. This is only called from
361 * mono_w32handle_new, and scan_mutex must be held.
363 static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
364 gpointer handle_specific)
367 static guint32 last = 0;
368 gboolean retry = FALSE;
370 /* A linear scan should be fast enough. Start from the last
371 * allocation, assuming that handles are allocated more often
372 * than they're freed. Leave the space reserved for file
376 if (last < mono_w32handle_fd_reserve) {
377 last = mono_w32handle_fd_reserve;
384 for(i = SLOT_INDEX (count); i < private_handles_slots_count; i++) {
385 if (private_handles [i]) {
386 for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
387 MonoW32HandleBase *handle = &private_handles [i][k];
389 if(handle->type == MONO_W32HANDLE_UNUSED) {
392 mono_w32handle_init_handle (handle, type, handle_specific);
400 if(retry && last > mono_w32handle_fd_reserve) {
401 /* Try again from the beginning */
402 last = mono_w32handle_fd_reserve;
406 /* Will need to expand the array. The caller will sort it out */
412 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
414 guint32 handle_idx = 0;
417 g_assert (!shutting_down);
419 g_assert(!type_is_fd(type));
421 mono_os_mutex_lock (&scan_mutex);
423 while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == 0) {
424 /* Try and expand the array, and have another go */
425 int idx = SLOT_INDEX (private_handles_count);
426 if (idx >= SLOT_MAX) {
430 private_handles [idx] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
432 private_handles_count += HANDLE_PER_SLOT;
433 private_handles_slots_count ++;
436 mono_os_mutex_unlock (&scan_mutex);
438 if (handle_idx == 0) {
439 /* We ran out of slots */
440 handle = INVALID_HANDLE_VALUE;
441 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type));
445 /* Make sure we left the space for fd mappings */
446 g_assert (handle_idx >= mono_w32handle_fd_reserve);
448 handle = GUINT_TO_POINTER (handle_idx);
450 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
456 gpointer mono_w32handle_new_fd (MonoW32HandleType type, int fd,
457 gpointer handle_specific)
459 MonoW32HandleBase *handle_data;
460 int fd_index, fd_offset;
462 g_assert (!shutting_down);
464 g_assert(type_is_fd(type));
466 if (fd >= mono_w32handle_fd_reserve) {
467 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));
469 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
472 fd_index = SLOT_INDEX (fd);
473 fd_offset = SLOT_OFFSET (fd);
475 /* Initialize the array entries on demand */
476 if (!private_handles [fd_index]) {
477 mono_os_mutex_lock (&scan_mutex);
479 if (!private_handles [fd_index])
480 private_handles [fd_index] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
482 mono_os_mutex_unlock (&scan_mutex);
485 handle_data = &private_handles [fd_index][fd_offset];
487 if (handle_data->type != MONO_W32HANDLE_UNUSED) {
488 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));
489 /* FIXME: clean up this handle? We can't do anything
490 * with the fd, cos thats the new one
492 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
495 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), GUINT_TO_POINTER(fd));
497 mono_w32handle_init_handle (handle_data, type, handle_specific);
499 return(GUINT_TO_POINTER(fd));
503 mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
504 gpointer *handle_specific)
506 MonoW32HandleBase *handle_data;
508 g_assert (handle_specific);
510 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
514 if (handle_data->type != type) {
518 *handle_specific = handle_data->specific;
524 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data);
527 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guint minimum);
530 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
534 mono_os_mutex_lock (&scan_mutex);
536 for (i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
537 if (!private_handles [i])
539 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
540 MonoW32HandleBase *handle_data = NULL;
542 gboolean destroy, finished;
544 handle_data = &private_handles [i][k];
545 if (handle_data->type == MONO_W32HANDLE_UNUSED)
548 handle = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
550 if (!mono_w32handle_ref_core (handle, handle_data)) {
551 /* we are racing with mono_w32handle_unref:
552 * the handle ref has been decremented, but it
553 * hasn't yet been destroyed. */
557 finished = on_each (handle, handle_data->specific, user_data);
559 /* we do not want to have to destroy the handle here,
560 * as it would means the ref/unref are unbalanced */
561 destroy = mono_w32handle_unref_core (handle, handle_data, 2);
570 mono_os_mutex_unlock (&scan_mutex);
574 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data)
579 old = handle_data->ref;
584 } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
586 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: ref %s handle %p, ref: %d -> %d",
587 __func__, mono_w32handle_ops_typename (handle_data->type), handle, old, new);
593 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guint minimum)
595 MonoW32HandleType type;
598 type = handle_data->type;
601 old = handle_data->ref;
602 if (!(old >= minimum))
603 g_error ("%s: handle %p has ref %d, it should be >= %d", __func__, handle, old, minimum);
606 } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
608 /* handle_data might contain invalid data from now on, if
609 * another thread is unref'ing this handle at the same time */
611 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unref %s handle %p, ref: %d -> %d destroy: %s",
612 __func__, mono_w32handle_ops_typename (type), handle, old, new, new == 0 ? "true" : "false");
617 void mono_w32handle_ref (gpointer handle)
619 MonoW32HandleBase *handle_data;
621 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
622 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to ref handle %p, unknown handle", __func__, handle);
626 if (!mono_w32handle_ref_core (handle, handle_data))
627 g_error ("%s: failed to ref handle %p", __func__, handle);
630 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer);
632 /* The handle must not be locked on entry to this function */
634 mono_w32handle_unref (gpointer handle)
636 MonoW32HandleBase *handle_data;
639 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
640 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to unref handle %p, unknown handle",
645 destroy = mono_w32handle_unref_core (handle, handle_data, 1);
648 /* Need to copy the handle info, reset the slot in the
649 * array, and _only then_ call the close function to
650 * avoid race conditions (eg file descriptors being
651 * closed, and another file being opened getting the
652 * same fd racing the memset())
654 MonoW32HandleType type;
655 gpointer handle_specific;
656 void (*close_func)(gpointer, gpointer);
658 type = handle_data->type;
659 handle_specific = handle_data->specific;
661 mono_os_mutex_lock (&scan_mutex);
663 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
665 mono_os_mutex_destroy (&handle_data->signal_mutex);
666 mono_os_cond_destroy (&handle_data->signal_cond);
668 memset (handle_data, 0, sizeof (MonoW32HandleBase));
670 mono_os_mutex_unlock (&scan_mutex);
672 close_func = _wapi_handle_ops_get_close_func (type);
673 if (close_func != NULL) {
674 close_func (handle, handle_specific);
677 g_free (handle_specific);
682 mono_w32handle_register_ops (MonoW32HandleType type, MonoW32HandleOps *ops)
684 handle_ops [type] = ops;
687 void mono_w32handle_register_capabilities (MonoW32HandleType type,
688 MonoW32HandleCapability caps)
690 handle_caps[type] = caps;
693 gboolean mono_w32handle_test_capabilities (gpointer handle,
694 MonoW32HandleCapability caps)
696 MonoW32HandleBase *handle_data;
697 MonoW32HandleType type;
699 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
703 type = handle_data->type;
705 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
706 handle_caps[type], caps, handle_caps[type] & caps);
708 return((handle_caps[type] & caps) != 0);
711 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer)
713 if (handle_ops[type] != NULL &&
714 handle_ops[type]->close != NULL) {
715 return (handle_ops[type]->close);
721 void mono_w32handle_ops_close (gpointer handle, gpointer data)
723 MonoW32HandleBase *handle_data;
724 MonoW32HandleType type;
726 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
730 type = handle_data->type;
732 if (handle_ops[type] != NULL &&
733 handle_ops[type]->close != NULL) {
734 handle_ops[type]->close (handle, data);
738 void mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
740 if (handle_ops[type] != NULL &&
741 handle_ops[type]->details != NULL) {
742 handle_ops[type]->details (data);
746 const gchar* mono_w32handle_ops_typename (MonoW32HandleType type)
748 g_assert (handle_ops [type]);
749 g_assert (handle_ops [type]->typename);
750 return handle_ops [type]->typename ();
753 gsize mono_w32handle_ops_typesize (MonoW32HandleType type)
755 g_assert (handle_ops [type]);
756 g_assert (handle_ops [type]->typesize);
757 return handle_ops [type]->typesize ();
760 void mono_w32handle_ops_signal (gpointer handle)
762 MonoW32HandleBase *handle_data;
763 MonoW32HandleType type;
765 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
769 type = handle_data->type;
771 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
772 handle_ops[type]->signal (handle);
776 gboolean mono_w32handle_ops_own (gpointer handle, guint32 *statuscode)
778 MonoW32HandleBase *handle_data;
779 MonoW32HandleType type;
781 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
785 type = handle_data->type;
787 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
788 return(handle_ops[type]->own_handle (handle, statuscode));
794 gboolean mono_w32handle_ops_isowned (gpointer handle)
796 MonoW32HandleBase *handle_data;
797 MonoW32HandleType type;
799 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
803 type = handle_data->type;
805 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
806 return(handle_ops[type]->is_owned (handle));
813 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted)
815 MonoW32HandleBase *handle_data;
816 MonoW32HandleType type;
818 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
822 type = handle_data->type;
824 if (handle_ops[type] != NULL &&
825 handle_ops[type]->special_wait != NULL) {
826 return(handle_ops[type]->special_wait (handle, timeout, alerted));
832 void mono_w32handle_ops_prewait (gpointer handle)
834 MonoW32HandleBase *handle_data;
835 MonoW32HandleType type;
837 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
841 type = handle_data->type;
843 if (handle_ops[type] != NULL &&
844 handle_ops[type]->prewait != NULL) {
845 handle_ops[type]->prewait (handle);
852 struct timespec sleepytime;
854 g_assert (ms < 1000);
856 sleepytime.tv_sec = 0;
857 sleepytime.tv_nsec = ms * 1000000;
858 nanosleep (&sleepytime, NULL);
862 mono_w32handle_lock_handles (gpointer *handles, gsize numhandles)
866 /* Lock all the handles, with backoff */
868 for(i=0; i<numhandles; i++) {
869 gpointer handle = handles[i];
871 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempting to lock %p", __func__, handle);
873 if (!mono_w32handle_trylock_handle (handle)) {
876 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p: %s", __func__,
882 mono_w32handle_unlock_handle (handle);
885 /* If iter ever reaches 100 the nanosleep will
886 * return EINVAL immediately, but we have a
887 * design flaw if that happens.
891 g_warning ("%s: iteration overflow!",
896 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
904 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
908 mono_w32handle_unlock_handles (gpointer *handles, gsize numhandles)
912 for(i=0; i<numhandles; i++) {
913 gpointer handle = handles[i];
915 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlocking handle %p", __func__, handle);
917 mono_w32handle_unlock_handle (handle);
922 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
927 res = mono_os_cond_timedwait (cond, mutex, timeout);
929 /* This is needed when waiting for process handles */
932 * pthread_cond_(timed)wait() can return 0 even if the condition was not
933 * signalled. This happens at least on Darwin. We surface this, i.e., we
934 * get spurious wake-ups.
936 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
938 res = mono_os_cond_timedwait (cond, mutex, timeout);
941 /* Real timeout is less than 100ms time */
942 res = mono_os_cond_timedwait (cond, mutex, timeout);
944 res = mono_os_cond_timedwait (cond, mutex, 100);
946 /* Mask the fake timeout, this will cause
947 * another poll if the cond was not really signaled
959 signal_global (gpointer unused)
961 /* If we reach here, then interrupt token is set to the flag value, which
962 * means that the target thread is either
963 * - before the first CAS in timedwait, which means it won't enter the wait.
964 * - it is after the first CAS, so it is already waiting, or it will enter
965 * the wait, and it will be interrupted by the broadcast. */
966 mono_os_mutex_lock (&global_signal_mutex);
967 mono_os_cond_broadcast (&global_signal_cond);
968 mono_os_mutex_unlock (&global_signal_mutex);
972 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
976 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
982 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
987 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
990 mono_thread_info_uninstall_interrupt (alerted);
996 signal_handle_and_unref (gpointer handle)
998 MonoW32HandleBase *handle_data;
1000 mono_mutex_t *mutex;
1002 if (!mono_w32handle_lookup_data (handle, &handle_data))
1003 g_error ("cannot signal unknown handle %p", handle);
1005 /* If we reach here, then interrupt token is set to the flag value, which
1006 * means that the target thread is either
1007 * - before the first CAS in timedwait, which means it won't enter the wait.
1008 * - it is after the first CAS, so it is already waiting, or it will enter
1009 * the wait, and it will be interrupted by the broadcast. */
1010 cond = &handle_data->signal_cond;
1011 mutex = &handle_data->signal_mutex;
1013 mono_os_mutex_lock (mutex);
1014 mono_os_cond_broadcast (cond);
1015 mono_os_mutex_unlock (mutex);
1017 mono_w32handle_unref (handle);
1021 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1023 MonoW32HandleBase *handle_data;
1026 if (!mono_w32handle_lookup_data (handle, &handle_data))
1027 g_error ("cannot wait on unknown handle %p", handle);
1029 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for %p (type %s)", __func__, handle,
1030 mono_w32handle_ops_typename (mono_w32handle_get_type (handle)));
1036 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1039 mono_w32handle_ref (handle);
1042 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
1045 mono_thread_info_uninstall_interrupt (alerted);
1047 /* if it is alerted, then the handle is unref in the interrupt callback */
1048 mono_w32handle_unref (handle);
1056 dump_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
1058 MonoW32HandleBase *handle_data;
1060 if (!mono_w32handle_lookup_data (handle, &handle_data))
1061 g_error ("cannot dump unknown handle %p", handle);
1063 g_print ("%p [%7s] signalled: %5s ref: %3d ",
1064 handle, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref);
1065 mono_w32handle_ops_details (handle_data->type, handle_data->specific);
1071 void mono_w32handle_dump (void)
1073 mono_w32handle_foreach (dump_callback, NULL);
1077 own_if_signalled (gpointer handle, guint32 *statuscode)
1079 if (!mono_w32handle_issignalled (handle))
1082 *statuscode = WAIT_OBJECT_0;
1083 mono_w32handle_ops_own (handle, statuscode);
1088 own_if_owned( gpointer handle, guint32 *statuscode)
1090 if (!mono_w32handle_ops_isowned (handle))
1093 *statuscode = WAIT_OBJECT_0;
1094 mono_w32handle_ops_own (handle, statuscode);
1098 MonoW32HandleWaitRet
1099 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
1101 MonoW32HandleWaitRet ret;
1104 guint32 statuscode = 0;
1108 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1109 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p has special wait",
1112 return mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL);
1115 if (!mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_WAIT)) {
1116 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1119 return MONO_W32HANDLE_WAIT_RET_FAILED;
1122 mono_w32handle_lock_handle (handle);
1124 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) {
1125 if (own_if_owned (handle, &statuscode)) {
1126 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1129 ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1134 if (timeout != INFINITE)
1135 start = mono_msec_ticks ();
1140 if (own_if_signalled (handle, &statuscode)) {
1141 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1144 ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1148 mono_w32handle_ops_prewait (handle);
1150 if (timeout == INFINITE) {
1151 waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1155 elapsed = mono_msec_ticks () - start;
1156 if (elapsed > timeout) {
1157 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1161 waited = mono_w32handle_timedwait_signal_handle (handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1165 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1170 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1176 mono_w32handle_unlock_handle (handle);
1181 MonoW32HandleWaitRet
1182 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
1184 MonoW32HandleWaitRet ret;
1185 gboolean alerted, poll;
1188 gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1189 guint32 statuscodes [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
1192 return MONO_W32HANDLE_WAIT_RET_FAILED;
1195 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1199 if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
1200 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: too many handles: %zd",
1201 __func__, nhandles);
1203 return MONO_W32HANDLE_WAIT_RET_FAILED;
1206 for (i = 0; i < nhandles; ++i) {
1207 if (!mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT)
1208 && !mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
1210 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1211 __func__, handles [i]);
1213 return MONO_W32HANDLE_WAIT_RET_FAILED;
1216 handles_sorted [i] = handles [i];
1219 qsort (handles_sorted, nhandles, sizeof (gpointer), g_direct_equal);
1220 for (i = 1; i < nhandles; ++i) {
1221 if (handles_sorted [i - 1] == handles_sorted [i]) {
1222 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p is duplicated",
1223 __func__, handles_sorted [i]);
1225 return MONO_W32HANDLE_WAIT_RET_FAILED;
1230 for (i = 0; i < nhandles; ++i) {
1231 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS) {
1232 /* Can't wait for a process handle + another handle without polling */
1237 if (timeout != INFINITE)
1238 start = mono_msec_ticks ();
1240 for (i = 0; i < nhandles; ++i) {
1241 /* Add a reference, as we need to ensure the handle wont
1242 * disappear from under us while we're waiting in the loop
1243 * (not lock, as we don't want exclusive access here) */
1244 mono_w32handle_ref (handles [i]);
1248 gsize count, lowest;
1255 mono_w32handle_lock_handles (handles, nhandles);
1257 for (i = 0; i < nhandles; i++) {
1258 if ((mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles [i]))
1259 || mono_w32handle_issignalled (handles [i]))
1268 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1271 for (i = 0; i < nhandles; i++)
1272 own_if_signalled (handles [i], &statuscodes [i]);
1275 mono_w32handle_unlock_handles (handles, nhandles);
1278 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
1279 for (i = lowest; i < nhandles; i++) {
1280 if (statuscodes [i] == WAIT_ABANDONED_0) {
1281 ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest;
1288 for (i = 0; i < nhandles; i++) {
1289 mono_w32handle_ops_prewait (handles[i]);
1291 if (mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1292 && !mono_w32handle_issignalled (handles [i]))
1294 mono_w32handle_ops_specialwait (handles [i], 0, alertable ? &alerted : NULL);
1298 mono_w32handle_lock_signal_mutex ();
1302 for (i = 0; i < nhandles; ++i) {
1303 if (!mono_w32handle_issignalled (handles [i])) {
1310 for (i = 0; i < nhandles; ++i) {
1311 if (mono_w32handle_issignalled (handles [i])) {
1321 if (timeout == INFINITE) {
1322 waited = mono_w32handle_timedwait_signal (INFINITE, poll, alertable ? &alerted : NULL);
1326 elapsed = mono_msec_ticks () - start;
1327 if (elapsed > timeout) {
1328 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1330 mono_w32handle_unlock_signal_mutex ();
1335 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1339 mono_w32handle_unlock_signal_mutex ();
1342 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1347 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1353 for (i = 0; i < nhandles; i++) {
1354 /* Unref everything we reffed above */
1355 mono_w32handle_unref (handles [i]);
1361 MonoW32HandleWaitRet
1362 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1364 MonoW32HandleWaitRet ret;
1367 guint32 statuscode = 0;
1368 gpointer handles [2];
1372 if (!mono_w32handle_test_capabilities (signal_handle, MONO_W32HANDLE_CAP_SIGNAL))
1373 return MONO_W32HANDLE_WAIT_RET_FAILED;
1374 if (!mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_WAIT))
1375 return MONO_W32HANDLE_WAIT_RET_FAILED;
1377 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1378 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle);
1379 return MONO_W32HANDLE_WAIT_RET_FAILED;
1382 handles [0] = wait_handle;
1383 handles [1] = signal_handle;
1385 mono_w32handle_lock_handles (handles, 2);
1387 mono_w32handle_ops_signal (signal_handle);
1389 mono_w32handle_unlock_handle (signal_handle);
1391 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) {
1392 if (own_if_owned (wait_handle, &statuscode)) {
1393 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1394 __func__, wait_handle);
1396 ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1401 if (timeout != INFINITE)
1402 start = mono_msec_ticks ();
1407 if (own_if_signalled (wait_handle, &statuscode)) {
1408 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1409 __func__, wait_handle);
1411 ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1415 mono_w32handle_ops_prewait (wait_handle);
1417 if (timeout == INFINITE) {
1418 waited = mono_w32handle_timedwait_signal_handle (wait_handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1422 elapsed = mono_msec_ticks () - start;
1423 if (elapsed > timeout) {
1424 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1428 waited = mono_w32handle_timedwait_signal_handle (wait_handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1432 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1437 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1443 mono_w32handle_unlock_handle (wait_handle);
1448 #endif /* !defined(HOST_WIN32) */