3 * Generic and internal operations on handles
6 * Dick Porter (dick@ximian.com)
7 * Ludovic Henry (luhenry@microsoft.com)
9 * (C) 2002-2011 Novell, Inc.
10 * Copyright 2011 Xamarin Inc
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
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"
28 #define SLOT_MAX (1024 * 32)
30 /* must be a power of 2 */
31 #define HANDLE_PER_SLOT (256)
34 MonoW32HandleType type
;
38 mono_mutex_t signal_mutex
;
39 mono_cond_t signal_cond
;
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
;
69 mono_w32handle_lookup_data (gpointer handle
, MonoW32HandleBase
**handle_data
)
73 g_assert (handle_data
);
75 index
= SLOT_INDEX ((gsize
) handle
);
76 if (index
>= SLOT_MAX
)
78 if (!private_handles
[index
])
81 offset
= SLOT_OFFSET ((gsize
) handle
);
82 if (private_handles
[index
][offset
].type
== MONO_W32HANDLE_UNUSED
)
85 *handle_data
= &private_handles
[index
][offset
];
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
;
101 mono_w32handle_ops_typename (MonoW32HandleType type
);
104 mono_w32handle_get_typename (MonoW32HandleType type
)
106 return mono_w32handle_ops_typename (type
);
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
)) {
119 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__
,
120 handle
, state
?"TRUE":"FALSE", broadcast
?"TRUE":"FALSE");
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
);
139 mono_os_cond_signal (&handle_data
->signal_cond
);
142 /* Tell everyone blocking on multiple handles that something
145 mono_os_cond_broadcast (&global_signal_cond
);
147 mono_os_mutex_unlock (&global_signal_mutex
);
149 handle_data
->signalled
=state
;
154 mono_w32handle_issignalled (gpointer handle
)
156 MonoW32HandleBase
*handle_data
;
158 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
162 return handle_data
->signalled
;
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
;
177 mono_w32handle_lock_signal_mutex (void)
180 g_message ("%s: lock global signal mutex", __func__
);
183 mono_os_mutex_lock (&global_signal_mutex
);
187 mono_w32handle_unlock_signal_mutex (void)
190 g_message ("%s: unlock global signal mutex", __func__
);
193 mono_os_mutex_unlock (&global_signal_mutex
);
197 mono_w32handle_ref (gpointer handle
);
200 mono_w32handle_unref (gpointer handle
);
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
);
218 mono_w32handle_trylock_handle (gpointer handle
)
220 MonoW32HandleBase
*handle_data
;
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;
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");
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
);
255 mono_w32handle_init (void)
257 static gboolean initialized
= FALSE
;
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
);
274 mono_w32handle_cleanup (void)
278 g_assert (!shutting_down
);
279 shutting_down
= TRUE
;
281 for (i
= 0; i
< SLOT_MAX
; ++i
)
282 g_free (private_handles
[i
]);
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
)
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
310 /* We need to go from 1 since a handle of value 0 can be considered invalid in managed code */
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
) {
326 g_assert (handle
->ref
== 0);
329 handle
->signalled
= FALSE
;
332 mono_os_cond_init (&handle
->signal_cond
);
333 mono_os_mutex_init (&handle
->signal_mutex
);
336 handle
->specific
= g_memdup (handle_specific
, mono_w32handle_ops_typesize (type
));
346 /* Try again from the beginning */
352 /* Will need to expand the array. The caller will sort it out */
358 mono_w32handle_new (MonoW32HandleType type
, gpointer handle_specific
)
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
);
390 mono_w32handle_ref_core (gpointer handle
, MonoW32HandleBase
*handle_data
);
393 mono_w32handle_unref_core (gpointer handle
, MonoW32HandleBase
*handle_data
);
396 w32handle_destroy (gpointer handle
);
399 mono_w32handle_duplicate (gpointer handle
)
401 MonoW32HandleBase
*handle_data
;
403 if (handle
== INVALID_HANDLE_VALUE
)
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
);
415 mono_w32handle_close (gpointer handle
)
417 MonoW32HandleBase
*handle_data
;
420 if (handle
== INVALID_HANDLE_VALUE
)
422 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
425 destroy
= mono_w32handle_unref_core (handle
, handle_data
);
427 w32handle_destroy (handle
);
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
)) {
444 if (handle_data
->type
!= type
) {
448 *handle_specific
= handle_data
->specific
;
454 mono_w32handle_foreach (gboolean (*on_each
)(gpointer handle
, gpointer data
, gpointer user_data
), gpointer user_data
)
456 GPtrArray
*handles_to_destroy
;
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
])
466 for (k
= SLOT_OFFSET (0); k
< HANDLE_PER_SLOT
; k
++) {
467 MonoW32HandleBase
*handle_data
= NULL
;
469 gboolean destroy
, finished
;
471 handle_data
= &private_handles
[i
][k
];
472 if (handle_data
->type
== MONO_W32HANDLE_UNUSED
)
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. */
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
);
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
494 if (!handles_to_destroy
)
495 handles_to_destroy
= g_ptr_array_sized_new (4);
496 g_ptr_array_add (handles_to_destroy
, handle
);
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
);
516 mono_w32handle_ref_core (gpointer handle
, MonoW32HandleBase
*handle_data
)
521 old
= handle_data
->ref
;
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);
535 mono_w32handle_unref_core (gpointer handle
, MonoW32HandleBase
*handle_data
)
537 MonoW32HandleType type
;
540 type
= handle_data
->type
;
543 old
= handle_data
->ref
;
545 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__
, handle
, old
);
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");
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
);
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 */
618 mono_w32handle_unref (gpointer handle
)
620 MonoW32HandleBase
*handle_data
;
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
);
628 w32handle_destroy (handle
);
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
)) {
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
);
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
);
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 ();
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 ();
697 mono_w32handle_ops_signal (gpointer handle
)
699 MonoW32HandleBase
*handle_data
;
700 MonoW32HandleType type
;
702 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
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
);
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
)) {
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
));
733 mono_w32handle_ops_isowned (gpointer handle
)
735 MonoW32HandleBase
*handle_data
;
736 MonoW32HandleType type
;
738 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
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
));
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
));
767 return MONO_W32HANDLE_WAIT_RET_FAILED
;
772 mono_w32handle_ops_prewait (gpointer handle
)
774 MonoW32HandleBase
*handle_data
;
775 MonoW32HandleType type
;
777 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
781 type
= handle_data
->type
;
783 if (handle_ops
[type
] != NULL
&&
784 handle_ops
[type
]->prewait
!= NULL
) {
785 handle_ops
[type
]->prewait (handle
);
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 */
806 mono_w32handle_lock_handles (gpointer
*handles
, gsize numhandles
)
810 /* Lock all the handles, with backoff */
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
)) {
820 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: attempt failed for %p.", __func__
,
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.
835 g_warning ("%s: iteration overflow!",
840 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: Backing off for %d ms", __func__
,
848 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: Locked all handles", __func__
);
852 mono_w32handle_unlock_handles (gpointer
*handles
, gsize numhandles
)
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
);
866 mono_w32handle_timedwait_signal_naked (mono_cond_t
*cond
, mono_mutex_t
*mutex
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
871 res
= mono_os_cond_timedwait (cond
, mutex
, timeout
);
873 /* This is needed when waiting for process handles */
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
);
885 /* Real timeout is less than 100ms time */
886 res
= mono_os_cond_timedwait (cond
, mutex
, timeout
);
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
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
);
916 mono_w32handle_timedwait_signal (guint32 timeout
, gboolean poll
, gboolean
*alerted
)
920 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: waiting for global", __func__
);
926 mono_thread_info_install_interrupt (signal_global
, NULL
, alerted
);
931 res
= mono_w32handle_timedwait_signal_naked (&global_signal_cond
, &global_signal_mutex
, timeout
, poll
, alerted
);
934 mono_thread_info_uninstall_interrupt (alerted
);
940 signal_handle_and_unref (gpointer handle
)
942 MonoW32HandleBase
*handle_data
;
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
);
965 mono_w32handle_timedwait_signal_handle (gpointer handle
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
967 MonoW32HandleBase
*handle_data
;
968 gpointer handle_duplicate
;
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
)));
981 mono_thread_info_install_interrupt (signal_handle_and_unref
, handle_duplicate
= mono_w32handle_duplicate (handle
), alerted
);
983 mono_w32handle_close (handle_duplicate
);
988 res
= mono_w32handle_timedwait_signal_naked (&handle_data
->signal_cond
, &handle_data
->signal_mutex
, timeout
, poll
, alerted
);
991 mono_thread_info_uninstall_interrupt (alerted
);
993 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
994 mono_w32handle_close (handle_duplicate
);
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
);
1017 void mono_w32handle_dump (void)
1019 mono_w32handle_foreach (dump_callback
, NULL
);
1023 own_if_signalled (gpointer handle
, gboolean
*abandoned
)
1025 if (!mono_w32handle_issignalled (handle
))
1029 mono_w32handle_ops_own (handle
, abandoned
);
1034 own_if_owned( gpointer handle
, gboolean
*abandoned
)
1036 if (!mono_w32handle_ops_isowned (handle
))
1040 mono_w32handle_ops_own (handle
, abandoned
);
1044 MonoW32HandleWaitRet
1045 mono_w32handle_wait_one (gpointer handle
, guint32 timeout
, gboolean alertable
)
1047 MonoW32HandleWaitRet ret
;
1050 gboolean abandoned
= 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",
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",
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",
1075 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1080 if (timeout
!= MONO_INFINITE_WAIT
)
1081 start
= mono_msec_ticks ();
1083 mono_w32handle_set_in_use (handle
, TRUE
);
1088 if (own_if_signalled (handle
, &abandoned
)) {
1089 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p signalled",
1092 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
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
);
1103 elapsed
= mono_msec_ticks () - start
;
1104 if (elapsed
> timeout
) {
1105 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1109 waited
= mono_w32handle_timedwait_signal_handle (handle
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
1113 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1118 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1124 mono_w32handle_set_in_use (handle
, FALSE
);
1126 mono_w32handle_unlock_handle (handle
);
1131 MonoW32HandleWaitRet
1132 mono_w32handle_wait_multiple (gpointer
*handles
, gsize nhandles
, gboolean waitall
, guint32 timeout
, gboolean alertable
)
1134 MonoW32HandleWaitRet ret
;
1135 gboolean alerted
, poll
;
1138 gpointer handles_sorted
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
];
1139 gboolean abandoned
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
] = {0};
1142 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1145 return mono_w32handle_wait_one (handles
[0], timeout
, alertable
);
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
;
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 */
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
]);
1198 gsize count
, lowest
;
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
]))
1218 signalled
= (waitall
&& count
== nhandles
) || (!waitall
&& count
> 0);
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 */
1230 mono_w32handle_unlock_handles (handles
, nhandles
);
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
;
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 ();
1257 for (i
= 0; i
< nhandles
; ++i
) {
1258 if (!mono_w32handle_issignalled (handles
[i
])) {
1265 for (i
= 0; i
< nhandles
; ++i
) {
1266 if (mono_w32handle_issignalled (handles
[i
])) {
1276 if (timeout
== MONO_INFINITE_WAIT
) {
1277 waited
= mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT
, poll
, alertable
? &alerted
: NULL
);
1281 elapsed
= mono_msec_ticks () - start
;
1282 if (elapsed
> timeout
) {
1283 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1285 mono_w32handle_unlock_signal_mutex ();
1290 waited
= mono_w32handle_timedwait_signal (timeout
- elapsed
, poll
, alertable
? &alerted
: NULL
);
1294 mono_w32handle_unlock_signal_mutex ();
1297 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1302 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1308 for (i
= 0; i
< nhandles
; i
++) {
1309 /* Unref everything we reffed above */
1310 mono_w32handle_unref (handles
[i
]);
1316 MonoW32HandleWaitRet
1317 mono_w32handle_signal_and_wait (gpointer signal_handle
, gpointer wait_handle
, guint32 timeout
, gboolean alertable
)
1319 MonoW32HandleWaitRet ret
;
1322 gboolean abandoned
= FALSE
;
1323 gpointer handles
[2];
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
;
1356 if (timeout
!= MONO_INFINITE_WAIT
)
1357 start
= mono_msec_ticks ();
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
;
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
);
1377 elapsed
= mono_msec_ticks () - start
;
1378 if (elapsed
> timeout
) {
1379 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1383 waited
= mono_w32handle_timedwait_signal_handle (wait_handle
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
1387 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1392 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1398 mono_w32handle_unlock_handle (wait_handle
);