[LoongArch64] Part-5:add loongarch support in some files for LoongArch64. (#21769)
[mono-project.git] / mono / metadata / w32handle.c
blob6de478863912b716aae1f4d475fe47221d1634df
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>
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"
24 #undef DEBUG_REFS
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;
52 static const gchar*
53 mono_w32handle_ops_typename (MonoW32Type type);
55 const gchar*
56 mono_w32handle_get_typename (MonoW32Type type)
58 return mono_w32handle_ops_typename (type);
61 void
62 mono_w32handle_set_signal_state (MonoW32Handle *handle_data, gboolean state, gboolean broadcast)
64 #ifdef DEBUG
65 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
66 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
67 #endif
69 if (state) {
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;
82 if (broadcast)
83 mono_coop_cond_broadcast (&handle_data->signal_cond);
84 else
85 mono_coop_cond_signal (&handle_data->signal_cond);
87 /* Tell everyone blocking on multiple handles that something
88 * was signalled
90 mono_coop_cond_broadcast (&global_signal_cond);
92 mono_coop_mutex_unlock (&global_signal_mutex);
93 } else {
94 handle_data->signalled = FALSE;
98 gboolean
99 mono_w32handle_issignalled (MonoW32Handle *handle_data)
101 return handle_data->signalled;
104 static void
105 mono_w32handle_set_in_use (MonoW32Handle *handle_data, gboolean in_use)
107 handle_data->in_use = in_use;
110 static void
111 mono_w32handle_lock_signal_mutex (void)
113 #ifdef DEBUG
114 g_message ("%s: lock global signal mutex", __func__);
115 #endif
117 mono_coop_mutex_lock (&global_signal_mutex);
120 static void
121 mono_w32handle_unlock_signal_mutex (void)
123 #ifdef DEBUG
124 g_message ("%s: unlock global signal mutex", __func__);
125 #endif
127 mono_coop_mutex_unlock (&global_signal_mutex);
130 void
131 mono_w32handle_lock (MonoW32Handle *handle_data)
133 mono_coop_mutex_lock (&handle_data->signal_mutex);
136 gboolean
137 mono_w32handle_trylock (MonoW32Handle *handle_data)
139 return mono_coop_mutex_trylock (&handle_data->signal_mutex) == 0;
142 void
143 mono_w32handle_unlock (MonoW32Handle *handle_data)
145 mono_coop_mutex_unlock (&handle_data->signal_mutex);
148 void
149 mono_w32handle_init (void)
151 static gboolean initialized = FALSE;
153 if (initialized)
154 return;
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);
163 initialized = TRUE;
166 void
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;
176 g_free (slot);
180 static gsize
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;
197 guint32 index;
198 gboolean retried;
200 if (!slot_last) {
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:
209 retried = FALSE;
211 slot = slot_last;
212 g_assert (slot);
214 index = index_last;
215 g_assert (index >= 0);
216 g_assert (index <= HANDLES_PER_SLOT);
218 retry:
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) {
224 slot_last = slot;
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);
236 if (handle_specific)
237 handle_data->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
239 return handle_data;
242 index = 0;
245 if (!retried) {
246 /* Try again from the beginning */
247 slot = handles_slots_first;
248 index = 0;
249 retried = TRUE;
250 goto retry;
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;
258 index_last = 0;
261 gpointer
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;
280 static gboolean
281 mono_w32handle_ref_core (MonoW32Handle *handle_data);
283 static gboolean
284 mono_w32handle_unref_core (MonoW32Handle *handle_data);
286 static void
287 w32handle_destroy (MonoW32Handle *handle_data);
289 gpointer
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;
298 gboolean
299 mono_w32handle_close (gpointer handle)
301 MonoW32Handle *handle_data;
302 gboolean destroy;
304 if (handle == INVALID_HANDLE_VALUE)
305 return FALSE;
307 handle_data = (MonoW32Handle*) handle;
309 if (handle_data->type == MONO_W32TYPE_UNUSED)
310 return FALSE;
312 destroy = mono_w32handle_unref_core (handle_data);
313 if (destroy)
314 w32handle_destroy (handle_data);
316 return TRUE;
319 gboolean
320 mono_w32handle_lookup_and_ref (gpointer handle, MonoW32Handle **handle_data)
322 g_assert (handle_data);
324 if (handle == INVALID_HANDLE_VALUE)
325 return FALSE;
327 *handle_data = (MonoW32Handle*) handle;
329 if (!mono_w32handle_ref_core (*handle_data))
330 return FALSE;
332 if ((*handle_data)->type == MONO_W32TYPE_UNUSED) {
333 mono_w32handle_unref_core (*handle_data);
334 return FALSE;
337 return TRUE;
340 void
341 mono_w32handle_foreach (gboolean (*on_each)(MonoW32Handle *handle_data, gpointer user_data), gpointer user_data)
343 MonoW32HandleSlot *slot;
344 GPtrArray *handles_to_destroy;
345 guint32 i;
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)
358 continue;
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. */
364 continue;
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);
372 if (destroy) {
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
376 * to a deadlock */
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);
382 if (finished)
383 goto done;
387 done:
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);
398 static gboolean
399 mono_w32handle_ref_core (MonoW32Handle *handle_data)
401 guint old, new_;
403 do {
404 old = handle_data->ref;
405 if (old == 0)
406 return FALSE;
408 new_ = old + 1;
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_);
414 return TRUE;
417 static gboolean
418 mono_w32handle_unref_core (MonoW32Handle *handle_data)
420 MonoW32Type type;
421 guint old, new_;
423 type = handle_data->type;
425 do {
426 old = handle_data->ref;
427 if (!(old >= 1))
428 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__, handle_data, old);
430 new_ = old - 1;
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");
439 return new_ == 0;
442 static void
443 mono_w32handle_ops_close (MonoW32Type type, gpointer handle_specific);
445 static void
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())
454 MonoW32Type type;
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 */
481 void
482 mono_w32handle_unref (MonoW32Handle *handle_data)
484 gboolean destroy;
486 destroy = mono_w32handle_unref_core (handle_data);
487 if (destroy)
488 w32handle_destroy (handle_data);
491 void
492 mono_w32handle_register_ops (MonoW32Type type, const MonoW32HandleOps *ops)
494 handle_ops [type] = ops;
497 void
498 mono_w32handle_register_capabilities (MonoW32Type type, MonoW32HandleCapability caps)
500 handle_caps[type] = caps;
503 static gboolean
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;
512 static void
513 mono_w32handle_ops_close (MonoW32Type type, gpointer data)
515 const MonoW32HandleOps *ops = handle_ops [type];
516 if (ops && ops->close)
517 ops->close (data);
520 static void
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);
527 static const gchar*
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 ();
535 static gsize
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 ();
543 static gint32
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;
552 static gboolean
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);
558 return FALSE;
561 static gboolean
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);
567 return FALSE;
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;
579 static void
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);
586 static void
587 mono_w32handle_lock_handles (MonoW32Handle **handles_data, gsize nhandles)
589 gint i, j, iter = 0;
590 #ifndef HOST_WIN32
591 struct timespec sleepytime;
592 #endif
594 /* Lock all the handles, with backoff */
595 again:
596 for (i = 0; i < nhandles; i++) {
597 if (!handles_data [i])
598 continue;
599 if (!mono_w32handle_trylock (handles_data [i])) {
601 for (j = i - 1; j >= 0; j--) {
602 if (!handles_data [j])
603 continue;
604 mono_w32handle_unlock (handles_data [j]);
607 iter += 10;
608 if (iter == 1000)
609 iter = 10;
611 MONO_ENTER_GC_SAFE;
612 #ifdef HOST_WIN32
613 SleepEx (iter, TRUE);
614 #else
615 /* If iter ever reaches 1000 the nanosleep will
616 * return EINVAL immediately, but we have a
617 * design flaw if that happens. */
618 g_assert (iter < 1000);
620 sleepytime.tv_sec = 0;
621 sleepytime.tv_nsec = iter * 1000000;
622 nanosleep (&sleepytime, NULL);
623 #endif /* HOST_WIN32 */
624 MONO_EXIT_GC_SAFE;
626 goto again;
630 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: Locked all handles", __func__);
633 static void
634 mono_w32handle_unlock_handles (MonoW32Handle **handles_data, gsize nhandles)
636 gint i;
638 for (i = nhandles - 1; i >= 0; i--) {
639 if (!handles_data [i])
640 continue;
641 mono_w32handle_unlock (handles_data [i]);
645 static int
646 mono_w32handle_timedwait_signal_naked (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
648 int res;
650 if (!poll) {
651 res = mono_coop_cond_timedwait (cond, mutex, timeout);
652 } else {
653 /* This is needed when waiting for process handles */
654 if (!alerted) {
656 * pthread_cond_(timed)wait() can return 0 even if the condition was not
657 * signalled. This happens at least on Darwin. We surface this, i.e., we
658 * get spurious wake-ups.
660 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
662 res = mono_coop_cond_timedwait (cond, mutex, timeout);
663 } else {
664 if (timeout < 100) {
665 /* Real timeout is less than 100ms time */
666 res = mono_coop_cond_timedwait (cond, mutex, timeout);
667 } else {
668 res = mono_coop_cond_timedwait (cond, mutex, 100);
670 /* Mask the fake timeout, this will cause
671 * another poll if the cond was not really signaled
673 if (res == -1)
674 res = 0;
679 return res;
682 static void
683 signal_global (gpointer unused)
685 /* If we reach here, then interrupt token is set to the flag value, which
686 * means that the target thread is either
687 * - before the first CAS in timedwait, which means it won't enter the wait.
688 * - it is after the first CAS, so it is already waiting, or it will enter
689 * the wait, and it will be interrupted by the broadcast. */
690 mono_coop_mutex_lock (&global_signal_mutex);
691 mono_coop_cond_broadcast (&global_signal_cond);
692 mono_coop_mutex_unlock (&global_signal_mutex);
695 static int
696 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
698 int res;
700 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for global", __func__);
702 if (alerted)
703 *alerted = FALSE;
705 if (alerted) {
706 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
707 if (*alerted)
708 return 0;
711 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
713 if (alerted)
714 mono_thread_info_uninstall_interrupt (alerted);
716 return res;
719 static void
720 signal_handle_and_unref (gpointer handle_duplicate)
722 MonoW32Handle *handle_data;
723 MonoCoopCond *cond;
724 MonoCoopMutex *mutex;
726 if (!mono_w32handle_lookup_and_ref (handle_duplicate, &handle_data))
727 g_error ("%s: unknown handle %p", __func__, handle_duplicate);
729 /* If we reach here, then interrupt token is set to the flag value, which
730 * means that the target thread is either
731 * - before the first CAS in timedwait, which means it won't enter the wait.
732 * - it is after the first CAS, so it is already waiting, or it will enter
733 * the wait, and it will be interrupted by the broadcast. */
734 cond = &handle_data->signal_cond;
735 mutex = &handle_data->signal_mutex;
737 mono_coop_mutex_lock (mutex);
738 mono_coop_cond_broadcast (cond);
739 mono_coop_mutex_unlock (mutex);
741 mono_w32handle_unref (handle_data);
743 mono_w32handle_close (handle_duplicate);
746 static int
747 mono_w32handle_timedwait_signal_handle (MonoW32Handle *handle_data, guint32 timeout, gboolean poll, gboolean *alerted)
749 gpointer handle_duplicate;
750 int res;
752 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for %p (type %s)", __func__, handle_data,
753 mono_w32handle_ops_typename (handle_data->type));
755 if (alerted)
756 *alerted = FALSE;
758 if (alerted) {
759 mono_thread_info_install_interrupt (signal_handle_and_unref, handle_duplicate = mono_w32handle_duplicate (handle_data), alerted);
760 if (*alerted) {
761 mono_w32handle_close (handle_duplicate);
762 return 0;
766 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
768 if (alerted) {
769 mono_thread_info_uninstall_interrupt (alerted);
770 if (!*alerted) {
771 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
772 mono_w32handle_close (handle_duplicate);
776 return res;
779 static gboolean
780 dump_callback (MonoW32Handle *handle_data, gpointer user_data)
782 g_print ("%p [%7s] signalled: %5s ref: %3d ",
783 handle_data, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref - 1 /* foreach increase ref by 1 */);
784 mono_w32handle_ops_details (handle_data);
785 g_print ("\n");
787 return FALSE;
790 void mono_w32handle_dump (void)
792 mono_w32handle_foreach (dump_callback, NULL);
795 static gboolean
796 own_if_signalled (MonoW32Handle *handle_data, gboolean *abandoned)
798 if (!mono_w32handle_issignalled (handle_data))
799 return FALSE;
801 *abandoned = FALSE;
802 mono_w32handle_ops_own (handle_data, abandoned);
803 return TRUE;
806 static gboolean
807 own_if_owned (MonoW32Handle *handle_data, gboolean *abandoned)
809 if (!mono_w32handle_ops_isowned (handle_data))
810 return FALSE;
812 *abandoned = FALSE;
813 mono_w32handle_ops_own (handle_data, abandoned);
814 return TRUE;
817 gboolean
818 mono_w32handle_handle_is_signalled (gpointer handle)
820 MonoW32Handle *handle_data;
821 gboolean res;
823 if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
824 return FALSE;
826 res = mono_w32handle_issignalled (handle_data);
827 mono_w32handle_unref (handle_data);
828 return res;
831 gboolean
832 mono_w32handle_handle_is_owned (gpointer handle)
834 MonoW32Handle *handle_data;
835 gboolean res;
837 if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
838 return FALSE;
840 res = mono_w32handle_ops_isowned (handle_data);
841 mono_w32handle_unref (handle_data);
842 return res;
845 #ifdef HOST_WIN32
846 MonoW32HandleWaitRet
847 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
849 return mono_w32handle_convert_wait_ret (mono_coop_win32_wait_for_single_object_ex (handle, timeout, alertable), 1);
851 #else
852 MonoW32HandleWaitRet
853 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
855 MonoW32Handle *handle_data;
856 MonoW32HandleWaitRet ret;
857 gboolean alerted;
858 gint64 start = 0;
859 gboolean abandoned = FALSE;
861 alerted = FALSE;
863 if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
864 return MONO_W32HANDLE_WAIT_RET_FAILED;
866 if (mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
867 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p has special wait", __func__, handle_data);
869 mono_w32handle_unref (handle_data);
870 return mono_w32handle_ops_specialwait (handle_data, timeout, alertable ? &alerted : NULL);
873 if (!mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_WAIT)) {
874 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p can't be waited for", __func__, handle_data);
876 mono_w32handle_unref (handle_data);
877 return MONO_W32HANDLE_WAIT_RET_FAILED;
880 mono_w32handle_lock (handle_data);
882 if (mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_OWN)) {
883 if (own_if_owned (handle_data, &abandoned)) {
884 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p already owned", __func__, handle_data);
886 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
887 goto done;
891 if (timeout != MONO_INFINITE_WAIT)
892 start = mono_msec_ticks ();
894 mono_w32handle_set_in_use (handle_data, TRUE);
896 for (;;) {
897 gint waited;
899 if (own_if_signalled (handle_data, &abandoned)) {
900 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p signalled", __func__, handle_data);
902 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
903 goto done;
906 mono_w32handle_ops_prewait (handle_data);
908 if (timeout == MONO_INFINITE_WAIT) {
909 waited = mono_w32handle_timedwait_signal_handle (handle_data, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
910 } else {
911 gint64 elapsed;
913 elapsed = mono_msec_ticks () - start;
914 if (elapsed > timeout) {
915 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
916 goto done;
919 waited = mono_w32handle_timedwait_signal_handle (handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
922 if (alerted) {
923 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
924 goto done;
927 if (waited != 0) {
928 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
929 goto done;
933 done:
934 mono_w32handle_set_in_use (handle_data, FALSE);
936 mono_w32handle_unlock (handle_data);
938 mono_w32handle_unref (handle_data);
940 return ret;
942 #endif /* HOST_WIN32 */
944 static MonoW32Handle*
945 mono_w32handle_has_duplicates (MonoW32Handle *handles [ ], gsize nhandles)
947 if (nhandles < 2 || nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
948 return NULL;
950 MonoW32Handle *sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS]; // 64
951 memcpy (sorted, handles, nhandles * sizeof (handles[0]));
952 mono_qsort (sorted, nhandles, sizeof (sorted [0]), g_direct_equal);
953 for (gsize i = 1; i < nhandles; ++i) {
954 MonoW32Handle * const h1 = sorted [i - 1];
955 MonoW32Handle * const h2 = sorted [i];
956 if (h1 == h2)
957 return h1;
960 return NULL;
963 static void
964 mono_w32handle_clear_duplicates (MonoW32Handle *handles [ ], gsize nhandles)
966 for (gsize i = 0; i < nhandles; ++i) {
967 if (!handles [i])
968 continue;
969 for (gsize j = i + 1; j < nhandles; ++j) {
970 if (handles [i] == handles [j]) {
971 mono_w32handle_unref (handles [j]);
972 handles [j] = NULL;
978 static void
979 mono_w32handle_check_duplicates (MonoW32Handle *handles [ ], gsize nhandles, gboolean waitall, MonoError *error)
981 // Duplication is ok for WaitAny, exception for WaitAll.
982 // System.DuplicateWaitObjectException: Duplicate objects in argument.
984 MonoW32Handle *duplicate = mono_w32handle_has_duplicates (handles, nhandles);
985 if (!duplicate)
986 return;
988 if (waitall) {
989 mono_error_set_duplicate_wait_object (error);
990 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "mono_w32handle_wait_multiple: handle %p is duplicated", duplicate);
991 return;
994 // There is at least one duplicate. This is not an error.
995 // Remove all duplicates -- in-place in order to return the
996 // lowest signaled, equal to the caller's indices, and ease
997 // the exit path's dereference.
998 // That is, we cannot use sorted data, nor can we
999 // compress the array to remove elements. We must operate
1000 // on each element in its original index, but we can skip some.
1002 mono_w32handle_clear_duplicates (handles, nhandles);
1005 #ifdef HOST_WIN32
1006 MonoW32HandleWaitRet
1007 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable, MonoError *error)
1009 DWORD const wait_result = (nhandles != 1)
1010 ? mono_coop_win32_wait_for_multiple_objects_ex (nhandles, handles, waitall, timeout, alertable, error)
1011 : mono_coop_win32_wait_for_single_object_ex (handles [0], timeout, alertable);
1012 return mono_w32handle_convert_wait_ret (wait_result, nhandles);
1014 #else
1015 MonoW32HandleWaitRet
1016 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable, MonoError *error)
1018 MonoW32HandleWaitRet ret;
1019 gboolean alerted, poll;
1020 gint i;
1021 gint64 start = 0;
1022 MonoW32Handle *handles_data [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1023 gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
1025 if (nhandles == 0)
1026 return MONO_W32HANDLE_WAIT_RET_FAILED;
1028 if (nhandles == 1)
1029 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1031 alerted = FALSE;
1033 if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
1034 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: too many handles: %" G_GSIZE_FORMAT "d",
1035 __func__, nhandles);
1037 return MONO_W32HANDLE_WAIT_RET_FAILED;
1040 for (i = 0; i < nhandles; ++i) {
1041 if (!mono_w32handle_lookup_and_ref (handles [i], &handles_data [i])) {
1042 for (; i >= 0; --i)
1043 mono_w32handle_unref (handles_data [i]);
1044 return MONO_W32HANDLE_WAIT_RET_FAILED;
1048 for (i = 0; i < nhandles; ++i) {
1049 if (!mono_w32handle_test_capabilities (handles_data[i], MONO_W32HANDLE_CAP_WAIT)
1050 && !mono_w32handle_test_capabilities (handles_data[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
1052 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p can't be waited for", __func__, handles_data [i]);
1054 ret = MONO_W32HANDLE_WAIT_RET_FAILED;
1055 goto done;
1059 mono_w32handle_check_duplicates (handles_data, nhandles, waitall, error);
1060 if (!is_ok (error)) {
1061 ret = MONO_W32HANDLE_WAIT_RET_FAILED;
1062 goto done;
1065 poll = FALSE;
1066 for (i = 0; i < nhandles; ++i) {
1067 if (!handles_data [i])
1068 continue;
1069 if (handles_data [i]->type == MONO_W32TYPE_PROCESS) {
1070 /* Can't wait for a process handle + another handle without polling */
1071 poll = TRUE;
1075 if (timeout != MONO_INFINITE_WAIT)
1076 start = mono_msec_ticks ();
1078 for (;;) {
1079 gsize count, lowest;
1080 gboolean signalled;
1081 gint waited;
1083 count = 0;
1084 lowest = nhandles;
1086 mono_w32handle_lock_handles (handles_data, nhandles);
1088 for (i = 0; i < nhandles; i++) {
1089 if (!handles_data [i])
1090 continue;
1091 if ((mono_w32handle_test_capabilities (handles_data [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles_data [i]))
1092 || mono_w32handle_issignalled (handles_data [i]))
1094 count ++;
1096 if (i < lowest)
1097 lowest = i;
1101 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1103 if (signalled) {
1104 for (i = 0; i < nhandles; i++) {
1105 if (!handles_data [i])
1106 continue;
1107 if (own_if_signalled (handles_data [i], &abandoned [i]) && !waitall) {
1108 /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
1109 * throw AbandonedMutexException in case we owned it but didn't release it */
1110 break;
1115 mono_w32handle_unlock_handles (handles_data, nhandles);
1117 if (signalled) {
1118 ret = (MonoW32HandleWaitRet)(MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest);
1119 for (i = lowest; i < nhandles; i++) {
1120 if (abandoned [i]) {
1121 ret = (MonoW32HandleWaitRet)(MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest);
1122 break;
1125 goto done;
1128 for (i = 0; i < nhandles; i++) {
1129 if (!handles_data [i])
1130 continue;
1131 mono_w32handle_ops_prewait (handles_data [i]);
1133 if (mono_w32handle_test_capabilities (handles_data [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1134 && !mono_w32handle_issignalled (handles_data [i]))
1136 mono_w32handle_ops_specialwait (handles_data [i], 0, alertable ? &alerted : NULL);
1140 mono_w32handle_lock_signal_mutex ();
1142 // FIXME These two loops can be just one.
1143 if (waitall) {
1144 signalled = TRUE;
1145 for (i = 0; i < nhandles; ++i) {
1146 if (!handles_data [i])
1147 continue;
1148 if (!mono_w32handle_issignalled (handles_data [i])) {
1149 signalled = FALSE;
1150 break;
1153 } else {
1154 signalled = FALSE;
1155 for (i = 0; i < nhandles; ++i) {
1156 if (!handles_data [i])
1157 continue;
1158 if (mono_w32handle_issignalled (handles_data [i])) {
1159 signalled = TRUE;
1160 break;
1165 waited = 0;
1167 if (!signalled) {
1168 if (timeout == MONO_INFINITE_WAIT) {
1169 waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL);
1170 } else {
1171 gint64 elapsed;
1173 elapsed = mono_msec_ticks () - start;
1174 if (elapsed > timeout) {
1175 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1177 mono_w32handle_unlock_signal_mutex ();
1179 goto done;
1182 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1186 mono_w32handle_unlock_signal_mutex ();
1188 if (alerted) {
1189 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1190 goto done;
1193 if (waited != 0) {
1194 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1195 goto done;
1199 done:
1200 for (i = nhandles - 1; i >= 0; i--) {
1201 /* Unref everything we reffed above */
1202 if (!handles_data [i])
1203 continue;
1204 mono_w32handle_unref (handles_data [i]);
1207 return ret;
1209 #endif /* HOST_WIN32 */
1211 #ifdef HOST_WIN32
1212 MonoW32HandleWaitRet
1213 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1215 return mono_w32handle_convert_wait_ret (mono_coop_win32_signal_object_and_wait (signal_handle, wait_handle, timeout, alertable), 1);
1217 #else
1218 MonoW32HandleWaitRet
1219 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1221 MonoW32Handle *signal_handle_data, *wait_handle_data, *handles_data [2];
1222 MonoW32HandleWaitRet ret;
1223 gint64 start = 0;
1224 gboolean alerted;
1225 gboolean abandoned = FALSE;
1227 alerted = FALSE;
1229 if (!mono_w32handle_lookup_and_ref (signal_handle, &signal_handle_data)) {
1230 return MONO_W32HANDLE_WAIT_RET_FAILED;
1232 if (!mono_w32handle_lookup_and_ref (wait_handle, &wait_handle_data)) {
1233 mono_w32handle_unref (signal_handle_data);
1234 return MONO_W32HANDLE_WAIT_RET_FAILED;
1237 if (!mono_w32handle_test_capabilities (signal_handle_data, MONO_W32HANDLE_CAP_SIGNAL)) {
1238 mono_w32handle_unref (wait_handle_data);
1239 mono_w32handle_unref (signal_handle_data);
1240 return MONO_W32HANDLE_WAIT_RET_FAILED;
1242 if (!mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_WAIT)) {
1243 mono_w32handle_unref (wait_handle_data);
1244 mono_w32handle_unref (signal_handle_data);
1245 return MONO_W32HANDLE_WAIT_RET_FAILED;
1248 if (mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1249 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle_data);
1250 mono_w32handle_unref (wait_handle_data);
1251 mono_w32handle_unref (signal_handle_data);
1252 return MONO_W32HANDLE_WAIT_RET_FAILED;
1255 handles_data [0] = wait_handle_data;
1256 handles_data [1] = signal_handle_data;
1258 mono_w32handle_lock_handles (handles_data, 2);
1260 gint32 signal_ret = mono_w32handle_ops_signal (signal_handle_data);
1262 mono_w32handle_unlock (signal_handle_data);
1264 if (signal_ret == MONO_W32HANDLE_WAIT_RET_TOO_MANY_POSTS ||
1265 signal_ret == MONO_W32HANDLE_WAIT_RET_NOT_OWNED_BY_CALLER) {
1266 ret = (MonoW32HandleWaitRet) signal_ret;
1267 goto done;
1270 if (mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_OWN)) {
1271 if (own_if_owned (wait_handle_data, &abandoned)) {
1272 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p already owned", __func__, wait_handle_data);
1274 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1275 goto done;
1279 if (timeout != MONO_INFINITE_WAIT)
1280 start = mono_msec_ticks ();
1282 for (;;) {
1283 gint waited;
1285 if (own_if_signalled (wait_handle_data, &abandoned)) {
1286 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p signalled", __func__, wait_handle_data);
1288 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1289 goto done;
1292 mono_w32handle_ops_prewait (wait_handle_data);
1294 if (timeout == MONO_INFINITE_WAIT) {
1295 waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
1296 } else {
1297 gint64 elapsed;
1299 elapsed = mono_msec_ticks () - start;
1300 if (elapsed > timeout) {
1301 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1302 goto done;
1305 waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1308 if (alerted) {
1309 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1310 goto done;
1313 if (waited != 0) {
1314 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1315 goto done;
1319 done:
1320 mono_w32handle_unlock (wait_handle_data);
1322 mono_w32handle_unref (wait_handle_data);
1323 mono_w32handle_unref (signal_handle_data);
1325 return ret;
1327 #endif /* HOST_WIN32 */