3 * Generic and internal operations on handles
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.
16 #include "w32handle.h"
17 #include "utils/atomic.h"
18 #include "utils/mono-logger-internals.h"
19 #include "utils/mono-proclib.h"
20 #include "utils/mono-threads.h"
21 #include "utils/mono-time.h"
22 #include "utils/mono-error-internals.h"
26 #define HANDLES_PER_SLOT 240
28 typedef struct _MonoW32HandleSlot MonoW32HandleSlot
;
29 struct _MonoW32HandleSlot
{
30 MonoW32HandleSlot
*next
;
31 MonoW32Handle handles
[HANDLES_PER_SLOT
];
34 static MonoW32HandleCapability handle_caps
[MONO_W32TYPE_COUNT
];
35 static MonoW32HandleOps
const *handle_ops
[MONO_W32TYPE_COUNT
];
37 static MonoW32HandleSlot
*handles_slots_first
;
38 static MonoW32HandleSlot
*handles_slots_last
;
41 * This is an internal handle which is used for handling waiting for multiple handles.
42 * Threads which wait for multiple handles wait on this one handle, and when a handle
43 * is signalled, this handle is signalled too.
45 static MonoCoopMutex global_signal_mutex
;
46 static MonoCoopCond global_signal_cond
;
48 static MonoCoopMutex scan_mutex
;
50 static gboolean shutting_down
;
53 mono_w32handle_ops_typename (MonoW32Type type
);
56 mono_w32handle_get_typename (MonoW32Type type
)
58 return mono_w32handle_ops_typename (type
);
62 mono_w32handle_set_signal_state (MonoW32Handle
*handle_data
, gboolean state
, gboolean broadcast
)
65 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__
,
66 handle
, state
?"TRUE":"FALSE", broadcast
?"TRUE":"FALSE");
70 /* Tell everyone blocking on a single handle */
72 /* The condition the global signal cond is waiting on is the signalling of
73 * _any_ handle. So lock it before setting the signalled state.
75 mono_coop_mutex_lock (&global_signal_mutex
);
77 /* This function _must_ be called with
78 * handle->signal_mutex locked
80 handle_data
->signalled
= TRUE
;
83 mono_coop_cond_broadcast (&handle_data
->signal_cond
);
85 mono_coop_cond_signal (&handle_data
->signal_cond
);
87 /* Tell everyone blocking on multiple handles that something
90 mono_coop_cond_broadcast (&global_signal_cond
);
92 mono_coop_mutex_unlock (&global_signal_mutex
);
94 handle_data
->signalled
= FALSE
;
99 mono_w32handle_issignalled (MonoW32Handle
*handle_data
)
101 return handle_data
->signalled
;
105 mono_w32handle_set_in_use (MonoW32Handle
*handle_data
, gboolean in_use
)
107 handle_data
->in_use
= in_use
;
111 mono_w32handle_lock_signal_mutex (void)
114 g_message ("%s: lock global signal mutex", __func__
);
117 mono_coop_mutex_lock (&global_signal_mutex
);
121 mono_w32handle_unlock_signal_mutex (void)
124 g_message ("%s: unlock global signal mutex", __func__
);
127 mono_coop_mutex_unlock (&global_signal_mutex
);
131 mono_w32handle_lock (MonoW32Handle
*handle_data
)
133 mono_coop_mutex_lock (&handle_data
->signal_mutex
);
137 mono_w32handle_trylock (MonoW32Handle
*handle_data
)
139 return mono_coop_mutex_trylock (&handle_data
->signal_mutex
) == 0;
143 mono_w32handle_unlock (MonoW32Handle
*handle_data
)
145 mono_coop_mutex_unlock (&handle_data
->signal_mutex
);
149 mono_w32handle_init (void)
151 static gboolean initialized
= FALSE
;
156 mono_coop_mutex_init (&scan_mutex
);
158 mono_coop_cond_init (&global_signal_cond
);
159 mono_coop_mutex_init (&global_signal_mutex
);
161 handles_slots_first
= handles_slots_last
= g_new0 (MonoW32HandleSlot
, 1);
167 mono_w32handle_cleanup (void)
169 MonoW32HandleSlot
*slot
, *slot_next
;
171 g_assert (!shutting_down
);
172 shutting_down
= TRUE
;
174 for (slot
= handles_slots_first
; slot
; slot
= slot_next
) {
175 slot_next
= slot
->next
;
181 mono_w32handle_ops_typesize (MonoW32Type type
);
184 * mono_w32handle_new_internal:
185 * @type: Init handle to this type
187 * Search for a free handle and initialize it. Return the handle on
188 * success and 0 on failure. This is only called from
189 * mono_w32handle_new, and scan_mutex must be held.
191 static MonoW32Handle
*
192 mono_w32handle_new_internal (MonoW32Type type
, gpointer handle_specific
)
194 static MonoW32HandleSlot
*slot_last
= NULL
;
195 static guint32 index_last
= 0;
196 MonoW32HandleSlot
*slot
;
201 slot_last
= handles_slots_first
;
202 g_assert (slot_last
);
205 /* A linear scan should be fast enough. Start from the last allocation, assuming that handles are allocated more
206 * often than they're freed. */
208 retry_from_beginning
:
215 g_assert (index
>= 0);
216 g_assert (index
<= HANDLES_PER_SLOT
);
219 for(; slot
; slot
= slot
->next
) {
220 for (; index
< HANDLES_PER_SLOT
; index
++) {
221 MonoW32Handle
*handle_data
= &slot
->handles
[index
];
223 if (handle_data
->type
== MONO_W32TYPE_UNUSED
) {
225 index_last
= index
+ 1;
227 g_assert (handle_data
->ref
== 0);
229 handle_data
->type
= type
;
230 handle_data
->signalled
= FALSE
;
231 handle_data
->ref
= 1;
233 mono_coop_cond_init (&handle_data
->signal_cond
);
234 mono_coop_mutex_init (&handle_data
->signal_mutex
);
237 handle_data
->specific
= g_memdup (handle_specific
, mono_w32handle_ops_typesize (type
));
246 /* Try again from the beginning */
247 slot
= handles_slots_first
;
253 handles_slots_last
= (handles_slots_last
->next
= g_new0 (MonoW32HandleSlot
, 1));
254 goto retry_from_beginning
;
256 /* We already went around and didn't find a slot, so let's put ourselves on the empty slot we just allocated */
257 slot_last
= handles_slots_last
;
262 mono_w32handle_new (MonoW32Type type
, gpointer handle_specific
)
264 MonoW32Handle
*handle_data
;
266 g_assert (!shutting_down
);
268 mono_coop_mutex_lock (&scan_mutex
);
270 handle_data
= mono_w32handle_new_internal (type
, handle_specific
);
271 g_assert (handle_data
);
273 mono_coop_mutex_unlock (&scan_mutex
);
275 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: create %s handle %p", __func__
, mono_w32handle_ops_typename (type
), handle_data
);
277 return (gpointer
) handle_data
;
281 mono_w32handle_ref_core (MonoW32Handle
*handle_data
);
284 mono_w32handle_unref_core (MonoW32Handle
*handle_data
);
287 w32handle_destroy (MonoW32Handle
*handle_data
);
290 mono_w32handle_duplicate (MonoW32Handle
*handle_data
)
292 if (!mono_w32handle_ref_core (handle_data
))
293 g_error ("%s: unknown handle %p", __func__
, handle_data
);
295 return (gpointer
) handle_data
;
299 mono_w32handle_close (gpointer handle
)
301 MonoW32Handle
*handle_data
;
304 if (handle
== INVALID_HANDLE_VALUE
)
307 handle_data
= (MonoW32Handle
*) handle
;
309 if (handle_data
->type
== MONO_W32TYPE_UNUSED
)
312 destroy
= mono_w32handle_unref_core (handle_data
);
314 w32handle_destroy (handle_data
);
320 mono_w32handle_lookup_and_ref (gpointer handle
, MonoW32Handle
**handle_data
)
322 g_assert (handle_data
);
324 if (handle
== INVALID_HANDLE_VALUE
)
327 *handle_data
= (MonoW32Handle
*) handle
;
329 if (!mono_w32handle_ref_core (*handle_data
))
332 if ((*handle_data
)->type
== MONO_W32TYPE_UNUSED
) {
333 mono_w32handle_unref_core (*handle_data
);
341 mono_w32handle_foreach (gboolean (*on_each
)(MonoW32Handle
*handle_data
, gpointer user_data
), gpointer user_data
)
343 MonoW32HandleSlot
*slot
;
344 GPtrArray
*handles_to_destroy
;
347 handles_to_destroy
= NULL
;
349 mono_coop_mutex_lock (&scan_mutex
);
351 for (slot
= handles_slots_first
; slot
; slot
= slot
->next
) {
352 for (i
= 0; i
< HANDLES_PER_SLOT
; i
++) {
353 MonoW32Handle
*handle_data
;
354 gboolean destroy
, finished
;
356 handle_data
= &slot
->handles
[i
];
357 if (handle_data
->type
== MONO_W32TYPE_UNUSED
)
360 if (!mono_w32handle_ref_core (handle_data
)) {
361 /* we are racing with mono_w32handle_unref:
362 * the handle ref has been decremented, but it
363 * hasn't yet been destroyed. */
367 finished
= on_each (handle_data
, user_data
);
369 /* we might have to destroy the handle here, as
370 * it could have been unrefed in another thread */
371 destroy
= mono_w32handle_unref_core (handle_data
);
373 /* we do not destroy it while holding the scan_mutex
374 * lock, because w32handle_destroy also needs to take
375 * the lock, and it calls user code which might lead
377 if (!handles_to_destroy
)
378 handles_to_destroy
= g_ptr_array_sized_new (4);
379 g_ptr_array_add (handles_to_destroy
, (gpointer
) handle_data
);
388 mono_coop_mutex_unlock (&scan_mutex
);
390 if (handles_to_destroy
) {
391 for (i
= 0; i
< handles_to_destroy
->len
; ++i
)
392 w32handle_destroy ((MonoW32Handle
*) handles_to_destroy
->pdata
[i
]);
394 g_ptr_array_free (handles_to_destroy
, TRUE
);
399 mono_w32handle_ref_core (MonoW32Handle
*handle_data
)
404 old
= handle_data
->ref
;
409 } while (mono_atomic_cas_i32 ((gint32
*) &handle_data
->ref
, (gint32
)new_
, (gint32
)old
) != (gint32
)old
);
411 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: ref %s handle %p, ref: %d -> %d",
412 __func__
, mono_w32handle_ops_typename (handle_data
->type
), handle_data
, old
, new_
);
418 mono_w32handle_unref_core (MonoW32Handle
*handle_data
)
423 type
= handle_data
->type
;
426 old
= handle_data
->ref
;
428 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__
, handle_data
, old
);
431 } while (mono_atomic_cas_i32 ((gint32
*) &handle_data
->ref
, (gint32
)new_
, (gint32
)old
) != (gint32
)old
);
433 /* handle_data might contain invalid data from now on, if
434 * another thread is unref'ing this handle at the same time */
436 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: unref %s handle %p, ref: %d -> %d destroy: %s",
437 __func__
, mono_w32handle_ops_typename (type
), handle_data
, old
, new_
, new_
== 0 ? "true" : "false");
443 mono_w32handle_ops_close (MonoW32Type type
, gpointer handle_specific
);
446 w32handle_destroy (MonoW32Handle
*handle_data
)
448 /* Need to copy the handle info, reset the slot in the
449 * array, and _only then_ call the close function to
450 * avoid race conditions (eg file descriptors being
451 * closed, and another file being opened getting the
452 * same fd racing the memset())
455 gpointer handle_specific
;
457 g_assert (!handle_data
->in_use
);
459 type
= handle_data
->type
;
460 handle_specific
= handle_data
->specific
;
462 mono_coop_mutex_lock (&scan_mutex
);
464 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: destroy %s handle %p", __func__
, mono_w32handle_ops_typename (type
), handle_data
);
466 mono_coop_mutex_destroy (&handle_data
->signal_mutex
);
467 mono_coop_cond_destroy (&handle_data
->signal_cond
);
469 memset (handle_data
, 0, sizeof (MonoW32Handle
));
471 mono_coop_mutex_unlock (&scan_mutex
);
473 mono_w32handle_ops_close (type
, handle_specific
);
475 memset (handle_specific
, 0, mono_w32handle_ops_typesize (type
));
477 g_free (handle_specific
);
480 /* The handle must not be locked on entry to this function */
482 mono_w32handle_unref (MonoW32Handle
*handle_data
)
486 destroy
= mono_w32handle_unref_core (handle_data
);
488 w32handle_destroy (handle_data
);
492 mono_w32handle_register_ops (MonoW32Type type
, const MonoW32HandleOps
*ops
)
494 handle_ops
[type
] = ops
;
498 mono_w32handle_register_capabilities (MonoW32Type type
, MonoW32HandleCapability caps
)
500 handle_caps
[type
] = caps
;
504 mono_w32handle_test_capabilities (MonoW32Handle
*handle_data
, MonoW32HandleCapability caps
)
506 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: testing 0x%x against 0x%x (%d)", __func__
,
507 handle_caps
[handle_data
->type
], caps
, handle_caps
[handle_data
->type
] & caps
);
509 return (handle_caps
[handle_data
->type
] & caps
) != 0;
513 mono_w32handle_ops_close (MonoW32Type type
, gpointer data
)
515 const MonoW32HandleOps
*ops
= handle_ops
[type
];
516 if (ops
&& ops
->close
)
521 mono_w32handle_ops_details (MonoW32Handle
*handle_data
)
523 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->details
!= NULL
)
524 handle_ops
[handle_data
->type
]->details (handle_data
);
528 mono_w32handle_ops_typename (MonoW32Type type
)
530 g_assert (handle_ops
[type
]);
531 g_assert (handle_ops
[type
]->type_name
);
532 return handle_ops
[type
]->type_name ();
536 mono_w32handle_ops_typesize (MonoW32Type type
)
538 g_assert (handle_ops
[type
]);
539 g_assert (handle_ops
[type
]->typesize
);
540 return handle_ops
[type
]->typesize ();
544 mono_w32handle_ops_signal (MonoW32Handle
*handle_data
)
546 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->signal
)
547 return handle_ops
[handle_data
->type
]->signal (handle_data
);
549 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
553 mono_w32handle_ops_own (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
555 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->own_handle
)
556 return handle_ops
[handle_data
->type
]->own_handle (handle_data
, abandoned
);
562 mono_w32handle_ops_isowned (MonoW32Handle
*handle_data
)
564 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->is_owned
)
565 return handle_ops
[handle_data
->type
]->is_owned (handle_data
);
570 static MonoW32HandleWaitRet
571 mono_w32handle_ops_specialwait (MonoW32Handle
*handle_data
, guint32 timeout
, gboolean
*alerted
)
573 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->special_wait
)
574 return handle_ops
[handle_data
->type
]->special_wait (handle_data
, timeout
, alerted
);
576 return MONO_W32HANDLE_WAIT_RET_FAILED
;
580 mono_w32handle_ops_prewait (MonoW32Handle
*handle_data
)
582 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->prewait
)
583 handle_ops
[handle_data
->type
]->prewait (handle_data
);
587 mono_w32handle_lock_handles (MonoW32Handle
**handles_data
, gsize nhandles
)
591 struct timespec sleepytime
;
594 /* Lock all the handles, with backoff */
596 for (i
= 0; i
< nhandles
; i
++) {
597 if (!handles_data
[i
])
599 if (!mono_w32handle_trylock (handles_data
[i
])) {
602 for (j
= i
- 1; j
>= 0; j
--) {
603 if (!handles_data
[j
])
605 mono_w32handle_unlock (handles_data
[j
]);
614 SleepEx (iter
, TRUE
);
616 /* If iter ever reaches 1000 the nanosleep will
617 * return EINVAL immediately, but we have a
618 * design flaw if that happens. */
619 g_assert (iter
< 1000);
621 sleepytime
.tv_sec
= 0;
622 sleepytime
.tv_nsec
= iter
* 1000000;
623 nanosleep (&sleepytime
, NULL
);
624 #endif /* HOST_WIN32 */
631 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: Locked all handles", __func__
);
635 mono_w32handle_unlock_handles (MonoW32Handle
**handles_data
, gsize nhandles
)
639 for (i
= nhandles
- 1; i
>= 0; i
--) {
640 if (!handles_data
[i
])
642 mono_w32handle_unlock (handles_data
[i
]);
647 mono_w32handle_timedwait_signal_naked (MonoCoopCond
*cond
, MonoCoopMutex
*mutex
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
652 res
= mono_coop_cond_timedwait (cond
, mutex
, timeout
);
654 /* This is needed when waiting for process handles */
657 * pthread_cond_(timed)wait() can return 0 even if the condition was not
658 * signalled. This happens at least on Darwin. We surface this, i.e., we
659 * get spurious wake-ups.
661 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
663 res
= mono_coop_cond_timedwait (cond
, mutex
, timeout
);
666 /* Real timeout is less than 100ms time */
667 res
= mono_coop_cond_timedwait (cond
, mutex
, timeout
);
669 res
= mono_coop_cond_timedwait (cond
, mutex
, 100);
671 /* Mask the fake timeout, this will cause
672 * another poll if the cond was not really signaled
684 signal_global (gpointer unused
)
686 /* If we reach here, then interrupt token is set to the flag value, which
687 * means that the target thread is either
688 * - before the first CAS in timedwait, which means it won't enter the wait.
689 * - it is after the first CAS, so it is already waiting, or it will enter
690 * the wait, and it will be interrupted by the broadcast. */
691 mono_coop_mutex_lock (&global_signal_mutex
);
692 mono_coop_cond_broadcast (&global_signal_cond
);
693 mono_coop_mutex_unlock (&global_signal_mutex
);
697 mono_w32handle_timedwait_signal (guint32 timeout
, gboolean poll
, gboolean
*alerted
)
701 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: waiting for global", __func__
);
707 mono_thread_info_install_interrupt (signal_global
, NULL
, alerted
);
712 res
= mono_w32handle_timedwait_signal_naked (&global_signal_cond
, &global_signal_mutex
, timeout
, poll
, alerted
);
715 mono_thread_info_uninstall_interrupt (alerted
);
721 signal_handle_and_unref (gpointer handle_duplicate
)
723 MonoW32Handle
*handle_data
;
725 MonoCoopMutex
*mutex
;
727 if (!mono_w32handle_lookup_and_ref (handle_duplicate
, &handle_data
))
728 g_error ("%s: unknown handle %p", __func__
, handle_duplicate
);
730 /* If we reach here, then interrupt token is set to the flag value, which
731 * means that the target thread is either
732 * - before the first CAS in timedwait, which means it won't enter the wait.
733 * - it is after the first CAS, so it is already waiting, or it will enter
734 * the wait, and it will be interrupted by the broadcast. */
735 cond
= &handle_data
->signal_cond
;
736 mutex
= &handle_data
->signal_mutex
;
738 mono_coop_mutex_lock (mutex
);
739 mono_coop_cond_broadcast (cond
);
740 mono_coop_mutex_unlock (mutex
);
742 mono_w32handle_unref (handle_data
);
744 mono_w32handle_close (handle_duplicate
);
748 mono_w32handle_timedwait_signal_handle (MonoW32Handle
*handle_data
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
750 gpointer handle_duplicate
;
753 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: waiting for %p (type %s)", __func__
, handle_data
,
754 mono_w32handle_ops_typename (handle_data
->type
));
760 mono_thread_info_install_interrupt (signal_handle_and_unref
, handle_duplicate
= mono_w32handle_duplicate (handle_data
), alerted
);
762 mono_w32handle_close (handle_duplicate
);
767 res
= mono_w32handle_timedwait_signal_naked (&handle_data
->signal_cond
, &handle_data
->signal_mutex
, timeout
, poll
, alerted
);
770 mono_thread_info_uninstall_interrupt (alerted
);
772 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
773 mono_w32handle_close (handle_duplicate
);
781 dump_callback (MonoW32Handle
*handle_data
, gpointer user_data
)
783 g_print ("%p [%7s] signalled: %5s ref: %3d ",
784 handle_data
, mono_w32handle_ops_typename (handle_data
->type
), handle_data
->signalled
? "true" : "false", handle_data
->ref
- 1 /* foreach increase ref by 1 */);
785 mono_w32handle_ops_details (handle_data
);
791 void mono_w32handle_dump (void)
793 mono_w32handle_foreach (dump_callback
, NULL
);
797 own_if_signalled (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
799 if (!mono_w32handle_issignalled (handle_data
))
803 mono_w32handle_ops_own (handle_data
, abandoned
);
808 own_if_owned (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
810 if (!mono_w32handle_ops_isowned (handle_data
))
814 mono_w32handle_ops_own (handle_data
, abandoned
);
820 mono_w32handle_wait_one (gpointer handle
, guint32 timeout
, gboolean alertable
)
822 return mono_w32handle_convert_wait_ret (mono_coop_win32_wait_for_single_object_ex (handle
, timeout
, alertable
), 1);
826 mono_w32handle_wait_one (gpointer handle
, guint32 timeout
, gboolean alertable
)
828 MonoW32Handle
*handle_data
;
829 MonoW32HandleWaitRet ret
;
832 gboolean abandoned
= FALSE
;
836 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
))
837 return MONO_W32HANDLE_WAIT_RET_FAILED
;
839 if (mono_w32handle_test_capabilities (handle_data
, MONO_W32HANDLE_CAP_SPECIAL_WAIT
)) {
840 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p has special wait", __func__
, handle_data
);
842 mono_w32handle_unref (handle_data
);
843 return mono_w32handle_ops_specialwait (handle_data
, timeout
, alertable
? &alerted
: NULL
);
846 if (!mono_w32handle_test_capabilities (handle_data
, MONO_W32HANDLE_CAP_WAIT
)) {
847 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p can't be waited for", __func__
, handle_data
);
849 mono_w32handle_unref (handle_data
);
850 return MONO_W32HANDLE_WAIT_RET_FAILED
;
853 mono_w32handle_lock (handle_data
);
855 if (mono_w32handle_test_capabilities (handle_data
, MONO_W32HANDLE_CAP_OWN
)) {
856 if (own_if_owned (handle_data
, &abandoned
)) {
857 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p already owned", __func__
, handle_data
);
859 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
864 if (timeout
!= MONO_INFINITE_WAIT
)
865 start
= mono_msec_ticks ();
867 mono_w32handle_set_in_use (handle_data
, TRUE
);
872 if (own_if_signalled (handle_data
, &abandoned
)) {
873 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p signalled", __func__
, handle_data
);
875 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
879 mono_w32handle_ops_prewait (handle_data
);
881 if (timeout
== MONO_INFINITE_WAIT
) {
882 waited
= mono_w32handle_timedwait_signal_handle (handle_data
, MONO_INFINITE_WAIT
, FALSE
, alertable
? &alerted
: NULL
);
886 elapsed
= mono_msec_ticks () - start
;
887 if (elapsed
> timeout
) {
888 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
892 waited
= mono_w32handle_timedwait_signal_handle (handle_data
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
896 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
901 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
907 mono_w32handle_set_in_use (handle_data
, FALSE
);
909 mono_w32handle_unlock (handle_data
);
911 mono_w32handle_unref (handle_data
);
915 #endif /* HOST_WIN32 */
917 static MonoW32Handle
*
918 mono_w32handle_has_duplicates (MonoW32Handle
*handles
[ ], gsize nhandles
)
920 if (nhandles
< 2 || nhandles
> MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
)
923 MonoW32Handle
*sorted
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
]; // 64
924 memcpy (sorted
, handles
, nhandles
* sizeof (handles
[0]));
925 mono_qsort (sorted
, nhandles
, sizeof (sorted
[0]), g_direct_equal
);
926 for (gsize i
= 1; i
< nhandles
; ++i
) {
927 MonoW32Handle
* const h1
= sorted
[i
- 1];
928 MonoW32Handle
* const h2
= sorted
[i
];
937 mono_w32handle_clear_duplicates (MonoW32Handle
*handles
[ ], gsize nhandles
)
939 for (gsize i
= 0; i
< nhandles
; ++i
) {
942 for (gsize j
= i
+ 1; j
< nhandles
; ++j
) {
943 if (handles
[i
] == handles
[j
]) {
944 mono_w32handle_unref (handles
[j
]);
952 mono_w32handle_check_duplicates (MonoW32Handle
*handles
[ ], gsize nhandles
, gboolean waitall
, MonoError
*error
)
954 // Duplication is ok for WaitAny, exception for WaitAll.
955 // System.DuplicateWaitObjectException: Duplicate objects in argument.
957 MonoW32Handle
*duplicate
= mono_w32handle_has_duplicates (handles
, nhandles
);
962 mono_error_set_duplicate_wait_object (error
);
963 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "mono_w32handle_wait_multiple: handle %p is duplicated", duplicate
);
967 // There is at least one duplicate. This is not an error.
968 // Remove all duplicates -- in-place in order to return the
969 // lowest signaled, equal to the caller's indices, and ease
970 // the exit path's dereference.
971 // That is, we cannot use sorted data, nor can we
972 // compress the array to remove elements. We must operate
973 // on each element in its original index, but we can skip some.
975 mono_w32handle_clear_duplicates (handles
, nhandles
);
980 mono_w32handle_wait_multiple (gpointer
*handles
, gsize nhandles
, gboolean waitall
, guint32 timeout
, gboolean alertable
, MonoError
*error
)
982 DWORD
const wait_result
= (nhandles
!= 1)
983 ? mono_coop_win32_wait_for_multiple_objects_ex (nhandles
, handles
, waitall
, timeout
, alertable
, error
)
984 : mono_coop_win32_wait_for_single_object_ex (handles
[0], timeout
, alertable
);
985 return mono_w32handle_convert_wait_ret (wait_result
, nhandles
);
989 mono_w32handle_wait_multiple (gpointer
*handles
, gsize nhandles
, gboolean waitall
, guint32 timeout
, gboolean alertable
, MonoError
*error
)
991 MonoW32HandleWaitRet ret
;
992 gboolean alerted
, poll
;
995 MonoW32Handle
*handles_data
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
];
996 gboolean abandoned
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
] = {0};
999 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1002 return mono_w32handle_wait_one (handles
[0], timeout
, alertable
);
1006 if (nhandles
> MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
) {
1007 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: too many handles: %zd",
1008 __func__
, nhandles
);
1010 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1013 for (i
= 0; i
< nhandles
; ++i
) {
1014 if (!mono_w32handle_lookup_and_ref (handles
[i
], &handles_data
[i
])) {
1016 mono_w32handle_unref (handles_data
[i
]);
1017 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1021 for (i
= 0; i
< nhandles
; ++i
) {
1022 if (!mono_w32handle_test_capabilities (handles_data
[i
], MONO_W32HANDLE_CAP_WAIT
)
1023 && !mono_w32handle_test_capabilities (handles_data
[i
], MONO_W32HANDLE_CAP_SPECIAL_WAIT
))
1025 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p can't be waited for", __func__
, handles_data
[i
]);
1027 ret
= MONO_W32HANDLE_WAIT_RET_FAILED
;
1032 mono_w32handle_check_duplicates (handles_data
, nhandles
, waitall
, error
);
1033 if (!is_ok (error
)) {
1034 ret
= MONO_W32HANDLE_WAIT_RET_FAILED
;
1039 for (i
= 0; i
< nhandles
; ++i
) {
1040 if (!handles_data
[i
])
1042 if (handles_data
[i
]->type
== MONO_W32TYPE_PROCESS
) {
1043 /* Can't wait for a process handle + another handle without polling */
1048 if (timeout
!= MONO_INFINITE_WAIT
)
1049 start
= mono_msec_ticks ();
1052 gsize count
, lowest
;
1059 mono_w32handle_lock_handles (handles_data
, nhandles
);
1061 for (i
= 0; i
< nhandles
; i
++) {
1062 if (!handles_data
[i
])
1064 if ((mono_w32handle_test_capabilities (handles_data
[i
], MONO_W32HANDLE_CAP_OWN
) && mono_w32handle_ops_isowned (handles_data
[i
]))
1065 || mono_w32handle_issignalled (handles_data
[i
]))
1074 signalled
= (waitall
&& count
== nhandles
) || (!waitall
&& count
> 0);
1077 for (i
= 0; i
< nhandles
; i
++) {
1078 if (!handles_data
[i
])
1080 if (own_if_signalled (handles_data
[i
], &abandoned
[i
]) && !waitall
) {
1081 /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
1082 * throw AbandonedMutexException in case we owned it but didn't release it */
1088 mono_w32handle_unlock_handles (handles_data
, nhandles
);
1091 ret
= (MonoW32HandleWaitRet
)(MONO_W32HANDLE_WAIT_RET_SUCCESS_0
+ lowest
);
1092 for (i
= lowest
; i
< nhandles
; i
++) {
1093 if (abandoned
[i
]) {
1094 ret
= (MonoW32HandleWaitRet
)(MONO_W32HANDLE_WAIT_RET_ABANDONED_0
+ lowest
);
1101 for (i
= 0; i
< nhandles
; i
++) {
1102 if (!handles_data
[i
])
1104 mono_w32handle_ops_prewait (handles_data
[i
]);
1106 if (mono_w32handle_test_capabilities (handles_data
[i
], MONO_W32HANDLE_CAP_SPECIAL_WAIT
)
1107 && !mono_w32handle_issignalled (handles_data
[i
]))
1109 mono_w32handle_ops_specialwait (handles_data
[i
], 0, alertable
? &alerted
: NULL
);
1113 mono_w32handle_lock_signal_mutex ();
1115 // FIXME These two loops can be just one.
1118 for (i
= 0; i
< nhandles
; ++i
) {
1119 if (!handles_data
[i
])
1121 if (!mono_w32handle_issignalled (handles_data
[i
])) {
1128 for (i
= 0; i
< nhandles
; ++i
) {
1129 if (!handles_data
[i
])
1131 if (mono_w32handle_issignalled (handles_data
[i
])) {
1141 if (timeout
== MONO_INFINITE_WAIT
) {
1142 waited
= mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT
, poll
, alertable
? &alerted
: NULL
);
1146 elapsed
= mono_msec_ticks () - start
;
1147 if (elapsed
> timeout
) {
1148 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1150 mono_w32handle_unlock_signal_mutex ();
1155 waited
= mono_w32handle_timedwait_signal (timeout
- elapsed
, poll
, alertable
? &alerted
: NULL
);
1159 mono_w32handle_unlock_signal_mutex ();
1162 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1167 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1173 for (i
= nhandles
- 1; i
>= 0; i
--) {
1174 /* Unref everything we reffed above */
1175 if (!handles_data
[i
])
1177 mono_w32handle_unref (handles_data
[i
]);
1182 #endif /* HOST_WIN32 */
1185 MonoW32HandleWaitRet
1186 mono_w32handle_signal_and_wait (gpointer signal_handle
, gpointer wait_handle
, guint32 timeout
, gboolean alertable
)
1188 return mono_w32handle_convert_wait_ret (mono_coop_win32_signal_object_and_wait (signal_handle
, wait_handle
, timeout
, alertable
), 1);
1191 MonoW32HandleWaitRet
1192 mono_w32handle_signal_and_wait (gpointer signal_handle
, gpointer wait_handle
, guint32 timeout
, gboolean alertable
)
1194 MonoW32Handle
*signal_handle_data
, *wait_handle_data
, *handles_data
[2];
1195 MonoW32HandleWaitRet ret
;
1198 gboolean abandoned
= FALSE
;
1202 if (!mono_w32handle_lookup_and_ref (signal_handle
, &signal_handle_data
)) {
1203 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1205 if (!mono_w32handle_lookup_and_ref (wait_handle
, &wait_handle_data
)) {
1206 mono_w32handle_unref (signal_handle_data
);
1207 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1210 if (!mono_w32handle_test_capabilities (signal_handle_data
, MONO_W32HANDLE_CAP_SIGNAL
)) {
1211 mono_w32handle_unref (wait_handle_data
);
1212 mono_w32handle_unref (signal_handle_data
);
1213 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1215 if (!mono_w32handle_test_capabilities (wait_handle_data
, MONO_W32HANDLE_CAP_WAIT
)) {
1216 mono_w32handle_unref (wait_handle_data
);
1217 mono_w32handle_unref (signal_handle_data
);
1218 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1221 if (mono_w32handle_test_capabilities (wait_handle_data
, MONO_W32HANDLE_CAP_SPECIAL_WAIT
)) {
1222 g_warning ("%s: handle %p has special wait, implement me!!", __func__
, wait_handle_data
);
1223 mono_w32handle_unref (wait_handle_data
);
1224 mono_w32handle_unref (signal_handle_data
);
1225 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1228 handles_data
[0] = wait_handle_data
;
1229 handles_data
[1] = signal_handle_data
;
1231 mono_w32handle_lock_handles (handles_data
, 2);
1233 gint32 signal_ret
= mono_w32handle_ops_signal (signal_handle_data
);
1235 mono_w32handle_unlock (signal_handle_data
);
1237 if (signal_ret
== MONO_W32HANDLE_WAIT_RET_TOO_MANY_POSTS
||
1238 signal_ret
== MONO_W32HANDLE_WAIT_RET_NOT_OWNED_BY_CALLER
) {
1239 ret
= (MonoW32HandleWaitRet
) signal_ret
;
1243 if (mono_w32handle_test_capabilities (wait_handle_data
, MONO_W32HANDLE_CAP_OWN
)) {
1244 if (own_if_owned (wait_handle_data
, &abandoned
)) {
1245 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p already owned", __func__
, wait_handle_data
);
1247 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1252 if (timeout
!= MONO_INFINITE_WAIT
)
1253 start
= mono_msec_ticks ();
1258 if (own_if_signalled (wait_handle_data
, &abandoned
)) {
1259 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p signalled", __func__
, wait_handle_data
);
1261 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1265 mono_w32handle_ops_prewait (wait_handle_data
);
1267 if (timeout
== MONO_INFINITE_WAIT
) {
1268 waited
= mono_w32handle_timedwait_signal_handle (wait_handle_data
, MONO_INFINITE_WAIT
, FALSE
, alertable
? &alerted
: NULL
);
1272 elapsed
= mono_msec_ticks () - start
;
1273 if (elapsed
> timeout
) {
1274 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1278 waited
= mono_w32handle_timedwait_signal_handle (wait_handle_data
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
1282 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1287 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1293 mono_w32handle_unlock (wait_handle_data
);
1295 mono_w32handle_unref (wait_handle_data
);
1296 mono_w32handle_unref (signal_handle_data
);
1300 #endif /* HOST_WIN32 */