Replace DEBUG messages with calls to mono_trace
[mono.git] / mono / io-layer / wait.c
1 /*
2  * wait.c:  wait for handles to become signalled
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002-2006 Novell, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <string.h>
13 #include <errno.h>
14
15 #include <mono/io-layer/wapi.h>
16 #include <mono/io-layer/handles-private.h>
17 #include <mono/io-layer/wapi-private.h>
18 #include <mono/io-layer/io-trace.h>
19 #include <mono/utils/mono-logger-internals.h>
20
21 static gboolean own_if_signalled(gpointer handle)
22 {
23         gboolean ret = FALSE;
24         
25         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
26                 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
27                         return (FALSE);
28                 }
29         }
30         
31         if (_wapi_handle_issignalled (handle)) {
32                 _wapi_handle_ops_own (handle);
33                 ret = TRUE;
34         }
35
36         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
37                 _wapi_handle_unlock_shared_handles ();
38         }
39
40         return(ret);
41 }
42
43 static gboolean own_if_owned(gpointer handle)
44 {
45         gboolean ret = FALSE;
46         
47         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
48                 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
49                         return (FALSE);
50                 }
51         }
52         
53         if (_wapi_handle_ops_isowned (handle)) {
54                 _wapi_handle_ops_own (handle);
55                 ret = TRUE;
56         }
57
58         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
59                 _wapi_handle_unlock_shared_handles ();
60         }
61
62         return(ret);
63 }
64
65 /**
66  * WaitForSingleObjectEx:
67  * @handle: an object to wait for
68  * @timeout: the maximum time in milliseconds to wait for
69  * @alertable: if TRUE, the wait can be interrupted by an APC call
70  *
71  * This function returns when either @handle is signalled, or @timeout
72  * ms elapses.  If @timeout is zero, the object's state is tested and
73  * the function returns immediately.  If @timeout is %INFINITE, the
74  * function waits forever.
75  *
76  * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
77  * released by the owning thread when it exited.  Ownership of the
78  * mutex object is granted to the calling thread and the mutex is set
79  * to nonsignalled.  %WAIT_OBJECT_0 - The state of @handle is
80  * signalled.  %WAIT_TIMEOUT - The @timeout interval elapsed and
81  * @handle's state is still not signalled.  %WAIT_FAILED - an error
82  * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
83  */
84 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
85                               gboolean alertable)
86 {
87         guint32 ret, waited;
88         int thr_ret;
89         gboolean apc_pending = FALSE;
90         gpointer current_thread = wapi_get_current_thread_handle ();
91         
92         if (current_thread == NULL) {
93                 SetLastError (ERROR_INVALID_HANDLE);
94                 return(WAIT_FAILED);
95         }
96
97         if (handle == _WAPI_THREAD_CURRENT) {
98                 handle = wapi_get_current_thread_handle ();
99                 if (handle == NULL) {
100                         SetLastError (ERROR_INVALID_HANDLE);
101                         return(WAIT_FAILED);
102                 }
103         }
104
105         if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
106                 SetLastError (ERROR_INVALID_HANDLE);
107                 return(WAIT_FAILED);
108         }
109         
110         if (_wapi_handle_test_capabilities (handle,
111                                             WAPI_HANDLE_CAP_WAIT) == FALSE) {
112                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p can't be waited for", __func__,
113                            handle);
114
115                 return(WAIT_FAILED);
116         }
117
118         _wapi_handle_ops_prewait (handle);
119         
120         if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
121                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p has special wait", __func__, handle);
122
123                 ret = _wapi_handle_ops_special_wait (handle, timeout, alertable);
124         
125                 if (alertable && _wapi_thread_cur_apc_pending ())
126                         ret = WAIT_IO_COMPLETION;
127
128                 return ret;
129         }
130         
131         
132         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, handle);
133
134         thr_ret = _wapi_handle_lock_handle (handle);
135         g_assert (thr_ret == 0);
136
137         if (_wapi_handle_test_capabilities (handle,
138                                             WAPI_HANDLE_CAP_OWN) == TRUE) {
139                 if (own_if_owned (handle) == TRUE) {
140                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__,
141                                    handle);
142                         ret = WAIT_OBJECT_0;
143                         goto done;
144                 }
145         }
146
147         if (own_if_signalled (handle) == TRUE) {
148                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__,
149                            handle);
150
151                 ret=WAIT_OBJECT_0;
152                 goto done;
153         }
154
155         if (timeout == 0) {
156                 ret = WAIT_TIMEOUT;
157                 goto done;
158         }
159         
160         do {
161                 /* Check before waiting on the condition, just in case
162                  */
163                 _wapi_handle_ops_prewait (handle);
164
165                 if (own_if_signalled (handle)) {
166                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
167                                    handle);
168
169                         ret = WAIT_OBJECT_0;
170                         goto done;
171                 }
172
173                 waited = _wapi_handle_timedwait_signal_handle (handle, timeout, alertable, FALSE, &apc_pending);
174
175                 if(waited==0 && !apc_pending) {
176                         /* Condition was signalled, so hopefully
177                          * handle is signalled now.  (It might not be
178                          * if someone else got in before us.)
179                          */
180                         if (own_if_signalled (handle)) {
181                                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
182                                            handle);
183
184                                 ret=WAIT_OBJECT_0;
185                                 goto done;
186                         }
187                 
188                         /* Better luck next time */
189                 }
190         } while(waited == 0 && !apc_pending);
191
192         /* Timeout or other error */
193         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, handle,
194                    strerror (waited));
195
196         ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
197
198 done:
199
200         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, handle);
201         
202         thr_ret = _wapi_handle_unlock_handle (handle);
203         g_assert (thr_ret == 0);
204
205         return(ret);
206 }
207
208 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
209 {
210         return WaitForSingleObjectEx (handle, timeout, FALSE);
211 }
212
213
214 /**
215  * SignalObjectAndWait:
216  * @signal_handle: An object to signal
217  * @wait: An object to wait for
218  * @timeout: The maximum time in milliseconds to wait for
219  * @alertable: Specifies whether the function returnes when the system
220  * queues an I/O completion routine or an APC for the calling thread.
221  *
222  * Atomically signals @signal and waits for @wait to become signalled,
223  * or @timeout ms elapses.  If @timeout is zero, the object's state is
224  * tested and the function returns immediately.  If @timeout is
225  * %INFINITE, the function waits forever.
226  *
227  * @signal can be a semaphore, mutex or event object.
228  *
229  * If @alertable is %TRUE and the system queues an I/O completion
230  * routine or an APC for the calling thread, the function returns and
231  * the thread calls the completion routine or APC function.  If
232  * %FALSE, the function does not return, and the thread does not call
233  * the completion routine or APC function.  A completion routine is
234  * queued when the ReadFileEx() or WriteFileEx() function in which it
235  * was specified has completed.  The calling thread is the thread that
236  * initiated the read or write operation.  An APC is queued when
237  * QueueUserAPC() is called.  Currently completion routines and APC
238  * functions are not supported.
239  *
240  * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
241  * released by the owning thread when it exited.  Ownershop of the
242  * mutex object is granted to the calling thread and the mutex is set
243  * to nonsignalled.  %WAIT_IO_COMPLETION - the wait was ended by one
244  * or more user-mode asynchronous procedure calls queued to the
245  * thread.  %WAIT_OBJECT_0 - The state of @wait is signalled.
246  * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
247  * still not signalled.  %WAIT_FAILED - an error occurred.
248  */
249 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
250                             guint32 timeout, gboolean alertable)
251 {
252         guint32 ret = 0, waited;
253         int thr_ret;
254         gboolean apc_pending = FALSE;
255         gpointer current_thread = wapi_get_current_thread_handle ();
256         
257         if (current_thread == NULL) {
258                 SetLastError (ERROR_INVALID_HANDLE);
259                 return(WAIT_FAILED);
260         }
261
262         if (signal_handle == _WAPI_THREAD_CURRENT) {
263                 signal_handle = wapi_get_current_thread_handle ();
264                 if (signal_handle == NULL) {
265                         SetLastError (ERROR_INVALID_HANDLE);
266                         return(WAIT_FAILED);
267                 }
268         }
269
270         if (wait == _WAPI_THREAD_CURRENT) {
271                 wait = wapi_get_current_thread_handle ();
272                 if (wait == NULL) {
273                         SetLastError (ERROR_INVALID_HANDLE);
274                         return(WAIT_FAILED);
275                 }
276         }
277
278         if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
279                 SetLastError (ERROR_INVALID_HANDLE);
280                 return(WAIT_FAILED);
281         }
282
283         if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
284                 SetLastError (ERROR_INVALID_HANDLE);
285                 return(WAIT_FAILED);
286         }
287         
288         if (_wapi_handle_test_capabilities (signal_handle,
289                                             WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
290                 return(WAIT_FAILED);
291         }
292         
293         if (_wapi_handle_test_capabilities (wait,
294                                             WAPI_HANDLE_CAP_WAIT)==FALSE) {
295                 return(WAIT_FAILED);
296         }
297
298         _wapi_handle_ops_prewait (wait);
299         
300         if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
301                 g_warning ("%s: handle %p has special wait, implement me!!",
302                            __func__, wait);
303
304                 return (WAIT_FAILED);
305         }
306
307         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, wait);
308
309         thr_ret = _wapi_handle_lock_handle (wait);
310         g_assert (thr_ret == 0);
311
312         _wapi_handle_ops_signal (signal_handle);
313
314         if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
315                 if (own_if_owned (wait)) {
316                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__,
317                                    wait);
318                         ret = WAIT_OBJECT_0;
319                         goto done;
320                 }
321         }
322
323         if (own_if_signalled (wait)) {
324                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__, wait);
325
326                 ret = WAIT_OBJECT_0;
327                 goto done;
328         }
329
330         do {
331                 /* Check before waiting on the condition, just in case
332                  */
333                 _wapi_handle_ops_prewait (wait);
334         
335                 if (own_if_signalled (wait)) {
336                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, wait);
337
338                         ret = WAIT_OBJECT_0;
339                         goto done;
340                 }
341
342                 waited = _wapi_handle_timedwait_signal_handle (wait, timeout, alertable, FALSE, &apc_pending);
343
344                 if (waited==0 && !apc_pending) {
345                         /* Condition was signalled, so hopefully
346                          * handle is signalled now.  (It might not be
347                          * if someone else got in before us.)
348                          */
349                         if (own_if_signalled (wait)) {
350                                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
351                                            wait);
352
353                                 ret = WAIT_OBJECT_0;
354                                 goto done;
355                         }
356                 
357                         /* Better luck next time */
358                 }
359         } while(waited == 0 && !apc_pending);
360
361         /* Timeout or other error */
362         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, wait, strerror (ret));
363
364         ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
365
366 done:
367
368         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, wait);
369
370         thr_ret = _wapi_handle_unlock_handle (wait);
371         g_assert (thr_ret == 0);
372
373         return(ret);
374 }
375
376 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
377                               gboolean waitall, guint32 *count,
378                               guint32 *lowest)
379 {
380         gboolean done;
381         int i;
382         
383         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handles", __func__);
384         
385         done = _wapi_handle_count_signalled_handles (numobjects, handles,
386                                                      waitall, count, lowest);
387         if (done == TRUE) {
388                 if (waitall == TRUE) {
389                         for (i = 0; i < numobjects; i++) {
390                                 own_if_signalled (handles[i]);
391                         }
392                 } else {
393                         own_if_signalled (handles[*lowest]);
394                 }
395         }
396         
397         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handles", __func__);
398
399         _wapi_handle_unlock_handles (numobjects, handles);
400
401         return(done);
402 }
403
404 /**
405  * WaitForMultipleObjectsEx:
406  * @numobjects: The number of objects in @handles. The maximum allowed
407  * is %MAXIMUM_WAIT_OBJECTS.
408  * @handles: An array of object handles.  Duplicates are not allowed.
409  * @waitall: If %TRUE, this function waits until all of the handles
410  * are signalled.  If %FALSE, this function returns when any object is
411  * signalled.
412  * @timeout: The maximum time in milliseconds to wait for.
413  * @alertable: if TRUE, the wait can be interrupted by an APC call
414  * 
415  * This function returns when either one or more of @handles is
416  * signalled, or @timeout ms elapses.  If @timeout is zero, the state
417  * of each item of @handles is tested and the function returns
418  * immediately.  If @timeout is %INFINITE, the function waits forever.
419  *
420  * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
421  * if @waitall is %TRUE, indicates that all objects are signalled.  If
422  * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
423  * the first index into @handles of the objects that are signalled.
424  * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
425  * @waitall is %TRUE, indicates that all objects are signalled, and at
426  * least one object is an abandoned mutex object (See
427  * WaitForSingleObject() for a description of abandoned mutexes.)  If
428  * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
429  * indicates the first index into @handles of an abandoned mutex.
430  * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
431  * @handles are signalled.  %WAIT_FAILED - an error occurred.
432  * %WAIT_IO_COMPLETION - the wait was ended by an APC.
433  */
434 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
435                                  gboolean waitall, guint32 timeout,
436                                  gboolean alertable)
437 {
438         gboolean duplicate = FALSE, bogustype = FALSE, done;
439         guint32 count, lowest;
440         guint i;
441         guint32 ret;
442         int thr_ret;
443         gpointer current_thread = wapi_get_current_thread_handle ();
444         guint32 retval;
445         gboolean poll;
446         gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
447         gboolean apc_pending = FALSE;
448         
449         if (current_thread == NULL) {
450                 SetLastError (ERROR_INVALID_HANDLE);
451                 return(WAIT_FAILED);
452         }
453         
454         if (numobjects > MAXIMUM_WAIT_OBJECTS) {
455                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Too many handles: %d", __func__, numobjects);
456
457                 return(WAIT_FAILED);
458         }
459         
460         if (numobjects == 1) {
461                 return WaitForSingleObjectEx (handles [0], timeout, alertable);
462         }
463
464         /* Check for duplicates */
465         for (i = 0; i < numobjects; i++) {
466                 if (handles[i] == _WAPI_THREAD_CURRENT) {
467                         handles[i] = wapi_get_current_thread_handle ();
468                         
469                         if (handles[i] == NULL) {
470                                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %d bogus", __func__, i);
471
472                                 bogustype = TRUE;
473                                 break;
474                         }
475                 }
476
477                 if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
478                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %d pseudo process", __func__,
479                                    i);
480
481                         bogustype = TRUE;
482                         break;
483                 }
484
485                 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
486                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %p can't be waited for",
487                                    __func__, handles[i]);
488
489                         bogustype = TRUE;
490                         break;
491                 }
492
493                 sorted_handles [i] = handles [i];
494                 _wapi_handle_ops_prewait (handles[i]);
495         }
496
497         qsort (sorted_handles, numobjects, sizeof (gpointer), g_direct_equal);
498         for (i = 1; i < numobjects; i++) {
499                 if (sorted_handles [i - 1] == sorted_handles [i]) {
500                         duplicate = TRUE;
501                         break;
502                 }
503         }
504
505         if (duplicate == TRUE) {
506                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to duplicates", __func__);
507
508                 return(WAIT_FAILED);
509         }
510
511         if (bogustype == TRUE) {
512                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to bogus type", __func__);
513
514                 return(WAIT_FAILED);
515         }
516
517         poll = FALSE;
518         for (i = 0; i < numobjects; ++i)
519                 if (_wapi_handle_type (handles [i]) == WAPI_HANDLE_PROCESS || _WAPI_SHARED_HANDLE (_wapi_handle_type (handles[i]))) 
520                         /* Can't wait for a process handle + another handle without polling */
521                         poll = TRUE;
522
523         done = test_and_own (numobjects, handles, waitall, &count, &lowest);
524         if (done == TRUE) {
525                 return(WAIT_OBJECT_0+lowest);
526         }
527         
528         if (timeout == 0) {
529                 return WAIT_TIMEOUT;
530         }
531         /* Have to wait for some or all handles to become signalled
532          */
533
534         for (i = 0; i < numobjects; i++) {
535                 /* Add a reference, as we need to ensure the handle wont
536                  * disappear from under us while we're waiting in the loop
537                  * (not lock, as we don't want exclusive access here)
538                  */
539                 _wapi_handle_ref (handles[i]);
540         }
541
542         while(1) {
543                 /* Prod all handles with prewait methods and
544                  * special-wait handles that aren't already signalled
545                  */
546                 for (i = 0; i < numobjects; i++) {
547                         _wapi_handle_ops_prewait (handles[i]);
548                 
549                         if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
550                                 _wapi_handle_ops_special_wait (handles[i], 0, alertable);
551                         }
552                 }
553                 
554                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking signal mutex", __func__);
555
556                 thr_ret = _wapi_handle_lock_signal_mutex ();
557                 g_assert (thr_ret == 0);
558
559                 /* Check the signalled state of handles inside the critical section */
560                 if (waitall) {
561                         done = TRUE;
562                         for (i = 0; i < numobjects; i++)
563                                 if (!_wapi_handle_issignalled (handles [i]))
564                                         done = FALSE;
565                 } else {
566                         done = FALSE;
567                         for (i = 0; i < numobjects; i++)
568                                 if (_wapi_handle_issignalled (handles [i]))
569                                         done = TRUE;
570                 }
571                 
572                 if (!done) {
573                         /* Enter the wait */
574                         ret = _wapi_handle_timedwait_signal (timeout, poll, &apc_pending);
575                 } else {
576                         /* No need to wait */
577                         ret = 0;
578                 }
579
580                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking signal mutex", __func__);
581
582                 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
583                 g_assert (thr_ret == 0);
584                 
585                 if (alertable && apc_pending) {
586                         retval = WAIT_IO_COMPLETION;
587                         break;
588                 }
589         
590                 /* Check if everything is signalled, as we can't
591                  * guarantee to notice a shared signal even if the
592                  * wait timed out
593                  */
594                 done = test_and_own (numobjects, handles, waitall,
595                                      &count, &lowest);
596                 if (done == TRUE) {
597                         retval = WAIT_OBJECT_0+lowest;
598                         break;
599                 } else if (ret != 0) {
600                         /* Didn't get all handles, and there was a
601                          * timeout or other error
602                          */
603                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait returned error: %s", __func__,
604                                    strerror (ret));
605
606                         if(ret==ETIMEDOUT) {
607                                 retval = WAIT_TIMEOUT;
608                         } else {
609                                 retval = WAIT_FAILED;
610                         }
611                         break;
612                 }
613         }
614
615         for (i = 0; i < numobjects; i++) {
616                 /* Unref everything we reffed above */
617                 _wapi_handle_unref (handles[i]);
618         }
619
620         return retval;
621 }
622
623 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
624                                gboolean waitall, guint32 timeout)
625 {
626         return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
627 }
628
629 /**
630  * WaitForInputIdle:
631  * @handle: a handle to the process to wait for
632  * @timeout: the maximum time in milliseconds to wait for
633  *
634  * This function returns when either @handle process is waiting
635  * for input, or @timeout ms elapses.  If @timeout is zero, the
636  * process state is tested and the function returns immediately.
637  * If @timeout is %INFINITE, the function waits forever.
638  *
639  * Return value: 0 - @handle process is waiting for input.
640  * %WAIT_TIMEOUT - The @timeout interval elapsed and
641  * @handle process is not waiting for input.  %WAIT_FAILED - an error
642  * occurred. 
643  */
644 guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
645 {
646         /*TODO: Not implemented*/
647         return WAIT_TIMEOUT;
648 }
649