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