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