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 handle_ops
[handle_data
->type
]->signal (handle_data
);
551 mono_w32handle_ops_own (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
553 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->own_handle
)
554 return handle_ops
[handle_data
->type
]->own_handle (handle_data
, abandoned
);
560 mono_w32handle_ops_isowned (MonoW32Handle
*handle_data
)
562 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->is_owned
)
563 return handle_ops
[handle_data
->type
]->is_owned (handle_data
);
568 static MonoW32HandleWaitRet
569 mono_w32handle_ops_specialwait (MonoW32Handle
*handle_data
, guint32 timeout
, gboolean
*alerted
)
571 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->special_wait
)
572 return handle_ops
[handle_data
->type
]->special_wait (handle_data
, timeout
, alerted
);
574 return MONO_W32HANDLE_WAIT_RET_FAILED
;
578 mono_w32handle_ops_prewait (MonoW32Handle
*handle_data
)
580 if (handle_ops
[handle_data
->type
] && handle_ops
[handle_data
->type
]->prewait
)
581 handle_ops
[handle_data
->type
]->prewait (handle_data
);
585 mono_w32handle_lock_handles (MonoW32Handle
**handles_data
, gsize nhandles
)
589 struct timespec sleepytime
;
592 /* Lock all the handles, with backoff */
594 for (i
= 0; i
< nhandles
; i
++) {
595 if (!handles_data
[i
])
597 if (!mono_w32handle_trylock (handles_data
[i
])) {
600 for (j
= i
- 1; j
>= 0; j
--) {
601 if (!handles_data
[j
])
603 mono_w32handle_unlock (handles_data
[j
]);
612 SleepEx (iter
, TRUE
);
614 /* If iter ever reaches 1000 the nanosleep will
615 * return EINVAL immediately, but we have a
616 * design flaw if that happens. */
617 g_assert (iter
< 1000);
619 sleepytime
.tv_sec
= 0;
620 sleepytime
.tv_nsec
= iter
* 1000000;
621 nanosleep (&sleepytime
, NULL
);
622 #endif /* HOST_WIN32 */
629 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: Locked all handles", __func__
);
633 mono_w32handle_unlock_handles (MonoW32Handle
**handles_data
, gsize nhandles
)
637 for (i
= nhandles
- 1; i
>= 0; i
--) {
638 if (!handles_data
[i
])
640 mono_w32handle_unlock (handles_data
[i
]);
645 mono_w32handle_timedwait_signal_naked (MonoCoopCond
*cond
, MonoCoopMutex
*mutex
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
650 res
= mono_coop_cond_timedwait (cond
, mutex
, timeout
);
652 /* This is needed when waiting for process handles */
655 * pthread_cond_(timed)wait() can return 0 even if the condition was not
656 * signalled. This happens at least on Darwin. We surface this, i.e., we
657 * get spurious wake-ups.
659 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
661 res
= mono_coop_cond_timedwait (cond
, mutex
, timeout
);
664 /* Real timeout is less than 100ms time */
665 res
= mono_coop_cond_timedwait (cond
, mutex
, timeout
);
667 res
= mono_coop_cond_timedwait (cond
, mutex
, 100);
669 /* Mask the fake timeout, this will cause
670 * another poll if the cond was not really signaled
682 signal_global (gpointer unused
)
684 /* If we reach here, then interrupt token is set to the flag value, which
685 * means that the target thread is either
686 * - before the first CAS in timedwait, which means it won't enter the wait.
687 * - it is after the first CAS, so it is already waiting, or it will enter
688 * the wait, and it will be interrupted by the broadcast. */
689 mono_coop_mutex_lock (&global_signal_mutex
);
690 mono_coop_cond_broadcast (&global_signal_cond
);
691 mono_coop_mutex_unlock (&global_signal_mutex
);
695 mono_w32handle_timedwait_signal (guint32 timeout
, gboolean poll
, gboolean
*alerted
)
699 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: waiting for global", __func__
);
705 mono_thread_info_install_interrupt (signal_global
, NULL
, alerted
);
710 res
= mono_w32handle_timedwait_signal_naked (&global_signal_cond
, &global_signal_mutex
, timeout
, poll
, alerted
);
713 mono_thread_info_uninstall_interrupt (alerted
);
719 signal_handle_and_unref (gpointer handle_duplicate
)
721 MonoW32Handle
*handle_data
;
723 MonoCoopMutex
*mutex
;
725 if (!mono_w32handle_lookup_and_ref (handle_duplicate
, &handle_data
))
726 g_error ("%s: unknown handle %p", __func__
, handle_duplicate
);
728 /* If we reach here, then interrupt token is set to the flag value, which
729 * means that the target thread is either
730 * - before the first CAS in timedwait, which means it won't enter the wait.
731 * - it is after the first CAS, so it is already waiting, or it will enter
732 * the wait, and it will be interrupted by the broadcast. */
733 cond
= &handle_data
->signal_cond
;
734 mutex
= &handle_data
->signal_mutex
;
736 mono_coop_mutex_lock (mutex
);
737 mono_coop_cond_broadcast (cond
);
738 mono_coop_mutex_unlock (mutex
);
740 mono_w32handle_unref (handle_data
);
742 mono_w32handle_close (handle_duplicate
);
746 mono_w32handle_timedwait_signal_handle (MonoW32Handle
*handle_data
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
748 gpointer handle_duplicate
;
751 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: waiting for %p (type %s)", __func__
, handle_data
,
752 mono_w32handle_ops_typename (handle_data
->type
));
758 mono_thread_info_install_interrupt (signal_handle_and_unref
, handle_duplicate
= mono_w32handle_duplicate (handle_data
), alerted
);
760 mono_w32handle_close (handle_duplicate
);
765 res
= mono_w32handle_timedwait_signal_naked (&handle_data
->signal_cond
, &handle_data
->signal_mutex
, timeout
, poll
, alerted
);
768 mono_thread_info_uninstall_interrupt (alerted
);
770 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
771 mono_w32handle_close (handle_duplicate
);
779 dump_callback (MonoW32Handle
*handle_data
, gpointer user_data
)
781 g_print ("%p [%7s] signalled: %5s ref: %3d ",
782 handle_data
, mono_w32handle_ops_typename (handle_data
->type
), handle_data
->signalled
? "true" : "false", handle_data
->ref
- 1 /* foreach increase ref by 1 */);
783 mono_w32handle_ops_details (handle_data
);
789 void mono_w32handle_dump (void)
791 mono_w32handle_foreach (dump_callback
, NULL
);
795 own_if_signalled (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
797 if (!mono_w32handle_issignalled (handle_data
))
801 mono_w32handle_ops_own (handle_data
, abandoned
);
806 own_if_owned (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
808 if (!mono_w32handle_ops_isowned (handle_data
))
812 mono_w32handle_ops_own (handle_data
, abandoned
);
817 mono_w32handle_wait_one (gpointer handle
, guint32 timeout
, gboolean alertable
)
819 MonoW32Handle
*handle_data
;
820 MonoW32HandleWaitRet ret
;
823 gboolean abandoned
= FALSE
;
827 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
))
828 return MONO_W32HANDLE_WAIT_RET_FAILED
;
830 if (mono_w32handle_test_capabilities (handle_data
, MONO_W32HANDLE_CAP_SPECIAL_WAIT
)) {
831 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p has special wait", __func__
, handle_data
);
833 mono_w32handle_unref (handle_data
);
834 return mono_w32handle_ops_specialwait (handle_data
, timeout
, alertable
? &alerted
: NULL
);
837 if (!mono_w32handle_test_capabilities (handle_data
, MONO_W32HANDLE_CAP_WAIT
)) {
838 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p can't be waited for", __func__
, handle_data
);
840 mono_w32handle_unref (handle_data
);
841 return MONO_W32HANDLE_WAIT_RET_FAILED
;
844 mono_w32handle_lock (handle_data
);
846 if (mono_w32handle_test_capabilities (handle_data
, MONO_W32HANDLE_CAP_OWN
)) {
847 if (own_if_owned (handle_data
, &abandoned
)) {
848 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p already owned", __func__
, handle_data
);
850 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
855 if (timeout
!= MONO_INFINITE_WAIT
)
856 start
= mono_msec_ticks ();
858 mono_w32handle_set_in_use (handle_data
, TRUE
);
863 if (own_if_signalled (handle_data
, &abandoned
)) {
864 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p signalled", __func__
, handle_data
);
866 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
870 mono_w32handle_ops_prewait (handle_data
);
872 if (timeout
== MONO_INFINITE_WAIT
) {
873 waited
= mono_w32handle_timedwait_signal_handle (handle_data
, MONO_INFINITE_WAIT
, FALSE
, alertable
? &alerted
: NULL
);
877 elapsed
= mono_msec_ticks () - start
;
878 if (elapsed
> timeout
) {
879 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
883 waited
= mono_w32handle_timedwait_signal_handle (handle_data
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
887 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
892 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
898 mono_w32handle_set_in_use (handle_data
, FALSE
);
900 mono_w32handle_unlock (handle_data
);
902 mono_w32handle_unref (handle_data
);
907 static MonoW32Handle
*
908 mono_w32handle_has_duplicates (MonoW32Handle
*handles
[ ], gsize nhandles
)
910 if (nhandles
< 2 || nhandles
> MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
)
913 MonoW32Handle
*sorted
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
]; // 64
914 memcpy (sorted
, handles
, nhandles
* sizeof (handles
[0]));
915 qsort (sorted
, nhandles
, sizeof (sorted
[0]), g_direct_equal
);
916 for (gsize i
= 1; i
< nhandles
; ++i
) {
917 MonoW32Handle
* const h1
= sorted
[i
- 1];
918 MonoW32Handle
* const h2
= sorted
[i
];
927 mono_w32handle_clear_duplicates (MonoW32Handle
*handles
[ ], gsize nhandles
)
929 for (gsize i
= 0; i
< nhandles
; ++i
) {
932 for (gsize j
= i
+ 1; j
< nhandles
; ++j
) {
933 if (handles
[i
] == handles
[j
]) {
934 mono_w32handle_unref (handles
[j
]);
942 mono_w32handle_check_duplicates (MonoW32Handle
*handles
[ ], gsize nhandles
, gboolean waitall
, MonoError
*error
)
944 // Duplication is ok for WaitAny, exception for WaitAll.
945 // System.DuplicateWaitObjectException: Duplicate objects in argument.
947 MonoW32Handle
*duplicate
= mono_w32handle_has_duplicates (handles
, nhandles
);
952 mono_error_set_duplicate_wait_object (error
);
953 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "mono_w32handle_wait_multiple: handle %p is duplicated", duplicate
);
957 // There is at least one duplicate. This is not an error.
958 // Remove all duplicates -- in-place in order to return the
959 // lowest signaled, equal to the caller's indices, and ease
960 // the exit path's dereference.
961 // That is, we cannot use sorted data, nor can we
962 // compress the array to remove elements. We must operate
963 // on each element in its original index, but we can skip some.
965 mono_w32handle_clear_duplicates (handles
, nhandles
);
969 mono_w32handle_wait_multiple (gpointer
*handles
, gsize nhandles
, gboolean waitall
, guint32 timeout
, gboolean alertable
, MonoError
*error
)
971 MonoW32HandleWaitRet ret
;
972 gboolean alerted
, poll
;
975 MonoW32Handle
*handles_data
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
];
976 gboolean abandoned
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
] = {0};
979 return MONO_W32HANDLE_WAIT_RET_FAILED
;
982 return mono_w32handle_wait_one (handles
[0], timeout
, alertable
);
986 if (nhandles
> MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
) {
987 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: too many handles: %zd",
990 return MONO_W32HANDLE_WAIT_RET_FAILED
;
993 for (i
= 0; i
< nhandles
; ++i
) {
994 if (!mono_w32handle_lookup_and_ref (handles
[i
], &handles_data
[i
])) {
996 mono_w32handle_unref (handles_data
[i
]);
997 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1001 for (i
= 0; i
< nhandles
; ++i
) {
1002 if (!mono_w32handle_test_capabilities (handles_data
[i
], MONO_W32HANDLE_CAP_WAIT
)
1003 && !mono_w32handle_test_capabilities (handles_data
[i
], MONO_W32HANDLE_CAP_SPECIAL_WAIT
))
1005 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p can't be waited for", __func__
, handles_data
[i
]);
1007 ret
= MONO_W32HANDLE_WAIT_RET_FAILED
;
1012 mono_w32handle_check_duplicates (handles_data
, nhandles
, waitall
, error
);
1013 if (!is_ok (error
)) {
1014 ret
= MONO_W32HANDLE_WAIT_RET_FAILED
;
1019 for (i
= 0; i
< nhandles
; ++i
) {
1020 if (!handles_data
[i
])
1022 if (handles_data
[i
]->type
== MONO_W32TYPE_PROCESS
) {
1023 /* Can't wait for a process handle + another handle without polling */
1028 if (timeout
!= MONO_INFINITE_WAIT
)
1029 start
= mono_msec_ticks ();
1032 gsize count
, lowest
;
1039 mono_w32handle_lock_handles (handles_data
, nhandles
);
1041 for (i
= 0; i
< nhandles
; i
++) {
1042 if (!handles_data
[i
])
1044 if ((mono_w32handle_test_capabilities (handles_data
[i
], MONO_W32HANDLE_CAP_OWN
) && mono_w32handle_ops_isowned (handles_data
[i
]))
1045 || mono_w32handle_issignalled (handles_data
[i
]))
1054 signalled
= (waitall
&& count
== nhandles
) || (!waitall
&& count
> 0);
1057 for (i
= 0; i
< nhandles
; i
++) {
1058 if (!handles_data
[i
])
1060 if (own_if_signalled (handles_data
[i
], &abandoned
[i
]) && !waitall
) {
1061 /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
1062 * throw AbandonedMutexException in case we owned it but didn't release it */
1068 mono_w32handle_unlock_handles (handles_data
, nhandles
);
1071 ret
= (MonoW32HandleWaitRet
)(MONO_W32HANDLE_WAIT_RET_SUCCESS_0
+ lowest
);
1072 for (i
= lowest
; i
< nhandles
; i
++) {
1073 if (abandoned
[i
]) {
1074 ret
= (MonoW32HandleWaitRet
)(MONO_W32HANDLE_WAIT_RET_ABANDONED_0
+ lowest
);
1081 for (i
= 0; i
< nhandles
; i
++) {
1082 if (!handles_data
[i
])
1084 mono_w32handle_ops_prewait (handles_data
[i
]);
1086 if (mono_w32handle_test_capabilities (handles_data
[i
], MONO_W32HANDLE_CAP_SPECIAL_WAIT
)
1087 && !mono_w32handle_issignalled (handles_data
[i
]))
1089 mono_w32handle_ops_specialwait (handles_data
[i
], 0, alertable
? &alerted
: NULL
);
1093 mono_w32handle_lock_signal_mutex ();
1095 // FIXME These two loops can be just one.
1098 for (i
= 0; i
< nhandles
; ++i
) {
1099 if (!handles_data
[i
])
1101 if (!mono_w32handle_issignalled (handles_data
[i
])) {
1108 for (i
= 0; i
< nhandles
; ++i
) {
1109 if (!handles_data
[i
])
1111 if (mono_w32handle_issignalled (handles_data
[i
])) {
1121 if (timeout
== MONO_INFINITE_WAIT
) {
1122 waited
= mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT
, poll
, alertable
? &alerted
: NULL
);
1126 elapsed
= mono_msec_ticks () - start
;
1127 if (elapsed
> timeout
) {
1128 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1130 mono_w32handle_unlock_signal_mutex ();
1135 waited
= mono_w32handle_timedwait_signal (timeout
- elapsed
, poll
, alertable
? &alerted
: NULL
);
1139 mono_w32handle_unlock_signal_mutex ();
1142 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1147 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1153 for (i
= nhandles
- 1; i
>= 0; i
--) {
1154 /* Unref everything we reffed above */
1155 if (!handles_data
[i
])
1157 mono_w32handle_unref (handles_data
[i
]);
1163 MonoW32HandleWaitRet
1164 mono_w32handle_signal_and_wait (gpointer signal_handle
, gpointer wait_handle
, guint32 timeout
, gboolean alertable
)
1166 MonoW32Handle
*signal_handle_data
, *wait_handle_data
, *handles_data
[2];
1167 MonoW32HandleWaitRet ret
;
1170 gboolean abandoned
= FALSE
;
1174 if (!mono_w32handle_lookup_and_ref (signal_handle
, &signal_handle_data
)) {
1175 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1177 if (!mono_w32handle_lookup_and_ref (wait_handle
, &wait_handle_data
)) {
1178 mono_w32handle_unref (signal_handle_data
);
1179 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1182 if (!mono_w32handle_test_capabilities (signal_handle_data
, MONO_W32HANDLE_CAP_SIGNAL
)) {
1183 mono_w32handle_unref (wait_handle_data
);
1184 mono_w32handle_unref (signal_handle_data
);
1185 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1187 if (!mono_w32handle_test_capabilities (wait_handle_data
, MONO_W32HANDLE_CAP_WAIT
)) {
1188 mono_w32handle_unref (wait_handle_data
);
1189 mono_w32handle_unref (signal_handle_data
);
1190 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1193 if (mono_w32handle_test_capabilities (wait_handle_data
, MONO_W32HANDLE_CAP_SPECIAL_WAIT
)) {
1194 g_warning ("%s: handle %p has special wait, implement me!!", __func__
, wait_handle_data
);
1195 mono_w32handle_unref (wait_handle_data
);
1196 mono_w32handle_unref (signal_handle_data
);
1197 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1200 handles_data
[0] = wait_handle_data
;
1201 handles_data
[1] = signal_handle_data
;
1203 mono_w32handle_lock_handles (handles_data
, 2);
1205 mono_w32handle_ops_signal (signal_handle_data
);
1207 mono_w32handle_unlock (signal_handle_data
);
1209 if (mono_w32handle_test_capabilities (wait_handle_data
, MONO_W32HANDLE_CAP_OWN
)) {
1210 if (own_if_owned (wait_handle_data
, &abandoned
)) {
1211 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p already owned", __func__
, wait_handle_data
);
1213 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1218 if (timeout
!= MONO_INFINITE_WAIT
)
1219 start
= mono_msec_ticks ();
1224 if (own_if_signalled (wait_handle_data
, &abandoned
)) {
1225 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_HANDLE
, "%s: handle %p signalled", __func__
, wait_handle_data
);
1227 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1231 mono_w32handle_ops_prewait (wait_handle_data
);
1233 if (timeout
== MONO_INFINITE_WAIT
) {
1234 waited
= mono_w32handle_timedwait_signal_handle (wait_handle_data
, MONO_INFINITE_WAIT
, FALSE
, alertable
? &alerted
: NULL
);
1238 elapsed
= mono_msec_ticks () - start
;
1239 if (elapsed
> timeout
) {
1240 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1244 waited
= mono_w32handle_timedwait_signal_handle (wait_handle_data
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
1248 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1253 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1259 mono_w32handle_unlock (wait_handle_data
);
1261 mono_w32handle_unref (wait_handle_data
);
1262 mono_w32handle_unref (signal_handle_data
);