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