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