Merge pull request #3938 from ntherning/UnicastIPAddressInformation.IPv4Mask-from...
[mono.git] / mono / metadata / w32handle.c
1 /*
2  * w32handle.c:  Generic and internal operations on handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *      Ludovic Henry (luhenry@microsoft.com)
7  *
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.
11  */
12
13 #include <config.h>
14
15 #if !defined(HOST_WIN32)
16
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
42
43 #include "w32handle.h"
44
45 #include "utils/atomic.h"
46 #include "utils/mono-logger-internals.h"
47 #include "utils/mono-os-mutex.h"
48 #include "utils/mono-proclib.h"
49 #include "utils/mono-threads.h"
50 #include "utils/mono-time.h"
51
52 #undef DEBUG_REFS
53
54 #define SLOT_MAX                (1024 * 16)
55
56 /* must be a power of 2 */
57 #define HANDLE_PER_SLOT (256)
58
59 #define INFINITE 0xFFFFFFFF
60
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;
69
70 static MonoW32HandleCapability handle_caps [MONO_W32HANDLE_COUNT];
71 static MonoW32HandleOps *handle_ops [MONO_W32HANDLE_COUNT];
72
73 /*
74  * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
75  * If 4M handles are not enough... Oh, well... we will crash.
76  */
77 #define SLOT_INDEX(x)   (x / HANDLE_PER_SLOT)
78 #define SLOT_OFFSET(x)  (x % HANDLE_PER_SLOT)
79
80 static MonoW32HandleBase *private_handles [SLOT_MAX];
81 static guint32 private_handles_count = 0;
82 static guint32 private_handles_slots_count = 0;
83
84 guint32 mono_w32handle_fd_reserve;
85
86 /*
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.
90  */
91 static mono_mutex_t global_signal_mutex;
92 static mono_cond_t global_signal_cond;
93
94 static mono_mutex_t scan_mutex;
95
96 static gboolean shutting_down = FALSE;
97
98 static gboolean
99 type_is_fd (MonoW32HandleType type)
100 {
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;
109         }
110 }
111
112 static gboolean
113 mono_w32handle_lookup_data (gpointer handle, MonoW32HandleBase **handle_data)
114 {
115         gsize index, offset;
116
117         g_assert (handle_data);
118
119         index = SLOT_INDEX ((gsize) handle);
120         if (index >= SLOT_MAX)
121                 return FALSE;
122         if (!private_handles [index])
123                 return FALSE;
124
125         offset = SLOT_OFFSET ((gsize) handle);
126         if (private_handles [index][offset].type == MONO_W32HANDLE_UNUSED)
127                 return FALSE;
128
129         *handle_data = &private_handles [index][offset];
130         return TRUE;
131 }
132
133 MonoW32HandleType
134 mono_w32handle_get_type (gpointer handle)
135 {
136         MonoW32HandleBase *handle_data;
137
138         if (!mono_w32handle_lookup_data (handle, &handle_data))
139                 return MONO_W32HANDLE_UNUSED;   /* An impossible type */
140
141         return handle_data->type;
142 }
143
144 void
145 mono_w32handle_set_signal_state (gpointer handle, gboolean state, gboolean broadcast)
146 {
147         MonoW32HandleBase *handle_data;
148
149         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
150                 return;
151         }
152
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
157
158         if (state == TRUE) {
159                 /* Tell everyone blocking on a single handle */
160
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.
163                  */
164                 mono_os_mutex_lock (&global_signal_mutex);
165
166                 /* This function _must_ be called with
167                  * handle->signal_mutex locked
168                  */
169                 handle_data->signalled=state;
170
171                 if (broadcast == TRUE) {
172                         mono_os_cond_broadcast (&handle_data->signal_cond);
173                 } else {
174                         mono_os_cond_signal (&handle_data->signal_cond);
175                 }
176
177                 /* Tell everyone blocking on multiple handles that something
178                  * was signalled
179                  */
180                 mono_os_cond_broadcast (&global_signal_cond);
181
182                 mono_os_mutex_unlock (&global_signal_mutex);
183         } else {
184                 handle_data->signalled=state;
185         }
186 }
187
188 gboolean
189 mono_w32handle_issignalled (gpointer handle)
190 {
191         MonoW32HandleBase *handle_data;
192
193         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
194                 return(FALSE);
195         }
196
197         return handle_data->signalled;
198 }
199
200 static void
201 mono_w32handle_lock_signal_mutex (void)
202 {
203 #ifdef DEBUG
204         g_message ("%s: lock global signal mutex", __func__);
205 #endif
206
207         mono_os_mutex_lock (&global_signal_mutex);
208 }
209
210 static void
211 mono_w32handle_unlock_signal_mutex (void)
212 {
213 #ifdef DEBUG
214         g_message ("%s: unlock global signal mutex", __func__);
215 #endif
216
217         mono_os_mutex_unlock (&global_signal_mutex);
218 }
219
220 void
221 mono_w32handle_lock_handle (gpointer handle)
222 {
223         MonoW32HandleBase *handle_data;
224
225         if (!mono_w32handle_lookup_data (handle, &handle_data))
226                 g_error ("%s: failed to lookup handle %p", __func__, handle);
227
228         mono_w32handle_ref (handle);
229
230         mono_os_mutex_lock (&handle_data->signal_mutex);
231
232         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: lock handle %p", __func__, handle);
233 }
234
235 gboolean
236 mono_w32handle_trylock_handle (gpointer handle)
237 {
238         MonoW32HandleBase *handle_data;
239         gboolean locked;
240
241         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p", __func__, handle);
242
243         if (!mono_w32handle_lookup_data (handle, &handle_data))
244                 g_error ("%s: failed to lookup handle %p", __func__, handle);
245
246         mono_w32handle_ref (handle);
247
248         locked = mono_os_mutex_trylock (&handle_data->signal_mutex) == 0;
249         if (!locked)
250                 mono_w32handle_unref (handle);
251
252         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: trylock handle %p, locked: %s", __func__, handle, locked ? "true" : "false");
253
254         return locked;
255 }
256
257 void
258 mono_w32handle_unlock_handle (gpointer handle)
259 {
260         MonoW32HandleBase *handle_data;
261
262         if (!mono_w32handle_lookup_data (handle, &handle_data))
263                 g_error ("%s: failed to lookup handle %p", __func__, handle);
264
265         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlock handle %p", __func__, handle);
266
267         mono_os_mutex_unlock (&handle_data->signal_mutex);
268
269         mono_w32handle_unref (handle);
270 }
271
272 /*
273  * wapi_init:
274  *
275  *   Initialize the io-layer.
276  */
277 void
278 mono_w32handle_init (void)
279 {
280         static gboolean initialized = FALSE;
281
282         if (initialized)
283                 return;
284
285         g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
286                   == MONO_W32HANDLE_COUNT);
287
288         /* This is needed by the code in mono_w32handle_new_internal */
289         mono_w32handle_fd_reserve = (eg_getdtablesize () + (HANDLE_PER_SLOT - 1)) & ~(HANDLE_PER_SLOT - 1);
290
291         do {
292                 /*
293                  * The entries in private_handles reserved for fds are allocated lazily to
294                  * save memory.
295                  */
296
297                 private_handles_count += HANDLE_PER_SLOT;
298                 private_handles_slots_count ++;
299         } while(mono_w32handle_fd_reserve > private_handles_count);
300
301         mono_os_mutex_init (&scan_mutex);
302
303         mono_os_cond_init (&global_signal_cond);
304         mono_os_mutex_init (&global_signal_mutex);
305
306         initialized = TRUE;
307 }
308
309 void
310 mono_w32handle_cleanup (void)
311 {
312         int i, j, k;
313
314         g_assert (!shutting_down);
315         shutting_down = TRUE;
316
317         /* Every shared handle we were using ought really to be closed
318          * by now, but to make sure just blow them all away.  The
319          * exiting finalizer thread in particular races us to the
320          * program exit and doesn't always win, so it can be left
321          * cluttering up the shared file.  Anything else left over is
322          * really a bug.
323          */
324         for(i = SLOT_INDEX (0); private_handles[i] != NULL; i++) {
325                 for(j = SLOT_OFFSET (0); j < HANDLE_PER_SLOT; j++) {
326                         MonoW32HandleBase *handle_data = &private_handles[i][j];
327                         gpointer handle = GINT_TO_POINTER (i*HANDLE_PER_SLOT+j);
328
329                         for(k = handle_data->ref; k > 0; k--) {
330                                 mono_w32handle_unref (handle);
331                         }
332                 }
333         }
334
335         for (i = 0; i < SLOT_MAX; ++i)
336                 g_free (private_handles [i]);
337 }
338
339 static void mono_w32handle_init_handle (MonoW32HandleBase *handle,
340                                MonoW32HandleType type, gpointer handle_specific)
341 {
342         g_assert (handle->ref == 0);
343
344         handle->type = type;
345         handle->signalled = FALSE;
346         handle->ref = 1;
347
348         mono_os_cond_init (&handle->signal_cond);
349         mono_os_mutex_init (&handle->signal_mutex);
350
351         if (handle_specific)
352                 handle->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
353 }
354
355 /*
356  * mono_w32handle_new_internal:
357  * @type: Init handle to this type
358  *
359  * Search for a free handle and initialize it. Return the handle on
360  * success and 0 on failure.  This is only called from
361  * mono_w32handle_new, and scan_mutex must be held.
362  */
363 static guint32 mono_w32handle_new_internal (MonoW32HandleType type,
364                                           gpointer handle_specific)
365 {
366         guint32 i, k, count;
367         static guint32 last = 0;
368         gboolean retry = FALSE;
369         
370         /* A linear scan should be fast enough.  Start from the last
371          * allocation, assuming that handles are allocated more often
372          * than they're freed. Leave the space reserved for file
373          * descriptors
374          */
375
376         if (last < mono_w32handle_fd_reserve) {
377                 last = mono_w32handle_fd_reserve;
378         } else {
379                 retry = TRUE;
380         }
381
382 again:
383         count = last;
384         for(i = SLOT_INDEX (count); i < private_handles_slots_count; i++) {
385                 if (private_handles [i]) {
386                         for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
387                                 MonoW32HandleBase *handle = &private_handles [i][k];
388
389                                 if(handle->type == MONO_W32HANDLE_UNUSED) {
390                                         last = count + 1;
391
392                                         mono_w32handle_init_handle (handle, type, handle_specific);
393                                         return (count);
394                                 }
395                                 count++;
396                         }
397                 }
398         }
399
400         if(retry && last > mono_w32handle_fd_reserve) {
401                 /* Try again from the beginning */
402                 last = mono_w32handle_fd_reserve;
403                 goto again;
404         }
405
406         /* Will need to expand the array.  The caller will sort it out */
407
408         return(0);
409 }
410
411 gpointer
412 mono_w32handle_new (MonoW32HandleType type, gpointer handle_specific)
413 {
414         guint32 handle_idx = 0;
415         gpointer handle;
416
417         g_assert (!shutting_down);
418
419         g_assert(!type_is_fd(type));
420
421         mono_os_mutex_lock (&scan_mutex);
422
423         while ((handle_idx = mono_w32handle_new_internal (type, handle_specific)) == 0) {
424                 /* Try and expand the array, and have another go */
425                 int idx = SLOT_INDEX (private_handles_count);
426                 if (idx >= SLOT_MAX) {
427                         break;
428                 }
429
430                 private_handles [idx] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
431
432                 private_handles_count += HANDLE_PER_SLOT;
433                 private_handles_slots_count ++;
434         }
435
436         mono_os_mutex_unlock (&scan_mutex);
437
438         if (handle_idx == 0) {
439                 /* We ran out of slots */
440                 handle = INVALID_HANDLE_VALUE;
441                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type));
442                 goto done;
443         }
444
445         /* Make sure we left the space for fd mappings */
446         g_assert (handle_idx >= mono_w32handle_fd_reserve);
447
448         handle = GUINT_TO_POINTER (handle_idx);
449
450         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
451
452 done:
453         return(handle);
454 }
455
456 gpointer mono_w32handle_new_fd (MonoW32HandleType type, int fd,
457                               gpointer handle_specific)
458 {
459         MonoW32HandleBase *handle_data;
460         int fd_index, fd_offset;
461
462         g_assert (!shutting_down);
463
464         g_assert(type_is_fd(type));
465
466         if (fd >= mono_w32handle_fd_reserve) {
467                 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));
468
469                 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
470         }
471
472         fd_index = SLOT_INDEX (fd);
473         fd_offset = SLOT_OFFSET (fd);
474
475         /* Initialize the array entries on demand */
476         if (!private_handles [fd_index]) {
477                 mono_os_mutex_lock (&scan_mutex);
478
479                 if (!private_handles [fd_index])
480                         private_handles [fd_index] = g_new0 (MonoW32HandleBase, HANDLE_PER_SLOT);
481
482                 mono_os_mutex_unlock (&scan_mutex);
483         }
484
485         handle_data = &private_handles [fd_index][fd_offset];
486
487         if (handle_data->type != MONO_W32HANDLE_UNUSED) {
488                 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));
489                 /* FIXME: clean up this handle?  We can't do anything
490                  * with the fd, cos thats the new one
491                  */
492                 return(GUINT_TO_POINTER (INVALID_HANDLE_VALUE));
493         }
494
495         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), GUINT_TO_POINTER(fd));
496
497         mono_w32handle_init_handle (handle_data, type, handle_specific);
498
499         return(GUINT_TO_POINTER(fd));
500 }
501
502 gboolean
503 mono_w32handle_lookup (gpointer handle, MonoW32HandleType type,
504                               gpointer *handle_specific)
505 {
506         MonoW32HandleBase *handle_data;
507
508         g_assert (handle_specific);
509
510         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
511                 return(FALSE);
512         }
513
514         if (handle_data->type != type) {
515                 return(FALSE);
516         }
517
518         *handle_specific = handle_data->specific;
519
520         return(TRUE);
521 }
522
523 static gboolean
524 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data);
525
526 static gboolean
527 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guint minimum);
528
529 void
530 mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data)
531 {
532         guint32 i, k;
533
534         mono_os_mutex_lock (&scan_mutex);
535
536         for (i = SLOT_INDEX (0); i < private_handles_slots_count; i++) {
537                 if (!private_handles [i])
538                         continue;
539                 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
540                         MonoW32HandleBase *handle_data = NULL;
541                         gpointer handle;
542                         gboolean destroy, finished;
543
544                         handle_data = &private_handles [i][k];
545                         if (handle_data->type == MONO_W32HANDLE_UNUSED)
546                                 continue;
547
548                         handle = GUINT_TO_POINTER (i * HANDLE_PER_SLOT + k);
549
550                         if (!mono_w32handle_ref_core (handle, handle_data)) {
551                                 /* we are racing with mono_w32handle_unref:
552                                  *  the handle ref has been decremented, but it
553                                  *  hasn't yet been destroyed. */
554                                 continue;
555                         }
556
557                         finished = on_each (handle, handle_data->specific, user_data);
558
559                         /* we do not want to have to destroy the handle here,
560                          * as it would means the ref/unref are unbalanced */
561                         destroy = mono_w32handle_unref_core (handle, handle_data, 2);
562                         g_assert (!destroy);
563
564                         if (finished)
565                                 goto done;
566                 }
567         }
568
569 done:
570         mono_os_mutex_unlock (&scan_mutex);
571 }
572
573 static gboolean
574 mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data)
575 {
576         guint old, new;
577
578         do {
579                 old = handle_data->ref;
580                 if (old == 0)
581                         return FALSE;
582
583                 new = old + 1;
584         } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
585
586         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: ref %s handle %p, ref: %d -> %d",
587                 __func__, mono_w32handle_ops_typename (handle_data->type), handle, old, new);
588
589         return TRUE;
590 }
591
592 static gboolean
593 mono_w32handle_unref_core (gpointer handle, MonoW32HandleBase *handle_data, guint minimum)
594 {
595         MonoW32HandleType type;
596         guint old, new;
597
598         type = handle_data->type;
599
600         do {
601                 old = handle_data->ref;
602                 if (!(old >= minimum))
603                         g_error ("%s: handle %p has ref %d, it should be >= %d", __func__, handle, old, minimum);
604
605                 new = old - 1;
606         } while (InterlockedCompareExchange ((gint32*) &handle_data->ref, new, old) != old);
607
608         /* handle_data might contain invalid data from now on, if
609          * another thread is unref'ing this handle at the same time */
610
611         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unref %s handle %p, ref: %d -> %d destroy: %s",
612                 __func__, mono_w32handle_ops_typename (type), handle, old, new, new == 0 ? "true" : "false");
613
614         return new == 0;
615 }
616
617 void mono_w32handle_ref (gpointer handle)
618 {
619         MonoW32HandleBase *handle_data;
620
621         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
622                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to ref handle %p, unknown handle", __func__, handle);
623                 return;
624         }
625
626         if (!mono_w32handle_ref_core (handle, handle_data))
627                 g_error ("%s: failed to ref handle %p", __func__, handle);
628 }
629
630 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer);
631
632 /* The handle must not be locked on entry to this function */
633 void
634 mono_w32handle_unref (gpointer handle)
635 {
636         MonoW32HandleBase *handle_data;
637         gboolean destroy;
638
639         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
640                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: failed to unref handle %p, unknown handle",
641                         __func__, handle);
642                 return;
643         }
644
645         destroy = mono_w32handle_unref_core (handle, handle_data, 1);
646
647         if (destroy) {
648                 /* Need to copy the handle info, reset the slot in the
649                  * array, and _only then_ call the close function to
650                  * avoid race conditions (eg file descriptors being
651                  * closed, and another file being opened getting the
652                  * same fd racing the memset())
653                  */
654                 MonoW32HandleType type;
655                 gpointer handle_specific;
656                 void (*close_func)(gpointer, gpointer);
657
658                 type = handle_data->type;
659                 handle_specific = handle_data->specific;
660
661                 mono_os_mutex_lock (&scan_mutex);
662
663                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
664
665                 mono_os_mutex_destroy (&handle_data->signal_mutex);
666                 mono_os_cond_destroy (&handle_data->signal_cond);
667
668                 memset (handle_data, 0, sizeof (MonoW32HandleBase));
669
670                 mono_os_mutex_unlock (&scan_mutex);
671
672                 close_func = _wapi_handle_ops_get_close_func (type);
673                 if (close_func != NULL) {
674                         close_func (handle, handle_specific);
675                 }
676
677                 g_free (handle_specific);
678         }
679 }
680
681 void
682 mono_w32handle_register_ops (MonoW32HandleType type, MonoW32HandleOps *ops)
683 {
684         handle_ops [type] = ops;
685 }
686
687 void mono_w32handle_register_capabilities (MonoW32HandleType type,
688                                          MonoW32HandleCapability caps)
689 {
690         handle_caps[type] = caps;
691 }
692
693 gboolean mono_w32handle_test_capabilities (gpointer handle,
694                                          MonoW32HandleCapability caps)
695 {
696         MonoW32HandleBase *handle_data;
697         MonoW32HandleType type;
698
699         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
700                 return(FALSE);
701         }
702
703         type = handle_data->type;
704
705         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
706                    handle_caps[type], caps, handle_caps[type] & caps);
707
708         return((handle_caps[type] & caps) != 0);
709 }
710
711 static void (*_wapi_handle_ops_get_close_func (MonoW32HandleType type))(gpointer, gpointer)
712 {
713         if (handle_ops[type] != NULL &&
714             handle_ops[type]->close != NULL) {
715                 return (handle_ops[type]->close);
716         }
717
718         return (NULL);
719 }
720
721 void mono_w32handle_ops_close (gpointer handle, gpointer data)
722 {
723         MonoW32HandleBase *handle_data;
724         MonoW32HandleType type;
725
726         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
727                 return;
728         }
729
730         type = handle_data->type;
731
732         if (handle_ops[type] != NULL &&
733             handle_ops[type]->close != NULL) {
734                 handle_ops[type]->close (handle, data);
735         }
736 }
737
738 void mono_w32handle_ops_details (MonoW32HandleType type, gpointer data)
739 {
740         if (handle_ops[type] != NULL &&
741             handle_ops[type]->details != NULL) {
742                 handle_ops[type]->details (data);
743         }
744 }
745
746 const gchar* mono_w32handle_ops_typename (MonoW32HandleType type)
747 {
748         g_assert (handle_ops [type]);
749         g_assert (handle_ops [type]->typename);
750         return handle_ops [type]->typename ();
751 }
752
753 gsize mono_w32handle_ops_typesize (MonoW32HandleType type)
754 {
755         g_assert (handle_ops [type]);
756         g_assert (handle_ops [type]->typesize);
757         return handle_ops [type]->typesize ();
758 }
759
760 void mono_w32handle_ops_signal (gpointer handle)
761 {
762         MonoW32HandleBase *handle_data;
763         MonoW32HandleType type;
764
765         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
766                 return;
767         }
768
769         type = handle_data->type;
770
771         if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
772                 handle_ops[type]->signal (handle);
773         }
774 }
775
776 gboolean mono_w32handle_ops_own (gpointer handle, guint32 *statuscode)
777 {
778         MonoW32HandleBase *handle_data;
779         MonoW32HandleType type;
780
781         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
782                 return(FALSE);
783         }
784
785         type = handle_data->type;
786
787         if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
788                 return(handle_ops[type]->own_handle (handle, statuscode));
789         } else {
790                 return(FALSE);
791         }
792 }
793
794 gboolean mono_w32handle_ops_isowned (gpointer handle)
795 {
796         MonoW32HandleBase *handle_data;
797         MonoW32HandleType type;
798
799         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
800                 return(FALSE);
801         }
802
803         type = handle_data->type;
804
805         if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
806                 return(handle_ops[type]->is_owned (handle));
807         } else {
808                 return(FALSE);
809         }
810 }
811
812 MonoW32HandleWaitRet
813 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted)
814 {
815         MonoW32HandleBase *handle_data;
816         MonoW32HandleType type;
817
818         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
819                 return(WAIT_FAILED);
820         }
821
822         type = handle_data->type;
823
824         if (handle_ops[type] != NULL &&
825             handle_ops[type]->special_wait != NULL) {
826                 return(handle_ops[type]->special_wait (handle, timeout, alerted));
827         } else {
828                 return(WAIT_FAILED);
829         }
830 }
831
832 void mono_w32handle_ops_prewait (gpointer handle)
833 {
834         MonoW32HandleBase *handle_data;
835         MonoW32HandleType type;
836
837         if (!mono_w32handle_lookup_data (handle, &handle_data)) {
838                 return;
839         }
840
841         type = handle_data->type;
842
843         if (handle_ops[type] != NULL &&
844             handle_ops[type]->prewait != NULL) {
845                 handle_ops[type]->prewait (handle);
846         }
847 }
848
849 static void
850 spin (guint32 ms)
851 {
852         struct timespec sleepytime;
853
854         g_assert (ms < 1000);
855
856         sleepytime.tv_sec = 0;
857         sleepytime.tv_nsec = ms * 1000000;
858         nanosleep (&sleepytime, NULL);
859 }
860
861 static void
862 mono_w32handle_lock_handles (gpointer *handles, gsize numhandles)
863 {
864         guint32 i, iter=0;
865
866         /* Lock all the handles, with backoff */
867 again:
868         for(i=0; i<numhandles; i++) {
869                 gpointer handle = handles[i];
870
871                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempting to lock %p", __func__, handle);
872
873                 if (!mono_w32handle_trylock_handle (handle)) {
874                         /* Bummer */
875
876                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: attempt failed for %p: %s", __func__,
877                                    handle);
878
879                         while (i--) {
880                                 handle = handles[i];
881
882                                 mono_w32handle_unlock_handle (handle);
883                         }
884
885                         /* If iter ever reaches 100 the nanosleep will
886                          * return EINVAL immediately, but we have a
887                          * design flaw if that happens.
888                          */
889                         iter++;
890                         if(iter==100) {
891                                 g_warning ("%s: iteration overflow!",
892                                            __func__);
893                                 iter=1;
894                         }
895
896                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Backing off for %d ms", __func__,
897                                    iter*10);
898                         spin (10 * iter);
899
900                         goto again;
901                 }
902         }
903
904         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: Locked all handles", __func__);
905 }
906
907 static void
908 mono_w32handle_unlock_handles (gpointer *handles, gsize numhandles)
909 {
910         guint32 i;
911
912         for(i=0; i<numhandles; i++) {
913                 gpointer handle = handles[i];
914
915                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: unlocking handle %p", __func__, handle);
916
917                 mono_w32handle_unlock_handle (handle);
918         }
919 }
920
921 static int
922 mono_w32handle_timedwait_signal_naked (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
923 {
924         int res;
925
926         if (!poll) {
927                 res = mono_os_cond_timedwait (cond, mutex, timeout);
928         } else {
929                 /* This is needed when waiting for process handles */
930                 if (!alerted) {
931                         /*
932                          * pthread_cond_(timed)wait() can return 0 even if the condition was not
933                          * signalled.  This happens at least on Darwin.  We surface this, i.e., we
934                          * get spurious wake-ups.
935                          *
936                          * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
937                          */
938                         res = mono_os_cond_timedwait (cond, mutex, timeout);
939                 } else {
940                         if (timeout < 100) {
941                                 /* Real timeout is less than 100ms time */
942                                 res = mono_os_cond_timedwait (cond, mutex, timeout);
943                         } else {
944                                 res = mono_os_cond_timedwait (cond, mutex, 100);
945
946                                 /* Mask the fake timeout, this will cause
947                                  * another poll if the cond was not really signaled
948                                  */
949                                 if (res == -1)
950                                         res = 0;
951                         }
952                 }
953         }
954
955         return res;
956 }
957
958 static void
959 signal_global (gpointer unused)
960 {
961         /* If we reach here, then interrupt token is set to the flag value, which
962          * means that the target thread is either
963          * - before the first CAS in timedwait, which means it won't enter the wait.
964          * - it is after the first CAS, so it is already waiting, or it will enter
965          *    the wait, and it will be interrupted by the broadcast. */
966         mono_os_mutex_lock (&global_signal_mutex);
967         mono_os_cond_broadcast (&global_signal_cond);
968         mono_os_mutex_unlock (&global_signal_mutex);
969 }
970
971 static int
972 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
973 {
974         int res;
975
976         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for global", __func__);
977
978         if (alerted)
979                 *alerted = FALSE;
980
981         if (alerted) {
982                 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
983                 if (*alerted)
984                         return 0;
985         }
986
987         res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
988
989         if (alerted)
990                 mono_thread_info_uninstall_interrupt (alerted);
991
992         return res;
993 }
994
995 static void
996 signal_handle_and_unref (gpointer handle)
997 {
998         MonoW32HandleBase *handle_data;
999         mono_cond_t *cond;
1000         mono_mutex_t *mutex;
1001
1002         if (!mono_w32handle_lookup_data (handle, &handle_data))
1003                 g_error ("cannot signal unknown handle %p", handle);
1004
1005         /* If we reach here, then interrupt token is set to the flag value, which
1006          * means that the target thread is either
1007          * - before the first CAS in timedwait, which means it won't enter the wait.
1008          * - it is after the first CAS, so it is already waiting, or it will enter
1009          *    the wait, and it will be interrupted by the broadcast. */
1010         cond = &handle_data->signal_cond;
1011         mutex = &handle_data->signal_mutex;
1012
1013         mono_os_mutex_lock (mutex);
1014         mono_os_cond_broadcast (cond);
1015         mono_os_mutex_unlock (mutex);
1016
1017         mono_w32handle_unref (handle);
1018 }
1019
1020 static int
1021 mono_w32handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1022 {
1023         MonoW32HandleBase *handle_data;
1024         int res;
1025
1026         if (!mono_w32handle_lookup_data (handle, &handle_data))
1027                 g_error ("cannot wait on unknown handle %p", handle);
1028
1029         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: waiting for %p (type %s)", __func__, handle,
1030                    mono_w32handle_ops_typename (mono_w32handle_get_type (handle)));
1031
1032         if (alerted)
1033                 *alerted = FALSE;
1034
1035         if (alerted) {
1036                 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1037                 if (*alerted)
1038                         return 0;
1039                 mono_w32handle_ref (handle);
1040         }
1041
1042         res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
1043
1044         if (alerted) {
1045                 mono_thread_info_uninstall_interrupt (alerted);
1046                 if (!*alerted) {
1047                         /* if it is alerted, then the handle is unref in the interrupt callback */
1048                         mono_w32handle_unref (handle);
1049                 }
1050         }
1051
1052         return res;
1053 }
1054
1055 static gboolean
1056 dump_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
1057 {
1058         MonoW32HandleBase *handle_data;
1059
1060         if (!mono_w32handle_lookup_data (handle, &handle_data))
1061                 g_error ("cannot dump unknown handle %p", handle);
1062
1063         g_print ("%p [%7s] signalled: %5s ref: %3d ",
1064                 handle, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref);
1065         mono_w32handle_ops_details (handle_data->type, handle_data->specific);
1066         g_print ("\n");
1067
1068         return FALSE;
1069 }
1070
1071 void mono_w32handle_dump (void)
1072 {
1073         mono_w32handle_foreach (dump_callback, NULL);
1074 }
1075
1076 static gboolean
1077 own_if_signalled (gpointer handle, guint32 *statuscode)
1078 {
1079         if (!mono_w32handle_issignalled (handle))
1080                 return FALSE;
1081
1082         *statuscode = WAIT_OBJECT_0;
1083         mono_w32handle_ops_own (handle, statuscode);
1084         return TRUE;
1085 }
1086
1087 static gboolean
1088 own_if_owned( gpointer handle, guint32 *statuscode)
1089 {
1090         if (!mono_w32handle_ops_isowned (handle))
1091                 return FALSE;
1092
1093         *statuscode = WAIT_OBJECT_0;
1094         mono_w32handle_ops_own (handle, statuscode);
1095         return TRUE;
1096 }
1097
1098 MonoW32HandleWaitRet
1099 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
1100 {
1101         MonoW32HandleWaitRet ret;
1102         gboolean alerted;
1103         gint64 start;
1104         guint32 statuscode = 0;
1105
1106         alerted = FALSE;
1107
1108         if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1109                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p has special wait",
1110                         __func__, handle);
1111
1112                 return mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL);
1113         }
1114
1115         if (!mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_WAIT)) {
1116                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1117                         __func__, handle);
1118
1119                 return MONO_W32HANDLE_WAIT_RET_FAILED;
1120         }
1121
1122         mono_w32handle_lock_handle (handle);
1123
1124         if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_OWN)) {
1125                 if (own_if_owned (handle, &statuscode)) {
1126                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1127                                 __func__, handle);
1128
1129                         ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1130                         goto done;
1131                 }
1132         }
1133
1134         if (timeout != INFINITE)
1135                 start = mono_msec_ticks ();
1136
1137         for (;;) {
1138                 gint waited;
1139
1140                 if (own_if_signalled (handle, &statuscode)) {
1141                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1142                                 __func__, handle);
1143
1144                         ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1145                         goto done;
1146                 }
1147
1148                 mono_w32handle_ops_prewait (handle);
1149
1150                 if (timeout == INFINITE) {
1151                         waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1152                 } else {
1153                         gint64 elapsed;
1154
1155                         elapsed = mono_msec_ticks () - start;
1156                         if (elapsed > timeout) {
1157                                 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1158                                 goto done;
1159                         }
1160
1161                         waited = mono_w32handle_timedwait_signal_handle (handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1162                 }
1163
1164                 if (alerted) {
1165                         ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1166                         goto done;
1167                 }
1168
1169                 if (waited != 0) {
1170                         ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1171                         goto done;
1172                 }
1173         }
1174
1175 done:
1176         mono_w32handle_unlock_handle (handle);
1177
1178         return ret;
1179 }
1180
1181 MonoW32HandleWaitRet
1182 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
1183 {
1184         MonoW32HandleWaitRet ret;
1185         gboolean alerted, poll;
1186         gint i;
1187         gint64 start;
1188         gpointer handles_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1189         guint32 statuscodes [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
1190
1191         if (nhandles == 0)
1192                 return MONO_W32HANDLE_WAIT_RET_FAILED;
1193
1194         if (nhandles == 1)
1195                 return mono_w32handle_wait_one (handles [0], timeout, alertable);
1196
1197         alerted = FALSE;
1198
1199         if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
1200                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: too many handles: %zd",
1201                         __func__, nhandles);
1202
1203                 return MONO_W32HANDLE_WAIT_RET_FAILED;
1204         }
1205
1206         for (i = 0; i < nhandles; ++i) {
1207                 if (!mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT)
1208                          && !mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
1209                 {
1210                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p can't be waited for",
1211                                    __func__, handles [i]);
1212
1213                         return MONO_W32HANDLE_WAIT_RET_FAILED;
1214                 }
1215
1216                 handles_sorted [i] = handles [i];
1217         }
1218
1219         qsort (handles_sorted, nhandles, sizeof (gpointer), g_direct_equal);
1220         for (i = 1; i < nhandles; ++i) {
1221                 if (handles_sorted [i - 1] == handles_sorted [i]) {
1222                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p is duplicated",
1223                                 __func__, handles_sorted [i]);
1224
1225                         return MONO_W32HANDLE_WAIT_RET_FAILED;
1226                 }
1227         }
1228
1229         poll = FALSE;
1230         for (i = 0; i < nhandles; ++i) {
1231                 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS) {
1232                         /* Can't wait for a process handle + another handle without polling */
1233                         poll = TRUE;
1234                 }
1235         }
1236
1237         if (timeout != INFINITE)
1238                 start = mono_msec_ticks ();
1239
1240         for (i = 0; i < nhandles; ++i) {
1241                 /* Add a reference, as we need to ensure the handle wont
1242                  * disappear from under us while we're waiting in the loop
1243                  * (not lock, as we don't want exclusive access here) */
1244                 mono_w32handle_ref (handles [i]);
1245         }
1246
1247         for (;;) {
1248                 gsize count, lowest;
1249                 gboolean signalled;
1250                 gint waited;
1251
1252                 count = 0;
1253                 lowest = nhandles;
1254
1255                 mono_w32handle_lock_handles (handles, nhandles);
1256
1257                 for (i = 0; i < nhandles; i++) {
1258                         if ((mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles [i]))
1259                                  || mono_w32handle_issignalled (handles [i]))
1260                         {
1261                                 count ++;
1262
1263                                 if (i < lowest)
1264                                         lowest = i;
1265                         }
1266                 }
1267
1268                 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1269
1270                 if (signalled) {
1271                         for (i = 0; i < nhandles; i++)
1272                                 own_if_signalled (handles [i], &statuscodes [i]);
1273                 }
1274
1275                 mono_w32handle_unlock_handles (handles, nhandles);
1276
1277                 if (signalled) {
1278                         ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
1279                         for (i = lowest; i < nhandles; i++) {
1280                                 if (statuscodes [i] == WAIT_ABANDONED_0) {
1281                                         ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest;
1282                                         break;
1283                                 }
1284                         }
1285                         goto done;
1286                 }
1287
1288                 for (i = 0; i < nhandles; i++) {
1289                         mono_w32handle_ops_prewait (handles[i]);
1290
1291                         if (mono_w32handle_test_capabilities (handles [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1292                                  && !mono_w32handle_issignalled (handles [i]))
1293                         {
1294                                 mono_w32handle_ops_specialwait (handles [i], 0, alertable ? &alerted : NULL);
1295                         }
1296                 }
1297
1298                 mono_w32handle_lock_signal_mutex ();
1299
1300                 if (waitall) {
1301                         signalled = TRUE;
1302                         for (i = 0; i < nhandles; ++i) {
1303                                 if (!mono_w32handle_issignalled (handles [i])) {
1304                                         signalled = FALSE;
1305                                         break;
1306                                 }
1307                         }
1308                 } else {
1309                         signalled = FALSE;
1310                         for (i = 0; i < nhandles; ++i) {
1311                                 if (mono_w32handle_issignalled (handles [i])) {
1312                                         signalled = TRUE;
1313                                         break;
1314                                 }
1315                         }
1316                 }
1317
1318                 waited = 0;
1319
1320                 if (!signalled) {
1321                         if (timeout == INFINITE) {
1322                                 waited = mono_w32handle_timedwait_signal (INFINITE, poll, alertable ? &alerted : NULL);
1323                         } else {
1324                                 gint64 elapsed;
1325
1326                                 elapsed = mono_msec_ticks () - start;
1327                                 if (elapsed > timeout) {
1328                                         ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1329
1330                                         mono_w32handle_unlock_signal_mutex ();
1331
1332                                         goto done;
1333                                 }
1334
1335                                 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1336                         }
1337                 }
1338
1339                 mono_w32handle_unlock_signal_mutex ();
1340
1341                 if (alerted) {
1342                         ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1343                         goto done;
1344                 }
1345
1346                 if (waited != 0) {
1347                         ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1348                         goto done;
1349                 }
1350         }
1351
1352 done:
1353         for (i = 0; i < nhandles; i++) {
1354                 /* Unref everything we reffed above */
1355                 mono_w32handle_unref (handles [i]);
1356         }
1357
1358         return ret;
1359 }
1360
1361 MonoW32HandleWaitRet
1362 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1363 {
1364         MonoW32HandleWaitRet ret;
1365         gint64 start;
1366         gboolean alerted;
1367         guint32 statuscode = 0;
1368         gpointer handles [2];
1369
1370         alerted = FALSE;
1371
1372         if (!mono_w32handle_test_capabilities (signal_handle, MONO_W32HANDLE_CAP_SIGNAL))
1373                 return MONO_W32HANDLE_WAIT_RET_FAILED;
1374         if (!mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_WAIT))
1375                 return MONO_W32HANDLE_WAIT_RET_FAILED;
1376
1377         if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1378                 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle);
1379                 return MONO_W32HANDLE_WAIT_RET_FAILED;
1380         }
1381
1382         handles [0] = wait_handle;
1383         handles [1] = signal_handle;
1384
1385         mono_w32handle_lock_handles (handles, 2);
1386
1387         mono_w32handle_ops_signal (signal_handle);
1388
1389         mono_w32handle_unlock_handle (signal_handle);
1390
1391         if (mono_w32handle_test_capabilities (wait_handle, MONO_W32HANDLE_CAP_OWN)) {
1392                 if (own_if_owned (wait_handle, &statuscode)) {
1393                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p already owned",
1394                                 __func__, wait_handle);
1395
1396                         ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1397                         goto done;
1398                 }
1399         }
1400
1401         if (timeout != INFINITE)
1402                 start = mono_msec_ticks ();
1403
1404         for (;;) {
1405                 gint waited;
1406
1407                 if (own_if_signalled (wait_handle, &statuscode)) {
1408                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p signalled",
1409                                 __func__, wait_handle);
1410
1411                         ret = statuscode == WAIT_ABANDONED_0 ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1412                         goto done;
1413                 }
1414
1415                 mono_w32handle_ops_prewait (wait_handle);
1416
1417                 if (timeout == INFINITE) {
1418                         waited = mono_w32handle_timedwait_signal_handle (wait_handle, INFINITE, FALSE, alertable ? &alerted : NULL);
1419                 } else {
1420                         gint64 elapsed;
1421
1422                         elapsed = mono_msec_ticks () - start;
1423                         if (elapsed > timeout) {
1424                                 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1425                                 goto done;
1426                         }
1427
1428                         waited = mono_w32handle_timedwait_signal_handle (wait_handle, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1429                 }
1430
1431                 if (alerted) {
1432                         ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1433                         goto done;
1434                 }
1435
1436                 if (waited != 0) {
1437                         ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1438                         goto done;
1439                 }
1440         }
1441
1442 done:
1443         mono_w32handle_unlock_handle (wait_handle);
1444
1445         return ret;
1446 }
1447
1448 #endif /* !defined(HOST_WIN32) */