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