[netcore] Implement Thread.GetCurrentProcessorId (#18450)
[mono-project.git] / mono / metadata / w32handle.c
blobc7077aac850757f4e632ca3f1622801db4dbdba3
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])) {
600 /* Bummer */
602 for (j = i - 1; j >= 0; j--) {
603 if (!handles_data [j])
604 continue;
605 mono_w32handle_unlock (handles_data [j]);
608 iter += 10;
609 if (iter == 1000)
610 iter = 10;
612 MONO_ENTER_GC_SAFE;
613 #ifdef HOST_WIN32
614 SleepEx (iter, TRUE);
615 #else
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 */
625 MONO_EXIT_GC_SAFE;
627 goto again;
631 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: Locked all handles", __func__);
634 static void
635 mono_w32handle_unlock_handles (MonoW32Handle **handles_data, gsize nhandles)
637 gint i;
639 for (i = nhandles - 1; i >= 0; i--) {
640 if (!handles_data [i])
641 continue;
642 mono_w32handle_unlock (handles_data [i]);
646 static int
647 mono_w32handle_timedwait_signal_naked (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
649 int res;
651 if (!poll) {
652 res = mono_coop_cond_timedwait (cond, mutex, timeout);
653 } else {
654 /* This is needed when waiting for process handles */
655 if (!alerted) {
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);
664 } else {
665 if (timeout < 100) {
666 /* Real timeout is less than 100ms time */
667 res = mono_coop_cond_timedwait (cond, mutex, timeout);
668 } else {
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
674 if (res == -1)
675 res = 0;
680 return res;
683 static void
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);
696 static int
697 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
699 int res;
701 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for global", __func__);
703 if (alerted)
704 *alerted = FALSE;
706 if (alerted) {
707 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
708 if (*alerted)
709 return 0;
712 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
714 if (alerted)
715 mono_thread_info_uninstall_interrupt (alerted);
717 return res;
720 static void
721 signal_handle_and_unref (gpointer handle_duplicate)
723 MonoW32Handle *handle_data;
724 MonoCoopCond *cond;
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);
747 static int
748 mono_w32handle_timedwait_signal_handle (MonoW32Handle *handle_data, guint32 timeout, gboolean poll, gboolean *alerted)
750 gpointer handle_duplicate;
751 int res;
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));
756 if (alerted)
757 *alerted = FALSE;
759 if (alerted) {
760 mono_thread_info_install_interrupt (signal_handle_and_unref, handle_duplicate = mono_w32handle_duplicate (handle_data), alerted);
761 if (*alerted) {
762 mono_w32handle_close (handle_duplicate);
763 return 0;
767 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
769 if (alerted) {
770 mono_thread_info_uninstall_interrupt (alerted);
771 if (!*alerted) {
772 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
773 mono_w32handle_close (handle_duplicate);
777 return res;
780 static gboolean
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);
786 g_print ("\n");
788 return FALSE;
791 void mono_w32handle_dump (void)
793 mono_w32handle_foreach (dump_callback, NULL);
796 static gboolean
797 own_if_signalled (MonoW32Handle *handle_data, gboolean *abandoned)
799 if (!mono_w32handle_issignalled (handle_data))
800 return FALSE;
802 *abandoned = FALSE;
803 mono_w32handle_ops_own (handle_data, abandoned);
804 return TRUE;
807 static gboolean
808 own_if_owned (MonoW32Handle *handle_data, gboolean *abandoned)
810 if (!mono_w32handle_ops_isowned (handle_data))
811 return FALSE;
813 *abandoned = FALSE;
814 mono_w32handle_ops_own (handle_data, abandoned);
815 return TRUE;
818 #ifdef HOST_WIN32
819 MonoW32HandleWaitRet
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);
824 #else
825 MonoW32HandleWaitRet
826 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
828 MonoW32Handle *handle_data;
829 MonoW32HandleWaitRet ret;
830 gboolean alerted;
831 gint64 start = 0;
832 gboolean abandoned = FALSE;
834 alerted = 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;
860 goto done;
864 if (timeout != MONO_INFINITE_WAIT)
865 start = mono_msec_ticks ();
867 mono_w32handle_set_in_use (handle_data, TRUE);
869 for (;;) {
870 gint waited;
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;
876 goto done;
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);
883 } else {
884 gint64 elapsed;
886 elapsed = mono_msec_ticks () - start;
887 if (elapsed > timeout) {
888 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
889 goto done;
892 waited = mono_w32handle_timedwait_signal_handle (handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
895 if (alerted) {
896 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
897 goto done;
900 if (waited != 0) {
901 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
902 goto done;
906 done:
907 mono_w32handle_set_in_use (handle_data, FALSE);
909 mono_w32handle_unlock (handle_data);
911 mono_w32handle_unref (handle_data);
913 return ret;
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)
921 return NULL;
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];
929 if (h1 == h2)
930 return h1;
933 return NULL;
936 static void
937 mono_w32handle_clear_duplicates (MonoW32Handle *handles [ ], gsize nhandles)
939 for (gsize i = 0; i < nhandles; ++i) {
940 if (!handles [i])
941 continue;
942 for (gsize j = i + 1; j < nhandles; ++j) {
943 if (handles [i] == handles [j]) {
944 mono_w32handle_unref (handles [j]);
945 handles [j] = NULL;
951 static void
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);
958 if (!duplicate)
959 return;
961 if (waitall) {
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);
964 return;
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);
978 #ifdef HOST_WIN32
979 MonoW32HandleWaitRet
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);
987 #else
988 MonoW32HandleWaitRet
989 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable, MonoError *error)
991 MonoW32HandleWaitRet ret;
992 gboolean alerted, poll;
993 gint i;
994 gint64 start = 0;
995 MonoW32Handle *handles_data [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
996 gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
998 if (nhandles == 0)
999 return MONO_W32HANDLE_WAIT_RET_FAILED;
1001 if (nhandles == 1)
1002 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1004 alerted = FALSE;
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])) {
1015 for (; i >= 0; --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;
1028 goto done;
1032 mono_w32handle_check_duplicates (handles_data, nhandles, waitall, error);
1033 if (!is_ok (error)) {
1034 ret = MONO_W32HANDLE_WAIT_RET_FAILED;
1035 goto done;
1038 poll = FALSE;
1039 for (i = 0; i < nhandles; ++i) {
1040 if (!handles_data [i])
1041 continue;
1042 if (handles_data [i]->type == MONO_W32TYPE_PROCESS) {
1043 /* Can't wait for a process handle + another handle without polling */
1044 poll = TRUE;
1048 if (timeout != MONO_INFINITE_WAIT)
1049 start = mono_msec_ticks ();
1051 for (;;) {
1052 gsize count, lowest;
1053 gboolean signalled;
1054 gint waited;
1056 count = 0;
1057 lowest = nhandles;
1059 mono_w32handle_lock_handles (handles_data, nhandles);
1061 for (i = 0; i < nhandles; i++) {
1062 if (!handles_data [i])
1063 continue;
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]))
1067 count ++;
1069 if (i < lowest)
1070 lowest = i;
1074 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1076 if (signalled) {
1077 for (i = 0; i < nhandles; i++) {
1078 if (!handles_data [i])
1079 continue;
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 */
1083 break;
1088 mono_w32handle_unlock_handles (handles_data, nhandles);
1090 if (signalled) {
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);
1095 break;
1098 goto done;
1101 for (i = 0; i < nhandles; i++) {
1102 if (!handles_data [i])
1103 continue;
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.
1116 if (waitall) {
1117 signalled = TRUE;
1118 for (i = 0; i < nhandles; ++i) {
1119 if (!handles_data [i])
1120 continue;
1121 if (!mono_w32handle_issignalled (handles_data [i])) {
1122 signalled = FALSE;
1123 break;
1126 } else {
1127 signalled = FALSE;
1128 for (i = 0; i < nhandles; ++i) {
1129 if (!handles_data [i])
1130 continue;
1131 if (mono_w32handle_issignalled (handles_data [i])) {
1132 signalled = TRUE;
1133 break;
1138 waited = 0;
1140 if (!signalled) {
1141 if (timeout == MONO_INFINITE_WAIT) {
1142 waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL);
1143 } else {
1144 gint64 elapsed;
1146 elapsed = mono_msec_ticks () - start;
1147 if (elapsed > timeout) {
1148 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1150 mono_w32handle_unlock_signal_mutex ();
1152 goto done;
1155 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1159 mono_w32handle_unlock_signal_mutex ();
1161 if (alerted) {
1162 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1163 goto done;
1166 if (waited != 0) {
1167 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1168 goto done;
1172 done:
1173 for (i = nhandles - 1; i >= 0; i--) {
1174 /* Unref everything we reffed above */
1175 if (!handles_data [i])
1176 continue;
1177 mono_w32handle_unref (handles_data [i]);
1180 return ret;
1182 #endif /* HOST_WIN32 */
1184 #ifdef 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);
1190 #else
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;
1196 gint64 start = 0;
1197 gboolean alerted;
1198 gboolean abandoned = FALSE;
1200 alerted = 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;
1240 goto done;
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;
1248 goto done;
1252 if (timeout != MONO_INFINITE_WAIT)
1253 start = mono_msec_ticks ();
1255 for (;;) {
1256 gint waited;
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;
1262 goto done;
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);
1269 } else {
1270 gint64 elapsed;
1272 elapsed = mono_msec_ticks () - start;
1273 if (elapsed > timeout) {
1274 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1275 goto done;
1278 waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1281 if (alerted) {
1282 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1283 goto done;
1286 if (waited != 0) {
1287 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1288 goto done;
1292 done:
1293 mono_w32handle_unlock (wait_handle_data);
1295 mono_w32handle_unref (wait_handle_data);
1296 mono_w32handle_unref (signal_handle_data);
1298 return ret;
1300 #endif /* HOST_WIN32 */