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