[w32handle] Only own first handle if doing WaitHandle.WaitAny (#5625)
[mono-project.git] / mono / metadata / w32handle.c
blob159c6a3672ef3d10f4995bb380704b05e3c63647
1 /**
2 * \file
3 * Generic and internal operations on handles
5 * Author:
6 * Dick Porter (dick@ximian.com)
7 * Ludovic Henry (luhenry@microsoft.com)
9 * (C) 2002-2011 Novell, Inc.
10 * Copyright 2011 Xamarin Inc
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #include <config.h>
15 #include <glib.h>
17 #include "w32handle.h"
19 #include "utils/atomic.h"
20 #include "utils/mono-logger-internals.h"
21 #include "utils/mono-os-mutex.h"
22 #include "utils/mono-proclib.h"
23 #include "utils/mono-threads.h"
24 #include "utils/mono-time.h"
26 #undef DEBUG_REFS
28 #define SLOT_MAX (1024 * 32)
30 /* must be a power of 2 */
31 #define HANDLE_PER_SLOT (256)
33 typedef struct {
34 MonoW32HandleType type;
35 guint ref;
36 gboolean signalled;
37 mono_mutex_t signal_mutex;
38 mono_cond_t signal_cond;
39 gpointer specific;
40 } MonoW32HandleBase;
42 static MonoW32HandleCapability handle_caps [MONO_W32HANDLE_COUNT];
43 static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
46 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
47 * If 4M handles are not enough... Oh, well... we will crash.
49 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
50 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
52 static MonoW32HandleBase *private_handles [SLOT_MAX];
53 static guint32 private_handles_count = 0;
54 static guint32 private_handles_slots_count = 0;
56 guint32 mono_w32handle_fd_reserve;
59 * This is an internal handle which is used for handling waiting for multiple handles.
60 * Threads which wait for multiple handles wait on this one handle, and when a handle
61 * is signalled, this handle is signalled too.
63 static mono_mutex_t global_signal_mutex;
64 static mono_cond_t global_signal_cond;
66 static mono_mutex_t scan_mutex;
68 static gboolean shutting_down = FALSE;
70 static gboolean
71 type_is_fd (MonoW32HandleType type)
73 switch (type) {
74 case MONO_W32HANDLE_FILE:
75 case MONO_W32HANDLE_CONSOLE:
76 case MONO_W32HANDLE_SOCKET:
77 case MONO_W32HANDLE_PIPE:
78 return TRUE;
79 default:
80 return FALSE;
84 static gboolean
85 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
87 gsize index, offset;
89 g_assert (handle_data);
91 index = SLOT_INDEX ((gsize) handle);
92 if (index >= SLOT_MAX)
93 return FALSE;
94 if (!private_handles [index])
95 return FALSE;
97 offset = SLOT_OFFSET ((gsize) handle);
98 if (private_handles [index][offset].type == MONO_W32HANDLE_UNUSED)
99 return FALSE;
101 *handle_data = &private_handles [index][offset];
102 return TRUE;
105 MonoW32HandleType
106 mono_w32handle_get_type (gpointer handle)
108 MonoW32HandleBase *handle_data;
110 if (!mono_w32handle_lookup_data (handle, &handle_data))
111 return MONO_W32HANDLE_UNUSED; /* An impossible type */
113 return handle_data->type;
116 static const gchar*
117 mono_w32handle_ops_typename (MonoW32HandleType type);
119 const gchar*
120 mono_w32handle_get_typename (MonoW32HandleType type)
122 return mono_w32handle_ops_typename (type);
125 void
126 mono_w32handle_set_signal_state (gpointer handle, gboolean state, gboolean broadcast)
128 MonoW32HandleBase *handle_data;
130 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
131 return;
134 #ifdef DEBUG
135 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
136 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
137 #endif
139 if (state == TRUE) {
140 /* Tell everyone blocking on a single handle */
142 /* The condition the global signal cond is waiting on is the signalling of
143 * _any_ handle. So lock it before setting the signalled state.
145 mono_os_mutex_lock (&global_signal_mutex);
147 /* This function _must_ be called with
148 * handle->signal_mutex locked
150 handle_data->signalled=state;
152 if (broadcast == TRUE) {
153 mono_os_cond_broadcast (&handle_data->signal_cond);
154 } else {
155 mono_os_cond_signal (&handle_data->signal_cond);
158 /* Tell everyone blocking on multiple handles that something
159 * was signalled
161 mono_os_cond_broadcast (&global_signal_cond);
163 mono_os_mutex_unlock (&global_signal_mutex);
164 } else {
165 handle_data->signalled=state;
169 gboolean
170 mono_w32handle_issignalled (gpointer handle)
172 MonoW32HandleBase *handle_data;
174 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
175 return(FALSE);
178 return handle_data->signalled;
181 static void
182 mono_w32handle_lock_signal_mutex (void)
184 #ifdef DEBUG
185 g_message ("%s: lock global signal mutex", __func__);
186 #endif
188 mono_os_mutex_lock (&global_signal_mutex);
191 static void
192 mono_w32handle_unlock_signal_mutex (void)
194 #ifdef DEBUG
195 g_message ("%s: unlock global signal mutex", __func__);
196 #endif
198 mono_os_mutex_unlock (&global_signal_mutex);
201 void
202 mono_w32handle_lock_handle (gpointer handle)
204 MonoW32HandleBase *handle_data;
206 if (!mono_w32handle_lookup_data (handle, &handle_data))
207 g_error ("%s: failed to lookup handle %p", __func__, handle);
209 mono_w32handle_ref (handle);
211 mono_os_mutex_lock (&handle_data->signal_mutex);
213 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: lock handle %p", __func__, handle);
216 gboolean
217 mono_w32handle_trylock_handle (gpointer handle)
219 MonoW32HandleBase *handle_data;
220 gboolean locked;
222 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p", __func__, handle);
224 if (!mono_w32handle_lookup_data (handle, &handle_data))
225 g_error ("%s: failed to lookup handle %p", __func__, handle);
227 mono_w32handle_ref (handle);
229 locked = mono_os_mutex_trylock (&handle_data->signal_mutex) == 0;
230 if (!locked)
231 mono_w32handle_unref (handle);
233 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p, locked: %s", __func__, handle, locked ? "true" : "false");
235 return locked;
238 void
239 mono_w32handle_unlock_handle (gpointer handle)
241 MonoW32HandleBase *handle_data;
243 if (!mono_w32handle_lookup_data (handle, &handle_data))
244 g_error ("%s: failed to lookup handle %p", __func__, handle);
246 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlock handle %p", __func__, handle);
248 mono_os_mutex_unlock (&handle_data->signal_mutex);
250 mono_w32handle_unref (handle);
253 void
254 mono_w32handle_init (void)
256 static gboolean initialized = FALSE;
258 if (initialized)
259 return;
261 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
262 == MONO_W32HANDLE_COUNT);
264 /* This is needed by the code in mono_w32handle_new_internal */
265 mono_w32handle_fd_reserve = (eg_getdtablesize () + (HANDLE_PER_SLOT - 1)) & ~(HANDLE_PER_SLOT - 1);
267 do {
269 * The entries in private_handles reserved for fds are allocated lazily to
270 * save memory.
273 private_handles_count += HANDLE_PER_SLOT;
274 private_handles_slots_count ++;
275 } while(mono_w32handle_fd_reserve > private_handles_count);
277 mono_os_mutex_init (&scan_mutex);
279 mono_os_cond_init (&global_signal_cond);
280 mono_os_mutex_init (&global_signal_mutex);
282 initialized = TRUE;
285 void
286 mono_w32handle_cleanup (void)
288 int i;
290 g_assert (!shutting_down);
291 shutting_down = TRUE;
293 for (i = 0; i < SLOT_MAX; ++i)
294 g_free (private_handles [i]);
297 static gsize
298 mono_w32handle_ops_typesize (MonoW32HandleType type);
300 static void mono_w32handle_init_handle (MonoW32HandleBase *handle,
301 MonoW32HandleType type, gpointer handle_specific)
303 g_assert (handle->ref == 0);
305 handle->type = type;
306 handle->signalled = FALSE;
307 handle->ref = 1;
309 mono_os_cond_init (&handle->signal_cond);
310 mono_os_mutex_init (&handle->signal_mutex);
312 if (handle_specific)
313 handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
317 * mono_w32handle_new_internal:
318 * @type: Init handle to this type
320 * Search for a free handle and initialize it. Return the handle on
321 * success and 0 on failure. This is only called from
322 * mono_w32handle_new, and scan_mutex must be held.
324 static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
325 gpointer handle_specific)
327 guint32 i, k, count;
328 static guint32 last = 0;
329 gboolean retry = FALSE;
331 /* A linear scan should be fast enough. Start from the last
332 * allocation, assuming that handles are allocated more often
333 * than they're freed. Leave the space reserved for file
334 * descriptors
337 if (last < mono_w32handle_fd_reserve) {
338 last = mono_w32handle_fd_reserve;
339 } else {
340 retry = TRUE;
343 again:
344 count = last;
345 for(i = SLOT_INDEX (count); i < private_handles_slots_count; i++) {
346 if (private_handles [i]) {
347 for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
348 MonoW32HandleBase *handle = &private_handles [i][k];
350 if(handle->type == MONO_W32HANDLE_UNUSED) {
351 last = count + 1;
353 mono_w32handle_init_handle (handle, type, handle_specific);
354 return (count);
356 count++;
361 if(retry && last > mono_w32handle_fd_reserve) {
362 /* Try again from the beginning */
363 last = mono_w32handle_fd_reserve;
364 goto again;
367 /* Will need to expand the array. The caller will sort it out */
369 return(0);
372 gpointer
373 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
375 guint32 handle_idx = 0;
376 gpointer handle;
378 g_assert (!shutting_down);
380 g_assert(!type_is_fd(type));
382 mono_os_mutex_lock (&scan_mutex);
384 while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == 0) {
385 /* Try and expand the array, and have another go */
386 int idx = SLOT_INDEX (private_handles_count);
387 if (idx >= SLOT_MAX) {
388 break;
391 private_handles [idx] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
393 private_handles_count += HANDLE_PER_SLOT;
394 private_handles_slots_count ++;
397 mono_os_mutex_unlock (&scan_mutex);
399 if (handle_idx == 0) {
400 /* We ran out of slots */
401 handle = INVALID_HANDLE_VALUE;
402 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type));
403 goto done;
406 /* Make sure we left the space for fd mappings */
407 g_assert (handle_idx >= mono_w32handle_fd_reserve);
409 handle = GUINT_TO_POINTER (handle_idx);
411 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
413 done:
414 return(handle);
417 gpointer mono_w32handle_new_fd (MonoW32HandleType type, int fd,
418 gpointer handle_specific)
420 MonoW32HandleBase *handle_data;
421 int fd_index, fd_offset;
423 g_assert (!shutting_down);
425 g_assert(type_is_fd(type));
427 if (fd >= mono_w32handle_fd_reserve) {
428 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));
430 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
433 fd_index = SLOT_INDEX (fd);
434 fd_offset = SLOT_OFFSET (fd);
436 /* Initialize the array entries on demand */
437 if (!private_handles [fd_index]) {
438 mono_os_mutex_lock (&scan_mutex);
440 if (!private_handles [fd_index])
441 private_handles [fd_index] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
443 mono_os_mutex_unlock (&scan_mutex);
446 handle_data = &private_handles [fd_index][fd_offset];
448 if (handle_data->type != MONO_W32HANDLE_UNUSED) {
449 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));
450 /* FIXME: clean up this handle? We can't do anything
451 * with the fd, cos thats the new one
453 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
456 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), GUINT_TO_POINTER(fd));
458 mono_w32handle_init_handle (handle_data, type, handle_specific);
460 return(GUINT_TO_POINTER(fd));
463 gboolean
464 mono_w32handle_close (gpointer handle)
466 if (handle == INVALID_HANDLE_VALUE)
467 return FALSE;
468 if (handle == (gpointer) 0 && mono_w32handle_get_type (handle) != MONO_W32HANDLE_CONSOLE) {
469 /* Problem: because we map file descriptors to the
470 * same-numbered handle we can't tell the difference
471 * between a bogus handle and the handle to stdin.
472 * Assume that it's the console handle if that handle
473 * exists... */
474 return FALSE;
477 mono_w32handle_unref (handle);
478 return TRUE;
481 gboolean
482 mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
483 gpointer *handle_specific)
485 MonoW32HandleBase *handle_data;
487 g_assert (handle_specific);
489 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
490 return(FALSE);
493 if (handle_data->type != type) {
494 return(FALSE);
497 *handle_specific = handle_data->specific;
499 return(TRUE);
502 static gboolean
503 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data);
505 static gboolean
506 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data);
508 static void
509 w32handle_destroy (gpointer handle);
511 void
512 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
514 GPtrArray *handles_to_destroy;
515 guint32 i, k;
517 handles_to_destroy = NULL;
519 mono_os_mutex_lock (&scan_mutex);
521 for (i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
522 if (!private_handles [i])
523 continue;
524 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
525 MonoW32HandleBase *handle_data = NULL;
526 gpointer handle;
527 gboolean destroy, finished;
529 handle_data = &private_handles [i][k];
530 if (handle_data->type == MONO_W32HANDLE_UNUSED)
531 continue;
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. */
539 continue;
542 finished = on_each (handle, handle_data->specific, user_data);
544 /* we might have to destroy the handle here, as
545 * it could have been unrefed in another thread */
546 destroy = mono_w32handle_unref_core (handle, handle_data);
547 if (destroy) {
548 /* we do not destroy it while holding the scan_mutex
549 * lock, because w32handle_destroy also needs to take
550 * the lock, and it calls user code which might lead
551 * to a deadlock */
552 if (!handles_to_destroy)
553 handles_to_destroy = g_ptr_array_sized_new (4);
554 g_ptr_array_add (handles_to_destroy, handle);
557 if (finished)
558 goto done;
562 done:
563 mono_os_mutex_unlock (&scan_mutex);
565 if (handles_to_destroy) {
566 for (i = 0; i < handles_to_destroy->len; ++i)
567 w32handle_destroy (handles_to_destroy->pdata [i]);
569 g_ptr_array_free (handles_to_destroy, TRUE);
573 static gboolean
574 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data)
576 guint old, new;
578 do {
579 old = handle_data->ref;
580 if (old == 0)
581 return FALSE;
583 new = old + 1;
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);
589 return TRUE;
592 static gboolean
593 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data)
595 MonoW32HandleType type;
596 guint old, new;
598 type = handle_data->type;
600 do {
601 old = handle_data->ref;
602 if (!(old >= 1))
603 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__, handle, old);
605 new = old - 1;
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");
614 return new == 0;
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);
623 return;
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 static void
633 w32handle_destroy (gpointer handle)
635 /* Need to copy the handle info, reset the slot in the
636 * array, and _only then_ call the close function to
637 * avoid race conditions (eg file descriptors being
638 * closed, and another file being opened getting the
639 * same fd racing the memset())
641 MonoW32HandleBase *handle_data;
642 MonoW32HandleType type;
643 gpointer handle_specific;
644 void (*close_func)(gpointer, gpointer);
646 if (!mono_w32handle_lookup_data (handle, &handle_data))
647 g_error ("%s: unknown handle %p", __func__, handle);
649 type = handle_data->type;
650 handle_specific = handle_data->specific;
652 mono_os_mutex_lock (&scan_mutex);
654 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
656 mono_os_mutex_destroy (&handle_data->signal_mutex);
657 mono_os_cond_destroy (&handle_data->signal_cond);
659 memset (handle_data, 0, sizeof (MonoW32HandleBase));
661 mono_os_mutex_unlock (&scan_mutex);
663 close_func = _wapi_handle_ops_get_close_func (type);
664 if (close_func != NULL) {
665 close_func (handle, handle_specific);
668 g_free (handle_specific);
671 /* The handle must not be locked on entry to this function */
672 void
673 mono_w32handle_unref (gpointer handle)
675 MonoW32HandleBase *handle_data;
676 gboolean destroy;
678 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
679 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to unref handle %p, unknown handle",
680 __func__, handle);
681 return;
684 destroy = mono_w32handle_unref_core (handle, handle_data);
685 if (destroy)
686 w32handle_destroy (handle);
689 static void
690 mono_w32handle_ops_close (gpointer handle, gpointer data);
692 void
693 mono_w32handle_force_close (gpointer handle, gpointer data)
695 mono_w32handle_ops_close (handle, data);
698 void
699 mono_w32handle_register_ops (MonoW32HandleType type, MonoW32HandleOps *ops)
701 handle_ops [type] = ops;
704 void mono_w32handle_register_capabilities (MonoW32HandleType type,
705 MonoW32HandleCapability caps)
707 handle_caps[type] = caps;
710 gboolean mono_w32handle_test_capabilities (gpointer handle,
711 MonoW32HandleCapability caps)
713 MonoW32HandleBase *handle_data;
714 MonoW32HandleType type;
716 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
717 return(FALSE);
720 type = handle_data->type;
722 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
723 handle_caps[type], caps, handle_caps[type] & caps);
725 return((handle_caps[type] & caps) != 0);
728 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer)
730 if (handle_ops[type] != NULL &&
731 handle_ops[type]->close != NULL) {
732 return (handle_ops[type]->close);
735 return (NULL);
738 static void
739 mono_w32handle_ops_close (gpointer handle, gpointer data)
741 MonoW32HandleBase *handle_data;
742 MonoW32HandleType type;
744 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
745 return;
748 type = handle_data->type;
750 if (handle_ops[type] != NULL &&
751 handle_ops[type]->close != NULL) {
752 handle_ops[type]->close (handle, data);
756 static void
757 mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
759 if (handle_ops[type] != NULL &&
760 handle_ops[type]->details != NULL) {
761 handle_ops[type]->details (data);
765 static const gchar*
766 mono_w32handle_ops_typename (MonoW32HandleType type)
768 g_assert (handle_ops [type]);
769 g_assert (handle_ops [type]->typename);
770 return handle_ops [type]->typename ();
773 static gsize
774 mono_w32handle_ops_typesize (MonoW32HandleType type)
776 g_assert (handle_ops [type]);
777 g_assert (handle_ops [type]->typesize);
778 return handle_ops [type]->typesize ();
781 static void
782 mono_w32handle_ops_signal (gpointer handle)
784 MonoW32HandleBase *handle_data;
785 MonoW32HandleType type;
787 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
788 return;
791 type = handle_data->type;
793 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
794 handle_ops[type]->signal (handle);
798 static gboolean
799 mono_w32handle_ops_own (gpointer handle, gboolean *abandoned)
801 MonoW32HandleBase *handle_data;
802 MonoW32HandleType type;
804 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
805 return(FALSE);
808 type = handle_data->type;
810 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
811 return(handle_ops[type]->own_handle (handle, abandoned));
812 } else {
813 return(FALSE);
817 static gboolean
818 mono_w32handle_ops_isowned (gpointer handle)
820 MonoW32HandleBase *handle_data;
821 MonoW32HandleType type;
823 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
824 return(FALSE);
827 type = handle_data->type;
829 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
830 return(handle_ops[type]->is_owned (handle));
831 } else {
832 return(FALSE);
836 static MonoW32HandleWaitRet
837 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted)
839 MonoW32HandleBase *handle_data;
840 MonoW32HandleType type;
842 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
843 return MONO_W32HANDLE_WAIT_RET_FAILED;
846 type = handle_data->type;
848 if (handle_ops[type] != NULL &&
849 handle_ops[type]->special_wait != NULL) {
850 return(handle_ops[type]->special_wait (handle, timeout, alerted));
851 } else {
852 return MONO_W32HANDLE_WAIT_RET_FAILED;
856 static void
857 mono_w32handle_ops_prewait (gpointer handle)
859 MonoW32HandleBase *handle_data;
860 MonoW32HandleType type;
862 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
863 return;
866 type = handle_data->type;
868 if (handle_ops[type] != NULL &&
869 handle_ops[type]->prewait != NULL) {
870 handle_ops[type]->prewait (handle);
874 static void
875 spin (guint32 ms)
877 #ifdef HOST_WIN32
878 SleepEx (ms, TRUE);
879 #else
880 struct timespec sleepytime;
882 g_assert (ms < 1000);
884 sleepytime.tv_sec = 0;
885 sleepytime.tv_nsec = ms * 1000000;
886 nanosleep (&sleepytime, NULL);
887 #endif /* HOST_WIN32 */
890 static void
891 mono_w32handle_lock_handles (gpointer *handles, gsize numhandles)
893 guint32 i, iter=0;
895 /* Lock all the handles, with backoff */
896 again:
897 for(i=0; i<numhandles; i++) {
898 gpointer handle = handles[i];
900 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempting to lock %p", __func__, handle);
902 if (!mono_w32handle_trylock_handle (handle)) {
903 /* Bummer */
905 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p.", __func__,
906 handle);
908 while (i--) {
909 handle = handles[i];
911 mono_w32handle_unlock_handle (handle);
914 /* If iter ever reaches 100 the nanosleep will
915 * return EINVAL immediately, but we have a
916 * design flaw if that happens.
918 iter++;
919 if(iter==100) {
920 g_warning ("%s: iteration overflow!",
921 __func__);
922 iter=1;
925 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
926 iter*10);
927 spin (10 * iter);
929 goto again;
933 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
936 static void
937 mono_w32handle_unlock_handles (gpointer *handles, gsize numhandles)
939 guint32 i;
941 for(i=0; i<numhandles; i++) {
942 gpointer handle = handles[i];
944 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlocking handle %p", __func__, handle);
946 mono_w32handle_unlock_handle (handle);
950 static int
951 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
953 int res;
955 if (!poll) {
956 res = mono_os_cond_timedwait (cond, mutex, timeout);
957 } else {
958 /* This is needed when waiting for process handles */
959 if (!alerted) {
961 * pthread_cond_(timed)wait() can return 0 even if the condition was not
962 * signalled. This happens at least on Darwin. We surface this, i.e., we
963 * get spurious wake-ups.
965 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
967 res = mono_os_cond_timedwait (cond, mutex, timeout);
968 } else {
969 if (timeout < 100) {
970 /* Real timeout is less than 100ms time */
971 res = mono_os_cond_timedwait (cond, mutex, timeout);
972 } else {
973 res = mono_os_cond_timedwait (cond, mutex, 100);
975 /* Mask the fake timeout, this will cause
976 * another poll if the cond was not really signaled
978 if (res == -1)
979 res = 0;
984 return res;
987 static void
988 signal_global (gpointer unused)
990 /* If we reach here, then interrupt token is set to the flag value, which
991 * means that the target thread is either
992 * - before the first CAS in timedwait, which means it won't enter the wait.
993 * - it is after the first CAS, so it is already waiting, or it will enter
994 * the wait, and it will be interrupted by the broadcast. */
995 mono_os_mutex_lock (&global_signal_mutex);
996 mono_os_cond_broadcast (&global_signal_cond);
997 mono_os_mutex_unlock (&global_signal_mutex);
1000 static int
1001 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
1003 int res;
1005 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
1007 if (alerted)
1008 *alerted = FALSE;
1010 if (alerted) {
1011 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
1012 if (*alerted)
1013 return 0;
1016 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
1018 if (alerted)
1019 mono_thread_info_uninstall_interrupt (alerted);
1021 return res;
1024 static void
1025 signal_handle_and_unref (gpointer handle)
1027 MonoW32HandleBase *handle_data;
1028 mono_cond_t *cond;
1029 mono_mutex_t *mutex;
1031 if (!mono_w32handle_lookup_data (handle, &handle_data))
1032 g_error ("cannot signal unknown handle %p", handle);
1034 /* If we reach here, then interrupt token is set to the flag value, which
1035 * means that the target thread is either
1036 * - before the first CAS in timedwait, which means it won't enter the wait.
1037 * - it is after the first CAS, so it is already waiting, or it will enter
1038 * the wait, and it will be interrupted by the broadcast. */
1039 cond = &handle_data->signal_cond;
1040 mutex = &handle_data->signal_mutex;
1042 mono_os_mutex_lock (mutex);
1043 mono_os_cond_broadcast (cond);
1044 mono_os_mutex_unlock (mutex);
1046 mono_w32handle_unref (handle);
1049 static int
1050 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1052 MonoW32HandleBase *handle_data;
1053 int res;
1055 if (!mono_w32handle_lookup_data (handle, &handle_data))
1056 g_error ("cannot wait on unknown handle %p", handle);
1058 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for %p (type %s)", __func__, handle,
1059 mono_w32handle_ops_typename (mono_w32handle_get_type (handle)));
1061 if (alerted)
1062 *alerted = FALSE;
1064 if (alerted) {
1065 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1066 if (*alerted)
1067 return 0;
1068 mono_w32handle_ref (handle);
1071 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
1073 if (alerted) {
1074 mono_thread_info_uninstall_interrupt (alerted);
1075 if (!*alerted) {
1076 /* if it is alerted, then the handle is unref in the interrupt callback */
1077 mono_w32handle_unref (handle);
1081 return res;
1084 static gboolean
1085 dump_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
1087 MonoW32HandleBase *handle_data;
1089 if (!mono_w32handle_lookup_data (handle, &handle_data))
1090 g_error ("cannot dump unknown handle %p", handle);
1092 g_print ("%p [%7s] signalled: %5s ref: %3d ",
1093 handle, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref);
1094 mono_w32handle_ops_details (handle_data->type, handle_data->specific);
1095 g_print ("\n");
1097 return FALSE;
1100 void mono_w32handle_dump (void)
1102 mono_w32handle_foreach (dump_callback, NULL);
1105 static gboolean
1106 own_if_signalled (gpointer handle, gboolean *abandoned)
1108 if (!mono_w32handle_issignalled (handle))
1109 return FALSE;
1111 *abandoned = FALSE;
1112 mono_w32handle_ops_own (handle, abandoned);
1113 return TRUE;
1116 static gboolean
1117 own_if_owned( gpointer handle, gboolean *abandoned)
1119 if (!mono_w32handle_ops_isowned (handle))
1120 return FALSE;
1122 *abandoned = FALSE;
1123 mono_w32handle_ops_own (handle, abandoned);
1124 return TRUE;
1127 MonoW32HandleWaitRet
1128 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
1130 MonoW32HandleWaitRet ret;
1131 gboolean alerted;
1132 gint64 start;
1133 gboolean abandoned = FALSE;
1135 alerted = FALSE;
1137 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1138 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p has special wait",
1139 __func__, handle);
1141 return mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL);
1144 if (!mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_WAIT)) {
1145 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1146 __func__, handle);
1148 return MONO_W32HANDLE_WAIT_RET_FAILED;
1151 mono_w32handle_lock_handle (handle);
1153 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) {
1154 if (own_if_owned (handle, &abandoned)) {
1155 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1156 __func__, handle);
1158 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1159 goto done;
1163 if (timeout != MONO_INFINITE_WAIT)
1164 start = mono_msec_ticks ();
1166 for (;;) {
1167 gint waited;
1169 if (own_if_signalled (handle, &abandoned)) {
1170 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1171 __func__, handle);
1173 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1174 goto done;
1177 mono_w32handle_ops_prewait (handle);
1179 if (timeout == MONO_INFINITE_WAIT) {
1180 waited = mono_w32handle_timedwait_signal_handle (handle, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
1181 } else {
1182 gint64 elapsed;
1184 elapsed = mono_msec_ticks () - start;
1185 if (elapsed > timeout) {
1186 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1187 goto done;
1190 waited = mono_w32handle_timedwait_signal_handle (handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1193 if (alerted) {
1194 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1195 goto done;
1198 if (waited != 0) {
1199 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1200 goto done;
1204 done:
1205 mono_w32handle_unlock_handle (handle);
1207 return ret;
1210 MonoW32HandleWaitRet
1211 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
1213 MonoW32HandleWaitRet ret;
1214 gboolean alerted, poll;
1215 gint i;
1216 gint64 start;
1217 gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1218 gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
1220 if (nhandles == 0)
1221 return MONO_W32HANDLE_WAIT_RET_FAILED;
1223 if (nhandles == 1)
1224 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1226 alerted = FALSE;
1228 if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
1229 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: too many handles: %zd",
1230 __func__, nhandles);
1232 return MONO_W32HANDLE_WAIT_RET_FAILED;
1235 for (i = 0; i < nhandles; ++i) {
1236 if (!mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT)
1237 && !mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
1239 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1240 __func__, handles [i]);
1242 return MONO_W32HANDLE_WAIT_RET_FAILED;
1245 handles_sorted [i] = handles [i];
1248 qsort (handles_sorted, nhandles, sizeof (gpointer), g_direct_equal);
1249 for (i = 1; i < nhandles; ++i) {
1250 if (handles_sorted [i - 1] == handles_sorted [i]) {
1251 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p is duplicated",
1252 __func__, handles_sorted [i]);
1254 return MONO_W32HANDLE_WAIT_RET_FAILED;
1258 poll = FALSE;
1259 for (i = 0; i < nhandles; ++i) {
1260 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS) {
1261 /* Can't wait for a process handle + another handle without polling */
1262 poll = TRUE;
1266 if (timeout != MONO_INFINITE_WAIT)
1267 start = mono_msec_ticks ();
1269 for (i = 0; i < nhandles; ++i) {
1270 /* Add a reference, as we need to ensure the handle wont
1271 * disappear from under us while we're waiting in the loop
1272 * (not lock, as we don't want exclusive access here) */
1273 mono_w32handle_ref (handles [i]);
1276 for (;;) {
1277 gsize count, lowest;
1278 gboolean signalled;
1279 gint waited;
1281 count = 0;
1282 lowest = nhandles;
1284 mono_w32handle_lock_handles (handles, nhandles);
1286 for (i = 0; i < nhandles; i++) {
1287 if ((mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles [i]))
1288 || mono_w32handle_issignalled (handles [i]))
1290 count ++;
1292 if (i < lowest)
1293 lowest = i;
1297 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1299 if (signalled) {
1300 for (i = 0; i < nhandles; i++) {
1301 if (own_if_signalled (handles [i], &abandoned [i]) && !waitall) {
1302 /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
1303 * throw AbandonedMutexException in case we owned it but didn't release it */
1304 break;
1309 mono_w32handle_unlock_handles (handles, nhandles);
1311 if (signalled) {
1312 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
1313 for (i = lowest; i < nhandles; i++) {
1314 if (abandoned [i]) {
1315 ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest;
1316 break;
1319 goto done;
1322 for (i = 0; i < nhandles; i++) {
1323 mono_w32handle_ops_prewait (handles[i]);
1325 if (mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1326 && !mono_w32handle_issignalled (handles [i]))
1328 mono_w32handle_ops_specialwait (handles [i], 0, alertable ? &alerted : NULL);
1332 mono_w32handle_lock_signal_mutex ();
1334 if (waitall) {
1335 signalled = TRUE;
1336 for (i = 0; i < nhandles; ++i) {
1337 if (!mono_w32handle_issignalled (handles [i])) {
1338 signalled = FALSE;
1339 break;
1342 } else {
1343 signalled = FALSE;
1344 for (i = 0; i < nhandles; ++i) {
1345 if (mono_w32handle_issignalled (handles [i])) {
1346 signalled = TRUE;
1347 break;
1352 waited = 0;
1354 if (!signalled) {
1355 if (timeout == MONO_INFINITE_WAIT) {
1356 waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL);
1357 } else {
1358 gint64 elapsed;
1360 elapsed = mono_msec_ticks () - start;
1361 if (elapsed > timeout) {
1362 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1364 mono_w32handle_unlock_signal_mutex ();
1366 goto done;
1369 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1373 mono_w32handle_unlock_signal_mutex ();
1375 if (alerted) {
1376 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1377 goto done;
1380 if (waited != 0) {
1381 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1382 goto done;
1386 done:
1387 for (i = 0; i < nhandles; i++) {
1388 /* Unref everything we reffed above */
1389 mono_w32handle_unref (handles [i]);
1392 return ret;
1395 MonoW32HandleWaitRet
1396 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1398 MonoW32HandleWaitRet ret;
1399 gint64 start;
1400 gboolean alerted;
1401 gboolean abandoned = FALSE;
1402 gpointer handles [2];
1404 alerted = FALSE;
1406 if (!mono_w32handle_test_capabilities (signal_handle, MONO_W32HANDLE_CAP_SIGNAL))
1407 return MONO_W32HANDLE_WAIT_RET_FAILED;
1408 if (!mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_WAIT))
1409 return MONO_W32HANDLE_WAIT_RET_FAILED;
1411 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1412 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle);
1413 return MONO_W32HANDLE_WAIT_RET_FAILED;
1416 handles [0] = wait_handle;
1417 handles [1] = signal_handle;
1419 mono_w32handle_lock_handles (handles, 2);
1421 mono_w32handle_ops_signal (signal_handle);
1423 mono_w32handle_unlock_handle (signal_handle);
1425 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) {
1426 if (own_if_owned (wait_handle, &abandoned)) {
1427 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1428 __func__, wait_handle);
1430 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1431 goto done;
1435 if (timeout != MONO_INFINITE_WAIT)
1436 start = mono_msec_ticks ();
1438 for (;;) {
1439 gint waited;
1441 if (own_if_signalled (wait_handle, &abandoned)) {
1442 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1443 __func__, wait_handle);
1445 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1446 goto done;
1449 mono_w32handle_ops_prewait (wait_handle);
1451 if (timeout == MONO_INFINITE_WAIT) {
1452 waited = mono_w32handle_timedwait_signal_handle (wait_handle, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
1453 } else {
1454 gint64 elapsed;
1456 elapsed = mono_msec_ticks () - start;
1457 if (elapsed > timeout) {
1458 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1459 goto done;
1462 waited = mono_w32handle_timedwait_signal_handle (wait_handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1465 if (alerted) {
1466 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1467 goto done;
1470 if (waited != 0) {
1471 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1472 goto done;
1476 done:
1477 mono_w32handle_unlock_handle (wait_handle);
1479 return ret;