[w32handle] Initialize them earlier
[mono-project.git] / mono / utils / w32handle.c
blobbd98e37b702d157ae473d84c26e873e248f5c32c
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"
50 #include "mono-time.h"
52 #undef DEBUG_REFS
54 #define SLOT_MAX (1024 * 16)
56 /* must be a power of 2 */
57 #define HANDLE_PER_SLOT (256)
59 #define INFINITE 0xFFFFFFFF
61 typedef struct {
62 MonoW32HandleType type;
63 guint ref;
64 gboolean signalled;
65 mono_mutex_t signal_mutex;
66 mono_cond_t signal_cond;
67 gpointer specific;
68 } MonoW32HandleBase;
70 static MonoW32HandleCapability handle_caps [MONO_W32HANDLE_COUNT];
71 static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
74 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
75 * If 4M handles are not enough... Oh, well... we will crash.
77 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
78 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
80 static MonoW32HandleBase *private_handles [SLOT_MAX];
81 static guint32 private_handles_count = 0;
82 static guint32 private_handles_slots_count = 0;
84 guint32 mono_w32handle_fd_reserve;
87 * This is an internal handle which is used for handling waiting for multiple handles.
88 * Threads which wait for multiple handles wait on this one handle, and when a handle
89 * is signalled, this handle is signalled too.
91 static mono_mutex_t global_signal_mutex;
92 static mono_cond_t global_signal_cond;
94 static mono_mutex_t scan_mutex;
96 static gboolean shutting_down = FALSE;
98 static gboolean
99 type_is_fd (MonoW32HandleType type)
101 switch (type) {
102 case MONO_W32HANDLE_FILE:
103 case MONO_W32HANDLE_CONSOLE:
104 case MONO_W32HANDLE_SOCKET:
105 case MONO_W32HANDLE_PIPE:
106 return TRUE;
107 default:
108 return FALSE;
112 static gboolean
113 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
115 gsize index, offset;
117 g_assert (handle_data);
119 index = SLOT_INDEX ((gsize) handle);
120 if (index >= SLOT_MAX)
121 return FALSE;
122 if (!private_handles [index])
123 return FALSE;
125 offset = SLOT_OFFSET ((gsize) handle);
126 if (private_handles [index][offset].type == MONO_W32HANDLE_UNUSED)
127 return FALSE;
129 *handle_data = &private_handles [index][offset];
130 return TRUE;
133 MonoW32HandleType
134 mono_w32handle_get_type (gpointer handle)
136 MonoW32HandleBase *handle_data;
138 if (!mono_w32handle_lookup_data (handle, &handle_data))
139 return MONO_W32HANDLE_UNUSED; /* An impossible type */
141 return handle_data->type;
144 void
145 mono_w32handle_set_signal_state (gpointer handle, gboolean state, gboolean broadcast)
147 MonoW32HandleBase *handle_data;
149 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
150 return;
153 #ifdef DEBUG
154 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
155 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
156 #endif
158 if (state == TRUE) {
159 /* Tell everyone blocking on a single handle */
161 /* The condition the global signal cond is waiting on is the signalling of
162 * _any_ handle. So lock it before setting the signalled state.
164 mono_os_mutex_lock (&global_signal_mutex);
166 /* This function _must_ be called with
167 * handle->signal_mutex locked
169 handle_data->signalled=state;
171 if (broadcast == TRUE) {
172 mono_os_cond_broadcast (&handle_data->signal_cond);
173 } else {
174 mono_os_cond_signal (&handle_data->signal_cond);
177 /* Tell everyone blocking on multiple handles that something
178 * was signalled
180 mono_os_cond_broadcast (&global_signal_cond);
182 mono_os_mutex_unlock (&global_signal_mutex);
183 } else {
184 handle_data->signalled=state;
188 gboolean
189 mono_w32handle_issignalled (gpointer handle)
191 MonoW32HandleBase *handle_data;
193 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
194 return(FALSE);
197 return handle_data->signalled;
200 static int
201 mono_w32handle_lock_signal_mutex (void)
203 #ifdef DEBUG
204 g_message ("%s: lock global signal mutex", __func__);
205 #endif
207 mono_os_mutex_lock (&global_signal_mutex);
209 return 0;
212 static int
213 mono_w32handle_unlock_signal_mutex (void)
215 #ifdef DEBUG
216 g_message ("%s: unlock global signal mutex", __func__);
217 #endif
219 mono_os_mutex_unlock (&global_signal_mutex);
221 return 0;
225 mono_w32handle_lock_handle (gpointer handle)
227 MonoW32HandleBase *handle_data;
229 #ifdef DEBUG
230 g_message ("%s: locking handle %p", __func__, handle);
231 #endif
233 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
234 return(0);
237 mono_w32handle_ref (handle);
239 mono_os_mutex_lock (&handle_data->signal_mutex);
241 return 0;
245 mono_w32handle_trylock_handle (gpointer handle)
247 MonoW32HandleBase *handle_data;
248 int ret;
250 #ifdef DEBUG
251 g_message ("%s: locking handle %p", __func__, handle);
252 #endif
254 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
255 return(0);
258 mono_w32handle_ref (handle);
260 ret = mono_os_mutex_trylock (&handle_data->signal_mutex);
261 if (ret != 0) {
262 mono_w32handle_unref (handle);
265 return(ret);
269 mono_w32handle_unlock_handle (gpointer handle)
271 MonoW32HandleBase *handle_data;
273 #ifdef DEBUG
274 g_message ("%s: unlocking handle %p", __func__, handle);
275 #endif
277 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
278 return(0);
281 mono_os_mutex_unlock (&handle_data->signal_mutex);
283 mono_w32handle_unref (handle);
285 return 0;
289 * wapi_init:
291 * Initialize the io-layer.
293 void
294 mono_w32handle_init (void)
296 static gboolean initialized = FALSE;
298 if (initialized)
299 return;
301 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
302 == MONO_W32HANDLE_COUNT);
304 /* This is needed by the code in mono_w32handle_new_internal */
305 mono_w32handle_fd_reserve = (eg_getdtablesize () + (HANDLE_PER_SLOT - 1)) & ~(HANDLE_PER_SLOT - 1);
307 do {
309 * The entries in private_handles reserved for fds are allocated lazily to
310 * save memory.
313 private_handles_count += HANDLE_PER_SLOT;
314 private_handles_slots_count ++;
315 } while(mono_w32handle_fd_reserve > private_handles_count);
317 mono_os_mutex_init (&scan_mutex);
319 mono_os_cond_init (&global_signal_cond);
320 mono_os_mutex_init (&global_signal_mutex);
322 initialized = TRUE;
325 static void mono_w32handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles);
327 void
328 mono_w32handle_cleanup (void)
330 int i, j, k;
332 g_assert (!shutting_down);
333 shutting_down = TRUE;
335 /* Every shared handle we were using ought really to be closed
336 * by now, but to make sure just blow them all away. The
337 * exiting finalizer thread in particular races us to the
338 * program exit and doesn't always win, so it can be left
339 * cluttering up the shared file. Anything else left over is
340 * really a bug.
342 for(i = SLOT_INDEX (0); private_handles[i] != NULL; i++) {
343 for(j = SLOT_OFFSET (0); j < HANDLE_PER_SLOT; j++) {
344 MonoW32HandleBase *handle_data = &private_handles[i][j];
345 gpointer handle = GINT_TO_POINTER (i*HANDLE_PER_SLOT+j);
347 for(k = handle_data->ref; k > 0; k--) {
348 mono_w32handle_unref_full (handle, TRUE);
353 for (i = 0; i < SLOT_MAX; ++i)
354 g_free (private_handles [i]);
357 static void mono_w32handle_init_handle (MonoW32HandleBase *handle,
358 MonoW32HandleType type, gpointer handle_specific)
360 g_assert (!shutting_down);
362 handle->type = type;
363 handle->signalled = FALSE;
364 handle->ref = 1;
366 mono_os_cond_init (&handle->signal_cond);
367 mono_os_mutex_init (&handle->signal_mutex);
369 if (handle_specific)
370 handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
374 * mono_w32handle_new_internal:
375 * @type: Init handle to this type
377 * Search for a free handle and initialize it. Return the handle on
378 * success and 0 on failure. This is only called from
379 * mono_w32handle_new, and scan_mutex must be held.
381 static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
382 gpointer handle_specific)
384 guint32 i, k, count;
385 static guint32 last = 0;
386 gboolean retry = FALSE;
388 g_assert (!shutting_down);
390 /* A linear scan should be fast enough. Start from the last
391 * allocation, assuming that handles are allocated more often
392 * than they're freed. Leave the space reserved for file
393 * descriptors
396 if (last < mono_w32handle_fd_reserve) {
397 last = mono_w32handle_fd_reserve;
398 } else {
399 retry = TRUE;
402 again:
403 count = last;
404 for(i = SLOT_INDEX (count); i < private_handles_slots_count; i++) {
405 if (private_handles [i]) {
406 for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
407 MonoW32HandleBase *handle = &private_handles [i][k];
409 if(handle->type == MONO_W32HANDLE_UNUSED) {
410 last = count + 1;
412 mono_w32handle_init_handle (handle, type, handle_specific);
413 return (count);
415 count++;
420 if(retry && last > mono_w32handle_fd_reserve) {
421 /* Try again from the beginning */
422 last = mono_w32handle_fd_reserve;
423 goto again;
426 /* Will need to expand the array. The caller will sort it out */
428 return(0);
431 gpointer
432 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
434 guint32 handle_idx = 0;
435 gpointer handle;
437 g_assert (!shutting_down);
439 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Creating new handle of type %s", __func__,
440 mono_w32handle_ops_typename (type));
442 g_assert(!type_is_fd(type));
444 mono_os_mutex_lock (&scan_mutex);
446 while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == 0) {
447 /* Try and expand the array, and have another go */
448 int idx = SLOT_INDEX (private_handles_count);
449 if (idx >= SLOT_MAX) {
450 break;
453 private_handles [idx] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
455 private_handles_count += HANDLE_PER_SLOT;
456 private_handles_slots_count ++;
459 mono_os_mutex_unlock (&scan_mutex);
461 if (handle_idx == 0) {
462 /* We ran out of slots */
463 handle = INVALID_HANDLE_VALUE;
464 goto done;
467 /* Make sure we left the space for fd mappings */
468 g_assert (handle_idx >= mono_w32handle_fd_reserve);
470 handle = GUINT_TO_POINTER (handle_idx);
472 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Allocated new handle %p", __func__, handle);
474 done:
475 return(handle);
478 gpointer mono_w32handle_new_fd (MonoW32HandleType type, int fd,
479 gpointer handle_specific)
481 MonoW32HandleBase *handle_data;
482 int fd_index, fd_offset;
484 g_assert (!shutting_down);
486 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Creating new handle of type %s", __func__,
487 mono_w32handle_ops_typename (type));
489 g_assert(type_is_fd(type));
491 if (fd >= mono_w32handle_fd_reserve) {
492 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: fd %d is too big", __func__, fd);
494 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
497 fd_index = SLOT_INDEX (fd);
498 fd_offset = SLOT_OFFSET (fd);
500 /* Initialize the array entries on demand */
501 if (!private_handles [fd_index]) {
502 mono_os_mutex_lock (&scan_mutex);
504 if (!private_handles [fd_index])
505 private_handles [fd_index] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
507 mono_os_mutex_unlock (&scan_mutex);
510 handle_data = &private_handles [fd_index][fd_offset];
512 if (handle_data->type != MONO_W32HANDLE_UNUSED) {
513 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: fd %d is already in use!", __func__, fd);
514 /* FIXME: clean up this handle? We can't do anything
515 * with the fd, cos thats the new one
519 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Assigning new fd handle %p", __func__, (gpointer)(gsize)fd);
521 mono_w32handle_init_handle (handle_data, type, handle_specific);
523 return(GUINT_TO_POINTER(fd));
526 gboolean
527 mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
528 gpointer *handle_specific)
530 MonoW32HandleBase *handle_data;
532 g_assert (handle_specific);
534 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
535 return(FALSE);
538 if (handle_data->type != type) {
539 return(FALSE);
542 *handle_specific = handle_data->specific;
544 return(TRUE);
547 void
548 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
550 MonoW32HandleBase *handle_data = NULL;
551 gpointer handle;
552 guint32 i, k;
554 mono_os_mutex_lock (&scan_mutex);
556 for (i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
557 if (private_handles [i]) {
558 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
559 handle_data = &private_handles [i][k];
560 if (handle_data->type == MONO_W32HANDLE_UNUSED)
561 continue;
562 handle = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
563 if (on_each (handle, handle_data->specific, user_data) == TRUE)
564 goto done;
569 done:
570 mono_os_mutex_unlock (&scan_mutex);
573 /* This might list some shared handles twice if they are already
574 * opened by this process, and the check function returns FALSE the
575 * first time. Shared handles that are created during the search are
576 * unreffed if the check function returns FALSE, so callers must not
577 * rely on the handle persisting (unless the check function returns
578 * TRUE)
579 * The caller owns the returned handle.
581 gpointer mono_w32handle_search (MonoW32HandleType type,
582 gboolean (*check)(gpointer test, gpointer user),
583 gpointer user_data,
584 gpointer *handle_specific,
585 gboolean search_shared)
587 MonoW32HandleBase *handle_data = NULL;
588 gpointer ret = NULL;
589 guint32 i, k;
590 gboolean found = FALSE;
592 mono_os_mutex_lock (&scan_mutex);
594 for (i = SLOT_INDEX (0); !found && i < private_handles_slots_count; i++) {
595 if (private_handles [i]) {
596 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
597 handle_data = &private_handles [i][k];
599 if (handle_data->type == type) {
600 ret = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
601 if (check (ret, user_data) == TRUE) {
602 mono_w32handle_ref (ret);
603 found = TRUE;
604 break;
611 mono_os_mutex_unlock (&scan_mutex);
613 if (!found) {
614 ret = NULL;
615 goto done;
618 if(handle_specific != NULL) {
619 *handle_specific = handle_data->specific;
622 done:
623 return(ret);
626 void mono_w32handle_ref (gpointer handle)
628 MonoW32HandleBase *handle_data;
630 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
631 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Attempting to ref invalid private handle %p", __func__, handle);
632 return;
635 InterlockedIncrement ((gint32 *)&handle_data->ref);
637 #ifdef DEBUG_REFS
638 g_message ("%s: %s handle %p ref now %d",
639 __func__, mono_w32handle_ops_typename (handle_data->type), handle, handle_data->ref);
640 #endif
643 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer);
645 /* The handle must not be locked on entry to this function */
646 static void mono_w32handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles)
648 MonoW32HandleBase *handle_data;
649 gboolean destroy = FALSE, early_exit = FALSE;
650 int thr_ret;
652 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
653 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Attempting to unref invalid private handle %p",
654 __func__, handle);
655 return;
658 /* Possible race condition here if another thread refs the
659 * handle between here and setting the type to UNUSED. I
660 * could lock a mutex, but I'm not sure that allowing a handle
661 * reference to reach 0 isn't an application bug anyway.
663 destroy = (InterlockedDecrement ((gint32 *)&handle_data->ref) ==0);
665 #ifdef DEBUG_REFS
666 g_message ("%s: %s handle %p ref now %d (destroy %s)",
667 __func__, mono_w32handle_ops_typename (handle_data->type), handle, handle_data->ref, destroy?"TRUE":"FALSE");
668 #endif
670 if(destroy==TRUE) {
671 /* Need to copy the handle info, reset the slot in the
672 * array, and _only then_ call the close function to
673 * avoid race conditions (eg file descriptors being
674 * closed, and another file being opened getting the
675 * same fd racing the memset())
677 MonoW32HandleType type;
678 gpointer handle_specific;
679 void (*close_func)(gpointer, gpointer);
681 type = handle_data->type;
682 handle_specific = handle_data->specific;
684 mono_os_mutex_lock (&scan_mutex);
686 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Destroying handle %p", __func__, handle);
688 /* Destroy the mutex and cond var. We hope nobody
689 * tried to grab them between the handle unlock and
690 * now, but pthreads doesn't have a
691 * "unlock_and_destroy" atomic function.
693 thr_ret = mono_os_mutex_destroy (&handle_data->signal_mutex);
694 /*WARNING gross hack to make cleanup not crash when exiting without the whole runtime teardown.*/
695 if (thr_ret == EBUSY && ignore_private_busy_handles) {
696 early_exit = TRUE;
697 } else {
698 if (thr_ret != 0)
699 g_error ("Error destroying handle %p mutex due to %d\n", handle, thr_ret);
701 thr_ret = mono_os_cond_destroy (&handle_data->signal_cond);
702 if (thr_ret == EBUSY && ignore_private_busy_handles)
703 early_exit = TRUE;
704 else if (thr_ret != 0)
705 g_error ("Error destroying handle %p cond var due to %d\n", handle, thr_ret);
708 memset (handle_data, 0, sizeof (MonoW32HandleBase));
710 mono_os_mutex_unlock (&scan_mutex);
712 if (early_exit)
713 return;
715 close_func = _wapi_handle_ops_get_close_func (type);
716 if (close_func != NULL) {
717 close_func (handle, handle_specific);
720 g_free (handle_specific);
724 void mono_w32handle_unref (gpointer handle)
726 mono_w32handle_unref_full (handle, FALSE);
729 void
730 mono_w32handle_register_ops (MonoW32HandleType type, MonoW32HandleOps *ops)
732 handle_ops [type] = ops;
735 void mono_w32handle_register_capabilities (MonoW32HandleType type,
736 MonoW32HandleCapability caps)
738 handle_caps[type] = caps;
741 gboolean mono_w32handle_test_capabilities (gpointer handle,
742 MonoW32HandleCapability caps)
744 MonoW32HandleBase *handle_data;
745 MonoW32HandleType type;
747 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
748 return(FALSE);
751 type = handle_data->type;
753 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
754 handle_caps[type], caps, handle_caps[type] & caps);
756 return((handle_caps[type] & caps) != 0);
759 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer)
761 if (handle_ops[type] != NULL &&
762 handle_ops[type]->close != NULL) {
763 return (handle_ops[type]->close);
766 return (NULL);
769 void mono_w32handle_ops_close (gpointer handle, gpointer data)
771 MonoW32HandleBase *handle_data;
772 MonoW32HandleType type;
774 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
775 return;
778 type = handle_data->type;
780 if (handle_ops[type] != NULL &&
781 handle_ops[type]->close != NULL) {
782 handle_ops[type]->close (handle, data);
786 void mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
788 if (handle_ops[type] != NULL &&
789 handle_ops[type]->details != NULL) {
790 handle_ops[type]->details (data);
794 const gchar* mono_w32handle_ops_typename (MonoW32HandleType type)
796 g_assert (handle_ops [type]);
797 g_assert (handle_ops [type]->typename);
798 return handle_ops [type]->typename ();
801 gsize mono_w32handle_ops_typesize (MonoW32HandleType type)
803 g_assert (handle_ops [type]);
804 g_assert (handle_ops [type]->typesize);
805 return handle_ops [type]->typesize ();
808 void mono_w32handle_ops_signal (gpointer handle)
810 MonoW32HandleBase *handle_data;
811 MonoW32HandleType type;
813 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
814 return;
817 type = handle_data->type;
819 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
820 handle_ops[type]->signal (handle);
824 gboolean mono_w32handle_ops_own (gpointer handle)
826 MonoW32HandleBase *handle_data;
827 MonoW32HandleType type;
829 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
830 return(FALSE);
833 type = handle_data->type;
835 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
836 return(handle_ops[type]->own_handle (handle));
837 } else {
838 return(FALSE);
842 gboolean mono_w32handle_ops_isowned (gpointer handle)
844 MonoW32HandleBase *handle_data;
845 MonoW32HandleType type;
847 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
848 return(FALSE);
851 type = handle_data->type;
853 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
854 return(handle_ops[type]->is_owned (handle));
855 } else {
856 return(FALSE);
860 guint32 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted)
862 MonoW32HandleBase *handle_data;
863 MonoW32HandleType type;
865 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
866 return(WAIT_FAILED);
869 type = handle_data->type;
871 if (handle_ops[type] != NULL &&
872 handle_ops[type]->special_wait != NULL) {
873 return(handle_ops[type]->special_wait (handle, timeout, alerted));
874 } else {
875 return(WAIT_FAILED);
879 void mono_w32handle_ops_prewait (gpointer handle)
881 MonoW32HandleBase *handle_data;
882 MonoW32HandleType type;
884 if (!mono_w32handle_lookup_data (handle, &handle_data)) {
885 return;
888 type = handle_data->type;
890 if (handle_ops[type] != NULL &&
891 handle_ops[type]->prewait != NULL) {
892 handle_ops[type]->prewait (handle);
896 static void
897 spin (guint32 ms)
899 struct timespec sleepytime;
901 g_assert (ms < 1000);
903 sleepytime.tv_sec = 0;
904 sleepytime.tv_nsec = ms * 1000000;
905 nanosleep (&sleepytime, NULL);
908 static void
909 mono_w32handle_lock_handles (gpointer *handles, gsize numhandles)
911 guint32 i, iter=0;
912 int thr_ret;
914 /* Lock all the handles, with backoff */
915 again:
916 for(i=0; i<numhandles; i++) {
917 gpointer handle = handles[i];
919 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempting to lock %p", __func__, handle);
921 thr_ret = mono_w32handle_trylock_handle (handle);
923 if (thr_ret != 0) {
924 /* Bummer */
926 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p: %s", __func__,
927 handle, strerror (thr_ret));
929 while (i--) {
930 handle = handles[i];
932 thr_ret = mono_w32handle_unlock_handle (handle);
933 g_assert (thr_ret == 0);
936 /* If iter ever reaches 100 the nanosleep will
937 * return EINVAL immediately, but we have a
938 * design flaw if that happens.
940 iter++;
941 if(iter==100) {
942 g_warning ("%s: iteration overflow!",
943 __func__);
944 iter=1;
947 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
948 iter*10);
949 spin (10 * iter);
951 goto again;
955 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
958 static void
959 mono_w32handle_unlock_handles (gpointer *handles, gsize numhandles)
961 guint32 i;
962 int thr_ret;
964 for(i=0; i<numhandles; i++) {
965 gpointer handle = handles[i];
967 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlocking handle %p", __func__, handle);
969 thr_ret = mono_w32handle_unlock_handle (handle);
970 g_assert (thr_ret == 0);
974 static int
975 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
977 int res;
979 if (!poll) {
980 res = mono_os_cond_timedwait (cond, mutex, timeout);
981 } else {
982 /* This is needed when waiting for process handles */
983 if (!alerted) {
985 * pthread_cond_(timed)wait() can return 0 even if the condition was not
986 * signalled. This happens at least on Darwin. We surface this, i.e., we
987 * get spurious wake-ups.
989 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
991 res = mono_os_cond_timedwait (cond, mutex, timeout);
992 } else {
993 if (timeout < 100) {
994 /* Real timeout is less than 100ms time */
995 res = mono_os_cond_timedwait (cond, mutex, timeout);
996 } else {
997 res = mono_os_cond_timedwait (cond, mutex, 100);
999 /* Mask the fake timeout, this will cause
1000 * another poll if the cond was not really signaled
1002 if (res == -1)
1003 res = 0;
1008 return res;
1011 static void
1012 signal_global (gpointer unused)
1014 /* If we reach here, then interrupt token is set to the flag value, which
1015 * means that the target thread is either
1016 * - before the first CAS in timedwait, which means it won't enter the wait.
1017 * - it is after the first CAS, so it is already waiting, or it will enter
1018 * the wait, and it will be interrupted by the broadcast. */
1019 mono_os_mutex_lock (&global_signal_mutex);
1020 mono_os_cond_broadcast (&global_signal_cond);
1021 mono_os_mutex_unlock (&global_signal_mutex);
1024 static int
1025 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
1027 int res;
1029 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
1031 if (alerted)
1032 *alerted = FALSE;
1034 if (alerted) {
1035 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
1036 if (*alerted)
1037 return 0;
1040 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
1042 if (alerted)
1043 mono_thread_info_uninstall_interrupt (alerted);
1045 return res;
1048 static void
1049 signal_handle_and_unref (gpointer handle)
1051 MonoW32HandleBase *handle_data;
1052 mono_cond_t *cond;
1053 mono_mutex_t *mutex;
1055 if (!mono_w32handle_lookup_data (handle, &handle_data))
1056 g_error ("cannot signal unknown handle %p", handle);
1058 /* If we reach here, then interrupt token is set to the flag value, which
1059 * means that the target thread is either
1060 * - before the first CAS in timedwait, which means it won't enter the wait.
1061 * - it is after the first CAS, so it is already waiting, or it will enter
1062 * the wait, and it will be interrupted by the broadcast. */
1063 cond = &handle_data->signal_cond;
1064 mutex = &handle_data->signal_mutex;
1066 mono_os_mutex_lock (mutex);
1067 mono_os_cond_broadcast (cond);
1068 mono_os_mutex_unlock (mutex);
1070 mono_w32handle_unref (handle);
1073 static int
1074 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1076 MonoW32HandleBase *handle_data;
1077 int res;
1079 if (!mono_w32handle_lookup_data (handle, &handle_data))
1080 g_error ("cannot wait on unknown handle %p", handle);
1082 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for %p (type %s)", __func__, handle,
1083 mono_w32handle_ops_typename (mono_w32handle_get_type (handle)));
1085 if (alerted)
1086 *alerted = FALSE;
1088 if (alerted) {
1089 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1090 if (*alerted)
1091 return 0;
1092 mono_w32handle_ref (handle);
1095 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
1097 if (alerted) {
1098 mono_thread_info_uninstall_interrupt (alerted);
1099 if (!*alerted) {
1100 /* if it is alerted, then the handle is unref in the interrupt callback */
1101 mono_w32handle_unref (handle);
1105 return res;
1108 void mono_w32handle_dump (void)
1110 MonoW32HandleBase *handle_data;
1111 guint32 i, k;
1113 mono_os_mutex_lock (&scan_mutex);
1115 for(i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
1116 if (private_handles [i]) {
1117 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
1118 handle_data = &private_handles [i][k];
1120 if (handle_data->type == MONO_W32HANDLE_UNUSED) {
1121 continue;
1124 g_print ("%3x [%7s] %s %d ",
1125 i * HANDLE_PER_SLOT + k,
1126 mono_w32handle_ops_typename (handle_data->type),
1127 handle_data->signalled?"Sg":"Un",
1128 handle_data->ref);
1129 mono_w32handle_ops_details (handle_data->type, handle_data->specific);
1130 g_print ("\n");
1135 mono_os_mutex_unlock (&scan_mutex);
1138 static gboolean
1139 own_if_signalled (gpointer handle)
1141 if (!mono_w32handle_issignalled (handle))
1142 return FALSE;
1144 mono_w32handle_ops_own (handle);
1145 return TRUE;
1148 static gboolean
1149 own_if_owned( gpointer handle)
1151 if (!mono_w32handle_ops_isowned (handle))
1152 return FALSE;
1154 mono_w32handle_ops_own (handle);
1155 return TRUE;
1158 MonoW32HandleWaitRet
1159 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
1161 MonoW32HandleWaitRet ret;
1162 gboolean alerted;
1163 gint64 start;
1164 gint thr_ret;
1166 alerted = FALSE;
1168 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1169 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p has special wait",
1170 __func__, handle);
1172 switch (mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL)) {
1173 case WAIT_OBJECT_0:
1174 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1175 break;
1176 case WAIT_IO_COMPLETION:
1177 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1178 break;
1179 case WAIT_TIMEOUT:
1180 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1181 break;
1182 case WAIT_FAILED:
1183 ret = MONO_W32HANDLE_WAIT_RET_FAILED;
1184 break;
1185 default:
1186 g_assert_not_reached ();
1189 if (alerted)
1190 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1192 return ret;
1195 if (!mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_WAIT)) {
1196 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1197 __func__, handle);
1199 return MONO_W32HANDLE_WAIT_RET_FAILED;
1202 thr_ret = mono_w32handle_lock_handle (handle);
1203 g_assert (thr_ret == 0);
1205 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) {
1206 if (own_if_owned (handle)) {
1207 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1208 __func__, handle);
1210 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1211 goto done;
1215 if (timeout != INFINITE)
1216 start = mono_msec_ticks ();
1218 for (;;) {
1219 gint waited;
1221 if (own_if_signalled (handle)) {
1222 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1223 __func__, handle);
1225 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1226 goto done;
1229 mono_w32handle_ops_prewait (handle);
1231 if (timeout == INFINITE) {
1232 waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1233 } else {
1234 gint64 elapsed;
1236 elapsed = mono_msec_ticks () - start;
1237 if (elapsed > timeout) {
1238 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1239 goto done;
1242 waited = mono_w32handle_timedwait_signal_handle (handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1245 if (alerted) {
1246 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1247 goto done;
1250 if (waited != 0) {
1251 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1252 goto done;
1256 done:
1257 thr_ret = mono_w32handle_unlock_handle (handle);
1258 g_assert (thr_ret == 0);
1260 return ret;
1263 MonoW32HandleWaitRet
1264 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
1266 MonoW32HandleWaitRet ret;
1267 gboolean alerted, poll;
1268 gint i, thr_ret;
1269 gint64 start;
1270 gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1272 if (nhandles == 0)
1273 return MONO_W32HANDLE_WAIT_RET_FAILED;
1275 if (nhandles == 1)
1276 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1278 alerted = FALSE;
1280 if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
1281 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: too many handles: %d",
1282 __func__, nhandles);
1284 return MONO_W32HANDLE_WAIT_RET_FAILED;
1287 for (i = 0; i < nhandles; ++i) {
1288 if (!mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT)
1289 && !mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
1291 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1292 __func__, handles [i]);
1294 return MONO_W32HANDLE_WAIT_RET_FAILED;
1297 handles_sorted [i] = handles [i];
1300 qsort (handles_sorted, nhandles, sizeof (gpointer), g_direct_equal);
1301 for (i = 1; i < nhandles; ++i) {
1302 if (handles_sorted [i - 1] == handles_sorted [i]) {
1303 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p is duplicated",
1304 __func__, handles_sorted [i]);
1306 return MONO_W32HANDLE_WAIT_RET_FAILED;
1310 poll = FALSE;
1311 for (i = 0; i < nhandles; ++i) {
1312 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS) {
1313 /* Can't wait for a process handle + another handle without polling */
1314 poll = TRUE;
1318 if (timeout != INFINITE)
1319 start = mono_msec_ticks ();
1321 for (i = 0; i < nhandles; ++i) {
1322 /* Add a reference, as we need to ensure the handle wont
1323 * disappear from under us while we're waiting in the loop
1324 * (not lock, as we don't want exclusive access here) */
1325 mono_w32handle_ref (handles [i]);
1328 for (;;) {
1329 gsize count, lowest;
1330 gboolean signalled;
1331 gint waited;
1333 count = 0;
1334 lowest = nhandles;
1336 mono_w32handle_lock_handles (handles, nhandles);
1338 for (i = 0; i < nhandles; i++) {
1339 if ((mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles [i]))
1340 || mono_w32handle_issignalled (handles [i]))
1342 count ++;
1344 if (i < lowest)
1345 lowest = i;
1349 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1351 if (signalled) {
1352 for (i = 0; i < nhandles; i++)
1353 own_if_signalled (handles [i]);
1356 mono_w32handle_unlock_handles (handles, nhandles);
1358 if (signalled) {
1359 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
1360 goto done;
1363 for (i = 0; i < nhandles; i++) {
1364 mono_w32handle_ops_prewait (handles[i]);
1366 if (mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1367 && !mono_w32handle_issignalled (handles [i]))
1369 mono_w32handle_ops_specialwait (handles [i], 0, alertable ? &alerted : NULL);
1373 thr_ret = mono_w32handle_lock_signal_mutex ();
1374 g_assert (thr_ret == 0);
1376 if (waitall) {
1377 signalled = TRUE;
1378 for (i = 0; i < nhandles; ++i) {
1379 if (!mono_w32handle_issignalled (handles [i])) {
1380 signalled = FALSE;
1381 break;
1384 } else {
1385 signalled = FALSE;
1386 for (i = 0; i < nhandles; ++i) {
1387 if (mono_w32handle_issignalled (handles [i])) {
1388 signalled = TRUE;
1389 break;
1394 waited = 0;
1396 if (!signalled) {
1397 if (timeout == INFINITE) {
1398 waited = mono_w32handle_timedwait_signal (INFINITE, poll, alertable ? &alerted : NULL);
1399 } else {
1400 gint64 elapsed;
1402 elapsed = mono_msec_ticks () - start;
1403 if (elapsed > timeout) {
1404 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1406 thr_ret = mono_w32handle_unlock_signal_mutex ();
1407 g_assert (thr_ret == 0);
1409 goto done;
1412 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1416 thr_ret = mono_w32handle_unlock_signal_mutex ();
1417 g_assert (thr_ret == 0);
1419 if (alerted) {
1420 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1421 goto done;
1424 if (waited != 0) {
1425 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1426 goto done;
1430 done:
1431 for (i = 0; i < nhandles; i++) {
1432 /* Unref everything we reffed above */
1433 mono_w32handle_unref (handles [i]);
1436 return ret;
1439 MonoW32HandleWaitRet
1440 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1442 MonoW32HandleWaitRet ret;
1443 gint64 start;
1444 gboolean alerted;
1445 gint thr_ret;
1447 alerted = FALSE;
1449 if (!mono_w32handle_test_capabilities (signal_handle, MONO_W32HANDLE_CAP_SIGNAL))
1450 return MONO_W32HANDLE_WAIT_RET_FAILED;
1451 if (!mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_WAIT))
1452 return MONO_W32HANDLE_WAIT_RET_FAILED;
1454 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1455 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle);
1456 return MONO_W32HANDLE_WAIT_RET_FAILED;
1459 thr_ret = mono_w32handle_lock_handle (wait_handle);
1460 g_assert (thr_ret == 0);
1462 mono_w32handle_ops_signal (signal_handle);
1464 if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) {
1465 if (own_if_owned (wait_handle)) {
1466 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1467 __func__, wait_handle);
1469 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1470 goto done;
1474 if (timeout != INFINITE)
1475 start = mono_msec_ticks ();
1477 for (;;) {
1478 gint waited;
1480 if (own_if_signalled (wait_handle)) {
1481 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1482 __func__, wait_handle);
1484 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1485 goto done;
1488 mono_w32handle_ops_prewait (wait_handle);
1490 if (timeout == INFINITE) {
1491 waited = mono_w32handle_timedwait_signal_handle (wait_handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1492 } else {
1493 gint64 elapsed;
1495 elapsed = mono_msec_ticks () - start;
1496 if (elapsed > timeout) {
1497 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1498 goto done;
1501 waited = mono_w32handle_timedwait_signal_handle (wait_handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1504 if (alerted) {
1505 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1506 goto done;
1509 if (waited != 0) {
1510 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1511 goto done;
1515 done:
1516 thr_ret = mono_w32handle_unlock_handle (wait_handle);
1517 g_assert (thr_ret == 0);
1519 return ret;
1522 #endif /* !defined(HOST_WIN32) */