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"
46 #include "mono-logger-internals.h"
47 #include "mono-os-mutex.h"
48 #include "mono-proclib.h"
49 #include "mono-threads.h"
50 #include "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);
213 mono_w32handle_unlock_signal_mutex (void)
216 g_message ("%s: unlock global signal mutex", __func__);
219 mono_os_mutex_unlock (&global_signal_mutex);
225 mono_w32handle_lock_handle (gpointer handle)
227 MonoW32HandleBase *handle_data;
230 g_message ("%s: locking handle %p", __func__, handle);
233 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
237 mono_w32handle_ref (handle);
239 mono_os_mutex_lock (&handle_data->signal_mutex);
245 mono_w32handle_trylock_handle (gpointer handle)
247 MonoW32HandleBase *handle_data;
251 g_message ("%s: locking handle %p", __func__, handle);
254 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
258 mono_w32handle_ref (handle);
260 ret = mono_os_mutex_trylock (&handle_data->signal_mutex);
262 mono_w32handle_unref (handle);
269 mono_w32handle_unlock_handle (gpointer handle)
271 MonoW32HandleBase *handle_data;
274 g_message ("%s: unlocking handle %p", __func__, handle);
277 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
281 mono_os_mutex_unlock (&handle_data->signal_mutex);
283 mono_w32handle_unref (handle);
291 * Initialize the io-layer.
294 mono_w32handle_init (void)
296 static gboolean initialized = FALSE;
301 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
302 == MONO_W32HANDLE_COUNT);
304 /* This is needed by the code in mono_w32handle_new_internal */
305 mono_w32handle_fd_reserve = (eg_getdtablesize () + (HANDLE_PER_SLOT - 1)) & ~(HANDLE_PER_SLOT - 1);
309 * The entries in private_handles reserved for fds are allocated lazily to
313 private_handles_count += HANDLE_PER_SLOT;
314 private_handles_slots_count ++;
315 } while(mono_w32handle_fd_reserve > private_handles_count);
317 mono_os_mutex_init (&scan_mutex);
319 mono_os_cond_init (&global_signal_cond);
320 mono_os_mutex_init (&global_signal_mutex);
325 static void mono_w32handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles);
328 mono_w32handle_cleanup (void)
332 g_assert (!shutting_down);
333 shutting_down = TRUE;
335 /* Every shared handle we were using ought really to be closed
336 * by now, but to make sure just blow them all away. The
337 * exiting finalizer thread in particular races us to the
338 * program exit and doesn't always win, so it can be left
339 * cluttering up the shared file. Anything else left over is
342 for(i = SLOT_INDEX (0); private_handles[i] != NULL; i++) {
343 for(j = SLOT_OFFSET (0); j < HANDLE_PER_SLOT; j++) {
344 MonoW32HandleBase *handle_data = &private_handles[i][j];
345 gpointer handle = GINT_TO_POINTER (i*HANDLE_PER_SLOT+j);
347 for(k = handle_data->ref; k > 0; k--) {
348 mono_w32handle_unref_full (handle, TRUE);
353 for (i = 0; i < SLOT_MAX; ++i)
354 g_free (private_handles [i]);
357 static void mono_w32handle_init_handle (MonoW32HandleBase *handle,
358 MonoW32HandleType type, gpointer handle_specific)
361 handle->signalled = FALSE;
364 mono_os_cond_init (&handle->signal_cond);
365 mono_os_mutex_init (&handle->signal_mutex);
368 handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
372 * mono_w32handle_new_internal:
373 * @type: Init handle to this type
375 * Search for a free handle and initialize it. Return the handle on
376 * success and 0 on failure. This is only called from
377 * mono_w32handle_new, and scan_mutex must be held.
379 static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
380 gpointer handle_specific)
383 static guint32 last = 0;
384 gboolean retry = FALSE;
386 /* A linear scan should be fast enough. Start from the last
387 * allocation, assuming that handles are allocated more often
388 * than they're freed. Leave the space reserved for file
392 if (last < mono_w32handle_fd_reserve) {
393 last = mono_w32handle_fd_reserve;
400 for(i = SLOT_INDEX (count); i < private_handles_slots_count; i++) {
401 if (private_handles [i]) {
402 for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
403 MonoW32HandleBase *handle = &private_handles [i][k];
405 if(handle->type == MONO_W32HANDLE_UNUSED) {
408 mono_w32handle_init_handle (handle, type, handle_specific);
416 if(retry && last > mono_w32handle_fd_reserve) {
417 /* Try again from the beginning */
418 last = mono_w32handle_fd_reserve;
422 /* Will need to expand the array. The caller will sort it out */
428 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
430 guint32 handle_idx = 0;
433 g_assert (!shutting_down);
435 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Creating new handle of type %s", __func__,
436 mono_w32handle_ops_typename (type));
438 g_assert(!type_is_fd(type));
440 mono_os_mutex_lock (&scan_mutex);
442 while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == 0) {
443 /* Try and expand the array, and have another go */
444 int idx = SLOT_INDEX (private_handles_count);
445 if (idx >= SLOT_MAX) {
449 private_handles [idx] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
451 private_handles_count += HANDLE_PER_SLOT;
452 private_handles_slots_count ++;
455 mono_os_mutex_unlock (&scan_mutex);
457 if (handle_idx == 0) {
458 /* We ran out of slots */
459 handle = INVALID_HANDLE_VALUE;
463 /* Make sure we left the space for fd mappings */
464 g_assert (handle_idx >= mono_w32handle_fd_reserve);
466 handle = GUINT_TO_POINTER (handle_idx);
468 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Allocated new handle %p", __func__, handle);
474 gpointer mono_w32handle_new_fd (MonoW32HandleType type, int fd,
475 gpointer handle_specific)
477 MonoW32HandleBase *handle_data;
478 int fd_index, fd_offset;
480 g_assert (!shutting_down);
482 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Creating new handle of type %s", __func__,
483 mono_w32handle_ops_typename (type));
485 g_assert(type_is_fd(type));
487 if (fd >= mono_w32handle_fd_reserve) {
488 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: fd %d is too big", __func__, fd);
490 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
493 fd_index = SLOT_INDEX (fd);
494 fd_offset = SLOT_OFFSET (fd);
496 /* Initialize the array entries on demand */
497 if (!private_handles [fd_index]) {
498 mono_os_mutex_lock (&scan_mutex);
500 if (!private_handles [fd_index])
501 private_handles [fd_index] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
503 mono_os_mutex_unlock (&scan_mutex);
506 handle_data = &private_handles [fd_index][fd_offset];
508 if (handle_data->type != MONO_W32HANDLE_UNUSED) {
509 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: fd %d is already in use!", __func__, fd);
510 /* FIXME: clean up this handle? We can't do anything
511 * with the fd, cos thats the new one
515 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Assigning new fd handle %p", __func__, (gpointer)(gsize)fd);
517 mono_w32handle_init_handle (handle_data, type, handle_specific);
519 return(GUINT_TO_POINTER(fd));
523 mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
524 gpointer *handle_specific)
526 MonoW32HandleBase *handle_data;
528 g_assert (handle_specific);
530 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
534 if (handle_data->type != type) {
538 *handle_specific = handle_data->specific;
544 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
546 MonoW32HandleBase *handle_data = NULL;
550 mono_os_mutex_lock (&scan_mutex);
552 for (i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
553 if (private_handles [i]) {
554 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
555 handle_data = &private_handles [i][k];
556 if (handle_data->type == MONO_W32HANDLE_UNUSED)
558 handle = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
559 if (on_each (handle, handle_data->specific, user_data) == TRUE)
566 mono_os_mutex_unlock (&scan_mutex);
569 /* This might list some shared handles twice if they are already
570 * opened by this process, and the check function returns FALSE the
571 * first time. Shared handles that are created during the search are
572 * unreffed if the check function returns FALSE, so callers must not
573 * rely on the handle persisting (unless the check function returns
575 * The caller owns the returned handle.
577 gpointer mono_w32handle_search (MonoW32HandleType type,
578 gboolean (*check)(gpointer test, gpointer user),
580 gpointer *handle_specific,
581 gboolean search_shared)
583 MonoW32HandleBase *handle_data = NULL;
586 gboolean found = FALSE;
588 mono_os_mutex_lock (&scan_mutex);
590 for (i = SLOT_INDEX (0); !found && i < private_handles_slots_count; i++) {
591 if (private_handles [i]) {
592 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
593 handle_data = &private_handles [i][k];
595 if (handle_data->type == type) {
596 ret = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
597 if (check (ret, user_data) == TRUE) {
598 mono_w32handle_ref (ret);
607 mono_os_mutex_unlock (&scan_mutex);
614 if(handle_specific != NULL) {
615 *handle_specific = handle_data->specific;
622 void mono_w32handle_ref (gpointer handle)
624 MonoW32HandleBase *handle_data;
626 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
627 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Attempting to ref invalid private handle %p", __func__, handle);
631 InterlockedIncrement ((gint32 *)&handle_data->ref);
634 g_message ("%s: %s handle %p ref now %d",
635 __func__, mono_w32handle_ops_typename (handle_data->type), handle, handle_data->ref);
639 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer);
641 /* The handle must not be locked on entry to this function */
642 static void mono_w32handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles)
644 MonoW32HandleBase *handle_data;
645 gboolean destroy = FALSE, early_exit = FALSE;
648 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
649 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Attempting to unref invalid private handle %p",
654 /* Possible race condition here if another thread refs the
655 * handle between here and setting the type to UNUSED. I
656 * could lock a mutex, but I'm not sure that allowing a handle
657 * reference to reach 0 isn't an application bug anyway.
659 destroy = (InterlockedDecrement ((gint32 *)&handle_data->ref) ==0);
662 g_message ("%s: %s handle %p ref now %d (destroy %s)",
663 __func__, mono_w32handle_ops_typename (handle_data->type), handle, handle_data->ref, destroy?"TRUE":"FALSE");
667 /* Need to copy the handle info, reset the slot in the
668 * array, and _only then_ call the close function to
669 * avoid race conditions (eg file descriptors being
670 * closed, and another file being opened getting the
671 * same fd racing the memset())
673 MonoW32HandleType type;
674 gpointer handle_specific;
675 void (*close_func)(gpointer, gpointer);
677 type = handle_data->type;
678 handle_specific = handle_data->specific;
680 mono_os_mutex_lock (&scan_mutex);
682 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Destroying handle %p", __func__, handle);
684 /* Destroy the mutex and cond var. We hope nobody
685 * tried to grab them between the handle unlock and
686 * now, but pthreads doesn't have a
687 * "unlock_and_destroy" atomic function.
689 thr_ret = mono_os_mutex_destroy (&handle_data->signal_mutex);
690 /*WARNING gross hack to make cleanup not crash when exiting without the whole runtime teardown.*/
691 if (thr_ret == EBUSY && ignore_private_busy_handles) {
695 g_error ("Error destroying handle %p mutex due to %d\n", handle, thr_ret);
697 thr_ret = mono_os_cond_destroy (&handle_data->signal_cond);
698 if (thr_ret == EBUSY && ignore_private_busy_handles)
700 else if (thr_ret != 0)
701 g_error ("Error destroying handle %p cond var due to %d\n", handle, thr_ret);
704 memset (handle_data, 0, sizeof (MonoW32HandleBase));
706 mono_os_mutex_unlock (&scan_mutex);
711 close_func = _wapi_handle_ops_get_close_func (type);
712 if (close_func != NULL) {
713 close_func (handle, handle_specific);
716 g_free (handle_specific);
720 void mono_w32handle_unref (gpointer handle)
722 mono_w32handle_unref_full (handle, FALSE);
726 mono_w32handle_register_ops (MonoW32HandleType type, MonoW32HandleOps *ops)
728 handle_ops [type] = ops;
731 void mono_w32handle_register_capabilities (MonoW32HandleType type,
732 MonoW32HandleCapability caps)
734 handle_caps[type] = caps;
737 gboolean mono_w32handle_test_capabilities (gpointer handle,
738 MonoW32HandleCapability caps)
740 MonoW32HandleBase *handle_data;
741 MonoW32HandleType type;
743 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
747 type = handle_data->type;
749 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
750 handle_caps[type], caps, handle_caps[type] & caps);
752 return((handle_caps[type] & caps) != 0);
755 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer)
757 if (handle_ops[type] != NULL &&
758 handle_ops[type]->close != NULL) {
759 return (handle_ops[type]->close);
765 void mono_w32handle_ops_close (gpointer handle, gpointer data)
767 MonoW32HandleBase *handle_data;
768 MonoW32HandleType type;
770 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
774 type = handle_data->type;
776 if (handle_ops[type] != NULL &&
777 handle_ops[type]->close != NULL) {
778 handle_ops[type]->close (handle, data);
782 void mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
784 if (handle_ops[type] != NULL &&
785 handle_ops[type]->details != NULL) {
786 handle_ops[type]->details (data);
790 const gchar* mono_w32handle_ops_typename (MonoW32HandleType type)
792 g_assert (handle_ops [type]);
793 g_assert (handle_ops [type]->typename);
794 return handle_ops [type]->typename ();
797 gsize mono_w32handle_ops_typesize (MonoW32HandleType type)
799 g_assert (handle_ops [type]);
800 g_assert (handle_ops [type]->typesize);
801 return handle_ops [type]->typesize ();
804 void mono_w32handle_ops_signal (gpointer handle)
806 MonoW32HandleBase *handle_data;
807 MonoW32HandleType type;
809 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
813 type = handle_data->type;
815 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
816 handle_ops[type]->signal (handle);
820 gboolean mono_w32handle_ops_own (gpointer handle, guint32 *statuscode)
822 MonoW32HandleBase *handle_data;
823 MonoW32HandleType type;
825 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
829 type = handle_data->type;
831 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
832 return(handle_ops[type]->own_handle (handle, statuscode));
838 gboolean mono_w32handle_ops_isowned (gpointer handle)
840 MonoW32HandleBase *handle_data;
841 MonoW32HandleType type;
843 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
847 type = handle_data->type;
849 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
850 return(handle_ops[type]->is_owned (handle));
856 guint32 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted)
858 MonoW32HandleBase *handle_data;
859 MonoW32HandleType type;
861 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
865 type = handle_data->type;
867 if (handle_ops[type] != NULL &&
868 handle_ops[type]->special_wait != NULL) {
869 return(handle_ops[type]->special_wait (handle, timeout, alerted));
875 void mono_w32handle_ops_prewait (gpointer handle)
877 MonoW32HandleBase *handle_data;
878 MonoW32HandleType type;
880 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
884 type = handle_data->type;
886 if (handle_ops[type] != NULL &&
887 handle_ops[type]->prewait != NULL) {
888 handle_ops[type]->prewait (handle);
895 struct timespec sleepytime;
897 g_assert (ms < 1000);
899 sleepytime.tv_sec = 0;
900 sleepytime.tv_nsec = ms * 1000000;
901 nanosleep (&sleepytime, NULL);
905 mono_w32handle_lock_handles (gpointer *handles, gsize numhandles)
910 /* Lock all the handles, with backoff */
912 for(i=0; i<numhandles; i++) {
913 gpointer handle = handles[i];
915 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempting to lock %p", __func__, handle);
917 thr_ret = mono_w32handle_trylock_handle (handle);
922 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p: %s", __func__,
923 handle, strerror (thr_ret));
928 thr_ret = mono_w32handle_unlock_handle (handle);
929 g_assert (thr_ret == 0);
932 /* If iter ever reaches 100 the nanosleep will
933 * return EINVAL immediately, but we have a
934 * design flaw if that happens.
938 g_warning ("%s: iteration overflow!",
943 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
951 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
955 mono_w32handle_unlock_handles (gpointer *handles, gsize numhandles)
960 for(i=0; i<numhandles; i++) {
961 gpointer handle = handles[i];
963 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlocking handle %p", __func__, handle);
965 thr_ret = mono_w32handle_unlock_handle (handle);
966 g_assert (thr_ret == 0);
971 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
976 res = mono_os_cond_timedwait (cond, mutex, timeout);
978 /* This is needed when waiting for process handles */
981 * pthread_cond_(timed)wait() can return 0 even if the condition was not
982 * signalled. This happens at least on Darwin. We surface this, i.e., we
983 * get spurious wake-ups.
985 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
987 res = mono_os_cond_timedwait (cond, mutex, timeout);
990 /* Real timeout is less than 100ms time */
991 res = mono_os_cond_timedwait (cond, mutex, timeout);
993 res = mono_os_cond_timedwait (cond, mutex, 100);
995 /* Mask the fake timeout, this will cause
996 * another poll if the cond was not really signaled
1008 signal_global (gpointer unused)
1010 /* If we reach here, then interrupt token is set to the flag value, which
1011 * means that the target thread is either
1012 * - before the first CAS in timedwait, which means it won't enter the wait.
1013 * - it is after the first CAS, so it is already waiting, or it will enter
1014 * the wait, and it will be interrupted by the broadcast. */
1015 mono_os_mutex_lock (&global_signal_mutex);
1016 mono_os_cond_broadcast (&global_signal_cond);
1017 mono_os_mutex_unlock (&global_signal_mutex);
1021 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
1025 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
1031 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
1036 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
1039 mono_thread_info_uninstall_interrupt (alerted);
1045 signal_handle_and_unref (gpointer handle)
1047 MonoW32HandleBase *handle_data;
1049 mono_mutex_t *mutex;
1051 if (!mono_w32handle_lookup_data (handle, &handle_data))
1052 g_error ("cannot signal unknown handle %p", handle);
1054 /* If we reach here, then interrupt token is set to the flag value, which
1055 * means that the target thread is either
1056 * - before the first CAS in timedwait, which means it won't enter the wait.
1057 * - it is after the first CAS, so it is already waiting, or it will enter
1058 * the wait, and it will be interrupted by the broadcast. */
1059 cond = &handle_data->signal_cond;
1060 mutex = &handle_data->signal_mutex;
1062 mono_os_mutex_lock (mutex);
1063 mono_os_cond_broadcast (cond);
1064 mono_os_mutex_unlock (mutex);
1066 mono_w32handle_unref (handle);
1070 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1072 MonoW32HandleBase *handle_data;
1075 if (!mono_w32handle_lookup_data (handle, &handle_data))
1076 g_error ("cannot wait on unknown handle %p", handle);
1078 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for %p (type %s)", __func__, handle,
1079 mono_w32handle_ops_typename (mono_w32handle_get_type (handle)));
1085 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1088 mono_w32handle_ref (handle);
1091 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
1094 mono_thread_info_uninstall_interrupt (alerted);
1096 /* if it is alerted, then the handle is unref in the interrupt callback */
1097 mono_w32handle_unref (handle);
1104 void mono_w32handle_dump (void)
1106 MonoW32HandleBase *handle_data;
1109 mono_os_mutex_lock (&scan_mutex);
1111 for(i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
1112 if (private_handles [i]) {
1113 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
1114 handle_data = &private_handles [i][k];
1116 if (handle_data->type == MONO_W32HANDLE_UNUSED) {
1120 g_print ("%3x [%7s] %s %d ",
1121 i * HANDLE_PER_SLOT + k,
1122 mono_w32handle_ops_typename (handle_data->type),
1123 handle_data->signalled?"Sg":"Un",
1125 mono_w32handle_ops_details (handle_data->type, handle_data->specific);
1131 mono_os_mutex_unlock (&scan_mutex);
1135 own_if_signalled (gpointer handle, guint32 *statuscode)
1137 if (!mono_w32handle_issignalled (handle))
1140 *statuscode = WAIT_OBJECT_0;
1141 mono_w32handle_ops_own (handle, statuscode);
1146 own_if_owned( gpointer handle, guint32 *statuscode)
1148 if (!mono_w32handle_ops_isowned (handle))
1151 *statuscode = WAIT_OBJECT_0;
1152 mono_w32handle_ops_own (handle, statuscode);
1156 MonoW32HandleWaitRet
1157 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
1159 MonoW32HandleWaitRet ret;
1163 guint32 statuscode = 0;
1167 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1168 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p has special wait",
1171 switch (mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL)) {
1173 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1175 case WAIT_ABANDONED_0:
1176 ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0;
1178 case WAIT_IO_COMPLETION:
1179 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1182 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1185 ret = MONO_W32HANDLE_WAIT_RET_FAILED;
1188 g_assert_not_reached ();
1192 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1197 if (!mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_WAIT)) {
1198 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1201 return MONO_W32HANDLE_WAIT_RET_FAILED;
1204 thr_ret = mono_w32handle_lock_handle (handle);
1205 g_assert (thr_ret == 0);
1207 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) {
1208 if (own_if_owned (handle, &statuscode)) {
1209 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1212 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1217 if (timeout != INFINITE)
1218 start = mono_msec_ticks ();
1223 if (own_if_signalled (handle, &statuscode)) {
1224 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1227 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1231 mono_w32handle_ops_prewait (handle);
1233 if (timeout == INFINITE) {
1234 waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1238 elapsed = mono_msec_ticks () - start;
1239 if (elapsed > timeout) {
1240 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1244 waited = mono_w32handle_timedwait_signal_handle (handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1248 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1253 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1259 thr_ret = mono_w32handle_unlock_handle (handle);
1260 g_assert (thr_ret == 0);
1265 MonoW32HandleWaitRet
1266 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
1268 MonoW32HandleWaitRet ret;
1269 gboolean alerted, poll;
1272 gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1273 guint32 statuscode = 0;
1276 return MONO_W32HANDLE_WAIT_RET_FAILED;
1279 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1283 if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
1284 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: too many handles: %d",
1285 __func__, nhandles);
1287 return MONO_W32HANDLE_WAIT_RET_FAILED;
1290 for (i = 0; i < nhandles; ++i) {
1291 if (!mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT)
1292 && !mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
1294 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1295 __func__, handles [i]);
1297 return MONO_W32HANDLE_WAIT_RET_FAILED;
1300 handles_sorted [i] = handles [i];
1303 qsort (handles_sorted, nhandles, sizeof (gpointer), g_direct_equal);
1304 for (i = 1; i < nhandles; ++i) {
1305 if (handles_sorted [i - 1] == handles_sorted [i]) {
1306 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p is duplicated",
1307 __func__, handles_sorted [i]);
1309 return MONO_W32HANDLE_WAIT_RET_FAILED;
1314 for (i = 0; i < nhandles; ++i) {
1315 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS) {
1316 /* Can't wait for a process handle + another handle without polling */
1321 if (timeout != INFINITE)
1322 start = mono_msec_ticks ();
1324 for (i = 0; i < nhandles; ++i) {
1325 /* Add a reference, as we need to ensure the handle wont
1326 * disappear from under us while we're waiting in the loop
1327 * (not lock, as we don't want exclusive access here) */
1328 mono_w32handle_ref (handles [i]);
1332 gsize count, lowest;
1339 mono_w32handle_lock_handles (handles, nhandles);
1341 for (i = 0; i < nhandles; i++) {
1342 if ((mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles [i]))
1343 || mono_w32handle_issignalled (handles [i]))
1352 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1355 for (i = 0; i < nhandles; i++)
1356 own_if_signalled (handles [i], &statuscode);
1359 mono_w32handle_unlock_handles (handles, nhandles);
1362 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
1366 for (i = 0; i < nhandles; i++) {
1367 mono_w32handle_ops_prewait (handles[i]);
1369 if (mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1370 && !mono_w32handle_issignalled (handles [i]))
1372 mono_w32handle_ops_specialwait (handles [i], 0, alertable ? &alerted : NULL);
1376 thr_ret = mono_w32handle_lock_signal_mutex ();
1377 g_assert (thr_ret == 0);
1381 for (i = 0; i < nhandles; ++i) {
1382 if (!mono_w32handle_issignalled (handles [i])) {
1389 for (i = 0; i < nhandles; ++i) {
1390 if (mono_w32handle_issignalled (handles [i])) {
1400 if (timeout == INFINITE) {
1401 waited = mono_w32handle_timedwait_signal (INFINITE, poll, alertable ? &alerted : NULL);
1405 elapsed = mono_msec_ticks () - start;
1406 if (elapsed > timeout) {
1407 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1409 thr_ret = mono_w32handle_unlock_signal_mutex ();
1410 g_assert (thr_ret == 0);
1415 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1419 thr_ret = mono_w32handle_unlock_signal_mutex ();
1420 g_assert (thr_ret == 0);
1423 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1428 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1434 for (i = 0; i < nhandles; i++) {
1435 /* Unref everything we reffed above */
1436 mono_w32handle_unref (handles [i]);
1442 MonoW32HandleWaitRet
1443 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1445 MonoW32HandleWaitRet ret;
1449 guint32 statuscode = 0;
1453 if (!mono_w32handle_test_capabilities (signal_handle, MONO_W32HANDLE_CAP_SIGNAL))
1454 return MONO_W32HANDLE_WAIT_RET_FAILED;
1455 if (!mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_WAIT))
1456 return MONO_W32HANDLE_WAIT_RET_FAILED;
1458 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1459 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle);
1460 return MONO_W32HANDLE_WAIT_RET_FAILED;
1463 thr_ret = mono_w32handle_lock_handle (wait_handle);
1464 g_assert (thr_ret == 0);
1466 mono_w32handle_ops_signal (signal_handle);
1468 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) {
1469 if (own_if_owned (wait_handle, &statuscode)) {
1470 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1471 __func__, wait_handle);
1473 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1478 if (timeout != INFINITE)
1479 start = mono_msec_ticks ();
1484 if (own_if_signalled (wait_handle, &statuscode)) {
1485 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1486 __func__, wait_handle);
1488 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1492 mono_w32handle_ops_prewait (wait_handle);
1494 if (timeout == INFINITE) {
1495 waited = mono_w32handle_timedwait_signal_handle (wait_handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1499 elapsed = mono_msec_ticks () - start;
1500 if (elapsed > timeout) {
1501 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1505 waited = mono_w32handle_timedwait_signal_handle (wait_handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1509 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1514 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1520 thr_ret = mono_w32handle_unlock_handle (wait_handle);
1521 g_assert (thr_ret == 0);
1526 #endif /* !defined(HOST_WIN32) */