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