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