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
;
37 mono_mutex_t signal_mutex
;
38 mono_cond_t signal_cond
;
42 static MonoW32HandleCapability handle_caps
[MONO_W32HANDLE_COUNT
];
43 static MonoW32HandleOps
*handle_ops
[MONO_W32HANDLE_COUNT
];
46 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
47 * If 4M handles are not enough... Oh, well... we will crash.
49 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
50 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
52 static MonoW32HandleBase
*private_handles
[SLOT_MAX
];
53 static guint32 private_handles_count
= 0;
54 static guint32 private_handles_slots_count
= 0;
56 guint32 mono_w32handle_fd_reserve
;
59 * This is an internal handle which is used for handling waiting for multiple handles.
60 * Threads which wait for multiple handles wait on this one handle, and when a handle
61 * is signalled, this handle is signalled too.
63 static mono_mutex_t global_signal_mutex
;
64 static mono_cond_t global_signal_cond
;
66 static mono_mutex_t scan_mutex
;
68 static gboolean shutting_down
= FALSE
;
71 type_is_fd (MonoW32HandleType type
)
74 case MONO_W32HANDLE_FILE
:
75 case MONO_W32HANDLE_CONSOLE
:
76 case MONO_W32HANDLE_SOCKET
:
77 case MONO_W32HANDLE_PIPE
:
85 mono_w32handle_lookup_data (gpointer handle
, MonoW32HandleBase
**handle_data
)
89 g_assert (handle_data
);
91 index
= SLOT_INDEX ((gsize
) handle
);
92 if (index
>= SLOT_MAX
)
94 if (!private_handles
[index
])
97 offset
= SLOT_OFFSET ((gsize
) handle
);
98 if (private_handles
[index
][offset
].type
== MONO_W32HANDLE_UNUSED
)
101 *handle_data
= &private_handles
[index
][offset
];
106 mono_w32handle_get_type (gpointer handle
)
108 MonoW32HandleBase
*handle_data
;
110 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
111 return MONO_W32HANDLE_UNUSED
; /* An impossible type */
113 return handle_data
->type
;
117 mono_w32handle_ops_typename (MonoW32HandleType type
);
120 mono_w32handle_get_typename (MonoW32HandleType type
)
122 return mono_w32handle_ops_typename (type
);
126 mono_w32handle_set_signal_state (gpointer handle
, gboolean state
, gboolean broadcast
)
128 MonoW32HandleBase
*handle_data
;
130 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
135 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__
,
136 handle
, state
?"TRUE":"FALSE", broadcast
?"TRUE":"FALSE");
140 /* Tell everyone blocking on a single handle */
142 /* The condition the global signal cond is waiting on is the signalling of
143 * _any_ handle. So lock it before setting the signalled state.
145 mono_os_mutex_lock (&global_signal_mutex
);
147 /* This function _must_ be called with
148 * handle->signal_mutex locked
150 handle_data
->signalled
=state
;
152 if (broadcast
== TRUE
) {
153 mono_os_cond_broadcast (&handle_data
->signal_cond
);
155 mono_os_cond_signal (&handle_data
->signal_cond
);
158 /* Tell everyone blocking on multiple handles that something
161 mono_os_cond_broadcast (&global_signal_cond
);
163 mono_os_mutex_unlock (&global_signal_mutex
);
165 handle_data
->signalled
=state
;
170 mono_w32handle_issignalled (gpointer handle
)
172 MonoW32HandleBase
*handle_data
;
174 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
178 return handle_data
->signalled
;
182 mono_w32handle_lock_signal_mutex (void)
185 g_message ("%s: lock global signal mutex", __func__
);
188 mono_os_mutex_lock (&global_signal_mutex
);
192 mono_w32handle_unlock_signal_mutex (void)
195 g_message ("%s: unlock global signal mutex", __func__
);
198 mono_os_mutex_unlock (&global_signal_mutex
);
202 mono_w32handle_lock_handle (gpointer handle
)
204 MonoW32HandleBase
*handle_data
;
206 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
207 g_error ("%s: failed to lookup handle %p", __func__
, handle
);
209 mono_w32handle_ref (handle
);
211 mono_os_mutex_lock (&handle_data
->signal_mutex
);
213 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: lock handle %p", __func__
, handle
);
217 mono_w32handle_trylock_handle (gpointer handle
)
219 MonoW32HandleBase
*handle_data
;
222 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: trylock handle %p", __func__
, handle
);
224 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
225 g_error ("%s: failed to lookup handle %p", __func__
, handle
);
227 mono_w32handle_ref (handle
);
229 locked
= mono_os_mutex_trylock (&handle_data
->signal_mutex
) == 0;
231 mono_w32handle_unref (handle
);
233 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: trylock handle %p, locked: %s", __func__
, handle
, locked
? "true" : "false");
239 mono_w32handle_unlock_handle (gpointer handle
)
241 MonoW32HandleBase
*handle_data
;
243 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
244 g_error ("%s: failed to lookup handle %p", __func__
, handle
);
246 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: unlock handle %p", __func__
, handle
);
248 mono_os_mutex_unlock (&handle_data
->signal_mutex
);
250 mono_w32handle_unref (handle
);
254 mono_w32handle_init (void)
256 static gboolean initialized
= FALSE
;
261 g_assert ((sizeof (handle_ops
) / sizeof (handle_ops
[0]))
262 == MONO_W32HANDLE_COUNT
);
264 /* This is needed by the code in mono_w32handle_new_internal */
265 mono_w32handle_fd_reserve
= (eg_getdtablesize () + (HANDLE_PER_SLOT
- 1)) & ~(HANDLE_PER_SLOT
- 1);
269 * The entries in private_handles reserved for fds are allocated lazily to
273 private_handles_count
+= HANDLE_PER_SLOT
;
274 private_handles_slots_count
++;
275 } while(mono_w32handle_fd_reserve
> private_handles_count
);
277 mono_os_mutex_init (&scan_mutex
);
279 mono_os_cond_init (&global_signal_cond
);
280 mono_os_mutex_init (&global_signal_mutex
);
286 mono_w32handle_cleanup (void)
290 g_assert (!shutting_down
);
291 shutting_down
= TRUE
;
293 for (i
= 0; i
< SLOT_MAX
; ++i
)
294 g_free (private_handles
[i
]);
298 mono_w32handle_ops_typesize (MonoW32HandleType type
);
300 static void mono_w32handle_init_handle (MonoW32HandleBase
*handle
,
301 MonoW32HandleType type
, gpointer handle_specific
)
303 g_assert (handle
->ref
== 0);
306 handle
->signalled
= FALSE
;
309 mono_os_cond_init (&handle
->signal_cond
);
310 mono_os_mutex_init (&handle
->signal_mutex
);
313 handle
->specific
= g_memdup (handle_specific
, mono_w32handle_ops_typesize (type
));
317 * mono_w32handle_new_internal:
318 * @type: Init handle to this type
320 * Search for a free handle and initialize it. Return the handle on
321 * success and 0 on failure. This is only called from
322 * mono_w32handle_new, and scan_mutex must be held.
324 static guint32
mono_w32handle_new_internal (MonoW32HandleType type
,
325 gpointer handle_specific
)
328 static guint32 last
= 0;
329 gboolean retry
= FALSE
;
331 /* A linear scan should be fast enough. Start from the last
332 * allocation, assuming that handles are allocated more often
333 * than they're freed. Leave the space reserved for file
337 if (last
< mono_w32handle_fd_reserve
) {
338 last
= mono_w32handle_fd_reserve
;
345 for(i
= SLOT_INDEX (count
); i
< private_handles_slots_count
; i
++) {
346 if (private_handles
[i
]) {
347 for (k
= SLOT_OFFSET (count
); k
< HANDLE_PER_SLOT
; k
++) {
348 MonoW32HandleBase
*handle
= &private_handles
[i
][k
];
350 if(handle
->type
== MONO_W32HANDLE_UNUSED
) {
353 mono_w32handle_init_handle (handle
, type
, handle_specific
);
361 if(retry
&& last
> mono_w32handle_fd_reserve
) {
362 /* Try again from the beginning */
363 last
= mono_w32handle_fd_reserve
;
367 /* Will need to expand the array. The caller will sort it out */
373 mono_w32handle_new (MonoW32HandleType type
, gpointer handle_specific
)
375 guint32 handle_idx
= 0;
378 g_assert (!shutting_down
);
380 g_assert(!type_is_fd(type
));
382 mono_os_mutex_lock (&scan_mutex
);
384 while ((handle_idx
= mono_w32handle_new_internal (type
, handle_specific
)) == 0) {
385 /* Try and expand the array, and have another go */
386 int idx
= SLOT_INDEX (private_handles_count
);
387 if (idx
>= SLOT_MAX
) {
391 private_handles
[idx
] = g_new0 (MonoW32HandleBase
, HANDLE_PER_SLOT
);
393 private_handles_count
+= HANDLE_PER_SLOT
;
394 private_handles_slots_count
++;
397 mono_os_mutex_unlock (&scan_mutex
);
399 if (handle_idx
== 0) {
400 /* We ran out of slots */
401 handle
= INVALID_HANDLE_VALUE
;
402 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: failed to create %s handle", __func__
, mono_w32handle_ops_typename (type
));
406 /* Make sure we left the space for fd mappings */
407 g_assert (handle_idx
>= mono_w32handle_fd_reserve
);
409 handle
= GUINT_TO_POINTER (handle_idx
);
411 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: create %s handle %p", __func__
, mono_w32handle_ops_typename (type
), handle
);
417 gpointer
mono_w32handle_new_fd (MonoW32HandleType type
, int fd
,
418 gpointer handle_specific
)
420 MonoW32HandleBase
*handle_data
;
421 int fd_index
, fd_offset
;
423 g_assert (!shutting_down
);
425 g_assert(type_is_fd(type
));
427 if (fd
>= mono_w32handle_fd_reserve
) {
428 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: failed to create %s handle, fd is too big", __func__
, mono_w32handle_ops_typename (type
));
430 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE
));
433 fd_index
= SLOT_INDEX (fd
);
434 fd_offset
= SLOT_OFFSET (fd
);
436 /* Initialize the array entries on demand */
437 if (!private_handles
[fd_index
]) {
438 mono_os_mutex_lock (&scan_mutex
);
440 if (!private_handles
[fd_index
])
441 private_handles
[fd_index
] = g_new0 (MonoW32HandleBase
, HANDLE_PER_SLOT
);
443 mono_os_mutex_unlock (&scan_mutex
);
446 handle_data
= &private_handles
[fd_index
][fd_offset
];
448 if (handle_data
->type
!= MONO_W32HANDLE_UNUSED
) {
449 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: failed to create %s handle, fd is already in use", __func__
, mono_w32handle_ops_typename (type
));
450 /* FIXME: clean up this handle? We can't do anything
451 * with the fd, cos thats the new one
453 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE
));
456 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: create %s handle %p", __func__
, mono_w32handle_ops_typename (type
), GUINT_TO_POINTER(fd
));
458 mono_w32handle_init_handle (handle_data
, type
, handle_specific
);
460 return(GUINT_TO_POINTER(fd
));
464 mono_w32handle_close (gpointer handle
)
466 if (handle
== INVALID_HANDLE_VALUE
)
468 if (handle
== (gpointer
) 0 && mono_w32handle_get_type (handle
) != MONO_W32HANDLE_CONSOLE
) {
469 /* Problem: because we map file descriptors to the
470 * same-numbered handle we can't tell the difference
471 * between a bogus handle and the handle to stdin.
472 * Assume that it's the console handle if that handle
477 mono_w32handle_unref (handle
);
482 mono_w32handle_lookup (gpointer handle
, MonoW32HandleType type
,
483 gpointer
*handle_specific
)
485 MonoW32HandleBase
*handle_data
;
487 g_assert (handle_specific
);
489 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
493 if (handle_data
->type
!= type
) {
497 *handle_specific
= handle_data
->specific
;
503 mono_w32handle_ref_core (gpointer handle
, MonoW32HandleBase
*handle_data
);
506 mono_w32handle_unref_core (gpointer handle
, MonoW32HandleBase
*handle_data
);
509 w32handle_destroy (gpointer handle
);
512 mono_w32handle_foreach (gboolean (*on_each
)(gpointer handle
, gpointer data
, gpointer user_data
), gpointer user_data
)
514 GPtrArray
*handles_to_destroy
;
517 handles_to_destroy
= NULL
;
519 mono_os_mutex_lock (&scan_mutex
);
521 for (i
= SLOT_INDEX (0); i
< private_handles_slots_count
; i
++) {
522 if (!private_handles
[i
])
524 for (k
= SLOT_OFFSET (0); k
< HANDLE_PER_SLOT
; k
++) {
525 MonoW32HandleBase
*handle_data
= NULL
;
527 gboolean destroy
, finished
;
529 handle_data
= &private_handles
[i
][k
];
530 if (handle_data
->type
== MONO_W32HANDLE_UNUSED
)
533 handle
= GUINT_TO_POINTER (i
* HANDLE_PER_SLOT
+ k
);
535 if (!mono_w32handle_ref_core (handle
, handle_data
)) {
536 /* we are racing with mono_w32handle_unref:
537 * the handle ref has been decremented, but it
538 * hasn't yet been destroyed. */
542 finished
= on_each (handle
, handle_data
->specific
, user_data
);
544 /* we might have to destroy the handle here, as
545 * it could have been unrefed in another thread */
546 destroy
= mono_w32handle_unref_core (handle
, handle_data
);
548 /* we do not destroy it while holding the scan_mutex
549 * lock, because w32handle_destroy also needs to take
550 * the lock, and it calls user code which might lead
552 if (!handles_to_destroy
)
553 handles_to_destroy
= g_ptr_array_sized_new (4);
554 g_ptr_array_add (handles_to_destroy
, handle
);
563 mono_os_mutex_unlock (&scan_mutex
);
565 if (handles_to_destroy
) {
566 for (i
= 0; i
< handles_to_destroy
->len
; ++i
)
567 w32handle_destroy (handles_to_destroy
->pdata
[i
]);
569 g_ptr_array_free (handles_to_destroy
, TRUE
);
574 mono_w32handle_ref_core (gpointer handle
, MonoW32HandleBase
*handle_data
)
579 old
= handle_data
->ref
;
584 } while (InterlockedCompareExchange ((gint32
*) &handle_data
->ref
, new, old
) != old
);
586 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: ref %s handle %p, ref: %d -> %d",
587 __func__
, mono_w32handle_ops_typename (handle_data
->type
), handle
, old
, new);
593 mono_w32handle_unref_core (gpointer handle
, MonoW32HandleBase
*handle_data
)
595 MonoW32HandleType type
;
598 type
= handle_data
->type
;
601 old
= handle_data
->ref
;
603 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__
, handle
, old
);
606 } while (InterlockedCompareExchange ((gint32
*) &handle_data
->ref
, new, old
) != old
);
608 /* handle_data might contain invalid data from now on, if
609 * another thread is unref'ing this handle at the same time */
611 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: unref %s handle %p, ref: %d -> %d destroy: %s",
612 __func__
, mono_w32handle_ops_typename (type
), handle
, old
, new, new == 0 ? "true" : "false");
617 void mono_w32handle_ref (gpointer handle
)
619 MonoW32HandleBase
*handle_data
;
621 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
622 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: failed to ref handle %p, unknown handle", __func__
, handle
);
626 if (!mono_w32handle_ref_core (handle
, handle_data
))
627 g_error ("%s: failed to ref handle %p", __func__
, handle
);
630 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type
))(gpointer
, gpointer
);
633 w32handle_destroy (gpointer handle
)
635 /* Need to copy the handle info, reset the slot in the
636 * array, and _only then_ call the close function to
637 * avoid race conditions (eg file descriptors being
638 * closed, and another file being opened getting the
639 * same fd racing the memset())
641 MonoW32HandleBase
*handle_data
;
642 MonoW32HandleType type
;
643 gpointer handle_specific
;
644 void (*close_func
)(gpointer
, gpointer
);
646 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
647 g_error ("%s: unknown handle %p", __func__
, handle
);
649 type
= handle_data
->type
;
650 handle_specific
= handle_data
->specific
;
652 mono_os_mutex_lock (&scan_mutex
);
654 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: destroy %s handle %p", __func__
, mono_w32handle_ops_typename (type
), handle
);
656 mono_os_mutex_destroy (&handle_data
->signal_mutex
);
657 mono_os_cond_destroy (&handle_data
->signal_cond
);
659 memset (handle_data
, 0, sizeof (MonoW32HandleBase
));
661 mono_os_mutex_unlock (&scan_mutex
);
663 close_func
= _wapi_handle_ops_get_close_func (type
);
664 if (close_func
!= NULL
) {
665 close_func (handle
, handle_specific
);
668 g_free (handle_specific
);
671 /* The handle must not be locked on entry to this function */
673 mono_w32handle_unref (gpointer handle
)
675 MonoW32HandleBase
*handle_data
;
678 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
679 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: failed to unref handle %p, unknown handle",
684 destroy
= mono_w32handle_unref_core (handle
, handle_data
);
686 w32handle_destroy (handle
);
690 mono_w32handle_ops_close (gpointer handle
, gpointer data
);
693 mono_w32handle_force_close (gpointer handle
, gpointer data
)
695 mono_w32handle_ops_close (handle
, data
);
699 mono_w32handle_register_ops (MonoW32HandleType type
, MonoW32HandleOps
*ops
)
701 handle_ops
[type
] = ops
;
704 void mono_w32handle_register_capabilities (MonoW32HandleType type
,
705 MonoW32HandleCapability caps
)
707 handle_caps
[type
] = caps
;
710 gboolean
mono_w32handle_test_capabilities (gpointer handle
,
711 MonoW32HandleCapability caps
)
713 MonoW32HandleBase
*handle_data
;
714 MonoW32HandleType type
;
716 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
720 type
= handle_data
->type
;
722 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: testing 0x%x against 0x%x (%d)", __func__
,
723 handle_caps
[type
], caps
, handle_caps
[type
] & caps
);
725 return((handle_caps
[type
] & caps
) != 0);
728 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type
))(gpointer
, gpointer
)
730 if (handle_ops
[type
] != NULL
&&
731 handle_ops
[type
]->close
!= NULL
) {
732 return (handle_ops
[type
]->close
);
739 mono_w32handle_ops_close (gpointer handle
, gpointer data
)
741 MonoW32HandleBase
*handle_data
;
742 MonoW32HandleType type
;
744 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
748 type
= handle_data
->type
;
750 if (handle_ops
[type
] != NULL
&&
751 handle_ops
[type
]->close
!= NULL
) {
752 handle_ops
[type
]->close (handle
, data
);
757 mono_w32handle_ops_details (MonoW32HandleType type
, gpointer data
)
759 if (handle_ops
[type
] != NULL
&&
760 handle_ops
[type
]->details
!= NULL
) {
761 handle_ops
[type
]->details (data
);
766 mono_w32handle_ops_typename (MonoW32HandleType type
)
768 g_assert (handle_ops
[type
]);
769 g_assert (handle_ops
[type
]->typename
);
770 return handle_ops
[type
]->typename ();
774 mono_w32handle_ops_typesize (MonoW32HandleType type
)
776 g_assert (handle_ops
[type
]);
777 g_assert (handle_ops
[type
]->typesize
);
778 return handle_ops
[type
]->typesize ();
782 mono_w32handle_ops_signal (gpointer handle
)
784 MonoW32HandleBase
*handle_data
;
785 MonoW32HandleType type
;
787 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
791 type
= handle_data
->type
;
793 if (handle_ops
[type
] != NULL
&& handle_ops
[type
]->signal
!= NULL
) {
794 handle_ops
[type
]->signal (handle
);
799 mono_w32handle_ops_own (gpointer handle
, gboolean
*abandoned
)
801 MonoW32HandleBase
*handle_data
;
802 MonoW32HandleType type
;
804 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
808 type
= handle_data
->type
;
810 if (handle_ops
[type
] != NULL
&& handle_ops
[type
]->own_handle
!= NULL
) {
811 return(handle_ops
[type
]->own_handle (handle
, abandoned
));
818 mono_w32handle_ops_isowned (gpointer handle
)
820 MonoW32HandleBase
*handle_data
;
821 MonoW32HandleType type
;
823 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
827 type
= handle_data
->type
;
829 if (handle_ops
[type
] != NULL
&& handle_ops
[type
]->is_owned
!= NULL
) {
830 return(handle_ops
[type
]->is_owned (handle
));
836 static MonoW32HandleWaitRet
837 mono_w32handle_ops_specialwait (gpointer handle
, guint32 timeout
, gboolean
*alerted
)
839 MonoW32HandleBase
*handle_data
;
840 MonoW32HandleType type
;
842 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
843 return MONO_W32HANDLE_WAIT_RET_FAILED
;
846 type
= handle_data
->type
;
848 if (handle_ops
[type
] != NULL
&&
849 handle_ops
[type
]->special_wait
!= NULL
) {
850 return(handle_ops
[type
]->special_wait (handle
, timeout
, alerted
));
852 return MONO_W32HANDLE_WAIT_RET_FAILED
;
857 mono_w32handle_ops_prewait (gpointer handle
)
859 MonoW32HandleBase
*handle_data
;
860 MonoW32HandleType type
;
862 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
866 type
= handle_data
->type
;
868 if (handle_ops
[type
] != NULL
&&
869 handle_ops
[type
]->prewait
!= NULL
) {
870 handle_ops
[type
]->prewait (handle
);
880 struct timespec sleepytime
;
882 g_assert (ms
< 1000);
884 sleepytime
.tv_sec
= 0;
885 sleepytime
.tv_nsec
= ms
* 1000000;
886 nanosleep (&sleepytime
, NULL
);
887 #endif /* HOST_WIN32 */
891 mono_w32handle_lock_handles (gpointer
*handles
, gsize numhandles
)
895 /* Lock all the handles, with backoff */
897 for(i
=0; i
<numhandles
; i
++) {
898 gpointer handle
= handles
[i
];
900 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: attempting to lock %p", __func__
, handle
);
902 if (!mono_w32handle_trylock_handle (handle
)) {
905 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: attempt failed for %p.", __func__
,
911 mono_w32handle_unlock_handle (handle
);
914 /* If iter ever reaches 100 the nanosleep will
915 * return EINVAL immediately, but we have a
916 * design flaw if that happens.
920 g_warning ("%s: iteration overflow!",
925 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: Backing off for %d ms", __func__
,
933 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: Locked all handles", __func__
);
937 mono_w32handle_unlock_handles (gpointer
*handles
, gsize numhandles
)
941 for(i
=0; i
<numhandles
; i
++) {
942 gpointer handle
= handles
[i
];
944 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: unlocking handle %p", __func__
, handle
);
946 mono_w32handle_unlock_handle (handle
);
951 mono_w32handle_timedwait_signal_naked (mono_cond_t
*cond
, mono_mutex_t
*mutex
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
956 res
= mono_os_cond_timedwait (cond
, mutex
, timeout
);
958 /* This is needed when waiting for process handles */
961 * pthread_cond_(timed)wait() can return 0 even if the condition was not
962 * signalled. This happens at least on Darwin. We surface this, i.e., we
963 * get spurious wake-ups.
965 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
967 res
= mono_os_cond_timedwait (cond
, mutex
, timeout
);
970 /* Real timeout is less than 100ms time */
971 res
= mono_os_cond_timedwait (cond
, mutex
, timeout
);
973 res
= mono_os_cond_timedwait (cond
, mutex
, 100);
975 /* Mask the fake timeout, this will cause
976 * another poll if the cond was not really signaled
988 signal_global (gpointer unused
)
990 /* If we reach here, then interrupt token is set to the flag value, which
991 * means that the target thread is either
992 * - before the first CAS in timedwait, which means it won't enter the wait.
993 * - it is after the first CAS, so it is already waiting, or it will enter
994 * the wait, and it will be interrupted by the broadcast. */
995 mono_os_mutex_lock (&global_signal_mutex
);
996 mono_os_cond_broadcast (&global_signal_cond
);
997 mono_os_mutex_unlock (&global_signal_mutex
);
1001 mono_w32handle_timedwait_signal (guint32 timeout
, gboolean poll
, gboolean
*alerted
)
1005 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: waiting for global", __func__
);
1011 mono_thread_info_install_interrupt (signal_global
, NULL
, alerted
);
1016 res
= mono_w32handle_timedwait_signal_naked (&global_signal_cond
, &global_signal_mutex
, timeout
, poll
, alerted
);
1019 mono_thread_info_uninstall_interrupt (alerted
);
1025 signal_handle_and_unref (gpointer handle
)
1027 MonoW32HandleBase
*handle_data
;
1029 mono_mutex_t
*mutex
;
1031 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
1032 g_error ("cannot signal unknown handle %p", handle
);
1034 /* If we reach here, then interrupt token is set to the flag value, which
1035 * means that the target thread is either
1036 * - before the first CAS in timedwait, which means it won't enter the wait.
1037 * - it is after the first CAS, so it is already waiting, or it will enter
1038 * the wait, and it will be interrupted by the broadcast. */
1039 cond
= &handle_data
->signal_cond
;
1040 mutex
= &handle_data
->signal_mutex
;
1042 mono_os_mutex_lock (mutex
);
1043 mono_os_cond_broadcast (cond
);
1044 mono_os_mutex_unlock (mutex
);
1046 mono_w32handle_unref (handle
);
1050 mono_w32handle_timedwait_signal_handle (gpointer handle
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
1052 MonoW32HandleBase
*handle_data
;
1055 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
1056 g_error ("cannot wait on unknown handle %p", handle
);
1058 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: waiting for %p (type %s)", __func__
, handle
,
1059 mono_w32handle_ops_typename (mono_w32handle_get_type (handle
)));
1065 mono_thread_info_install_interrupt (signal_handle_and_unref
, handle
, alerted
);
1068 mono_w32handle_ref (handle
);
1071 res
= mono_w32handle_timedwait_signal_naked (&handle_data
->signal_cond
, &handle_data
->signal_mutex
, timeout
, poll
, alerted
);
1074 mono_thread_info_uninstall_interrupt (alerted
);
1076 /* if it is alerted, then the handle is unref in the interrupt callback */
1077 mono_w32handle_unref (handle
);
1085 dump_callback (gpointer handle
, gpointer handle_specific
, gpointer user_data
)
1087 MonoW32HandleBase
*handle_data
;
1089 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
1090 g_error ("cannot dump unknown handle %p", handle
);
1092 g_print ("%p [%7s] signalled: %5s ref: %3d ",
1093 handle
, mono_w32handle_ops_typename (handle_data
->type
), handle_data
->signalled
? "true" : "false", handle_data
->ref
);
1094 mono_w32handle_ops_details (handle_data
->type
, handle_data
->specific
);
1100 void mono_w32handle_dump (void)
1102 mono_w32handle_foreach (dump_callback
, NULL
);
1106 own_if_signalled (gpointer handle
, gboolean
*abandoned
)
1108 if (!mono_w32handle_issignalled (handle
))
1112 mono_w32handle_ops_own (handle
, abandoned
);
1117 own_if_owned( gpointer handle
, gboolean
*abandoned
)
1119 if (!mono_w32handle_ops_isowned (handle
))
1123 mono_w32handle_ops_own (handle
, abandoned
);
1127 MonoW32HandleWaitRet
1128 mono_w32handle_wait_one (gpointer handle
, guint32 timeout
, gboolean alertable
)
1130 MonoW32HandleWaitRet ret
;
1133 gboolean abandoned
= FALSE
;
1137 if (mono_w32handle_test_capabilities (handle
, MONO_W32HANDLE_CAP_SPECIAL_WAIT
)) {
1138 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p has special wait",
1141 return mono_w32handle_ops_specialwait (handle
, timeout
, alertable
? &alerted
: NULL
);
1144 if (!mono_w32handle_test_capabilities (handle
, MONO_W32HANDLE_CAP_WAIT
)) {
1145 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p can't be waited for",
1148 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1151 mono_w32handle_lock_handle (handle
);
1153 if (mono_w32handle_test_capabilities (handle
, MONO_W32HANDLE_CAP_OWN
)) {
1154 if (own_if_owned (handle
, &abandoned
)) {
1155 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p already owned",
1158 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1163 if (timeout
!= MONO_INFINITE_WAIT
)
1164 start
= mono_msec_ticks ();
1169 if (own_if_signalled (handle
, &abandoned
)) {
1170 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p signalled",
1173 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1177 mono_w32handle_ops_prewait (handle
);
1179 if (timeout
== MONO_INFINITE_WAIT
) {
1180 waited
= mono_w32handle_timedwait_signal_handle (handle
, MONO_INFINITE_WAIT
, FALSE
, alertable
? &alerted
: NULL
);
1184 elapsed
= mono_msec_ticks () - start
;
1185 if (elapsed
> timeout
) {
1186 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1190 waited
= mono_w32handle_timedwait_signal_handle (handle
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
1194 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1199 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1205 mono_w32handle_unlock_handle (handle
);
1210 MonoW32HandleWaitRet
1211 mono_w32handle_wait_multiple (gpointer
*handles
, gsize nhandles
, gboolean waitall
, guint32 timeout
, gboolean alertable
)
1213 MonoW32HandleWaitRet ret
;
1214 gboolean alerted
, poll
;
1217 gpointer handles_sorted
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
];
1218 gboolean abandoned
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
] = {0};
1221 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1224 return mono_w32handle_wait_one (handles
[0], timeout
, alertable
);
1228 if (nhandles
> MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
) {
1229 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: too many handles: %zd",
1230 __func__
, nhandles
);
1232 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1235 for (i
= 0; i
< nhandles
; ++i
) {
1236 if (!mono_w32handle_test_capabilities (handles
[i
], MONO_W32HANDLE_CAP_WAIT
)
1237 && !mono_w32handle_test_capabilities (handles
[i
], MONO_W32HANDLE_CAP_SPECIAL_WAIT
))
1239 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p can't be waited for",
1240 __func__
, handles
[i
]);
1242 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1245 handles_sorted
[i
] = handles
[i
];
1248 qsort (handles_sorted
, nhandles
, sizeof (gpointer
), g_direct_equal
);
1249 for (i
= 1; i
< nhandles
; ++i
) {
1250 if (handles_sorted
[i
- 1] == handles_sorted
[i
]) {
1251 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p is duplicated",
1252 __func__
, handles_sorted
[i
]);
1254 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1259 for (i
= 0; i
< nhandles
; ++i
) {
1260 if (mono_w32handle_get_type (handles
[i
]) == MONO_W32HANDLE_PROCESS
) {
1261 /* Can't wait for a process handle + another handle without polling */
1266 if (timeout
!= MONO_INFINITE_WAIT
)
1267 start
= mono_msec_ticks ();
1269 for (i
= 0; i
< nhandles
; ++i
) {
1270 /* Add a reference, as we need to ensure the handle wont
1271 * disappear from under us while we're waiting in the loop
1272 * (not lock, as we don't want exclusive access here) */
1273 mono_w32handle_ref (handles
[i
]);
1277 gsize count
, lowest
;
1284 mono_w32handle_lock_handles (handles
, nhandles
);
1286 for (i
= 0; i
< nhandles
; i
++) {
1287 if ((mono_w32handle_test_capabilities (handles
[i
], MONO_W32HANDLE_CAP_OWN
) && mono_w32handle_ops_isowned (handles
[i
]))
1288 || mono_w32handle_issignalled (handles
[i
]))
1297 signalled
= (waitall
&& count
== nhandles
) || (!waitall
&& count
> 0);
1300 for (i
= 0; i
< nhandles
; i
++) {
1301 if (own_if_signalled (handles
[i
], &abandoned
[i
]) && !waitall
) {
1302 /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
1303 * throw AbandonedMutexException in case we owned it but didn't release it */
1309 mono_w32handle_unlock_handles (handles
, nhandles
);
1312 ret
= MONO_W32HANDLE_WAIT_RET_SUCCESS_0
+ lowest
;
1313 for (i
= lowest
; i
< nhandles
; i
++) {
1314 if (abandoned
[i
]) {
1315 ret
= MONO_W32HANDLE_WAIT_RET_ABANDONED_0
+ lowest
;
1322 for (i
= 0; i
< nhandles
; i
++) {
1323 mono_w32handle_ops_prewait (handles
[i
]);
1325 if (mono_w32handle_test_capabilities (handles
[i
], MONO_W32HANDLE_CAP_SPECIAL_WAIT
)
1326 && !mono_w32handle_issignalled (handles
[i
]))
1328 mono_w32handle_ops_specialwait (handles
[i
], 0, alertable
? &alerted
: NULL
);
1332 mono_w32handle_lock_signal_mutex ();
1336 for (i
= 0; i
< nhandles
; ++i
) {
1337 if (!mono_w32handle_issignalled (handles
[i
])) {
1344 for (i
= 0; i
< nhandles
; ++i
) {
1345 if (mono_w32handle_issignalled (handles
[i
])) {
1355 if (timeout
== MONO_INFINITE_WAIT
) {
1356 waited
= mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT
, poll
, alertable
? &alerted
: NULL
);
1360 elapsed
= mono_msec_ticks () - start
;
1361 if (elapsed
> timeout
) {
1362 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1364 mono_w32handle_unlock_signal_mutex ();
1369 waited
= mono_w32handle_timedwait_signal (timeout
- elapsed
, poll
, alertable
? &alerted
: NULL
);
1373 mono_w32handle_unlock_signal_mutex ();
1376 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1381 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1387 for (i
= 0; i
< nhandles
; i
++) {
1388 /* Unref everything we reffed above */
1389 mono_w32handle_unref (handles
[i
]);
1395 MonoW32HandleWaitRet
1396 mono_w32handle_signal_and_wait (gpointer signal_handle
, gpointer wait_handle
, guint32 timeout
, gboolean alertable
)
1398 MonoW32HandleWaitRet ret
;
1401 gboolean abandoned
= FALSE
;
1402 gpointer handles
[2];
1406 if (!mono_w32handle_test_capabilities (signal_handle
, MONO_W32HANDLE_CAP_SIGNAL
))
1407 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1408 if (!mono_w32handle_test_capabilities (wait_handle
, MONO_W32HANDLE_CAP_WAIT
))
1409 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1411 if (mono_w32handle_test_capabilities (wait_handle
, MONO_W32HANDLE_CAP_SPECIAL_WAIT
)) {
1412 g_warning ("%s: handle %p has special wait, implement me!!", __func__
, wait_handle
);
1413 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1416 handles
[0] = wait_handle
;
1417 handles
[1] = signal_handle
;
1419 mono_w32handle_lock_handles (handles
, 2);
1421 mono_w32handle_ops_signal (signal_handle
);
1423 mono_w32handle_unlock_handle (signal_handle
);
1425 if (mono_w32handle_test_capabilities (wait_handle
, MONO_W32HANDLE_CAP_OWN
)) {
1426 if (own_if_owned (wait_handle
, &abandoned
)) {
1427 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p already owned",
1428 __func__
, wait_handle
);
1430 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1435 if (timeout
!= MONO_INFINITE_WAIT
)
1436 start
= mono_msec_ticks ();
1441 if (own_if_signalled (wait_handle
, &abandoned
)) {
1442 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p signalled",
1443 __func__
, wait_handle
);
1445 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1449 mono_w32handle_ops_prewait (wait_handle
);
1451 if (timeout
== MONO_INFINITE_WAIT
) {
1452 waited
= mono_w32handle_timedwait_signal_handle (wait_handle
, MONO_INFINITE_WAIT
, FALSE
, alertable
? &alerted
: NULL
);
1456 elapsed
= mono_msec_ticks () - start
;
1457 if (elapsed
> timeout
) {
1458 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1462 waited
= mono_w32handle_timedwait_signal_handle (wait_handle
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
1466 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1471 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1477 mono_w32handle_unlock_handle (wait_handle
);