[sre] Wrap mono_image_create_token with HANDLE_FUNCTION_{ENTER,RETURN}
[mono-project.git] / mono / metadata / w32handle.c
blob654b215538d800de195db57afeca5ccaaa9176e9
1 /**
2 * \file
3 * Generic and internal operations on handles
5 * Author:
6 * Dick Porter (dick@ximian.com)
7 * Ludovic Henry (luhenry@microsoft.com)
9 * (C) 2002-2011 Novell, Inc.
10 * Copyright 2011 Xamarin Inc
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #include <config.h>
15 #include <glib.h>
17 #include "w32handle.h"
19 #include "utils/atomic.h"
20 #include "utils/mono-logger-internals.h"
21 #include "utils/mono-os-mutex.h"
22 #include "utils/mono-proclib.h"
23 #include "utils/mono-threads.h"
24 #include "utils/mono-time.h"
26 #undef DEBUG_REFS
28 #define SLOT_MAX (1024 * 32)
30 /* must be a power of 2 */
31 #define HANDLE_PER_SLOT (256)
33 typedef struct {
34 MonoW32HandleType type;
35 guint ref;
36 gboolean signalled;
37 gboolean in_use;
38 mono_mutex_t signal_mutex;
39 mono_cond_t signal_cond;
40 gpointer specific;
41 } MonoW32HandleBase;
43 static MonoW32HandleCapability handle_caps [MONO_W32HANDLE_COUNT];
44 static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
47 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
48 * If 4M handles are not enough... Oh, well... we will crash.
50 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
51 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
53 static MonoW32HandleBase *private_handles [SLOT_MAX];
54 static guint32 private_handles_size = 0;
57 * This is an internal handle which is used for handling waiting for multiple handles.
58 * Threads which wait for multiple handles wait on this one handle, and when a handle
59 * is signalled, this handle is signalled too.
61 static mono_mutex_t global_signal_mutex;
62 static mono_cond_t global_signal_cond;
64 static mono_mutex_t scan_mutex;
66 static gboolean shutting_down = FALSE;
68 static gboolean
69 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
71 gsize index, offset;
73 g_assert (handle_data);
75 index = SLOT_INDEX ((gsize) handle);
76 if (index >= SLOT_MAX)
77 return FALSE;
78 if (!private_handles [index])
79 return FALSE;
81 offset = SLOT_OFFSET ((gsize) handle);
82 if (private_handles [index][offset].type == MONO_W32HANDLE_UNUSED)
83 return FALSE;
85 *handle_data = &private_handles [index][offset];
86 return TRUE;
89 MonoW32HandleType
90 mono_w32handle_get_type (gpointer handle)
92 MonoW32HandleBase *handle_data;
94 if (!mono_w32handle_lookup_data (handle, &handle_data))
95 return MONO_W32HANDLE_UNUSED; /* An impossible type */
97 return handle_data->type;
100 static const gchar*
101 mono_w32handle_ops_typename (MonoW32HandleType type);
103 const gchar*
104 mono_w32handle_get_typename (MonoW32HandleType type)
106 return mono_w32handle_ops_typename (type);
109 void
110 mono_w32handle_set_signal_state (gpointer handle, gboolean state, gboolean broadcast)
112 MonoW32HandleBase *handle_data;
114 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
115 return;
118 #ifdef DEBUG
119 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
120 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
121 #endif
123 if (state == TRUE) {
124 /* Tell everyone blocking on a single handle */
126 /* The condition the global signal cond is waiting on is the signalling of
127 * _any_ handle. So lock it before setting the signalled state.
129 mono_os_mutex_lock (&global_signal_mutex);
131 /* This function _must_ be called with
132 * handle->signal_mutex locked
134 handle_data->signalled=state;
136 if (broadcast == TRUE) {
137 mono_os_cond_broadcast (&handle_data->signal_cond);
138 } else {
139 mono_os_cond_signal (&handle_data->signal_cond);
142 /* Tell everyone blocking on multiple handles that something
143 * was signalled
145 mono_os_cond_broadcast (&global_signal_cond);
147 mono_os_mutex_unlock (&global_signal_mutex);
148 } else {
149 handle_data->signalled=state;
153 gboolean
154 mono_w32handle_issignalled (gpointer handle)
156 MonoW32HandleBase *handle_data;
158 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
159 return(FALSE);
162 return handle_data->signalled;
165 static void
166 mono_w32handle_set_in_use (gpointer handle, gboolean in_use)
168 MonoW32HandleBase *handle_data;
170 if (!mono_w32handle_lookup_data (handle, &handle_data))
171 g_assert_not_reached ();
173 handle_data->in_use = in_use;
176 static void
177 mono_w32handle_lock_signal_mutex (void)
179 #ifdef DEBUG
180 g_message ("%s: lock global signal mutex", __func__);
181 #endif
183 mono_os_mutex_lock (&global_signal_mutex);
186 static void
187 mono_w32handle_unlock_signal_mutex (void)
189 #ifdef DEBUG
190 g_message ("%s: unlock global signal mutex", __func__);
191 #endif
193 mono_os_mutex_unlock (&global_signal_mutex);
196 static void
197 mono_w32handle_ref (gpointer handle);
199 static void
200 mono_w32handle_unref (gpointer handle);
202 void
203 mono_w32handle_lock_handle (gpointer handle)
205 MonoW32HandleBase *handle_data;
207 if (!mono_w32handle_lookup_data (handle, &handle_data))
208 g_error ("%s: failed to lookup handle %p", __func__, handle);
210 mono_w32handle_ref (handle);
212 mono_os_mutex_lock (&handle_data->signal_mutex);
214 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: lock handle %p", __func__, handle);
217 gboolean
218 mono_w32handle_trylock_handle (gpointer handle)
220 MonoW32HandleBase *handle_data;
221 gboolean locked;
223 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p", __func__, handle);
225 if (!mono_w32handle_lookup_data (handle, &handle_data))
226 g_error ("%s: failed to lookup handle %p", __func__, handle);
228 mono_w32handle_ref (handle);
230 locked = mono_os_mutex_trylock (&handle_data->signal_mutex) == 0;
231 if (!locked)
232 mono_w32handle_unref (handle);
234 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p, locked: %s", __func__, handle, locked ? "true" : "false");
236 return locked;
239 void
240 mono_w32handle_unlock_handle (gpointer handle)
242 MonoW32HandleBase *handle_data;
244 if (!mono_w32handle_lookup_data (handle, &handle_data))
245 g_error ("%s: failed to lookup handle %p", __func__, handle);
247 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlock handle %p", __func__, handle);
249 mono_os_mutex_unlock (&handle_data->signal_mutex);
251 mono_w32handle_unref (handle);
254 void
255 mono_w32handle_init (void)
257 static gboolean initialized = FALSE;
259 if (initialized)
260 return;
262 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
263 == MONO_W32HANDLE_COUNT);
265 mono_os_mutex_init (&scan_mutex);
267 mono_os_cond_init (&global_signal_cond);
268 mono_os_mutex_init (&global_signal_mutex);
270 initialized = TRUE;
273 void
274 mono_w32handle_cleanup (void)
276 int i;
278 g_assert (!shutting_down);
279 shutting_down = TRUE;
281 for (i = 0; i < SLOT_MAX; ++i)
282 g_free (private_handles [i]);
285 static gsize
286 mono_w32handle_ops_typesize (MonoW32HandleType type);
289 * mono_w32handle_new_internal:
290 * @type: Init handle to this type
292 * Search for a free handle and initialize it. Return the handle on
293 * success and 0 on failure. This is only called from
294 * mono_w32handle_new, and scan_mutex must be held.
296 static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
297 gpointer handle_specific)
299 guint32 i, k, count;
300 static guint32 last = 0;
301 gboolean retry = FALSE;
303 /* A linear scan should be fast enough. Start from the last
304 * allocation, assuming that handles are allocated more often
305 * than they're freed. Leave the space reserved for file
306 * descriptors
309 if (last == 0) {
310 /* We need to go from 1 since a handle of value 0 can be considered invalid in managed code */
311 last = 1;
312 } else {
313 retry = TRUE;
316 again:
317 count = last;
318 for(i = SLOT_INDEX (count); i < private_handles_size; i++) {
319 if (private_handles [i]) {
320 for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
321 MonoW32HandleBase *handle = &private_handles [i][k];
323 if(handle->type == MONO_W32HANDLE_UNUSED) {
324 last = count + 1;
326 g_assert (handle->ref == 0);
328 handle->type = type;
329 handle->signalled = FALSE;
330 handle->ref = 1;
332 mono_os_cond_init (&handle->signal_cond);
333 mono_os_mutex_init (&handle->signal_mutex);
335 if (handle_specific)
336 handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
338 return (count);
340 count++;
345 if (retry) {
346 /* Try again from the beginning */
347 last = 1;
348 retry = FALSE;
349 goto again;
352 /* Will need to expand the array. The caller will sort it out */
354 return G_MAXUINT32;
357 gpointer
358 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
360 guint32 handle_idx;
361 gpointer handle;
363 g_assert (!shutting_down);
365 mono_os_mutex_lock (&scan_mutex);
367 while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == G_MAXUINT32) {
368 /* Try and expand the array, and have another go */
369 if (private_handles_size >= SLOT_MAX) {
370 mono_os_mutex_unlock (&scan_mutex);
372 /* We ran out of slots */
373 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type));
374 return INVALID_HANDLE_VALUE;
377 private_handles [private_handles_size ++] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
380 mono_os_mutex_unlock (&scan_mutex);
382 handle = GUINT_TO_POINTER (handle_idx);
384 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
386 return(handle);
389 static gboolean
390 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data);
392 static gboolean
393 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data);
395 static void
396 w32handle_destroy (gpointer handle);
398 gpointer
399 mono_w32handle_duplicate (gpointer handle)
401 MonoW32HandleBase *handle_data;
403 if (handle == INVALID_HANDLE_VALUE)
404 return handle;
405 if (!mono_w32handle_lookup_data (handle, &handle_data))
406 return INVALID_HANDLE_VALUE;
408 if (!mono_w32handle_ref_core (handle, handle_data))
409 g_error ("%s: failed to ref handle %p", __func__, handle);
411 return handle;
414 gboolean
415 mono_w32handle_close (gpointer handle)
417 MonoW32HandleBase *handle_data;
418 gboolean destroy;
420 if (handle == INVALID_HANDLE_VALUE)
421 return FALSE;
422 if (!mono_w32handle_lookup_data (handle, &handle_data))
423 return FALSE;
425 destroy = mono_w32handle_unref_core (handle, handle_data);
426 if (destroy)
427 w32handle_destroy (handle);
429 return TRUE;
432 gboolean
433 mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
434 gpointer *handle_specific)
436 MonoW32HandleBase *handle_data;
438 g_assert (handle_specific);
440 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
441 return(FALSE);
444 if (handle_data->type != type) {
445 return(FALSE);
448 *handle_specific = handle_data->specific;
450 return(TRUE);
453 void
454 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
456 GPtrArray *handles_to_destroy;
457 guint32 i, k;
459 handles_to_destroy = NULL;
461 mono_os_mutex_lock (&scan_mutex);
463 for (i = SLOT_INDEX (0); i < private_handles_size; i++) {
464 if (!private_handles [i])
465 continue;
466 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
467 MonoW32HandleBase *handle_data = NULL;
468 gpointer handle;
469 gboolean destroy, finished;
471 handle_data = &private_handles [i][k];
472 if (handle_data->type == MONO_W32HANDLE_UNUSED)
473 continue;
475 handle = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
477 if (!mono_w32handle_ref_core (handle, handle_data)) {
478 /* we are racing with mono_w32handle_unref:
479 * the handle ref has been decremented, but it
480 * hasn't yet been destroyed. */
481 continue;
484 finished = on_each (handle, handle_data->specific, user_data);
486 /* we might have to destroy the handle here, as
487 * it could have been unrefed in another thread */
488 destroy = mono_w32handle_unref_core (handle, handle_data);
489 if (destroy) {
490 /* we do not destroy it while holding the scan_mutex
491 * lock, because w32handle_destroy also needs to take
492 * the lock, and it calls user code which might lead
493 * to a deadlock */
494 if (!handles_to_destroy)
495 handles_to_destroy = g_ptr_array_sized_new (4);
496 g_ptr_array_add (handles_to_destroy, handle);
499 if (finished)
500 goto done;
504 done:
505 mono_os_mutex_unlock (&scan_mutex);
507 if (handles_to_destroy) {
508 for (i = 0; i < handles_to_destroy->len; ++i)
509 w32handle_destroy (handles_to_destroy->pdata [i]);
511 g_ptr_array_free (handles_to_destroy, TRUE);
515 static gboolean
516 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data)
518 guint old, new;
520 do {
521 old = handle_data->ref;
522 if (old == 0)
523 return FALSE;
525 new = old + 1;
526 } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
528 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: ref %s handle %p, ref: %d -> %d",
529 __func__, mono_w32handle_ops_typename (handle_data->type), handle, old, new);
531 return TRUE;
534 static gboolean
535 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data)
537 MonoW32HandleType type;
538 guint old, new;
540 type = handle_data->type;
542 do {
543 old = handle_data->ref;
544 if (!(old >= 1))
545 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__, handle, old);
547 new = old - 1;
548 } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
550 /* handle_data might contain invalid data from now on, if
551 * another thread is unref'ing this handle at the same time */
553 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unref %s handle %p, ref: %d -> %d destroy: %s",
554 __func__, mono_w32handle_ops_typename (type), handle, old, new, new == 0 ? "true" : "false");
556 return new == 0;
559 static void
560 mono_w32handle_ref (gpointer handle)
562 MonoW32HandleBase *handle_data;
564 if (!mono_w32handle_lookup_data (handle, &handle_data))
565 g_error ("%s: failed to ref handle %p, unknown handle", __func__, handle);
567 if (!mono_w32handle_ref_core (handle, handle_data))
568 g_error ("%s: failed to ref handle %p", __func__, handle);
571 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer);
573 static void
574 w32handle_destroy (gpointer handle)
576 /* Need to copy the handle info, reset the slot in the
577 * array, and _only then_ call the close function to
578 * avoid race conditions (eg file descriptors being
579 * closed, and another file being opened getting the
580 * same fd racing the memset())
582 MonoW32HandleBase *handle_data;
583 MonoW32HandleType type;
584 gpointer handle_specific;
585 void (*close_func)(gpointer, gpointer);
587 if (!mono_w32handle_lookup_data (handle, &handle_data))
588 g_error ("%s: unknown handle %p", __func__, handle);
590 g_assert (!handle_data->in_use);
592 type = handle_data->type;
593 handle_specific = handle_data->specific;
595 mono_os_mutex_lock (&scan_mutex);
597 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
599 mono_os_mutex_destroy (&handle_data->signal_mutex);
600 mono_os_cond_destroy (&handle_data->signal_cond);
602 memset (handle_data, 0, sizeof (MonoW32HandleBase));
604 mono_os_mutex_unlock (&scan_mutex);
606 close_func = _wapi_handle_ops_get_close_func (type);
607 if (close_func != NULL) {
608 close_func (handle, handle_specific);
611 memset (handle_specific, 0, mono_w32handle_ops_typesize (type));
613 g_free (handle_specific);
616 /* The handle must not be locked on entry to this function */
617 static void
618 mono_w32handle_unref (gpointer handle)
620 MonoW32HandleBase *handle_data;
621 gboolean destroy;
623 if (!mono_w32handle_lookup_data (handle, &handle_data))
624 g_error ("%s: failed to unref handle %p, unknown handle", __func__, handle);
626 destroy = mono_w32handle_unref_core (handle, handle_data);
627 if (destroy)
628 w32handle_destroy (handle);
631 void
632 mono_w32handle_register_ops (MonoW32HandleType type, MonoW32HandleOps *ops)
634 handle_ops [type] = ops;
637 void mono_w32handle_register_capabilities (MonoW32HandleType type,
638 MonoW32HandleCapability caps)
640 handle_caps[type] = caps;
643 gboolean mono_w32handle_test_capabilities (gpointer handle,
644 MonoW32HandleCapability caps)
646 MonoW32HandleBase *handle_data;
647 MonoW32HandleType type;
649 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
650 return(FALSE);
653 type = handle_data->type;
655 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
656 handle_caps[type], caps, handle_caps[type] & caps);
658 return((handle_caps[type] & caps) != 0);
661 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer)
663 if (handle_ops[type] != NULL &&
664 handle_ops[type]->close != NULL) {
665 return (handle_ops[type]->close);
668 return (NULL);
671 static void
672 mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
674 if (handle_ops[type] != NULL &&
675 handle_ops[type]->details != NULL) {
676 handle_ops[type]->details (data);
680 static const gchar*
681 mono_w32handle_ops_typename (MonoW32HandleType type)
683 g_assert (handle_ops [type]);
684 g_assert (handle_ops [type]->typename);
685 return handle_ops [type]->typename ();
688 static gsize
689 mono_w32handle_ops_typesize (MonoW32HandleType type)
691 g_assert (handle_ops [type]);
692 g_assert (handle_ops [type]->typesize);
693 return handle_ops [type]->typesize ();
696 static void
697 mono_w32handle_ops_signal (gpointer handle)
699 MonoW32HandleBase *handle_data;
700 MonoW32HandleType type;
702 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
703 return;
706 type = handle_data->type;
708 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
709 handle_ops[type]->signal (handle, handle_data->specific);
713 static gboolean
714 mono_w32handle_ops_own (gpointer handle, gboolean *abandoned)
716 MonoW32HandleBase *handle_data;
717 MonoW32HandleType type;
719 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
720 return(FALSE);
723 type = handle_data->type;
725 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
726 return(handle_ops[type]->own_handle (handle, abandoned));
727 } else {
728 return(FALSE);
732 static gboolean
733 mono_w32handle_ops_isowned (gpointer handle)
735 MonoW32HandleBase *handle_data;
736 MonoW32HandleType type;
738 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
739 return(FALSE);
742 type = handle_data->type;
744 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
745 return(handle_ops[type]->is_owned (handle));
746 } else {
747 return(FALSE);
751 static MonoW32HandleWaitRet
752 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted)
754 MonoW32HandleBase *handle_data;
755 MonoW32HandleType type;
757 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
758 return MONO_W32HANDLE_WAIT_RET_FAILED;
761 type = handle_data->type;
763 if (handle_ops[type] != NULL &&
764 handle_ops[type]->special_wait != NULL) {
765 return(handle_ops[type]->special_wait (handle, timeout, alerted));
766 } else {
767 return MONO_W32HANDLE_WAIT_RET_FAILED;
771 static void
772 mono_w32handle_ops_prewait (gpointer handle)
774 MonoW32HandleBase *handle_data;
775 MonoW32HandleType type;
777 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
778 return;
781 type = handle_data->type;
783 if (handle_ops[type] != NULL &&
784 handle_ops[type]->prewait != NULL) {
785 handle_ops[type]->prewait (handle);
789 static void
790 spin (guint32 ms)
792 #ifdef HOST_WIN32
793 SleepEx (ms, TRUE);
794 #else
795 struct timespec sleepytime;
797 g_assert (ms < 1000);
799 sleepytime.tv_sec = 0;
800 sleepytime.tv_nsec = ms * 1000000;
801 nanosleep (&sleepytime, NULL);
802 #endif /* HOST_WIN32 */
805 static void
806 mono_w32handle_lock_handles (gpointer *handles, gsize numhandles)
808 guint32 i, iter=0;
810 /* Lock all the handles, with backoff */
811 again:
812 for(i=0; i<numhandles; i++) {
813 gpointer handle = handles[i];
815 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempting to lock %p", __func__, handle);
817 if (!mono_w32handle_trylock_handle (handle)) {
818 /* Bummer */
820 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p.", __func__,
821 handle);
823 while (i--) {
824 handle = handles[i];
826 mono_w32handle_unlock_handle (handle);
829 /* If iter ever reaches 100 the nanosleep will
830 * return EINVAL immediately, but we have a
831 * design flaw if that happens.
833 iter++;
834 if(iter==100) {
835 g_warning ("%s: iteration overflow!",
836 __func__);
837 iter=1;
840 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
841 iter*10);
842 spin (10 * iter);
844 goto again;
848 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
851 static void
852 mono_w32handle_unlock_handles (gpointer *handles, gsize numhandles)
854 guint32 i;
856 for(i=0; i<numhandles; i++) {
857 gpointer handle = handles[i];
859 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlocking handle %p", __func__, handle);
861 mono_w32handle_unlock_handle (handle);
865 static int
866 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
868 int res;
870 if (!poll) {
871 res = mono_os_cond_timedwait (cond, mutex, timeout);
872 } else {
873 /* This is needed when waiting for process handles */
874 if (!alerted) {
876 * pthread_cond_(timed)wait() can return 0 even if the condition was not
877 * signalled. This happens at least on Darwin. We surface this, i.e., we
878 * get spurious wake-ups.
880 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
882 res = mono_os_cond_timedwait (cond, mutex, timeout);
883 } else {
884 if (timeout < 100) {
885 /* Real timeout is less than 100ms time */
886 res = mono_os_cond_timedwait (cond, mutex, timeout);
887 } else {
888 res = mono_os_cond_timedwait (cond, mutex, 100);
890 /* Mask the fake timeout, this will cause
891 * another poll if the cond was not really signaled
893 if (res == -1)
894 res = 0;
899 return res;
902 static void
903 signal_global (gpointer unused)
905 /* If we reach here, then interrupt token is set to the flag value, which
906 * means that the target thread is either
907 * - before the first CAS in timedwait, which means it won't enter the wait.
908 * - it is after the first CAS, so it is already waiting, or it will enter
909 * the wait, and it will be interrupted by the broadcast. */
910 mono_os_mutex_lock (&global_signal_mutex);
911 mono_os_cond_broadcast (&global_signal_cond);
912 mono_os_mutex_unlock (&global_signal_mutex);
915 static int
916 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
918 int res;
920 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
922 if (alerted)
923 *alerted = FALSE;
925 if (alerted) {
926 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
927 if (*alerted)
928 return 0;
931 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
933 if (alerted)
934 mono_thread_info_uninstall_interrupt (alerted);
936 return res;
939 static void
940 signal_handle_and_unref (gpointer handle)
942 MonoW32HandleBase *handle_data;
943 mono_cond_t *cond;
944 mono_mutex_t *mutex;
946 if (!mono_w32handle_lookup_data (handle, &handle_data))
947 g_error ("cannot signal unknown handle %p", handle);
949 /* If we reach here, then interrupt token is set to the flag value, which
950 * means that the target thread is either
951 * - before the first CAS in timedwait, which means it won't enter the wait.
952 * - it is after the first CAS, so it is already waiting, or it will enter
953 * the wait, and it will be interrupted by the broadcast. */
954 cond = &handle_data->signal_cond;
955 mutex = &handle_data->signal_mutex;
957 mono_os_mutex_lock (mutex);
958 mono_os_cond_broadcast (cond);
959 mono_os_mutex_unlock (mutex);
961 mono_w32handle_close (handle);
964 static int
965 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
967 MonoW32HandleBase *handle_data;
968 gpointer handle_duplicate;
969 int res;
971 if (!mono_w32handle_lookup_data (handle, &handle_data))
972 g_error ("cannot wait on unknown handle %p", handle);
974 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for %p (type %s)", __func__, handle,
975 mono_w32handle_ops_typename (mono_w32handle_get_type (handle)));
977 if (alerted)
978 *alerted = FALSE;
980 if (alerted) {
981 mono_thread_info_install_interrupt (signal_handle_and_unref, handle_duplicate = mono_w32handle_duplicate (handle), alerted);
982 if (*alerted) {
983 mono_w32handle_close (handle_duplicate);
984 return 0;
988 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
990 if (alerted) {
991 mono_thread_info_uninstall_interrupt (alerted);
992 if (!*alerted) {
993 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
994 mono_w32handle_close (handle_duplicate);
998 return res;
1001 static gboolean
1002 dump_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
1004 MonoW32HandleBase *handle_data;
1006 if (!mono_w32handle_lookup_data (handle, &handle_data))
1007 g_error ("cannot dump unknown handle %p", handle);
1009 g_print ("%p [%7s] signalled: %5s ref: %3d ",
1010 handle, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref - 1 /* foreach increase ref by 1 */);
1011 mono_w32handle_ops_details (handle_data->type, handle_data->specific);
1012 g_print ("\n");
1014 return FALSE;
1017 void mono_w32handle_dump (void)
1019 mono_w32handle_foreach (dump_callback, NULL);
1022 static gboolean
1023 own_if_signalled (gpointer handle, gboolean *abandoned)
1025 if (!mono_w32handle_issignalled (handle))
1026 return FALSE;
1028 *abandoned = FALSE;
1029 mono_w32handle_ops_own (handle, abandoned);
1030 return TRUE;
1033 static gboolean
1034 own_if_owned( gpointer handle, gboolean *abandoned)
1036 if (!mono_w32handle_ops_isowned (handle))
1037 return FALSE;
1039 *abandoned = FALSE;
1040 mono_w32handle_ops_own (handle, abandoned);
1041 return TRUE;
1044 MonoW32HandleWaitRet
1045 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
1047 MonoW32HandleWaitRet ret;
1048 gboolean alerted;
1049 gint64 start;
1050 gboolean abandoned = FALSE;
1052 alerted = FALSE;
1054 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1055 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p has special wait",
1056 __func__, handle);
1058 return mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL);
1061 if (!mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_WAIT)) {
1062 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1063 __func__, handle);
1065 return MONO_W32HANDLE_WAIT_RET_FAILED;
1068 mono_w32handle_lock_handle (handle);
1070 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) {
1071 if (own_if_owned (handle, &abandoned)) {
1072 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1073 __func__, handle);
1075 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1076 goto done;
1080 if (timeout != MONO_INFINITE_WAIT)
1081 start = mono_msec_ticks ();
1083 mono_w32handle_set_in_use (handle, TRUE);
1085 for (;;) {
1086 gint waited;
1088 if (own_if_signalled (handle, &abandoned)) {
1089 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1090 __func__, handle);
1092 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1093 goto done;
1096 mono_w32handle_ops_prewait (handle);
1098 if (timeout == MONO_INFINITE_WAIT) {
1099 waited = mono_w32handle_timedwait_signal_handle (handle, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
1100 } else {
1101 gint64 elapsed;
1103 elapsed = mono_msec_ticks () - start;
1104 if (elapsed > timeout) {
1105 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1106 goto done;
1109 waited = mono_w32handle_timedwait_signal_handle (handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1112 if (alerted) {
1113 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1114 goto done;
1117 if (waited != 0) {
1118 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1119 goto done;
1123 done:
1124 mono_w32handle_set_in_use (handle, FALSE);
1126 mono_w32handle_unlock_handle (handle);
1128 return ret;
1131 MonoW32HandleWaitRet
1132 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
1134 MonoW32HandleWaitRet ret;
1135 gboolean alerted, poll;
1136 gint i;
1137 gint64 start;
1138 gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1139 gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
1141 if (nhandles == 0)
1142 return MONO_W32HANDLE_WAIT_RET_FAILED;
1144 if (nhandles == 1)
1145 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1147 alerted = FALSE;
1149 if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
1150 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: too many handles: %zd",
1151 __func__, nhandles);
1153 return MONO_W32HANDLE_WAIT_RET_FAILED;
1156 for (i = 0; i < nhandles; ++i) {
1157 if (!mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT)
1158 && !mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
1160 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1161 __func__, handles [i]);
1163 return MONO_W32HANDLE_WAIT_RET_FAILED;
1166 handles_sorted [i] = handles [i];
1169 qsort (handles_sorted, nhandles, sizeof (gpointer), g_direct_equal);
1170 for (i = 1; i < nhandles; ++i) {
1171 if (handles_sorted [i - 1] == handles_sorted [i]) {
1172 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p is duplicated",
1173 __func__, handles_sorted [i]);
1175 return MONO_W32HANDLE_WAIT_RET_FAILED;
1179 poll = FALSE;
1180 for (i = 0; i < nhandles; ++i) {
1181 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS) {
1182 /* Can't wait for a process handle + another handle without polling */
1183 poll = TRUE;
1187 if (timeout != MONO_INFINITE_WAIT)
1188 start = mono_msec_ticks ();
1190 for (i = 0; i < nhandles; ++i) {
1191 /* Add a reference, as we need to ensure the handle wont
1192 * disappear from under us while we're waiting in the loop
1193 * (not lock, as we don't want exclusive access here) */
1194 mono_w32handle_ref (handles [i]);
1197 for (;;) {
1198 gsize count, lowest;
1199 gboolean signalled;
1200 gint waited;
1202 count = 0;
1203 lowest = nhandles;
1205 mono_w32handle_lock_handles (handles, nhandles);
1207 for (i = 0; i < nhandles; i++) {
1208 if ((mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles [i]))
1209 || mono_w32handle_issignalled (handles [i]))
1211 count ++;
1213 if (i < lowest)
1214 lowest = i;
1218 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1220 if (signalled) {
1221 for (i = 0; i < nhandles; i++) {
1222 if (own_if_signalled (handles [i], &abandoned [i]) && !waitall) {
1223 /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
1224 * throw AbandonedMutexException in case we owned it but didn't release it */
1225 break;
1230 mono_w32handle_unlock_handles (handles, nhandles);
1232 if (signalled) {
1233 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
1234 for (i = lowest; i < nhandles; i++) {
1235 if (abandoned [i]) {
1236 ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest;
1237 break;
1240 goto done;
1243 for (i = 0; i < nhandles; i++) {
1244 mono_w32handle_ops_prewait (handles[i]);
1246 if (mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1247 && !mono_w32handle_issignalled (handles [i]))
1249 mono_w32handle_ops_specialwait (handles [i], 0, alertable ? &alerted : NULL);
1253 mono_w32handle_lock_signal_mutex ();
1255 if (waitall) {
1256 signalled = TRUE;
1257 for (i = 0; i < nhandles; ++i) {
1258 if (!mono_w32handle_issignalled (handles [i])) {
1259 signalled = FALSE;
1260 break;
1263 } else {
1264 signalled = FALSE;
1265 for (i = 0; i < nhandles; ++i) {
1266 if (mono_w32handle_issignalled (handles [i])) {
1267 signalled = TRUE;
1268 break;
1273 waited = 0;
1275 if (!signalled) {
1276 if (timeout == MONO_INFINITE_WAIT) {
1277 waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL);
1278 } else {
1279 gint64 elapsed;
1281 elapsed = mono_msec_ticks () - start;
1282 if (elapsed > timeout) {
1283 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1285 mono_w32handle_unlock_signal_mutex ();
1287 goto done;
1290 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1294 mono_w32handle_unlock_signal_mutex ();
1296 if (alerted) {
1297 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1298 goto done;
1301 if (waited != 0) {
1302 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1303 goto done;
1307 done:
1308 for (i = 0; i < nhandles; i++) {
1309 /* Unref everything we reffed above */
1310 mono_w32handle_unref (handles [i]);
1313 return ret;
1316 MonoW32HandleWaitRet
1317 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1319 MonoW32HandleWaitRet ret;
1320 gint64 start;
1321 gboolean alerted;
1322 gboolean abandoned = FALSE;
1323 gpointer handles [2];
1325 alerted = FALSE;
1327 if (!mono_w32handle_test_capabilities (signal_handle, MONO_W32HANDLE_CAP_SIGNAL))
1328 return MONO_W32HANDLE_WAIT_RET_FAILED;
1329 if (!mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_WAIT))
1330 return MONO_W32HANDLE_WAIT_RET_FAILED;
1332 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1333 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle);
1334 return MONO_W32HANDLE_WAIT_RET_FAILED;
1337 handles [0] = wait_handle;
1338 handles [1] = signal_handle;
1340 mono_w32handle_lock_handles (handles, 2);
1342 mono_w32handle_ops_signal (signal_handle);
1344 mono_w32handle_unlock_handle (signal_handle);
1346 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) {
1347 if (own_if_owned (wait_handle, &abandoned)) {
1348 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1349 __func__, wait_handle);
1351 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1352 goto done;
1356 if (timeout != MONO_INFINITE_WAIT)
1357 start = mono_msec_ticks ();
1359 for (;;) {
1360 gint waited;
1362 if (own_if_signalled (wait_handle, &abandoned)) {
1363 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1364 __func__, wait_handle);
1366 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1367 goto done;
1370 mono_w32handle_ops_prewait (wait_handle);
1372 if (timeout == MONO_INFINITE_WAIT) {
1373 waited = mono_w32handle_timedwait_signal_handle (wait_handle, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
1374 } else {
1375 gint64 elapsed;
1377 elapsed = mono_msec_ticks () - start;
1378 if (elapsed > timeout) {
1379 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1380 goto done;
1383 waited = mono_w32handle_timedwait_signal_handle (wait_handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1386 if (alerted) {
1387 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1388 goto done;
1391 if (waited != 0) {
1392 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1393 goto done;
1397 done:
1398 mono_w32handle_unlock_handle (wait_handle);
1400 return ret;