[process] Fix Process.Exited event triggered too early
[mono-project.git] / mono / utils / w32handle.c
blobe67cd5d991f51e00a406b2613a4d4199c0cf2b23
1 /*
2 * w32handle.c: Generic and internal operations on handles
4 * Author:
5 * Dick Porter (dick@ximian.com)
6 * Ludovic Henry (luhenry@microsoft.com)
8 * (C) 2002-2011 Novell, Inc.
9 * Copyright 2011 Xamarin Inc
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <config.h>
15 #if !defined(HOST_WIN32)
17 #include <glib.h>
18 #include <pthread.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #ifdef HAVE_SIGNAL_H
22 #include <signal.h>
23 #endif
24 #include <string.h>
25 #include <sys/types.h>
26 #ifdef HAVE_SYS_SOCKET_H
27 # include <sys/socket.h>
28 #endif
29 #ifdef HAVE_SYS_UN_H
30 # include <sys/un.h>
31 #endif
32 #ifdef HAVE_SYS_MMAN_H
33 # include <sys/mman.h>
34 #endif
35 #ifdef HAVE_DIRENT_H
36 # include <dirent.h>
37 #endif
38 #include <sys/stat.h>
39 #ifdef HAVE_SYS_RESOURCE_H
40 # include <sys/resource.h>
41 #endif
43 #include "w32handle.h"
45 #include "atomic.h"
46 #include "mono-logger-internals.h"
47 #include "mono-os-mutex.h"
48 #include "mono-proclib.h"
49 #include "mono-threads.h"
51 #undef DEBUG_REFS
53 #define SLOT_MAX (1024 * 16)
55 /* must be a power of 2 */
56 #define HANDLE_PER_SLOT (256)
58 typedef struct {
59 MonoW32HandleType type;
60 guint ref;
61 gboolean signalled;
62 mono_mutex_t signal_mutex;
63 mono_cond_t signal_cond;
64 gpointer specific;
65 } MonoW32HandleBase;
67 static MonoW32HandleCapability handle_caps [MONO_W32HANDLE_COUNT];
68 static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
71 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
72 * If 4M handles are not enough... Oh, well... we will crash.
74 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
75 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
77 static MonoW32HandleBase *private_handles [SLOT_MAX];
78 static guint32 private_handles_count = 0;
79 static guint32 private_handles_slots_count = 0;
81 guint32 mono_w32handle_fd_reserve;
84 * This is an internal handle which is used for handling waiting for multiple handles.
85 * Threads which wait for multiple handles wait on this one handle, and when a handle
86 * is signalled, this handle is signalled too.
88 static mono_mutex_t global_signal_mutex;
89 static mono_cond_t global_signal_cond;
91 static mono_mutex_t scan_mutex;
93 static gboolean shutting_down = FALSE;
95 static gboolean
96 type_is_fd (MonoW32HandleType type)
98 switch (type) {
99 case MONO_W32HANDLE_FILE:
100 case MONO_W32HANDLE_CONSOLE:
101 case MONO_W32HANDLE_SOCKET:
102 case MONO_W32HANDLE_PIPE:
103 return TRUE;
104 default:
105 return FALSE;
109 static gboolean
110 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
112 gsize index, offset;
114 g_assert (handle_data);
116 index = SLOT_INDEX ((gsize) handle);
117 if (index >= SLOT_MAX)
118 return FALSE;
119 if (!private_handles [index])
120 return FALSE;
122 offset = SLOT_OFFSET ((gsize) handle);
123 if (private_handles [index][offset].type == MONO_W32HANDLE_UNUSED)
124 return FALSE;
126 *handle_data = &private_handles [index][offset];
127 return TRUE;
130 MonoW32HandleType
131 mono_w32handle_get_type (gpointer handle)
133 MonoW32HandleBase *handle_data;
135 if (!mono_w32handle_lookup_data (handle, &handle_data))
136 return MONO_W32HANDLE_UNUSED; /* An impossible type */
138 return handle_data->type;
141 void
142 mono_w32handle_set_signal_state (gpointer handle, gboolean state, gboolean broadcast)
144 MonoW32HandleBase *handle_data;
146 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
147 return;
150 #ifdef DEBUG
151 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
152 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
153 #endif
155 if (state == TRUE) {
156 /* Tell everyone blocking on a single handle */
158 /* The condition the global signal cond is waiting on is the signalling of
159 * _any_ handle. So lock it before setting the signalled state.
161 mono_os_mutex_lock (&global_signal_mutex);
163 /* This function _must_ be called with
164 * handle->signal_mutex locked
166 handle_data->signalled=state;
168 if (broadcast == TRUE) {
169 mono_os_cond_broadcast (&handle_data->signal_cond);
170 } else {
171 mono_os_cond_signal (&handle_data->signal_cond);
174 /* Tell everyone blocking on multiple handles that something
175 * was signalled
177 mono_os_cond_broadcast (&global_signal_cond);
179 mono_os_mutex_unlock (&global_signal_mutex);
180 } else {
181 handle_data->signalled=state;
185 gboolean
186 mono_w32handle_issignalled (gpointer handle)
188 MonoW32HandleBase *handle_data;
190 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
191 return(FALSE);
194 return handle_data->signalled;
198 mono_w32handle_lock_signal_mutex (void)
200 #ifdef DEBUG
201 g_message ("%s: lock global signal mutex", __func__);
202 #endif
204 mono_os_mutex_lock (&global_signal_mutex);
206 return 0;
210 mono_w32handle_unlock_signal_mutex (void)
212 #ifdef DEBUG
213 g_message ("%s: unlock global signal mutex", __func__);
214 #endif
216 mono_os_mutex_unlock (&global_signal_mutex);
218 return 0;
222 mono_w32handle_lock_handle (gpointer handle)
224 MonoW32HandleBase *handle_data;
226 #ifdef DEBUG
227 g_message ("%s: locking handle %p", __func__, handle);
228 #endif
230 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
231 return(0);
234 mono_w32handle_ref (handle);
236 mono_os_mutex_lock (&handle_data->signal_mutex);
238 return 0;
242 mono_w32handle_trylock_handle (gpointer handle)
244 MonoW32HandleBase *handle_data;
245 int ret;
247 #ifdef DEBUG
248 g_message ("%s: locking handle %p", __func__, handle);
249 #endif
251 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
252 return(0);
255 mono_w32handle_ref (handle);
257 ret = mono_os_mutex_trylock (&handle_data->signal_mutex);
258 if (ret != 0) {
259 mono_w32handle_unref (handle);
262 return(ret);
266 mono_w32handle_unlock_handle (gpointer handle)
268 MonoW32HandleBase *handle_data;
270 #ifdef DEBUG
271 g_message ("%s: unlocking handle %p", __func__, handle);
272 #endif
274 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
275 return(0);
278 mono_os_mutex_unlock (&handle_data->signal_mutex);
280 mono_w32handle_unref (handle);
282 return 0;
286 * wapi_init:
288 * Initialize the io-layer.
290 void
291 mono_w32handle_init (void)
293 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
294 == MONO_W32HANDLE_COUNT);
296 /* This is needed by the code in mono_w32handle_new_internal */
297 mono_w32handle_fd_reserve = (eg_getdtablesize () + (HANDLE_PER_SLOT - 1)) & ~(HANDLE_PER_SLOT - 1);
299 do {
301 * The entries in private_handles reserved for fds are allocated lazily to
302 * save memory.
305 private_handles_count += HANDLE_PER_SLOT;
306 private_handles_slots_count ++;
307 } while(mono_w32handle_fd_reserve > private_handles_count);
309 mono_os_mutex_init (&scan_mutex);
311 mono_os_cond_init (&global_signal_cond);
312 mono_os_mutex_init (&global_signal_mutex);
315 static void mono_w32handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles);
317 void
318 mono_w32handle_cleanup (void)
320 int i, j, k;
322 g_assert (!shutting_down);
323 shutting_down = TRUE;
325 /* Every shared handle we were using ought really to be closed
326 * by now, but to make sure just blow them all away. The
327 * exiting finalizer thread in particular races us to the
328 * program exit and doesn't always win, so it can be left
329 * cluttering up the shared file. Anything else left over is
330 * really a bug.
332 for(i = SLOT_INDEX (0); private_handles[i] != NULL; i++) {
333 for(j = SLOT_OFFSET (0); j < HANDLE_PER_SLOT; j++) {
334 MonoW32HandleBase *handle_data = &private_handles[i][j];
335 gpointer handle = GINT_TO_POINTER (i*HANDLE_PER_SLOT+j);
337 for(k = handle_data->ref; k > 0; k--) {
338 mono_w32handle_unref_full (handle, TRUE);
343 for (i = 0; i < SLOT_MAX; ++i)
344 g_free (private_handles [i]);
347 static void mono_w32handle_init_handle (MonoW32HandleBase *handle,
348 MonoW32HandleType type, gpointer handle_specific)
350 g_assert (!shutting_down);
352 handle->type = type;
353 handle->signalled = FALSE;
354 handle->ref = 1;
356 mono_os_cond_init (&handle->signal_cond);
357 mono_os_mutex_init (&handle->signal_mutex);
359 if (handle_specific)
360 handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
364 * mono_w32handle_new_internal:
365 * @type: Init handle to this type
367 * Search for a free handle and initialize it. Return the handle on
368 * success and 0 on failure. This is only called from
369 * mono_w32handle_new, and scan_mutex must be held.
371 static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
372 gpointer handle_specific)
374 guint32 i, k, count;
375 static guint32 last = 0;
376 gboolean retry = FALSE;
378 g_assert (!shutting_down);
380 /* A linear scan should be fast enough. Start from the last
381 * allocation, assuming that handles are allocated more often
382 * than they're freed. Leave the space reserved for file
383 * descriptors
386 if (last < mono_w32handle_fd_reserve) {
387 last = mono_w32handle_fd_reserve;
388 } else {
389 retry = TRUE;
392 again:
393 count = last;
394 for(i = SLOT_INDEX (count); i < private_handles_slots_count; i++) {
395 if (private_handles [i]) {
396 for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
397 MonoW32HandleBase *handle = &private_handles [i][k];
399 if(handle->type == MONO_W32HANDLE_UNUSED) {
400 last = count + 1;
402 mono_w32handle_init_handle (handle, type, handle_specific);
403 return (count);
405 count++;
410 if(retry && last > mono_w32handle_fd_reserve) {
411 /* Try again from the beginning */
412 last = mono_w32handle_fd_reserve;
413 goto again;
416 /* Will need to expand the array. The caller will sort it out */
418 return(0);
421 gpointer
422 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
424 guint32 handle_idx = 0;
425 gpointer handle;
427 g_assert (!shutting_down);
429 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Creating new handle of type %s", __func__,
430 mono_w32handle_ops_typename (type));
432 g_assert(!type_is_fd(type));
434 mono_os_mutex_lock (&scan_mutex);
436 while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == 0) {
437 /* Try and expand the array, and have another go */
438 int idx = SLOT_INDEX (private_handles_count);
439 if (idx >= SLOT_MAX) {
440 break;
443 private_handles [idx] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
445 private_handles_count += HANDLE_PER_SLOT;
446 private_handles_slots_count ++;
449 mono_os_mutex_unlock (&scan_mutex);
451 if (handle_idx == 0) {
452 /* We ran out of slots */
453 handle = INVALID_HANDLE_VALUE;
454 goto done;
457 /* Make sure we left the space for fd mappings */
458 g_assert (handle_idx >= mono_w32handle_fd_reserve);
460 handle = GUINT_TO_POINTER (handle_idx);
462 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Allocated new handle %p", __func__, handle);
464 done:
465 return(handle);
468 gpointer mono_w32handle_new_fd (MonoW32HandleType type, int fd,
469 gpointer handle_specific)
471 MonoW32HandleBase *handle_data;
472 int fd_index, fd_offset;
474 g_assert (!shutting_down);
476 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Creating new handle of type %s", __func__,
477 mono_w32handle_ops_typename (type));
479 g_assert(type_is_fd(type));
481 if (fd >= mono_w32handle_fd_reserve) {
482 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: fd %d is too big", __func__, fd);
484 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
487 fd_index = SLOT_INDEX (fd);
488 fd_offset = SLOT_OFFSET (fd);
490 /* Initialize the array entries on demand */
491 if (!private_handles [fd_index]) {
492 mono_os_mutex_lock (&scan_mutex);
494 if (!private_handles [fd_index])
495 private_handles [fd_index] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
497 mono_os_mutex_unlock (&scan_mutex);
500 handle_data = &private_handles [fd_index][fd_offset];
502 if (handle_data->type != MONO_W32HANDLE_UNUSED) {
503 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: fd %d is already in use!", __func__, fd);
504 /* FIXME: clean up this handle? We can't do anything
505 * with the fd, cos thats the new one
509 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Assigning new fd handle %p", __func__, (gpointer)(gsize)fd);
511 mono_w32handle_init_handle (handle_data, type, handle_specific);
513 return(GUINT_TO_POINTER(fd));
516 gboolean
517 mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
518 gpointer *handle_specific)
520 MonoW32HandleBase *handle_data;
522 g_assert (handle_specific);
524 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
525 return(FALSE);
528 if (handle_data->type != type) {
529 return(FALSE);
532 *handle_specific = handle_data->specific;
534 return(TRUE);
537 void
538 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
540 MonoW32HandleBase *handle_data = NULL;
541 gpointer handle;
542 guint32 i, k;
544 mono_os_mutex_lock (&scan_mutex);
546 for (i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
547 if (private_handles [i]) {
548 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
549 handle_data = &private_handles [i][k];
550 if (handle_data->type == MONO_W32HANDLE_UNUSED)
551 continue;
552 handle = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
553 if (on_each (handle, handle_data->specific, user_data) == TRUE)
554 goto done;
559 done:
560 mono_os_mutex_unlock (&scan_mutex);
563 /* This might list some shared handles twice if they are already
564 * opened by this process, and the check function returns FALSE the
565 * first time. Shared handles that are created during the search are
566 * unreffed if the check function returns FALSE, so callers must not
567 * rely on the handle persisting (unless the check function returns
568 * TRUE)
569 * The caller owns the returned handle.
571 gpointer mono_w32handle_search (MonoW32HandleType type,
572 gboolean (*check)(gpointer test, gpointer user),
573 gpointer user_data,
574 gpointer *handle_specific,
575 gboolean search_shared)
577 MonoW32HandleBase *handle_data = NULL;
578 gpointer ret = NULL;
579 guint32 i, k;
580 gboolean found = FALSE;
582 mono_os_mutex_lock (&scan_mutex);
584 for (i = SLOT_INDEX (0); !found && i < private_handles_slots_count; i++) {
585 if (private_handles [i]) {
586 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
587 handle_data = &private_handles [i][k];
589 if (handle_data->type == type) {
590 ret = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
591 if (check (ret, user_data) == TRUE) {
592 mono_w32handle_ref (ret);
593 found = TRUE;
594 break;
601 mono_os_mutex_unlock (&scan_mutex);
603 if (!found) {
604 ret = NULL;
605 goto done;
608 if(handle_specific != NULL) {
609 *handle_specific = handle_data->specific;
612 done:
613 return(ret);
616 void mono_w32handle_ref (gpointer handle)
618 MonoW32HandleBase *handle_data;
620 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
621 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Attempting to ref invalid private handle %p", __func__, handle);
622 return;
625 InterlockedIncrement ((gint32 *)&handle_data->ref);
627 #ifdef DEBUG_REFS
628 g_message ("%s: %s handle %p ref now %d",
629 __func__, mono_w32handle_ops_typename (handle_data->type), handle, handle_data->ref);
630 #endif
633 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer);
635 /* The handle must not be locked on entry to this function */
636 static void mono_w32handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles)
638 MonoW32HandleBase *handle_data;
639 gboolean destroy = FALSE, early_exit = FALSE;
640 int thr_ret;
642 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
643 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Attempting to unref invalid private handle %p",
644 __func__, handle);
645 return;
648 /* Possible race condition here if another thread refs the
649 * handle between here and setting the type to UNUSED. I
650 * could lock a mutex, but I'm not sure that allowing a handle
651 * reference to reach 0 isn't an application bug anyway.
653 destroy = (InterlockedDecrement ((gint32 *)&handle_data->ref) ==0);
655 #ifdef DEBUG_REFS
656 g_message ("%s: %s handle %p ref now %d (destroy %s)",
657 __func__, mono_w32handle_ops_typename (handle_data->type), handle, handle_data->ref, destroy?"TRUE":"FALSE");
658 #endif
660 if(destroy==TRUE) {
661 /* Need to copy the handle info, reset the slot in the
662 * array, and _only then_ call the close function to
663 * avoid race conditions (eg file descriptors being
664 * closed, and another file being opened getting the
665 * same fd racing the memset())
667 MonoW32HandleType type;
668 gpointer handle_specific;
669 void (*close_func)(gpointer, gpointer);
671 type = handle_data->type;
672 handle_specific = handle_data->specific;
674 mono_os_mutex_lock (&scan_mutex);
676 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Destroying handle %p", __func__, handle);
678 /* Destroy the mutex and cond var. We hope nobody
679 * tried to grab them between the handle unlock and
680 * now, but pthreads doesn't have a
681 * "unlock_and_destroy" atomic function.
683 thr_ret = mono_os_mutex_destroy (&handle_data->signal_mutex);
684 /*WARNING gross hack to make cleanup not crash when exiting without the whole runtime teardown.*/
685 if (thr_ret == EBUSY && ignore_private_busy_handles) {
686 early_exit = TRUE;
687 } else {
688 if (thr_ret != 0)
689 g_error ("Error destroying handle %p mutex due to %d\n", handle, thr_ret);
691 thr_ret = mono_os_cond_destroy (&handle_data->signal_cond);
692 if (thr_ret == EBUSY && ignore_private_busy_handles)
693 early_exit = TRUE;
694 else if (thr_ret != 0)
695 g_error ("Error destroying handle %p cond var due to %d\n", handle, thr_ret);
698 memset (handle_data, 0, sizeof (MonoW32HandleBase));
700 mono_os_mutex_unlock (&scan_mutex);
702 if (early_exit)
703 return;
705 close_func = _wapi_handle_ops_get_close_func (type);
706 if (close_func != NULL) {
707 close_func (handle, handle_specific);
710 g_free (handle_specific);
714 void mono_w32handle_unref (gpointer handle)
716 mono_w32handle_unref_full (handle, FALSE);
719 void
720 mono_w32handle_register_ops (MonoW32HandleType type, MonoW32HandleOps *ops)
722 handle_ops [type] = ops;
725 void mono_w32handle_register_capabilities (MonoW32HandleType type,
726 MonoW32HandleCapability caps)
728 handle_caps[type] = caps;
731 gboolean mono_w32handle_test_capabilities (gpointer handle,
732 MonoW32HandleCapability caps)
734 MonoW32HandleBase *handle_data;
735 MonoW32HandleType type;
737 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
738 return(FALSE);
741 type = handle_data->type;
743 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
744 handle_caps[type], caps, handle_caps[type] & caps);
746 return((handle_caps[type] & caps) != 0);
749 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer)
751 if (handle_ops[type] != NULL &&
752 handle_ops[type]->close != NULL) {
753 return (handle_ops[type]->close);
756 return (NULL);
759 void mono_w32handle_ops_close (gpointer handle, gpointer data)
761 MonoW32HandleBase *handle_data;
762 MonoW32HandleType type;
764 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
765 return;
768 type = handle_data->type;
770 if (handle_ops[type] != NULL &&
771 handle_ops[type]->close != NULL) {
772 handle_ops[type]->close (handle, data);
776 void mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
778 if (handle_ops[type] != NULL &&
779 handle_ops[type]->details != NULL) {
780 handle_ops[type]->details (data);
784 const gchar* mono_w32handle_ops_typename (MonoW32HandleType type)
786 g_assert (handle_ops [type]);
787 g_assert (handle_ops [type]->typename);
788 return handle_ops [type]->typename ();
791 gsize mono_w32handle_ops_typesize (MonoW32HandleType type)
793 g_assert (handle_ops [type]);
794 g_assert (handle_ops [type]->typesize);
795 return handle_ops [type]->typesize ();
798 void mono_w32handle_ops_signal (gpointer handle)
800 MonoW32HandleBase *handle_data;
801 MonoW32HandleType type;
803 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
804 return;
807 type = handle_data->type;
809 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
810 handle_ops[type]->signal (handle);
814 gboolean mono_w32handle_ops_own (gpointer handle)
816 MonoW32HandleBase *handle_data;
817 MonoW32HandleType type;
819 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
820 return(FALSE);
823 type = handle_data->type;
825 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
826 return(handle_ops[type]->own_handle (handle));
827 } else {
828 return(FALSE);
832 gboolean mono_w32handle_ops_isowned (gpointer handle)
834 MonoW32HandleBase *handle_data;
835 MonoW32HandleType type;
837 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
838 return(FALSE);
841 type = handle_data->type;
843 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
844 return(handle_ops[type]->is_owned (handle));
845 } else {
846 return(FALSE);
850 guint32 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean alertable)
852 MonoW32HandleBase *handle_data;
853 MonoW32HandleType type;
855 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
856 return(WAIT_FAILED);
859 type = handle_data->type;
861 if (handle_ops[type] != NULL &&
862 handle_ops[type]->special_wait != NULL) {
863 return(handle_ops[type]->special_wait (handle, timeout, alertable));
864 } else {
865 return(WAIT_FAILED);
869 void mono_w32handle_ops_prewait (gpointer handle)
871 MonoW32HandleBase *handle_data;
872 MonoW32HandleType type;
874 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
875 return;
878 type = handle_data->type;
880 if (handle_ops[type] != NULL &&
881 handle_ops[type]->prewait != NULL) {
882 handle_ops[type]->prewait (handle);
886 static void
887 spin (guint32 ms)
889 struct timespec sleepytime;
891 g_assert (ms < 1000);
893 sleepytime.tv_sec = 0;
894 sleepytime.tv_nsec = ms * 1000000;
895 nanosleep (&sleepytime, NULL);
898 gboolean
899 mono_w32handle_count_signalled_handles (guint32 numhandles, gpointer *handles,
900 gboolean waitall, guint32 *retcount, guint32 *lowest)
902 guint32 count, i, iter=0;
903 gboolean ret;
904 int thr_ret;
906 /* Lock all the handles, with backoff */
907 again:
908 for(i=0; i<numhandles; i++) {
909 gpointer handle = handles[i];
911 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempting to lock %p", __func__, handle);
913 thr_ret = mono_w32handle_trylock_handle (handle);
915 if (thr_ret != 0) {
916 /* Bummer */
918 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p: %s", __func__,
919 handle, strerror (thr_ret));
921 while (i--) {
922 handle = handles[i];
924 thr_ret = mono_w32handle_unlock_handle (handle);
925 g_assert (thr_ret == 0);
928 /* If iter ever reaches 100 the nanosleep will
929 * return EINVAL immediately, but we have a
930 * design flaw if that happens.
932 iter++;
933 if(iter==100) {
934 g_warning ("%s: iteration overflow!",
935 __func__);
936 iter=1;
939 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
940 iter*10);
941 spin (10 * iter);
943 goto again;
947 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
949 count=0;
950 *lowest=numhandles;
952 for(i=0; i<numhandles; i++) {
953 gpointer handle = handles[i];
955 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Checking handle %p", __func__, handle);
957 if(((mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)==TRUE) &&
958 (mono_w32handle_ops_isowned (handle) == TRUE)) ||
959 (mono_w32handle_issignalled (handle))) {
960 count++;
962 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Handle %p signalled", __func__,
963 handle);
964 if(*lowest>i) {
965 *lowest=i;
970 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: %d event handles signalled", __func__, count);
972 if ((waitall == TRUE && count == numhandles) ||
973 (waitall == FALSE && count > 0)) {
974 ret=TRUE;
975 } else {
976 ret=FALSE;
979 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Returning %d", __func__, ret);
981 *retcount=count;
983 return(ret);
986 void mono_w32handle_unlock_handles (guint32 numhandles, gpointer *handles)
988 guint32 i;
989 int thr_ret;
991 for(i=0; i<numhandles; i++) {
992 gpointer handle = handles[i];
994 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlocking handle %p", __func__, handle);
996 thr_ret = mono_w32handle_unlock_handle (handle);
997 g_assert (thr_ret == 0);
1001 static int
1002 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
1004 int res;
1006 if (!poll) {
1007 res = mono_os_cond_timedwait (cond, mutex, timeout);
1008 } else {
1009 /* This is needed when waiting for process handles */
1010 if (!alerted) {
1012 * pthread_cond_(timed)wait() can return 0 even if the condition was not
1013 * signalled. This happens at least on Darwin. We surface this, i.e., we
1014 * get spurious wake-ups.
1016 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
1018 res = mono_os_cond_timedwait (cond, mutex, timeout);
1019 } else {
1020 if (timeout < 100) {
1021 /* Real timeout is less than 100ms time */
1022 res = mono_os_cond_timedwait (cond, mutex, timeout);
1023 } else {
1024 res = mono_os_cond_timedwait (cond, mutex, 100);
1026 /* Mask the fake timeout, this will cause
1027 * another poll if the cond was not really signaled
1029 if (res == -1)
1030 res = 0;
1035 return res;
1038 static void
1039 signal_global (gpointer unused)
1041 /* If we reach here, then interrupt token is set to the flag value, which
1042 * means that the target thread is either
1043 * - before the first CAS in timedwait, which means it won't enter the wait.
1044 * - it is after the first CAS, so it is already waiting, or it will enter
1045 * the wait, and it will be interrupted by the broadcast. */
1046 mono_os_mutex_lock (&global_signal_mutex);
1047 mono_os_cond_broadcast (&global_signal_cond);
1048 mono_os_mutex_unlock (&global_signal_mutex);
1052 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
1054 int res;
1056 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
1058 if (alerted)
1059 *alerted = FALSE;
1061 if (alerted) {
1062 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
1063 if (*alerted)
1064 return 0;
1067 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
1069 if (alerted)
1070 mono_thread_info_uninstall_interrupt (alerted);
1072 return res;
1075 static void
1076 signal_handle_and_unref (gpointer handle)
1078 MonoW32HandleBase *handle_data;
1079 mono_cond_t *cond;
1080 mono_mutex_t *mutex;
1082 if (!mono_w32handle_lookup_data (handle, &handle_data))
1083 g_error ("cannot signal unknown handle %p", handle);
1085 /* If we reach here, then interrupt token is set to the flag value, which
1086 * means that the target thread is either
1087 * - before the first CAS in timedwait, which means it won't enter the wait.
1088 * - it is after the first CAS, so it is already waiting, or it will enter
1089 * the wait, and it will be interrupted by the broadcast. */
1090 cond = &handle_data->signal_cond;
1091 mutex = &handle_data->signal_mutex;
1093 mono_os_mutex_lock (mutex);
1094 mono_os_cond_broadcast (cond);
1095 mono_os_mutex_unlock (mutex);
1097 mono_w32handle_unref (handle);
1101 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1103 MonoW32HandleBase *handle_data;
1104 int res;
1106 if (!mono_w32handle_lookup_data (handle, &handle_data))
1107 g_error ("cannot wait on unknown handle %p", handle);
1109 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for %p (type %s)", __func__, handle,
1110 mono_w32handle_ops_typename (mono_w32handle_get_type (handle)));
1112 if (alerted)
1113 *alerted = FALSE;
1115 if (alerted) {
1116 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1117 if (*alerted)
1118 return 0;
1119 mono_w32handle_ref (handle);
1122 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
1124 if (alerted) {
1125 mono_thread_info_uninstall_interrupt (alerted);
1126 if (!*alerted) {
1127 /* if it is alerted, then the handle is unref in the interrupt callback */
1128 mono_w32handle_unref (handle);
1132 return res;
1135 void mono_w32handle_dump (void)
1137 MonoW32HandleBase *handle_data;
1138 guint32 i, k;
1140 mono_os_mutex_lock (&scan_mutex);
1142 for(i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
1143 if (private_handles [i]) {
1144 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
1145 handle_data = &private_handles [i][k];
1147 if (handle_data->type == MONO_W32HANDLE_UNUSED) {
1148 continue;
1151 g_print ("%3x [%7s] %s %d ",
1152 i * HANDLE_PER_SLOT + k,
1153 mono_w32handle_ops_typename (handle_data->type),
1154 handle_data->signalled?"Sg":"Un",
1155 handle_data->ref);
1156 mono_w32handle_ops_details (handle_data->type, handle_data->specific);
1157 g_print ("\n");
1162 mono_os_mutex_unlock (&scan_mutex);
1165 #endif /* !defined(HOST_WIN32) */