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