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