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