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