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