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