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