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.
16 #include "w32handle.h"
18 #include "utils/atomic.h"
19 #include "utils/mono-logger-internals.h"
20 #include "utils/mono-os-mutex.h"
21 #include "utils/mono-proclib.h"
22 #include "utils/mono-threads.h"
23 #include "utils/mono-time.h"
27 #define SLOT_MAX (1024 * 16)
29 /* must be a power of 2 */
30 #define HANDLE_PER_SLOT (256)
32 #define INFINITE 0xFFFFFFFF
35 MonoW32HandleType type;
38 mono_mutex_t signal_mutex;
39 mono_cond_t signal_cond;
43 static MonoW32HandleCapability handle_caps [MONO_W32HANDLE_COUNT];
44 static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
47 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
48 * If 4M handles are not enough... Oh, well... we will crash.
50 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
51 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
53 static MonoW32HandleBase *private_handles [SLOT_MAX];
54 static guint32 private_handles_count = 0;
55 static guint32 private_handles_slots_count = 0;
57 guint32 mono_w32handle_fd_reserve;
60 * This is an internal handle which is used for handling waiting for multiple handles.
61 * Threads which wait for multiple handles wait on this one handle, and when a handle
62 * is signalled, this handle is signalled too.
64 static mono_mutex_t global_signal_mutex;
65 static mono_cond_t global_signal_cond;
67 static mono_mutex_t scan_mutex;
69 static gboolean shutting_down = FALSE;
72 type_is_fd (MonoW32HandleType type)
75 case MONO_W32HANDLE_FILE:
76 case MONO_W32HANDLE_CONSOLE:
77 case MONO_W32HANDLE_SOCKET:
78 case MONO_W32HANDLE_PIPE:
86 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
90 g_assert (handle_data);
92 index = SLOT_INDEX ((gsize) handle);
93 if (index >= SLOT_MAX)
95 if (!private_handles [index])
98 offset = SLOT_OFFSET ((gsize) handle);
99 if (private_handles [index][offset].type == MONO_W32HANDLE_UNUSED)
102 *handle_data = &private_handles [index][offset];
107 mono_w32handle_get_type (gpointer handle)
109 MonoW32HandleBase *handle_data;
111 if (!mono_w32handle_lookup_data (handle, &handle_data))
112 return MONO_W32HANDLE_UNUSED; /* An impossible type */
114 return handle_data->type;
118 mono_w32handle_ops_typename (MonoW32HandleType type);
121 mono_w32handle_get_typename (MonoW32HandleType type)
123 return mono_w32handle_ops_typename (type);
127 mono_w32handle_set_signal_state (gpointer handle, gboolean state, gboolean broadcast)
129 MonoW32HandleBase *handle_data;
131 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
136 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
137 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
141 /* Tell everyone blocking on a single handle */
143 /* The condition the global signal cond is waiting on is the signalling of
144 * _any_ handle. So lock it before setting the signalled state.
146 mono_os_mutex_lock (&global_signal_mutex);
148 /* This function _must_ be called with
149 * handle->signal_mutex locked
151 handle_data->signalled=state;
153 if (broadcast == TRUE) {
154 mono_os_cond_broadcast (&handle_data->signal_cond);
156 mono_os_cond_signal (&handle_data->signal_cond);
159 /* Tell everyone blocking on multiple handles that something
162 mono_os_cond_broadcast (&global_signal_cond);
164 mono_os_mutex_unlock (&global_signal_mutex);
166 handle_data->signalled=state;
171 mono_w32handle_issignalled (gpointer handle)
173 MonoW32HandleBase *handle_data;
175 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
179 return handle_data->signalled;
183 mono_w32handle_lock_signal_mutex (void)
186 g_message ("%s: lock global signal mutex", __func__);
189 mono_os_mutex_lock (&global_signal_mutex);
193 mono_w32handle_unlock_signal_mutex (void)
196 g_message ("%s: unlock global signal mutex", __func__);
199 mono_os_mutex_unlock (&global_signal_mutex);
203 mono_w32handle_lock_handle (gpointer handle)
205 MonoW32HandleBase *handle_data;
207 if (!mono_w32handle_lookup_data (handle, &handle_data))
208 g_error ("%s: failed to lookup handle %p", __func__, handle);
210 mono_w32handle_ref (handle);
212 mono_os_mutex_lock (&handle_data->signal_mutex);
214 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: lock handle %p", __func__, handle);
218 mono_w32handle_trylock_handle (gpointer handle)
220 MonoW32HandleBase *handle_data;
223 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p", __func__, handle);
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 locked = mono_os_mutex_trylock (&handle_data->signal_mutex) == 0;
232 mono_w32handle_unref (handle);
234 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p, locked: %s", __func__, handle, locked ? "true" : "false");
240 mono_w32handle_unlock_handle (gpointer handle)
242 MonoW32HandleBase *handle_data;
244 if (!mono_w32handle_lookup_data (handle, &handle_data))
245 g_error ("%s: failed to lookup handle %p", __func__, handle);
247 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlock handle %p", __func__, handle);
249 mono_os_mutex_unlock (&handle_data->signal_mutex);
251 mono_w32handle_unref (handle);
257 * Initialize the io-layer.
260 mono_w32handle_init (void)
262 static gboolean initialized = FALSE;
267 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
268 == MONO_W32HANDLE_COUNT);
270 /* This is needed by the code in mono_w32handle_new_internal */
271 mono_w32handle_fd_reserve = (eg_getdtablesize () + (HANDLE_PER_SLOT - 1)) & ~(HANDLE_PER_SLOT - 1);
275 * The entries in private_handles reserved for fds are allocated lazily to
279 private_handles_count += HANDLE_PER_SLOT;
280 private_handles_slots_count ++;
281 } while(mono_w32handle_fd_reserve > private_handles_count);
283 mono_os_mutex_init (&scan_mutex);
285 mono_os_cond_init (&global_signal_cond);
286 mono_os_mutex_init (&global_signal_mutex);
292 mono_w32handle_cleanup (void)
296 g_assert (!shutting_down);
297 shutting_down = TRUE;
299 /* Every shared handle we were using ought really to be closed
300 * by now, but to make sure just blow them all away. The
301 * exiting finalizer thread in particular races us to the
302 * program exit and doesn't always win, so it can be left
303 * cluttering up the shared file. Anything else left over is
306 for(i = SLOT_INDEX (0); private_handles[i] != NULL; i++) {
307 for(j = SLOT_OFFSET (0); j < HANDLE_PER_SLOT; j++) {
308 MonoW32HandleBase *handle_data = &private_handles[i][j];
309 gpointer handle = GINT_TO_POINTER (i*HANDLE_PER_SLOT+j);
311 for(k = handle_data->ref; k > 0; k--) {
312 mono_w32handle_unref (handle);
317 for (i = 0; i < SLOT_MAX; ++i)
318 g_free (private_handles [i]);
322 mono_w32handle_ops_typesize (MonoW32HandleType type);
324 static void mono_w32handle_init_handle (MonoW32HandleBase *handle,
325 MonoW32HandleType type, gpointer handle_specific)
327 g_assert (handle->ref == 0);
330 handle->signalled = FALSE;
333 mono_os_cond_init (&handle->signal_cond);
334 mono_os_mutex_init (&handle->signal_mutex);
337 handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
341 * mono_w32handle_new_internal:
342 * @type: Init handle to this type
344 * Search for a free handle and initialize it. Return the handle on
345 * success and 0 on failure. This is only called from
346 * mono_w32handle_new, and scan_mutex must be held.
348 static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
349 gpointer handle_specific)
352 static guint32 last = 0;
353 gboolean retry = FALSE;
355 /* A linear scan should be fast enough. Start from the last
356 * allocation, assuming that handles are allocated more often
357 * than they're freed. Leave the space reserved for file
361 if (last < mono_w32handle_fd_reserve) {
362 last = mono_w32handle_fd_reserve;
369 for(i = SLOT_INDEX (count); i < private_handles_slots_count; i++) {
370 if (private_handles [i]) {
371 for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
372 MonoW32HandleBase *handle = &private_handles [i][k];
374 if(handle->type == MONO_W32HANDLE_UNUSED) {
377 mono_w32handle_init_handle (handle, type, handle_specific);
385 if(retry && last > mono_w32handle_fd_reserve) {
386 /* Try again from the beginning */
387 last = mono_w32handle_fd_reserve;
391 /* Will need to expand the array. The caller will sort it out */
397 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
399 guint32 handle_idx = 0;
402 g_assert (!shutting_down);
404 g_assert(!type_is_fd(type));
406 mono_os_mutex_lock (&scan_mutex);
408 while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == 0) {
409 /* Try and expand the array, and have another go */
410 int idx = SLOT_INDEX (private_handles_count);
411 if (idx >= SLOT_MAX) {
415 private_handles [idx] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
417 private_handles_count += HANDLE_PER_SLOT;
418 private_handles_slots_count ++;
421 mono_os_mutex_unlock (&scan_mutex);
423 if (handle_idx == 0) {
424 /* We ran out of slots */
425 handle = INVALID_HANDLE_VALUE;
426 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type));
430 /* Make sure we left the space for fd mappings */
431 g_assert (handle_idx >= mono_w32handle_fd_reserve);
433 handle = GUINT_TO_POINTER (handle_idx);
435 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
441 gpointer mono_w32handle_new_fd (MonoW32HandleType type, int fd,
442 gpointer handle_specific)
444 MonoW32HandleBase *handle_data;
445 int fd_index, fd_offset;
447 g_assert (!shutting_down);
449 g_assert(type_is_fd(type));
451 if (fd >= mono_w32handle_fd_reserve) {
452 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));
454 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
457 fd_index = SLOT_INDEX (fd);
458 fd_offset = SLOT_OFFSET (fd);
460 /* Initialize the array entries on demand */
461 if (!private_handles [fd_index]) {
462 mono_os_mutex_lock (&scan_mutex);
464 if (!private_handles [fd_index])
465 private_handles [fd_index] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
467 mono_os_mutex_unlock (&scan_mutex);
470 handle_data = &private_handles [fd_index][fd_offset];
472 if (handle_data->type != MONO_W32HANDLE_UNUSED) {
473 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));
474 /* FIXME: clean up this handle? We can't do anything
475 * with the fd, cos thats the new one
477 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
480 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), GUINT_TO_POINTER(fd));
482 mono_w32handle_init_handle (handle_data, type, handle_specific);
484 return(GUINT_TO_POINTER(fd));
488 mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
489 gpointer *handle_specific)
491 MonoW32HandleBase *handle_data;
493 g_assert (handle_specific);
495 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
499 if (handle_data->type != type) {
503 *handle_specific = handle_data->specific;
509 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data);
512 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guint minimum);
515 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
519 mono_os_mutex_lock (&scan_mutex);
521 for (i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
522 if (!private_handles [i])
524 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
525 MonoW32HandleBase *handle_data = NULL;
527 gboolean destroy, finished;
529 handle_data = &private_handles [i][k];
530 if (handle_data->type == MONO_W32HANDLE_UNUSED)
533 handle = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
535 if (!mono_w32handle_ref_core (handle, handle_data)) {
536 /* we are racing with mono_w32handle_unref:
537 * the handle ref has been decremented, but it
538 * hasn't yet been destroyed. */
542 finished = on_each (handle, handle_data->specific, user_data);
544 /* we do not want to have to destroy the handle here,
545 * as it would means the ref/unref are unbalanced */
546 destroy = mono_w32handle_unref_core (handle, handle_data, 2);
555 mono_os_mutex_unlock (&scan_mutex);
559 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data)
564 old = handle_data->ref;
569 } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
571 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: ref %s handle %p, ref: %d -> %d",
572 __func__, mono_w32handle_ops_typename (handle_data->type), handle, old, new);
578 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guint minimum)
580 MonoW32HandleType type;
583 type = handle_data->type;
586 old = handle_data->ref;
587 if (!(old >= minimum))
588 g_error ("%s: handle %p has ref %d, it should be >= %d", __func__, handle, old, minimum);
591 } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
593 /* handle_data might contain invalid data from now on, if
594 * another thread is unref'ing this handle at the same time */
596 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unref %s handle %p, ref: %d -> %d destroy: %s",
597 __func__, mono_w32handle_ops_typename (type), handle, old, new, new == 0 ? "true" : "false");
602 void mono_w32handle_ref (gpointer handle)
604 MonoW32HandleBase *handle_data;
606 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
607 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to ref handle %p, unknown handle", __func__, handle);
611 if (!mono_w32handle_ref_core (handle, handle_data))
612 g_error ("%s: failed to ref handle %p", __func__, handle);
615 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer);
617 /* The handle must not be locked on entry to this function */
619 mono_w32handle_unref (gpointer handle)
621 MonoW32HandleBase *handle_data;
624 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
625 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to unref handle %p, unknown handle",
630 destroy = mono_w32handle_unref_core (handle, handle_data, 1);
633 /* Need to copy the handle info, reset the slot in the
634 * array, and _only then_ call the close function to
635 * avoid race conditions (eg file descriptors being
636 * closed, and another file being opened getting the
637 * same fd racing the memset())
639 MonoW32HandleType type;
640 gpointer handle_specific;
641 void (*close_func)(gpointer, gpointer);
643 type = handle_data->type;
644 handle_specific = handle_data->specific;
646 mono_os_mutex_lock (&scan_mutex);
648 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
650 mono_os_mutex_destroy (&handle_data->signal_mutex);
651 mono_os_cond_destroy (&handle_data->signal_cond);
653 memset (handle_data, 0, sizeof (MonoW32HandleBase));
655 mono_os_mutex_unlock (&scan_mutex);
657 close_func = _wapi_handle_ops_get_close_func (type);
658 if (close_func != NULL) {
659 close_func (handle, handle_specific);
662 g_free (handle_specific);
667 mono_w32handle_ops_close (gpointer handle, gpointer data);
670 mono_w32handle_force_close (gpointer handle, gpointer data)
672 mono_w32handle_ops_close (handle, data);
676 mono_w32handle_register_ops (MonoW32HandleType type, MonoW32HandleOps *ops)
678 handle_ops [type] = ops;
681 void mono_w32handle_register_capabilities (MonoW32HandleType type,
682 MonoW32HandleCapability caps)
684 handle_caps[type] = caps;
687 gboolean mono_w32handle_test_capabilities (gpointer handle,
688 MonoW32HandleCapability caps)
690 MonoW32HandleBase *handle_data;
691 MonoW32HandleType type;
693 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
697 type = handle_data->type;
699 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
700 handle_caps[type], caps, handle_caps[type] & caps);
702 return((handle_caps[type] & caps) != 0);
705 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer)
707 if (handle_ops[type] != NULL &&
708 handle_ops[type]->close != NULL) {
709 return (handle_ops[type]->close);
716 mono_w32handle_ops_close (gpointer handle, gpointer data)
718 MonoW32HandleBase *handle_data;
719 MonoW32HandleType type;
721 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
725 type = handle_data->type;
727 if (handle_ops[type] != NULL &&
728 handle_ops[type]->close != NULL) {
729 handle_ops[type]->close (handle, data);
734 mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
736 if (handle_ops[type] != NULL &&
737 handle_ops[type]->details != NULL) {
738 handle_ops[type]->details (data);
743 mono_w32handle_ops_typename (MonoW32HandleType type)
745 g_assert (handle_ops [type]);
746 g_assert (handle_ops [type]->typename);
747 return handle_ops [type]->typename ();
751 mono_w32handle_ops_typesize (MonoW32HandleType type)
753 g_assert (handle_ops [type]);
754 g_assert (handle_ops [type]->typesize);
755 return handle_ops [type]->typesize ();
759 mono_w32handle_ops_signal (gpointer handle)
761 MonoW32HandleBase *handle_data;
762 MonoW32HandleType type;
764 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
768 type = handle_data->type;
770 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
771 handle_ops[type]->signal (handle);
776 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));
795 mono_w32handle_ops_isowned (gpointer handle)
797 MonoW32HandleBase *handle_data;
798 MonoW32HandleType type;
800 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
804 type = handle_data->type;
806 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
807 return(handle_ops[type]->is_owned (handle));
813 static MonoW32HandleWaitRet
814 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted)
816 MonoW32HandleBase *handle_data;
817 MonoW32HandleType type;
819 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
823 type = handle_data->type;
825 if (handle_ops[type] != NULL &&
826 handle_ops[type]->special_wait != NULL) {
827 return(handle_ops[type]->special_wait (handle, timeout, alerted));
834 mono_w32handle_ops_prewait (gpointer handle)
836 MonoW32HandleBase *handle_data;
837 MonoW32HandleType type;
839 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
843 type = handle_data->type;
845 if (handle_ops[type] != NULL &&
846 handle_ops[type]->prewait != NULL) {
847 handle_ops[type]->prewait (handle);
857 struct timespec sleepytime;
859 g_assert (ms < 1000);
861 sleepytime.tv_sec = 0;
862 sleepytime.tv_nsec = ms * 1000000;
863 nanosleep (&sleepytime, NULL);
864 #endif /* HOST_WIN32 */
868 mono_w32handle_lock_handles (gpointer *handles, gsize numhandles)
872 /* Lock all the handles, with backoff */
874 for(i=0; i<numhandles; i++) {
875 gpointer handle = handles[i];
877 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempting to lock %p", __func__, handle);
879 if (!mono_w32handle_trylock_handle (handle)) {
882 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p: %s", __func__,
888 mono_w32handle_unlock_handle (handle);
891 /* If iter ever reaches 100 the nanosleep will
892 * return EINVAL immediately, but we have a
893 * design flaw if that happens.
897 g_warning ("%s: iteration overflow!",
902 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
910 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
914 mono_w32handle_unlock_handles (gpointer *handles, gsize numhandles)
918 for(i=0; i<numhandles; i++) {
919 gpointer handle = handles[i];
921 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlocking handle %p", __func__, handle);
923 mono_w32handle_unlock_handle (handle);
928 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
933 res = mono_os_cond_timedwait (cond, mutex, timeout);
935 /* This is needed when waiting for process handles */
938 * pthread_cond_(timed)wait() can return 0 even if the condition was not
939 * signalled. This happens at least on Darwin. We surface this, i.e., we
940 * get spurious wake-ups.
942 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
944 res = mono_os_cond_timedwait (cond, mutex, timeout);
947 /* Real timeout is less than 100ms time */
948 res = mono_os_cond_timedwait (cond, mutex, timeout);
950 res = mono_os_cond_timedwait (cond, mutex, 100);
952 /* Mask the fake timeout, this will cause
953 * another poll if the cond was not really signaled
965 signal_global (gpointer unused)
967 /* If we reach here, then interrupt token is set to the flag value, which
968 * means that the target thread is either
969 * - before the first CAS in timedwait, which means it won't enter the wait.
970 * - it is after the first CAS, so it is already waiting, or it will enter
971 * the wait, and it will be interrupted by the broadcast. */
972 mono_os_mutex_lock (&global_signal_mutex);
973 mono_os_cond_broadcast (&global_signal_cond);
974 mono_os_mutex_unlock (&global_signal_mutex);
978 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
982 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
988 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
993 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
996 mono_thread_info_uninstall_interrupt (alerted);
1002 signal_handle_and_unref (gpointer handle)
1004 MonoW32HandleBase *handle_data;
1006 mono_mutex_t *mutex;
1008 if (!mono_w32handle_lookup_data (handle, &handle_data))
1009 g_error ("cannot signal unknown handle %p", handle);
1011 /* If we reach here, then interrupt token is set to the flag value, which
1012 * means that the target thread is either
1013 * - before the first CAS in timedwait, which means it won't enter the wait.
1014 * - it is after the first CAS, so it is already waiting, or it will enter
1015 * the wait, and it will be interrupted by the broadcast. */
1016 cond = &handle_data->signal_cond;
1017 mutex = &handle_data->signal_mutex;
1019 mono_os_mutex_lock (mutex);
1020 mono_os_cond_broadcast (cond);
1021 mono_os_mutex_unlock (mutex);
1023 mono_w32handle_unref (handle);
1027 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1029 MonoW32HandleBase *handle_data;
1032 if (!mono_w32handle_lookup_data (handle, &handle_data))
1033 g_error ("cannot wait on unknown handle %p", handle);
1035 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for %p (type %s)", __func__, handle,
1036 mono_w32handle_ops_typename (mono_w32handle_get_type (handle)));
1042 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1045 mono_w32handle_ref (handle);
1048 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
1051 mono_thread_info_uninstall_interrupt (alerted);
1053 /* if it is alerted, then the handle is unref in the interrupt callback */
1054 mono_w32handle_unref (handle);
1062 dump_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
1064 MonoW32HandleBase *handle_data;
1066 if (!mono_w32handle_lookup_data (handle, &handle_data))
1067 g_error ("cannot dump unknown handle %p", handle);
1069 g_print ("%p [%7s] signalled: %5s ref: %3d ",
1070 handle, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref);
1071 mono_w32handle_ops_details (handle_data->type, handle_data->specific);
1077 void mono_w32handle_dump (void)
1079 mono_w32handle_foreach (dump_callback, NULL);
1083 own_if_signalled (gpointer handle, guint32 *statuscode)
1085 if (!mono_w32handle_issignalled (handle))
1088 *statuscode = WAIT_OBJECT_0;
1089 mono_w32handle_ops_own (handle, statuscode);
1094 own_if_owned( gpointer handle, guint32 *statuscode)
1096 if (!mono_w32handle_ops_isowned (handle))
1099 *statuscode = WAIT_OBJECT_0;
1100 mono_w32handle_ops_own (handle, statuscode);
1104 MonoW32HandleWaitRet
1105 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
1107 MonoW32HandleWaitRet ret;
1110 guint32 statuscode = 0;
1114 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1115 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p has special wait",
1118 return mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL);
1121 if (!mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_WAIT)) {
1122 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1125 return MONO_W32HANDLE_WAIT_RET_FAILED;
1128 mono_w32handle_lock_handle (handle);
1130 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) {
1131 if (own_if_owned (handle, &statuscode)) {
1132 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1135 ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1140 if (timeout != INFINITE)
1141 start = mono_msec_ticks ();
1146 if (own_if_signalled (handle, &statuscode)) {
1147 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1150 ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1154 mono_w32handle_ops_prewait (handle);
1156 if (timeout == INFINITE) {
1157 waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1161 elapsed = mono_msec_ticks () - start;
1162 if (elapsed > timeout) {
1163 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1167 waited = mono_w32handle_timedwait_signal_handle (handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1171 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1176 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1182 mono_w32handle_unlock_handle (handle);
1187 MonoW32HandleWaitRet
1188 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
1190 MonoW32HandleWaitRet ret;
1191 gboolean alerted, poll;
1194 gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1195 guint32 statuscodes [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
1198 return MONO_W32HANDLE_WAIT_RET_FAILED;
1201 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1205 if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
1206 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: too many handles: %zd",
1207 __func__, nhandles);
1209 return MONO_W32HANDLE_WAIT_RET_FAILED;
1212 for (i = 0; i < nhandles; ++i) {
1213 if (!mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT)
1214 && !mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
1216 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1217 __func__, handles [i]);
1219 return MONO_W32HANDLE_WAIT_RET_FAILED;
1222 handles_sorted [i] = handles [i];
1225 qsort (handles_sorted, nhandles, sizeof (gpointer), g_direct_equal);
1226 for (i = 1; i < nhandles; ++i) {
1227 if (handles_sorted [i - 1] == handles_sorted [i]) {
1228 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p is duplicated",
1229 __func__, handles_sorted [i]);
1231 return MONO_W32HANDLE_WAIT_RET_FAILED;
1236 for (i = 0; i < nhandles; ++i) {
1237 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS) {
1238 /* Can't wait for a process handle + another handle without polling */
1243 if (timeout != INFINITE)
1244 start = mono_msec_ticks ();
1246 for (i = 0; i < nhandles; ++i) {
1247 /* Add a reference, as we need to ensure the handle wont
1248 * disappear from under us while we're waiting in the loop
1249 * (not lock, as we don't want exclusive access here) */
1250 mono_w32handle_ref (handles [i]);
1254 gsize count, lowest;
1261 mono_w32handle_lock_handles (handles, nhandles);
1263 for (i = 0; i < nhandles; i++) {
1264 if ((mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles [i]))
1265 || mono_w32handle_issignalled (handles [i]))
1274 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1277 for (i = 0; i < nhandles; i++)
1278 own_if_signalled (handles [i], &statuscodes [i]);
1281 mono_w32handle_unlock_handles (handles, nhandles);
1284 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
1285 for (i = lowest; i < nhandles; i++) {
1286 if (statuscodes [i] == WAIT_ABANDONED_0) {
1287 ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest;
1294 for (i = 0; i < nhandles; i++) {
1295 mono_w32handle_ops_prewait (handles[i]);
1297 if (mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1298 && !mono_w32handle_issignalled (handles [i]))
1300 mono_w32handle_ops_specialwait (handles [i], 0, alertable ? &alerted : NULL);
1304 mono_w32handle_lock_signal_mutex ();
1308 for (i = 0; i < nhandles; ++i) {
1309 if (!mono_w32handle_issignalled (handles [i])) {
1316 for (i = 0; i < nhandles; ++i) {
1317 if (mono_w32handle_issignalled (handles [i])) {
1327 if (timeout == INFINITE) {
1328 waited = mono_w32handle_timedwait_signal (INFINITE, poll, alertable ? &alerted : NULL);
1332 elapsed = mono_msec_ticks () - start;
1333 if (elapsed > timeout) {
1334 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1336 mono_w32handle_unlock_signal_mutex ();
1341 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1345 mono_w32handle_unlock_signal_mutex ();
1348 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1353 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1359 for (i = 0; i < nhandles; i++) {
1360 /* Unref everything we reffed above */
1361 mono_w32handle_unref (handles [i]);
1367 MonoW32HandleWaitRet
1368 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1370 MonoW32HandleWaitRet ret;
1373 guint32 statuscode = 0;
1374 gpointer handles [2];
1378 if (!mono_w32handle_test_capabilities (signal_handle, MONO_W32HANDLE_CAP_SIGNAL))
1379 return MONO_W32HANDLE_WAIT_RET_FAILED;
1380 if (!mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_WAIT))
1381 return MONO_W32HANDLE_WAIT_RET_FAILED;
1383 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1384 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle);
1385 return MONO_W32HANDLE_WAIT_RET_FAILED;
1388 handles [0] = wait_handle;
1389 handles [1] = signal_handle;
1391 mono_w32handle_lock_handles (handles, 2);
1393 mono_w32handle_ops_signal (signal_handle);
1395 mono_w32handle_unlock_handle (signal_handle);
1397 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) {
1398 if (own_if_owned (wait_handle, &statuscode)) {
1399 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1400 __func__, wait_handle);
1402 ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1407 if (timeout != INFINITE)
1408 start = mono_msec_ticks ();
1413 if (own_if_signalled (wait_handle, &statuscode)) {
1414 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1415 __func__, wait_handle);
1417 ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1421 mono_w32handle_ops_prewait (wait_handle);
1423 if (timeout == INFINITE) {
1424 waited = mono_w32handle_timedwait_signal_handle (wait_handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1428 elapsed = mono_msec_ticks () - start;
1429 if (elapsed > timeout) {
1430 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1434 waited = mono_w32handle_timedwait_signal_handle (wait_handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1438 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1443 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1449 mono_w32handle_unlock_handle (wait_handle);