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_count
= 0;
55 static guint32 private_handles_slots_count
= 0;
57 guint32 mono_w32handle_fd_reserve
;
60 * This is an internal handle which is used for handling waiting for multiple handles.
61 * Threads which wait for multiple handles wait on this one handle, and when a handle
62 * is signalled, this handle is signalled too.
64 static mono_mutex_t global_signal_mutex
;
65 static mono_cond_t global_signal_cond
;
67 static mono_mutex_t scan_mutex
;
69 static gboolean shutting_down
= FALSE
;
72 type_is_fd (MonoW32HandleType type
)
75 case MONO_W32HANDLE_FILE
:
76 case MONO_W32HANDLE_CONSOLE
:
77 case MONO_W32HANDLE_SOCKET
:
78 case MONO_W32HANDLE_PIPE
:
86 mono_w32handle_lookup_data (gpointer handle
, MonoW32HandleBase
**handle_data
)
90 g_assert (handle_data
);
92 index
= SLOT_INDEX ((gsize
) handle
);
93 if (index
>= SLOT_MAX
)
95 if (!private_handles
[index
])
98 offset
= SLOT_OFFSET ((gsize
) handle
);
99 if (private_handles
[index
][offset
].type
== MONO_W32HANDLE_UNUSED
)
102 *handle_data
= &private_handles
[index
][offset
];
107 mono_w32handle_get_type (gpointer handle
)
109 MonoW32HandleBase
*handle_data
;
111 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
112 return MONO_W32HANDLE_UNUSED
; /* An impossible type */
114 return handle_data
->type
;
118 mono_w32handle_ops_typename (MonoW32HandleType type
);
121 mono_w32handle_get_typename (MonoW32HandleType type
)
123 return mono_w32handle_ops_typename (type
);
127 mono_w32handle_set_signal_state (gpointer handle
, gboolean state
, gboolean broadcast
)
129 MonoW32HandleBase
*handle_data
;
131 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
136 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__
,
137 handle
, state
?"TRUE":"FALSE", broadcast
?"TRUE":"FALSE");
141 /* Tell everyone blocking on a single handle */
143 /* The condition the global signal cond is waiting on is the signalling of
144 * _any_ handle. So lock it before setting the signalled state.
146 mono_os_mutex_lock (&global_signal_mutex
);
148 /* This function _must_ be called with
149 * handle->signal_mutex locked
151 handle_data
->signalled
=state
;
153 if (broadcast
== TRUE
) {
154 mono_os_cond_broadcast (&handle_data
->signal_cond
);
156 mono_os_cond_signal (&handle_data
->signal_cond
);
159 /* Tell everyone blocking on multiple handles that something
162 mono_os_cond_broadcast (&global_signal_cond
);
164 mono_os_mutex_unlock (&global_signal_mutex
);
166 handle_data
->signalled
=state
;
171 mono_w32handle_issignalled (gpointer handle
)
173 MonoW32HandleBase
*handle_data
;
175 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
179 return handle_data
->signalled
;
183 mono_w32handle_set_in_use (gpointer handle
, gboolean in_use
)
185 MonoW32HandleBase
*handle_data
;
187 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
188 g_assert_not_reached ();
190 handle_data
->in_use
= in_use
;
194 mono_w32handle_lock_signal_mutex (void)
197 g_message ("%s: lock global signal mutex", __func__
);
200 mono_os_mutex_lock (&global_signal_mutex
);
204 mono_w32handle_unlock_signal_mutex (void)
207 g_message ("%s: unlock global signal mutex", __func__
);
210 mono_os_mutex_unlock (&global_signal_mutex
);
214 mono_w32handle_ref (gpointer handle
);
217 mono_w32handle_unref (gpointer handle
);
220 mono_w32handle_lock_handle (gpointer handle
)
222 MonoW32HandleBase
*handle_data
;
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 mono_os_mutex_lock (&handle_data
->signal_mutex
);
231 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: lock handle %p", __func__
, handle
);
235 mono_w32handle_trylock_handle (gpointer handle
)
237 MonoW32HandleBase
*handle_data
;
240 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: trylock handle %p", __func__
, handle
);
242 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
243 g_error ("%s: failed to lookup handle %p", __func__
, handle
);
245 mono_w32handle_ref (handle
);
247 locked
= mono_os_mutex_trylock (&handle_data
->signal_mutex
) == 0;
249 mono_w32handle_unref (handle
);
251 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: trylock handle %p, locked: %s", __func__
, handle
, locked
? "true" : "false");
257 mono_w32handle_unlock_handle (gpointer handle
)
259 MonoW32HandleBase
*handle_data
;
261 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
262 g_error ("%s: failed to lookup handle %p", __func__
, handle
);
264 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: unlock handle %p", __func__
, handle
);
266 mono_os_mutex_unlock (&handle_data
->signal_mutex
);
268 mono_w32handle_unref (handle
);
272 mono_w32handle_init (void)
274 static gboolean initialized
= FALSE
;
279 g_assert ((sizeof (handle_ops
) / sizeof (handle_ops
[0]))
280 == MONO_W32HANDLE_COUNT
);
282 /* This is needed by the code in mono_w32handle_new_internal */
283 mono_w32handle_fd_reserve
= (eg_getdtablesize () + (HANDLE_PER_SLOT
- 1)) & ~(HANDLE_PER_SLOT
- 1);
287 * The entries in private_handles reserved for fds are allocated lazily to
291 private_handles_count
+= HANDLE_PER_SLOT
;
292 private_handles_slots_count
++;
293 } while(mono_w32handle_fd_reserve
> private_handles_count
);
295 mono_os_mutex_init (&scan_mutex
);
297 mono_os_cond_init (&global_signal_cond
);
298 mono_os_mutex_init (&global_signal_mutex
);
304 mono_w32handle_cleanup (void)
308 g_assert (!shutting_down
);
309 shutting_down
= TRUE
;
311 for (i
= 0; i
< SLOT_MAX
; ++i
)
312 g_free (private_handles
[i
]);
316 mono_w32handle_ops_typesize (MonoW32HandleType type
);
318 static void mono_w32handle_init_handle (MonoW32HandleBase
*handle
,
319 MonoW32HandleType type
, gpointer handle_specific
)
321 g_assert (handle
->ref
== 0);
324 handle
->signalled
= FALSE
;
327 mono_os_cond_init (&handle
->signal_cond
);
328 mono_os_mutex_init (&handle
->signal_mutex
);
331 handle
->specific
= g_memdup (handle_specific
, mono_w32handle_ops_typesize (type
));
335 * mono_w32handle_new_internal:
336 * @type: Init handle to this type
338 * Search for a free handle and initialize it. Return the handle on
339 * success and 0 on failure. This is only called from
340 * mono_w32handle_new, and scan_mutex must be held.
342 static guint32
mono_w32handle_new_internal (MonoW32HandleType type
,
343 gpointer handle_specific
)
346 static guint32 last
= 0;
347 gboolean retry
= FALSE
;
349 /* A linear scan should be fast enough. Start from the last
350 * allocation, assuming that handles are allocated more often
351 * than they're freed. Leave the space reserved for file
355 if (last
< mono_w32handle_fd_reserve
) {
356 last
= mono_w32handle_fd_reserve
;
363 for(i
= SLOT_INDEX (count
); i
< private_handles_slots_count
; i
++) {
364 if (private_handles
[i
]) {
365 for (k
= SLOT_OFFSET (count
); k
< HANDLE_PER_SLOT
; k
++) {
366 MonoW32HandleBase
*handle
= &private_handles
[i
][k
];
368 if(handle
->type
== MONO_W32HANDLE_UNUSED
) {
371 mono_w32handle_init_handle (handle
, type
, handle_specific
);
379 if(retry
&& last
> mono_w32handle_fd_reserve
) {
380 /* Try again from the beginning */
381 last
= mono_w32handle_fd_reserve
;
385 /* Will need to expand the array. The caller will sort it out */
391 mono_w32handle_new (MonoW32HandleType type
, gpointer handle_specific
)
393 guint32 handle_idx
= 0;
396 g_assert (!shutting_down
);
398 g_assert(!type_is_fd(type
));
400 mono_os_mutex_lock (&scan_mutex
);
402 while ((handle_idx
= mono_w32handle_new_internal (type
, handle_specific
)) == 0) {
403 /* Try and expand the array, and have another go */
404 int idx
= SLOT_INDEX (private_handles_count
);
405 if (idx
>= SLOT_MAX
) {
409 private_handles
[idx
] = g_new0 (MonoW32HandleBase
, HANDLE_PER_SLOT
);
411 private_handles_count
+= HANDLE_PER_SLOT
;
412 private_handles_slots_count
++;
415 mono_os_mutex_unlock (&scan_mutex
);
417 if (handle_idx
== 0) {
418 /* We ran out of slots */
419 handle
= INVALID_HANDLE_VALUE
;
420 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: failed to create %s handle", __func__
, mono_w32handle_ops_typename (type
));
424 /* Make sure we left the space for fd mappings */
425 g_assert (handle_idx
>= mono_w32handle_fd_reserve
);
427 handle
= GUINT_TO_POINTER (handle_idx
);
429 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: create %s handle %p", __func__
, mono_w32handle_ops_typename (type
), handle
);
435 gpointer
mono_w32handle_new_fd (MonoW32HandleType type
, int fd
,
436 gpointer handle_specific
)
438 MonoW32HandleBase
*handle_data
;
439 int fd_index
, fd_offset
;
441 g_assert (!shutting_down
);
443 g_assert(type_is_fd(type
));
445 if (fd
>= mono_w32handle_fd_reserve
) {
446 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
));
448 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE
));
451 fd_index
= SLOT_INDEX (fd
);
452 fd_offset
= SLOT_OFFSET (fd
);
454 /* Initialize the array entries on demand */
455 if (!private_handles
[fd_index
]) {
456 mono_os_mutex_lock (&scan_mutex
);
458 if (!private_handles
[fd_index
])
459 private_handles
[fd_index
] = g_new0 (MonoW32HandleBase
, HANDLE_PER_SLOT
);
461 mono_os_mutex_unlock (&scan_mutex
);
464 handle_data
= &private_handles
[fd_index
][fd_offset
];
466 if (handle_data
->type
!= MONO_W32HANDLE_UNUSED
) {
467 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
));
468 /* FIXME: clean up this handle? We can't do anything
469 * with the fd, cos thats the new one
471 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE
));
474 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: create %s handle %p", __func__
, mono_w32handle_ops_typename (type
), GUINT_TO_POINTER(fd
));
476 mono_w32handle_init_handle (handle_data
, type
, handle_specific
);
478 return(GUINT_TO_POINTER(fd
));
482 mono_w32handle_ref_core (gpointer handle
, MonoW32HandleBase
*handle_data
);
485 mono_w32handle_unref_core (gpointer handle
, MonoW32HandleBase
*handle_data
);
488 w32handle_destroy (gpointer handle
);
491 mono_w32handle_duplicate (gpointer handle
)
493 MonoW32HandleBase
*handle_data
;
495 if (handle
== INVALID_HANDLE_VALUE
)
497 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
498 return INVALID_HANDLE_VALUE
;
499 if (handle
== (gpointer
) 0 && handle_data
->type
!= MONO_W32HANDLE_CONSOLE
)
502 if (!mono_w32handle_ref_core (handle
, handle_data
))
503 g_error ("%s: failed to ref handle %p", __func__
, handle
);
509 mono_w32handle_close (gpointer handle
)
511 MonoW32HandleBase
*handle_data
;
514 if (handle
== INVALID_HANDLE_VALUE
)
516 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
518 if (handle
== (gpointer
) 0 && handle_data
->type
!= MONO_W32HANDLE_CONSOLE
) {
519 /* Problem: because we map file descriptors to the
520 * same-numbered handle we can't tell the difference
521 * between a bogus handle and the handle to stdin.
522 * Assume that it's the console handle if that handle
527 destroy
= mono_w32handle_unref_core (handle
, handle_data
);
529 w32handle_destroy (handle
);
535 mono_w32handle_lookup (gpointer handle
, MonoW32HandleType type
,
536 gpointer
*handle_specific
)
538 MonoW32HandleBase
*handle_data
;
540 g_assert (handle_specific
);
542 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
546 if (handle_data
->type
!= type
) {
550 *handle_specific
= handle_data
->specific
;
556 mono_w32handle_foreach (gboolean (*on_each
)(gpointer handle
, gpointer data
, gpointer user_data
), gpointer user_data
)
558 GPtrArray
*handles_to_destroy
;
561 handles_to_destroy
= NULL
;
563 mono_os_mutex_lock (&scan_mutex
);
565 for (i
= SLOT_INDEX (0); i
< private_handles_slots_count
; i
++) {
566 if (!private_handles
[i
])
568 for (k
= SLOT_OFFSET (0); k
< HANDLE_PER_SLOT
; k
++) {
569 MonoW32HandleBase
*handle_data
= NULL
;
571 gboolean destroy
, finished
;
573 handle_data
= &private_handles
[i
][k
];
574 if (handle_data
->type
== MONO_W32HANDLE_UNUSED
)
577 handle
= GUINT_TO_POINTER (i
* HANDLE_PER_SLOT
+ k
);
579 if (!mono_w32handle_ref_core (handle
, handle_data
)) {
580 /* we are racing with mono_w32handle_unref:
581 * the handle ref has been decremented, but it
582 * hasn't yet been destroyed. */
586 finished
= on_each (handle
, handle_data
->specific
, user_data
);
588 /* we might have to destroy the handle here, as
589 * it could have been unrefed in another thread */
590 destroy
= mono_w32handle_unref_core (handle
, handle_data
);
592 /* we do not destroy it while holding the scan_mutex
593 * lock, because w32handle_destroy also needs to take
594 * the lock, and it calls user code which might lead
596 if (!handles_to_destroy
)
597 handles_to_destroy
= g_ptr_array_sized_new (4);
598 g_ptr_array_add (handles_to_destroy
, handle
);
607 mono_os_mutex_unlock (&scan_mutex
);
609 if (handles_to_destroy
) {
610 for (i
= 0; i
< handles_to_destroy
->len
; ++i
)
611 w32handle_destroy (handles_to_destroy
->pdata
[i
]);
613 g_ptr_array_free (handles_to_destroy
, TRUE
);
618 mono_w32handle_ref_core (gpointer handle
, MonoW32HandleBase
*handle_data
)
623 old
= handle_data
->ref
;
628 } while (InterlockedCompareExchange ((gint32
*) &handle_data
->ref
, new, old
) != old
);
630 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: ref %s handle %p, ref: %d -> %d",
631 __func__
, mono_w32handle_ops_typename (handle_data
->type
), handle
, old
, new);
637 mono_w32handle_unref_core (gpointer handle
, MonoW32HandleBase
*handle_data
)
639 MonoW32HandleType type
;
642 type
= handle_data
->type
;
645 old
= handle_data
->ref
;
647 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__
, handle
, old
);
650 } while (InterlockedCompareExchange ((gint32
*) &handle_data
->ref
, new, old
) != old
);
652 /* handle_data might contain invalid data from now on, if
653 * another thread is unref'ing this handle at the same time */
655 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: unref %s handle %p, ref: %d -> %d destroy: %s",
656 __func__
, mono_w32handle_ops_typename (type
), handle
, old
, new, new == 0 ? "true" : "false");
662 mono_w32handle_ref (gpointer handle
)
664 MonoW32HandleBase
*handle_data
;
666 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
667 g_error ("%s: failed to ref handle %p, unknown handle", __func__
, handle
);
669 if (!mono_w32handle_ref_core (handle
, handle_data
))
670 g_error ("%s: failed to ref handle %p", __func__
, handle
);
673 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type
))(gpointer
, gpointer
);
676 w32handle_destroy (gpointer handle
)
678 /* Need to copy the handle info, reset the slot in the
679 * array, and _only then_ call the close function to
680 * avoid race conditions (eg file descriptors being
681 * closed, and another file being opened getting the
682 * same fd racing the memset())
684 MonoW32HandleBase
*handle_data
;
685 MonoW32HandleType type
;
686 gpointer handle_specific
;
687 void (*close_func
)(gpointer
, gpointer
);
689 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
690 g_error ("%s: unknown handle %p", __func__
, handle
);
692 g_assert (!handle_data
->in_use
);
694 type
= handle_data
->type
;
695 handle_specific
= handle_data
->specific
;
697 mono_os_mutex_lock (&scan_mutex
);
699 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: destroy %s handle %p", __func__
, mono_w32handle_ops_typename (type
), handle
);
701 mono_os_mutex_destroy (&handle_data
->signal_mutex
);
702 mono_os_cond_destroy (&handle_data
->signal_cond
);
704 memset (handle_data
, 0, sizeof (MonoW32HandleBase
));
706 mono_os_mutex_unlock (&scan_mutex
);
708 close_func
= _wapi_handle_ops_get_close_func (type
);
709 if (close_func
!= NULL
) {
710 close_func (handle
, handle_specific
);
713 memset (handle_specific
, 0, mono_w32handle_ops_typesize (type
));
715 g_free (handle_specific
);
718 /* The handle must not be locked on entry to this function */
720 mono_w32handle_unref (gpointer handle
)
722 MonoW32HandleBase
*handle_data
;
725 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
726 g_error ("%s: failed to unref handle %p, unknown handle", __func__
, handle
);
728 destroy
= mono_w32handle_unref_core (handle
, handle_data
);
730 w32handle_destroy (handle
);
734 mono_w32handle_ops_close (gpointer handle
, gpointer data
);
737 mono_w32handle_force_close (gpointer handle
, gpointer data
)
739 mono_w32handle_ops_close (handle
, data
);
743 mono_w32handle_register_ops (MonoW32HandleType type
, MonoW32HandleOps
*ops
)
745 handle_ops
[type
] = ops
;
748 void mono_w32handle_register_capabilities (MonoW32HandleType type
,
749 MonoW32HandleCapability caps
)
751 handle_caps
[type
] = caps
;
754 gboolean
mono_w32handle_test_capabilities (gpointer handle
,
755 MonoW32HandleCapability caps
)
757 MonoW32HandleBase
*handle_data
;
758 MonoW32HandleType type
;
760 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
764 type
= handle_data
->type
;
766 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: testing 0x%x against 0x%x (%d)", __func__
,
767 handle_caps
[type
], caps
, handle_caps
[type
] & caps
);
769 return((handle_caps
[type
] & caps
) != 0);
772 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type
))(gpointer
, gpointer
)
774 if (handle_ops
[type
] != NULL
&&
775 handle_ops
[type
]->close
!= NULL
) {
776 return (handle_ops
[type
]->close
);
783 mono_w32handle_ops_close (gpointer handle
, gpointer data
)
785 MonoW32HandleBase
*handle_data
;
786 MonoW32HandleType type
;
788 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
792 type
= handle_data
->type
;
794 if (handle_ops
[type
] != NULL
&&
795 handle_ops
[type
]->close
!= NULL
) {
796 handle_ops
[type
]->close (handle
, data
);
801 mono_w32handle_ops_details (MonoW32HandleType type
, gpointer data
)
803 if (handle_ops
[type
] != NULL
&&
804 handle_ops
[type
]->details
!= NULL
) {
805 handle_ops
[type
]->details (data
);
810 mono_w32handle_ops_typename (MonoW32HandleType type
)
812 g_assert (handle_ops
[type
]);
813 g_assert (handle_ops
[type
]->typename
);
814 return handle_ops
[type
]->typename ();
818 mono_w32handle_ops_typesize (MonoW32HandleType type
)
820 g_assert (handle_ops
[type
]);
821 g_assert (handle_ops
[type
]->typesize
);
822 return handle_ops
[type
]->typesize ();
826 mono_w32handle_ops_signal (gpointer handle
)
828 MonoW32HandleBase
*handle_data
;
829 MonoW32HandleType type
;
831 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
835 type
= handle_data
->type
;
837 if (handle_ops
[type
] != NULL
&& handle_ops
[type
]->signal
!= NULL
) {
838 handle_ops
[type
]->signal (handle
, handle_data
->specific
);
843 mono_w32handle_ops_own (gpointer handle
, gboolean
*abandoned
)
845 MonoW32HandleBase
*handle_data
;
846 MonoW32HandleType type
;
848 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
852 type
= handle_data
->type
;
854 if (handle_ops
[type
] != NULL
&& handle_ops
[type
]->own_handle
!= NULL
) {
855 return(handle_ops
[type
]->own_handle (handle
, abandoned
));
862 mono_w32handle_ops_isowned (gpointer handle
)
864 MonoW32HandleBase
*handle_data
;
865 MonoW32HandleType type
;
867 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
871 type
= handle_data
->type
;
873 if (handle_ops
[type
] != NULL
&& handle_ops
[type
]->is_owned
!= NULL
) {
874 return(handle_ops
[type
]->is_owned (handle
));
880 static MonoW32HandleWaitRet
881 mono_w32handle_ops_specialwait (gpointer handle
, guint32 timeout
, gboolean
*alerted
)
883 MonoW32HandleBase
*handle_data
;
884 MonoW32HandleType type
;
886 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
887 return MONO_W32HANDLE_WAIT_RET_FAILED
;
890 type
= handle_data
->type
;
892 if (handle_ops
[type
] != NULL
&&
893 handle_ops
[type
]->special_wait
!= NULL
) {
894 return(handle_ops
[type
]->special_wait (handle
, timeout
, alerted
));
896 return MONO_W32HANDLE_WAIT_RET_FAILED
;
901 mono_w32handle_ops_prewait (gpointer handle
)
903 MonoW32HandleBase
*handle_data
;
904 MonoW32HandleType type
;
906 if (!mono_w32handle_lookup_data (handle
, &handle_data
)) {
910 type
= handle_data
->type
;
912 if (handle_ops
[type
] != NULL
&&
913 handle_ops
[type
]->prewait
!= NULL
) {
914 handle_ops
[type
]->prewait (handle
);
924 struct timespec sleepytime
;
926 g_assert (ms
< 1000);
928 sleepytime
.tv_sec
= 0;
929 sleepytime
.tv_nsec
= ms
* 1000000;
930 nanosleep (&sleepytime
, NULL
);
931 #endif /* HOST_WIN32 */
935 mono_w32handle_lock_handles (gpointer
*handles
, gsize numhandles
)
939 /* Lock all the handles, with backoff */
941 for(i
=0; i
<numhandles
; i
++) {
942 gpointer handle
= handles
[i
];
944 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: attempting to lock %p", __func__
, handle
);
946 if (!mono_w32handle_trylock_handle (handle
)) {
949 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: attempt failed for %p.", __func__
,
955 mono_w32handle_unlock_handle (handle
);
958 /* If iter ever reaches 100 the nanosleep will
959 * return EINVAL immediately, but we have a
960 * design flaw if that happens.
964 g_warning ("%s: iteration overflow!",
969 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: Backing off for %d ms", __func__
,
977 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: Locked all handles", __func__
);
981 mono_w32handle_unlock_handles (gpointer
*handles
, gsize numhandles
)
985 for(i
=0; i
<numhandles
; i
++) {
986 gpointer handle
= handles
[i
];
988 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: unlocking handle %p", __func__
, handle
);
990 mono_w32handle_unlock_handle (handle
);
995 mono_w32handle_timedwait_signal_naked (mono_cond_t
*cond
, mono_mutex_t
*mutex
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
1000 res
= mono_os_cond_timedwait (cond
, mutex
, timeout
);
1002 /* This is needed when waiting for process handles */
1005 * pthread_cond_(timed)wait() can return 0 even if the condition was not
1006 * signalled. This happens at least on Darwin. We surface this, i.e., we
1007 * get spurious wake-ups.
1009 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
1011 res
= mono_os_cond_timedwait (cond
, mutex
, timeout
);
1013 if (timeout
< 100) {
1014 /* Real timeout is less than 100ms time */
1015 res
= mono_os_cond_timedwait (cond
, mutex
, timeout
);
1017 res
= mono_os_cond_timedwait (cond
, mutex
, 100);
1019 /* Mask the fake timeout, this will cause
1020 * another poll if the cond was not really signaled
1032 signal_global (gpointer unused
)
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 mono_os_mutex_lock (&global_signal_mutex
);
1040 mono_os_cond_broadcast (&global_signal_cond
);
1041 mono_os_mutex_unlock (&global_signal_mutex
);
1045 mono_w32handle_timedwait_signal (guint32 timeout
, gboolean poll
, gboolean
*alerted
)
1049 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: waiting for global", __func__
);
1055 mono_thread_info_install_interrupt (signal_global
, NULL
, alerted
);
1060 res
= mono_w32handle_timedwait_signal_naked (&global_signal_cond
, &global_signal_mutex
, timeout
, poll
, alerted
);
1063 mono_thread_info_uninstall_interrupt (alerted
);
1069 signal_handle_and_unref (gpointer handle
)
1071 MonoW32HandleBase
*handle_data
;
1073 mono_mutex_t
*mutex
;
1075 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
1076 g_error ("cannot signal unknown handle %p", handle
);
1078 /* If we reach here, then interrupt token is set to the flag value, which
1079 * means that the target thread is either
1080 * - before the first CAS in timedwait, which means it won't enter the wait.
1081 * - it is after the first CAS, so it is already waiting, or it will enter
1082 * the wait, and it will be interrupted by the broadcast. */
1083 cond
= &handle_data
->signal_cond
;
1084 mutex
= &handle_data
->signal_mutex
;
1086 mono_os_mutex_lock (mutex
);
1087 mono_os_cond_broadcast (cond
);
1088 mono_os_mutex_unlock (mutex
);
1090 mono_w32handle_close (handle
);
1094 mono_w32handle_timedwait_signal_handle (gpointer handle
, guint32 timeout
, gboolean poll
, gboolean
*alerted
)
1096 MonoW32HandleBase
*handle_data
;
1097 gpointer handle_duplicate
;
1100 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
1101 g_error ("cannot wait on unknown handle %p", handle
);
1103 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: waiting for %p (type %s)", __func__
, handle
,
1104 mono_w32handle_ops_typename (mono_w32handle_get_type (handle
)));
1110 mono_thread_info_install_interrupt (signal_handle_and_unref
, handle_duplicate
= mono_w32handle_duplicate (handle
), alerted
);
1112 mono_w32handle_close (handle_duplicate
);
1117 res
= mono_w32handle_timedwait_signal_naked (&handle_data
->signal_cond
, &handle_data
->signal_mutex
, timeout
, poll
, alerted
);
1120 mono_thread_info_uninstall_interrupt (alerted
);
1122 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
1123 mono_w32handle_close (handle_duplicate
);
1131 dump_callback (gpointer handle
, gpointer handle_specific
, gpointer user_data
)
1133 MonoW32HandleBase
*handle_data
;
1135 if (!mono_w32handle_lookup_data (handle
, &handle_data
))
1136 g_error ("cannot dump unknown handle %p", handle
);
1138 g_print ("%p [%7s] signalled: %5s ref: %3d ",
1139 handle
, mono_w32handle_ops_typename (handle_data
->type
), handle_data
->signalled
? "true" : "false", handle_data
->ref
- 1 /* foreach increase ref by 1 */);
1140 mono_w32handle_ops_details (handle_data
->type
, handle_data
->specific
);
1146 void mono_w32handle_dump (void)
1148 mono_w32handle_foreach (dump_callback
, NULL
);
1152 own_if_signalled (gpointer handle
, gboolean
*abandoned
)
1154 if (!mono_w32handle_issignalled (handle
))
1158 mono_w32handle_ops_own (handle
, abandoned
);
1163 own_if_owned( gpointer handle
, gboolean
*abandoned
)
1165 if (!mono_w32handle_ops_isowned (handle
))
1169 mono_w32handle_ops_own (handle
, abandoned
);
1173 MonoW32HandleWaitRet
1174 mono_w32handle_wait_one (gpointer handle
, guint32 timeout
, gboolean alertable
)
1176 MonoW32HandleWaitRet ret
;
1179 gboolean abandoned
= FALSE
;
1183 if (mono_w32handle_test_capabilities (handle
, MONO_W32HANDLE_CAP_SPECIAL_WAIT
)) {
1184 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p has special wait",
1187 return mono_w32handle_ops_specialwait (handle
, timeout
, alertable
? &alerted
: NULL
);
1190 if (!mono_w32handle_test_capabilities (handle
, MONO_W32HANDLE_CAP_WAIT
)) {
1191 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p can't be waited for",
1194 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1197 mono_w32handle_lock_handle (handle
);
1199 if (mono_w32handle_test_capabilities (handle
, MONO_W32HANDLE_CAP_OWN
)) {
1200 if (own_if_owned (handle
, &abandoned
)) {
1201 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p already owned",
1204 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1209 if (timeout
!= MONO_INFINITE_WAIT
)
1210 start
= mono_msec_ticks ();
1212 mono_w32handle_set_in_use (handle
, TRUE
);
1217 if (own_if_signalled (handle
, &abandoned
)) {
1218 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p signalled",
1221 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1225 mono_w32handle_ops_prewait (handle
);
1227 if (timeout
== MONO_INFINITE_WAIT
) {
1228 waited
= mono_w32handle_timedwait_signal_handle (handle
, MONO_INFINITE_WAIT
, FALSE
, alertable
? &alerted
: NULL
);
1232 elapsed
= mono_msec_ticks () - start
;
1233 if (elapsed
> timeout
) {
1234 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1238 waited
= mono_w32handle_timedwait_signal_handle (handle
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
1242 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1247 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1253 mono_w32handle_set_in_use (handle
, FALSE
);
1255 mono_w32handle_unlock_handle (handle
);
1260 MonoW32HandleWaitRet
1261 mono_w32handle_wait_multiple (gpointer
*handles
, gsize nhandles
, gboolean waitall
, guint32 timeout
, gboolean alertable
)
1263 MonoW32HandleWaitRet ret
;
1264 gboolean alerted
, poll
;
1267 gpointer handles_sorted
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
];
1268 gboolean abandoned
[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
] = {0};
1271 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1274 return mono_w32handle_wait_one (handles
[0], timeout
, alertable
);
1278 if (nhandles
> MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
) {
1279 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: too many handles: %zd",
1280 __func__
, nhandles
);
1282 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1285 for (i
= 0; i
< nhandles
; ++i
) {
1286 if (!mono_w32handle_test_capabilities (handles
[i
], MONO_W32HANDLE_CAP_WAIT
)
1287 && !mono_w32handle_test_capabilities (handles
[i
], MONO_W32HANDLE_CAP_SPECIAL_WAIT
))
1289 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p can't be waited for",
1290 __func__
, handles
[i
]);
1292 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1295 handles_sorted
[i
] = handles
[i
];
1298 qsort (handles_sorted
, nhandles
, sizeof (gpointer
), g_direct_equal
);
1299 for (i
= 1; i
< nhandles
; ++i
) {
1300 if (handles_sorted
[i
- 1] == handles_sorted
[i
]) {
1301 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p is duplicated",
1302 __func__
, handles_sorted
[i
]);
1304 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1309 for (i
= 0; i
< nhandles
; ++i
) {
1310 if (mono_w32handle_get_type (handles
[i
]) == MONO_W32HANDLE_PROCESS
) {
1311 /* Can't wait for a process handle + another handle without polling */
1316 if (timeout
!= MONO_INFINITE_WAIT
)
1317 start
= mono_msec_ticks ();
1319 for (i
= 0; i
< nhandles
; ++i
) {
1320 /* Add a reference, as we need to ensure the handle wont
1321 * disappear from under us while we're waiting in the loop
1322 * (not lock, as we don't want exclusive access here) */
1323 mono_w32handle_ref (handles
[i
]);
1327 gsize count
, lowest
;
1334 mono_w32handle_lock_handles (handles
, nhandles
);
1336 for (i
= 0; i
< nhandles
; i
++) {
1337 if ((mono_w32handle_test_capabilities (handles
[i
], MONO_W32HANDLE_CAP_OWN
) && mono_w32handle_ops_isowned (handles
[i
]))
1338 || mono_w32handle_issignalled (handles
[i
]))
1347 signalled
= (waitall
&& count
== nhandles
) || (!waitall
&& count
> 0);
1350 for (i
= 0; i
< nhandles
; i
++)
1351 own_if_signalled (handles
[i
], &abandoned
[i
]);
1354 mono_w32handle_unlock_handles (handles
, nhandles
);
1357 ret
= MONO_W32HANDLE_WAIT_RET_SUCCESS_0
+ lowest
;
1358 for (i
= lowest
; i
< nhandles
; i
++) {
1359 if (abandoned
[i
]) {
1360 ret
= MONO_W32HANDLE_WAIT_RET_ABANDONED_0
+ lowest
;
1367 for (i
= 0; i
< nhandles
; i
++) {
1368 mono_w32handle_ops_prewait (handles
[i
]);
1370 if (mono_w32handle_test_capabilities (handles
[i
], MONO_W32HANDLE_CAP_SPECIAL_WAIT
)
1371 && !mono_w32handle_issignalled (handles
[i
]))
1373 mono_w32handle_ops_specialwait (handles
[i
], 0, alertable
? &alerted
: NULL
);
1377 mono_w32handle_lock_signal_mutex ();
1381 for (i
= 0; i
< nhandles
; ++i
) {
1382 if (!mono_w32handle_issignalled (handles
[i
])) {
1389 for (i
= 0; i
< nhandles
; ++i
) {
1390 if (mono_w32handle_issignalled (handles
[i
])) {
1400 if (timeout
== MONO_INFINITE_WAIT
) {
1401 waited
= mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT
, poll
, alertable
? &alerted
: NULL
);
1405 elapsed
= mono_msec_ticks () - start
;
1406 if (elapsed
> timeout
) {
1407 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1409 mono_w32handle_unlock_signal_mutex ();
1414 waited
= mono_w32handle_timedwait_signal (timeout
- elapsed
, poll
, alertable
? &alerted
: NULL
);
1418 mono_w32handle_unlock_signal_mutex ();
1421 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1426 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1432 for (i
= 0; i
< nhandles
; i
++) {
1433 /* Unref everything we reffed above */
1434 mono_w32handle_unref (handles
[i
]);
1440 MonoW32HandleWaitRet
1441 mono_w32handle_signal_and_wait (gpointer signal_handle
, gpointer wait_handle
, guint32 timeout
, gboolean alertable
)
1443 MonoW32HandleWaitRet ret
;
1446 gboolean abandoned
= FALSE
;
1447 gpointer handles
[2];
1451 if (!mono_w32handle_test_capabilities (signal_handle
, MONO_W32HANDLE_CAP_SIGNAL
))
1452 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1453 if (!mono_w32handle_test_capabilities (wait_handle
, MONO_W32HANDLE_CAP_WAIT
))
1454 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1456 if (mono_w32handle_test_capabilities (wait_handle
, MONO_W32HANDLE_CAP_SPECIAL_WAIT
)) {
1457 g_warning ("%s: handle %p has special wait, implement me!!", __func__
, wait_handle
);
1458 return MONO_W32HANDLE_WAIT_RET_FAILED
;
1461 handles
[0] = wait_handle
;
1462 handles
[1] = signal_handle
;
1464 mono_w32handle_lock_handles (handles
, 2);
1466 mono_w32handle_ops_signal (signal_handle
);
1468 mono_w32handle_unlock_handle (signal_handle
);
1470 if (mono_w32handle_test_capabilities (wait_handle
, MONO_W32HANDLE_CAP_OWN
)) {
1471 if (own_if_owned (wait_handle
, &abandoned
)) {
1472 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p already owned",
1473 __func__
, wait_handle
);
1475 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1480 if (timeout
!= MONO_INFINITE_WAIT
)
1481 start
= mono_msec_ticks ();
1486 if (own_if_signalled (wait_handle
, &abandoned
)) {
1487 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_W32HANDLE
, "%s: handle %p signalled",
1488 __func__
, wait_handle
);
1490 ret
= abandoned
? MONO_W32HANDLE_WAIT_RET_ABANDONED_0
: MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
1494 mono_w32handle_ops_prewait (wait_handle
);
1496 if (timeout
== MONO_INFINITE_WAIT
) {
1497 waited
= mono_w32handle_timedwait_signal_handle (wait_handle
, MONO_INFINITE_WAIT
, FALSE
, alertable
? &alerted
: NULL
);
1501 elapsed
= mono_msec_ticks () - start
;
1502 if (elapsed
> timeout
) {
1503 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1507 waited
= mono_w32handle_timedwait_signal_handle (wait_handle
, timeout
- elapsed
, FALSE
, alertable
? &alerted
: NULL
);
1511 ret
= MONO_W32HANDLE_WAIT_RET_ALERTED
;
1516 ret
= MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
1522 mono_w32handle_unlock_handle (wait_handle
);