[w32handle] Replace mono_w32handle_{ref,unref} by mono_w32handle_{duplicate, close...
[mono-project.git] / mono / metadata / w32handle.c
blob02aa4db826306b53f6affa76beffbd683e4f0ea0
1 /**
2 * \file
3 * Generic and internal operations on handles
5 * Author:
6 * Dick Porter (dick@ximian.com)
7 * Ludovic Henry (luhenry@microsoft.com)
9 * (C) 2002-2011 Novell, Inc.
10 * Copyright 2011 Xamarin Inc
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #include <config.h>
15 #include <glib.h>
17 #include "w32handle.h"
19 #include "utils/atomic.h"
20 #include "utils/mono-logger-internals.h"
21 #include "utils/mono-os-mutex.h"
22 #include "utils/mono-proclib.h"
23 #include "utils/mono-threads.h"
24 #include "utils/mono-time.h"
26 #undef DEBUG_REFS
28 #define SLOT_MAX (1024 * 32)
30 /* must be a power of 2 */
31 #define HANDLE_PER_SLOT (256)
33 typedef struct {
34 MonoW32HandleType type;
35 guint ref;
36 gboolean signalled;
37 gboolean in_use;
38 mono_mutex_t signal_mutex;
39 mono_cond_t signal_cond;
40 gpointer specific;
41 } MonoW32HandleBase;
43 static MonoW32HandleCapability handle_caps [MONO_W32HANDLE_COUNT];
44 static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
47 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
48 * If 4M handles are not enough... Oh, well... we will crash.
50 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
51 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
53 static MonoW32HandleBase *private_handles [SLOT_MAX];
54 static guint32 private_handles_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;
71 static gboolean
72 type_is_fd (MonoW32HandleType type)
74 switch (type) {
75 case MONO_W32HANDLE_FILE:
76 case MONO_W32HANDLE_CONSOLE:
77 case MONO_W32HANDLE_SOCKET:
78 case MONO_W32HANDLE_PIPE:
79 return TRUE;
80 default:
81 return FALSE;
85 static gboolean
86 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
88 gsize index, offset;
90 g_assert (handle_data);
92 index = SLOT_INDEX ((gsize) handle);
93 if (index >= SLOT_MAX)
94 return FALSE;
95 if (!private_handles [index])
96 return FALSE;
98 offset = SLOT_OFFSET ((gsize) handle);
99 if (private_handles [index][offset].type == MONO_W32HANDLE_UNUSED)
100 return FALSE;
102 *handle_data = &private_handles [index][offset];
103 return TRUE;
106 MonoW32HandleType
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;
117 static const gchar*
118 mono_w32handle_ops_typename (MonoW32HandleType type);
120 const gchar*
121 mono_w32handle_get_typename (MonoW32HandleType type)
123 return mono_w32handle_ops_typename (type);
126 void
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)) {
132 return;
135 #ifdef DEBUG
136 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
137 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
138 #endif
140 if (state == TRUE) {
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);
155 } else {
156 mono_os_cond_signal (&handle_data->signal_cond);
159 /* Tell everyone blocking on multiple handles that something
160 * was signalled
162 mono_os_cond_broadcast (&global_signal_cond);
164 mono_os_mutex_unlock (&global_signal_mutex);
165 } else {
166 handle_data->signalled=state;
170 gboolean
171 mono_w32handle_issignalled (gpointer handle)
173 MonoW32HandleBase *handle_data;
175 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
176 return(FALSE);
179 return handle_data->signalled;
182 static void
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;
193 static void
194 mono_w32handle_lock_signal_mutex (void)
196 #ifdef DEBUG
197 g_message ("%s: lock global signal mutex", __func__);
198 #endif
200 mono_os_mutex_lock (&global_signal_mutex);
203 static void
204 mono_w32handle_unlock_signal_mutex (void)
206 #ifdef DEBUG
207 g_message ("%s: unlock global signal mutex", __func__);
208 #endif
210 mono_os_mutex_unlock (&global_signal_mutex);
213 static void
214 mono_w32handle_ref (gpointer handle);
216 static void
217 mono_w32handle_unref (gpointer handle);
219 void
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);
234 gboolean
235 mono_w32handle_trylock_handle (gpointer handle)
237 MonoW32HandleBase *handle_data;
238 gboolean locked;
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;
248 if (!locked)
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");
253 return locked;
256 void
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);
271 void
272 mono_w32handle_init (void)
274 static gboolean initialized = FALSE;
276 if (initialized)
277 return;
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);
285 do {
287 * The entries in private_handles reserved for fds are allocated lazily to
288 * save memory.
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);
300 initialized = TRUE;
303 void
304 mono_w32handle_cleanup (void)
306 int i;
308 g_assert (!shutting_down);
309 shutting_down = TRUE;
311 for (i = 0; i < SLOT_MAX; ++i)
312 g_free (private_handles [i]);
315 static gsize
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);
323 handle->type = type;
324 handle->signalled = FALSE;
325 handle->ref = 1;
327 mono_os_cond_init (&handle->signal_cond);
328 mono_os_mutex_init (&handle->signal_mutex);
330 if (handle_specific)
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)
345 guint32 i, k, count;
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
352 * descriptors
355 if (last < mono_w32handle_fd_reserve) {
356 last = mono_w32handle_fd_reserve;
357 } else {
358 retry = TRUE;
361 again:
362 count = last;
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) {
369 last = count + 1;
371 mono_w32handle_init_handle (handle, type, handle_specific);
372 return (count);
374 count++;
379 if(retry && last > mono_w32handle_fd_reserve) {
380 /* Try again from the beginning */
381 last = mono_w32handle_fd_reserve;
382 goto again;
385 /* Will need to expand the array. The caller will sort it out */
387 return(0);
390 gpointer
391 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
393 guint32 handle_idx = 0;
394 gpointer handle;
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) {
406 break;
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));
421 goto done;
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);
431 done:
432 return(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));
481 static gboolean
482 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data);
484 static gboolean
485 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data);
487 static void
488 w32handle_destroy (gpointer handle);
490 gpointer
491 mono_w32handle_duplicate (gpointer handle)
493 MonoW32HandleBase *handle_data;
495 if (handle == INVALID_HANDLE_VALUE)
496 return handle;
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)
500 return handle;
502 if (!mono_w32handle_ref_core (handle, handle_data))
503 g_error ("%s: failed to ref handle %p", __func__, handle);
505 return handle;
508 gboolean
509 mono_w32handle_close (gpointer handle)
511 MonoW32HandleBase *handle_data;
512 gboolean destroy;
514 if (handle == INVALID_HANDLE_VALUE)
515 return FALSE;
516 if (!mono_w32handle_lookup_data (handle, &handle_data))
517 return FALSE;
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
523 * exists... */
524 return FALSE;
527 destroy = mono_w32handle_unref_core (handle, handle_data);
528 if (destroy)
529 w32handle_destroy (handle);
531 return TRUE;
534 gboolean
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)) {
543 return(FALSE);
546 if (handle_data->type != type) {
547 return(FALSE);
550 *handle_specific = handle_data->specific;
552 return(TRUE);
555 void
556 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
558 GPtrArray *handles_to_destroy;
559 guint32 i, k;
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])
567 continue;
568 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
569 MonoW32HandleBase *handle_data = NULL;
570 gpointer handle;
571 gboolean destroy, finished;
573 handle_data = &private_handles [i][k];
574 if (handle_data->type == MONO_W32HANDLE_UNUSED)
575 continue;
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. */
583 continue;
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);
591 if (destroy) {
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
595 * to a deadlock */
596 if (!handles_to_destroy)
597 handles_to_destroy = g_ptr_array_sized_new (4);
598 g_ptr_array_add (handles_to_destroy, handle);
601 if (finished)
602 goto done;
606 done:
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);
617 static gboolean
618 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data)
620 guint old, new;
622 do {
623 old = handle_data->ref;
624 if (old == 0)
625 return FALSE;
627 new = old + 1;
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);
633 return TRUE;
636 static gboolean
637 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data)
639 MonoW32HandleType type;
640 guint old, new;
642 type = handle_data->type;
644 do {
645 old = handle_data->ref;
646 if (!(old >= 1))
647 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__, handle, old);
649 new = old - 1;
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");
658 return new == 0;
661 static void
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);
675 static void
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 */
719 static void
720 mono_w32handle_unref (gpointer handle)
722 MonoW32HandleBase *handle_data;
723 gboolean destroy;
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);
729 if (destroy)
730 w32handle_destroy (handle);
733 static void
734 mono_w32handle_ops_close (gpointer handle, gpointer data);
736 void
737 mono_w32handle_force_close (gpointer handle, gpointer data)
739 mono_w32handle_ops_close (handle, data);
742 void
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)) {
761 return(FALSE);
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);
779 return (NULL);
782 static void
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)) {
789 return;
792 type = handle_data->type;
794 if (handle_ops[type] != NULL &&
795 handle_ops[type]->close != NULL) {
796 handle_ops[type]->close (handle, data);
800 static void
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);
809 static const gchar*
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 ();
817 static gsize
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 ();
825 static void
826 mono_w32handle_ops_signal (gpointer handle)
828 MonoW32HandleBase *handle_data;
829 MonoW32HandleType type;
831 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
832 return;
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);
842 static gboolean
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)) {
849 return(FALSE);
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));
856 } else {
857 return(FALSE);
861 static gboolean
862 mono_w32handle_ops_isowned (gpointer handle)
864 MonoW32HandleBase *handle_data;
865 MonoW32HandleType type;
867 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
868 return(FALSE);
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));
875 } else {
876 return(FALSE);
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));
895 } else {
896 return MONO_W32HANDLE_WAIT_RET_FAILED;
900 static void
901 mono_w32handle_ops_prewait (gpointer handle)
903 MonoW32HandleBase *handle_data;
904 MonoW32HandleType type;
906 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
907 return;
910 type = handle_data->type;
912 if (handle_ops[type] != NULL &&
913 handle_ops[type]->prewait != NULL) {
914 handle_ops[type]->prewait (handle);
918 static void
919 spin (guint32 ms)
921 #ifdef HOST_WIN32
922 SleepEx (ms, TRUE);
923 #else
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 */
934 static void
935 mono_w32handle_lock_handles (gpointer *handles, gsize numhandles)
937 guint32 i, iter=0;
939 /* Lock all the handles, with backoff */
940 again:
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)) {
947 /* Bummer */
949 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p.", __func__,
950 handle);
952 while (i--) {
953 handle = handles[i];
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.
962 iter++;
963 if(iter==100) {
964 g_warning ("%s: iteration overflow!",
965 __func__);
966 iter=1;
969 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
970 iter*10);
971 spin (10 * iter);
973 goto again;
977 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
980 static void
981 mono_w32handle_unlock_handles (gpointer *handles, gsize numhandles)
983 guint32 i;
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);
994 static int
995 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
997 int res;
999 if (!poll) {
1000 res = mono_os_cond_timedwait (cond, mutex, timeout);
1001 } else {
1002 /* This is needed when waiting for process handles */
1003 if (!alerted) {
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);
1012 } else {
1013 if (timeout < 100) {
1014 /* Real timeout is less than 100ms time */
1015 res = mono_os_cond_timedwait (cond, mutex, timeout);
1016 } else {
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
1022 if (res == -1)
1023 res = 0;
1028 return res;
1031 static void
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);
1044 static int
1045 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
1047 int res;
1049 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
1051 if (alerted)
1052 *alerted = FALSE;
1054 if (alerted) {
1055 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
1056 if (*alerted)
1057 return 0;
1060 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
1062 if (alerted)
1063 mono_thread_info_uninstall_interrupt (alerted);
1065 return res;
1068 static void
1069 signal_handle_and_unref (gpointer handle)
1071 MonoW32HandleBase *handle_data;
1072 mono_cond_t *cond;
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);
1093 static int
1094 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1096 MonoW32HandleBase *handle_data;
1097 gpointer handle_duplicate;
1098 int res;
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)));
1106 if (alerted)
1107 *alerted = FALSE;
1109 if (alerted) {
1110 mono_thread_info_install_interrupt (signal_handle_and_unref, handle_duplicate = mono_w32handle_duplicate (handle), alerted);
1111 if (*alerted) {
1112 mono_w32handle_close (handle_duplicate);
1113 return 0;
1117 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
1119 if (alerted) {
1120 mono_thread_info_uninstall_interrupt (alerted);
1121 if (!*alerted) {
1122 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
1123 mono_w32handle_close (handle_duplicate);
1127 return res;
1130 static gboolean
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);
1141 g_print ("\n");
1143 return FALSE;
1146 void mono_w32handle_dump (void)
1148 mono_w32handle_foreach (dump_callback, NULL);
1151 static gboolean
1152 own_if_signalled (gpointer handle, gboolean *abandoned)
1154 if (!mono_w32handle_issignalled (handle))
1155 return FALSE;
1157 *abandoned = FALSE;
1158 mono_w32handle_ops_own (handle, abandoned);
1159 return TRUE;
1162 static gboolean
1163 own_if_owned( gpointer handle, gboolean *abandoned)
1165 if (!mono_w32handle_ops_isowned (handle))
1166 return FALSE;
1168 *abandoned = FALSE;
1169 mono_w32handle_ops_own (handle, abandoned);
1170 return TRUE;
1173 MonoW32HandleWaitRet
1174 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
1176 MonoW32HandleWaitRet ret;
1177 gboolean alerted;
1178 gint64 start;
1179 gboolean abandoned = FALSE;
1181 alerted = 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",
1185 __func__, handle);
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",
1192 __func__, handle);
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",
1202 __func__, handle);
1204 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1205 goto done;
1209 if (timeout != MONO_INFINITE_WAIT)
1210 start = mono_msec_ticks ();
1212 mono_w32handle_set_in_use (handle, TRUE);
1214 for (;;) {
1215 gint waited;
1217 if (own_if_signalled (handle, &abandoned)) {
1218 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1219 __func__, handle);
1221 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1222 goto done;
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);
1229 } else {
1230 gint64 elapsed;
1232 elapsed = mono_msec_ticks () - start;
1233 if (elapsed > timeout) {
1234 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1235 goto done;
1238 waited = mono_w32handle_timedwait_signal_handle (handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1241 if (alerted) {
1242 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1243 goto done;
1246 if (waited != 0) {
1247 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1248 goto done;
1252 done:
1253 mono_w32handle_set_in_use (handle, FALSE);
1255 mono_w32handle_unlock_handle (handle);
1257 return ret;
1260 MonoW32HandleWaitRet
1261 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
1263 MonoW32HandleWaitRet ret;
1264 gboolean alerted, poll;
1265 gint i;
1266 gint64 start;
1267 gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1268 gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
1270 if (nhandles == 0)
1271 return MONO_W32HANDLE_WAIT_RET_FAILED;
1273 if (nhandles == 1)
1274 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1276 alerted = FALSE;
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;
1308 poll = FALSE;
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 */
1312 poll = TRUE;
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]);
1326 for (;;) {
1327 gsize count, lowest;
1328 gboolean signalled;
1329 gint waited;
1331 count = 0;
1332 lowest = nhandles;
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]))
1340 count ++;
1342 if (i < lowest)
1343 lowest = i;
1347 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1349 if (signalled) {
1350 for (i = 0; i < nhandles; i++)
1351 own_if_signalled (handles [i], &abandoned [i]);
1354 mono_w32handle_unlock_handles (handles, nhandles);
1356 if (signalled) {
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;
1361 break;
1364 goto done;
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 ();
1379 if (waitall) {
1380 signalled = TRUE;
1381 for (i = 0; i < nhandles; ++i) {
1382 if (!mono_w32handle_issignalled (handles [i])) {
1383 signalled = FALSE;
1384 break;
1387 } else {
1388 signalled = FALSE;
1389 for (i = 0; i < nhandles; ++i) {
1390 if (mono_w32handle_issignalled (handles [i])) {
1391 signalled = TRUE;
1392 break;
1397 waited = 0;
1399 if (!signalled) {
1400 if (timeout == MONO_INFINITE_WAIT) {
1401 waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL);
1402 } else {
1403 gint64 elapsed;
1405 elapsed = mono_msec_ticks () - start;
1406 if (elapsed > timeout) {
1407 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1409 mono_w32handle_unlock_signal_mutex ();
1411 goto done;
1414 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1418 mono_w32handle_unlock_signal_mutex ();
1420 if (alerted) {
1421 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1422 goto done;
1425 if (waited != 0) {
1426 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1427 goto done;
1431 done:
1432 for (i = 0; i < nhandles; i++) {
1433 /* Unref everything we reffed above */
1434 mono_w32handle_unref (handles [i]);
1437 return ret;
1440 MonoW32HandleWaitRet
1441 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1443 MonoW32HandleWaitRet ret;
1444 gint64 start;
1445 gboolean alerted;
1446 gboolean abandoned = FALSE;
1447 gpointer handles [2];
1449 alerted = FALSE;
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;
1476 goto done;
1480 if (timeout != MONO_INFINITE_WAIT)
1481 start = mono_msec_ticks ();
1483 for (;;) {
1484 gint waited;
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;
1491 goto done;
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);
1498 } else {
1499 gint64 elapsed;
1501 elapsed = mono_msec_ticks () - start;
1502 if (elapsed > timeout) {
1503 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1504 goto done;
1507 waited = mono_w32handle_timedwait_signal_handle (wait_handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1510 if (alerted) {
1511 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1512 goto done;
1515 if (waited != 0) {
1516 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1517 goto done;
1521 done:
1522 mono_w32handle_unlock_handle (wait_handle);
1524 return ret;