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