[sgen] Make sure we don't sweep a block if we're not supposed to
[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/wapi-private.h>
17 #include <mono/io-layer/io-trace.h>
18 #include <mono/utils/mono-logger-internals.h>
19 #include <mono/utils/mono-time.h>
20 #include <mono/utils/w32handle.h>
21 #include <mono/utils/mono-threads.h>
22
23 static gboolean own_if_signalled(gpointer handle)
24 {
25         gboolean ret = FALSE;
26
27         if (mono_w32handle_issignalled (handle)) {
28                 mono_w32handle_ops_own (handle);
29                 ret = TRUE;
30         }
31
32         return(ret);
33 }
34
35 static gboolean own_if_owned(gpointer handle)
36 {
37         gboolean ret = FALSE;
38
39         if (mono_w32handle_ops_isowned (handle)) {
40                 mono_w32handle_ops_own (handle);
41                 ret = TRUE;
42         }
43
44         return(ret);
45 }
46
47 /**
48  * WaitForSingleObjectEx:
49  * @handle: an object to wait for
50  * @timeout: the maximum time in milliseconds to wait for
51  * @alertable: if TRUE, the wait can be interrupted by an APC call
52  *
53  * This function returns when either @handle is signalled, or @timeout
54  * ms elapses.  If @timeout is zero, the object's state is tested and
55  * the function returns immediately.  If @timeout is %INFINITE, the
56  * function waits forever.
57  *
58  * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
59  * released by the owning thread when it exited.  Ownership of the
60  * mutex object is granted to the calling thread and the mutex is set
61  * to nonsignalled.  %WAIT_OBJECT_0 - The state of @handle is
62  * signalled.  %WAIT_TIMEOUT - The @timeout interval elapsed and
63  * @handle's state is still not signalled.  %WAIT_FAILED - an error
64  * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
65  */
66 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
67                               gboolean alertable)
68 {
69         guint32 ret, waited;
70         int thr_ret;
71         gboolean apc_pending = FALSE;
72         gint64 wait_start, timeout_in_ticks;
73
74         if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
75                 SetLastError (ERROR_INVALID_HANDLE);
76                 return(WAIT_FAILED);
77         }
78         
79         if (mono_w32handle_test_capabilities (handle,
80                                             MONO_W32HANDLE_CAP_WAIT) == FALSE) {
81                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p can't be waited for", __func__,
82                            handle);
83
84                 return(WAIT_FAILED);
85         }
86
87         mono_w32handle_ops_prewait (handle);
88         
89         if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
90                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p has special wait", __func__, handle);
91
92                 ret = mono_w32handle_ops_specialwait (handle, timeout, alertable ? &apc_pending : NULL);
93         
94                 if (apc_pending)
95                         ret = WAIT_IO_COMPLETION;
96
97                 return ret;
98         }
99         
100         
101         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, handle);
102
103         thr_ret = mono_w32handle_lock_handle (handle);
104         g_assert (thr_ret == 0);
105
106         if (mono_w32handle_test_capabilities (handle,
107                                             MONO_W32HANDLE_CAP_OWN) == TRUE) {
108                 if (own_if_owned (handle) == TRUE) {
109                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__,
110                                    handle);
111                         ret = WAIT_OBJECT_0;
112                         goto done;
113                 }
114         }
115
116         if (own_if_signalled (handle) == TRUE) {
117                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__,
118                            handle);
119
120                 ret=WAIT_OBJECT_0;
121                 goto done;
122         }
123
124         if (timeout == 0) {
125                 ret = WAIT_TIMEOUT;
126                 goto done;
127         }
128         
129         if (timeout != INFINITE) {
130                 wait_start = mono_100ns_ticks ();
131                 timeout_in_ticks = (gint64)timeout * 10 * 1000; //can't overflow as timeout is 32bits
132         }
133
134         do {
135                 /* Check before waiting on the condition, just in case
136                  */
137                 mono_w32handle_ops_prewait (handle);
138
139                 if (own_if_signalled (handle)) {
140                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
141                                    handle);
142
143                         ret = WAIT_OBJECT_0;
144                         goto done;
145                 }
146
147                 if (timeout == INFINITE) {
148                         waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &apc_pending : NULL);
149                 } else {
150                         gint64 elapsed = mono_100ns_ticks () - wait_start;
151                         if (elapsed >= timeout_in_ticks) {
152                                 ret = WAIT_TIMEOUT;
153                                 goto done;
154                         }
155
156                         waited = mono_w32handle_timedwait_signal_handle (handle, (timeout_in_ticks - elapsed) / 10 / 1000, FALSE, alertable ? &apc_pending : NULL);
157                 }
158
159                 if(waited==0 && !apc_pending) {
160                         /* Condition was signalled, so hopefully
161                          * handle is signalled now.  (It might not be
162                          * if someone else got in before us.)
163                          */
164                         if (own_if_signalled (handle)) {
165                                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
166                                            handle);
167
168                                 ret=WAIT_OBJECT_0;
169                                 goto done;
170                         }
171                 
172                         /* Better luck next time */
173                 }
174         } while(waited == 0 && !apc_pending);
175
176         /* Timeout or other error */
177         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, handle,
178                    strerror (waited));
179
180         ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
181
182 done:
183
184         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, handle);
185         
186         thr_ret = mono_w32handle_unlock_handle (handle);
187         g_assert (thr_ret == 0);
188
189         return(ret);
190 }
191
192 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
193 {
194         return WaitForSingleObjectEx (handle, timeout, FALSE);
195 }
196
197
198 /**
199  * SignalObjectAndWait:
200  * @signal_handle: An object to signal
201  * @wait: An object to wait for
202  * @timeout: The maximum time in milliseconds to wait for
203  * @alertable: Specifies whether the function returnes when the system
204  * queues an I/O completion routine or an APC for the calling thread.
205  *
206  * Atomically signals @signal and waits for @wait to become signalled,
207  * or @timeout ms elapses.  If @timeout is zero, the object's state is
208  * tested and the function returns immediately.  If @timeout is
209  * %INFINITE, the function waits forever.
210  *
211  * @signal can be a semaphore, mutex or event object.
212  *
213  * If @alertable is %TRUE and the system queues an I/O completion
214  * routine or an APC for the calling thread, the function returns and
215  * the thread calls the completion routine or APC function.  If
216  * %FALSE, the function does not return, and the thread does not call
217  * the completion routine or APC function.  A completion routine is
218  * queued when the ReadFileEx() or WriteFileEx() function in which it
219  * was specified has completed.  The calling thread is the thread that
220  * initiated the read or write operation.  An APC is queued when
221  * QueueUserAPC() is called.  Currently completion routines and APC
222  * functions are not supported.
223  *
224  * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
225  * released by the owning thread when it exited.  Ownershop of the
226  * mutex object is granted to the calling thread and the mutex is set
227  * to nonsignalled.  %WAIT_IO_COMPLETION - the wait was ended by one
228  * or more user-mode asynchronous procedure calls queued to the
229  * thread.  %WAIT_OBJECT_0 - The state of @wait is signalled.
230  * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
231  * still not signalled.  %WAIT_FAILED - an error occurred.
232  */
233 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
234                             guint32 timeout, gboolean alertable)
235 {
236         guint32 ret = 0, waited;
237         int thr_ret;
238         gboolean apc_pending = FALSE;
239         gint64 wait_start, timeout_in_ticks;
240
241         if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
242                 SetLastError (ERROR_INVALID_HANDLE);
243                 return(WAIT_FAILED);
244         }
245
246         if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
247                 SetLastError (ERROR_INVALID_HANDLE);
248                 return(WAIT_FAILED);
249         }
250         
251         if (mono_w32handle_test_capabilities (signal_handle,
252                                             MONO_W32HANDLE_CAP_SIGNAL)==FALSE) {
253                 return(WAIT_FAILED);
254         }
255         
256         if (mono_w32handle_test_capabilities (wait,
257                                             MONO_W32HANDLE_CAP_WAIT)==FALSE) {
258                 return(WAIT_FAILED);
259         }
260
261         mono_w32handle_ops_prewait (wait);
262         
263         if (mono_w32handle_test_capabilities (wait, MONO_W32HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
264                 g_warning ("%s: handle %p has special wait, implement me!!",
265                            __func__, wait);
266
267                 return (WAIT_FAILED);
268         }
269
270         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, wait);
271
272         thr_ret = mono_w32handle_lock_handle (wait);
273         g_assert (thr_ret == 0);
274
275         mono_w32handle_ops_signal (signal_handle);
276
277         if (mono_w32handle_test_capabilities (wait, MONO_W32HANDLE_CAP_OWN)==TRUE) {
278                 if (own_if_owned (wait)) {
279                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__,
280                                    wait);
281                         ret = WAIT_OBJECT_0;
282                         goto done;
283                 }
284         }
285
286         if (own_if_signalled (wait)) {
287                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__, wait);
288
289                 ret = WAIT_OBJECT_0;
290                 goto done;
291         }
292
293         if (timeout != INFINITE) {
294                 wait_start = mono_100ns_ticks ();
295                 timeout_in_ticks = (gint64)timeout * 10 * 1000; //can't overflow as timeout is 32bits
296         }
297         do {
298                 /* Check before waiting on the condition, just in case
299                  */
300                 mono_w32handle_ops_prewait (wait);
301         
302                 if (own_if_signalled (wait)) {
303                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, wait);
304
305                         ret = WAIT_OBJECT_0;
306                         goto done;
307                 }
308
309                 if (timeout == INFINITE) {
310                         waited = mono_w32handle_timedwait_signal_handle (wait, INFINITE, FALSE, alertable ? &apc_pending : NULL);
311                 } else {
312                         gint64 elapsed = mono_100ns_ticks () - wait_start;
313                         if (elapsed >= timeout_in_ticks) {
314                                 ret = WAIT_TIMEOUT;
315                                 goto done;
316                         }
317
318                         waited = mono_w32handle_timedwait_signal_handle (wait, (timeout_in_ticks - elapsed) / 10 / 1000, FALSE, alertable ? &apc_pending : NULL);
319                 }
320
321                 if (waited==0 && !apc_pending) {
322                         /* Condition was signalled, so hopefully
323                          * handle is signalled now.  (It might not be
324                          * if someone else got in before us.)
325                          */
326                         if (own_if_signalled (wait)) {
327                                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
328                                            wait);
329
330                                 ret = WAIT_OBJECT_0;
331                                 goto done;
332                         }
333                 
334                         /* Better luck next time */
335                 }
336         } while(waited == 0 && !apc_pending);
337
338         /* Timeout or other error */
339         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, wait, strerror (ret));
340
341         ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
342
343 done:
344
345         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, wait);
346
347         thr_ret = mono_w32handle_unlock_handle (wait);
348         g_assert (thr_ret == 0);
349
350         return(ret);
351 }
352
353 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
354                               gboolean waitall, guint32 *count,
355                               guint32 *lowest)
356 {
357         gboolean done;
358         int i;
359         
360         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handles", __func__);
361         
362         done = mono_w32handle_count_signalled_handles (numobjects, handles,
363                                                      waitall, count, lowest);
364         if (done == TRUE) {
365                 if (waitall == TRUE) {
366                         for (i = 0; i < numobjects; i++) {
367                                 own_if_signalled (handles[i]);
368                         }
369                 } else {
370                         own_if_signalled (handles[*lowest]);
371                 }
372         }
373         
374         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handles", __func__);
375
376         mono_w32handle_unlock_handles (numobjects, handles);
377
378         return(done);
379 }
380
381 /**
382  * WaitForMultipleObjectsEx:
383  * @numobjects: The number of objects in @handles. The maximum allowed
384  * is %MAXIMUM_WAIT_OBJECTS.
385  * @handles: An array of object handles.  Duplicates are not allowed.
386  * @waitall: If %TRUE, this function waits until all of the handles
387  * are signalled.  If %FALSE, this function returns when any object is
388  * signalled.
389  * @timeout: The maximum time in milliseconds to wait for.
390  * @alertable: if TRUE, the wait can be interrupted by an APC call
391  * 
392  * This function returns when either one or more of @handles is
393  * signalled, or @timeout ms elapses.  If @timeout is zero, the state
394  * of each item of @handles is tested and the function returns
395  * immediately.  If @timeout is %INFINITE, the function waits forever.
396  *
397  * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
398  * if @waitall is %TRUE, indicates that all objects are signalled.  If
399  * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
400  * the first index into @handles of the objects that are signalled.
401  * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
402  * @waitall is %TRUE, indicates that all objects are signalled, and at
403  * least one object is an abandoned mutex object (See
404  * WaitForSingleObject() for a description of abandoned mutexes.)  If
405  * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
406  * indicates the first index into @handles of an abandoned mutex.
407  * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
408  * @handles are signalled.  %WAIT_FAILED - an error occurred.
409  * %WAIT_IO_COMPLETION - the wait was ended by an APC.
410  */
411 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
412                                  gboolean waitall, guint32 timeout,
413                                  gboolean alertable)
414 {
415         gboolean duplicate = FALSE, bogustype = FALSE, done;
416         guint32 count, lowest;
417         guint i;
418         guint32 ret;
419         int thr_ret;
420         guint32 retval;
421         gboolean poll;
422         gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
423         gboolean apc_pending = FALSE;
424         gint64 wait_start, timeout_in_ticks;
425         
426         if (numobjects > MAXIMUM_WAIT_OBJECTS) {
427                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Too many handles: %d", __func__, numobjects);
428
429                 return(WAIT_FAILED);
430         }
431         
432         if (numobjects == 1) {
433                 return WaitForSingleObjectEx (handles [0], timeout, alertable);
434         }
435
436         /* Check for duplicates */
437         for (i = 0; i < numobjects; i++) {
438                 if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
439                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %d pseudo process", __func__,
440                                    i);
441
442                         bogustype = TRUE;
443                         break;
444                 }
445
446                 if (mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT) == FALSE) {
447                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %p can't be waited for",
448                                    __func__, handles[i]);
449
450                         bogustype = TRUE;
451                         break;
452                 }
453
454                 sorted_handles [i] = handles [i];
455                 mono_w32handle_ops_prewait (handles[i]);
456         }
457
458         qsort (sorted_handles, numobjects, sizeof (gpointer), g_direct_equal);
459         for (i = 1; i < numobjects; i++) {
460                 if (sorted_handles [i - 1] == sorted_handles [i]) {
461                         duplicate = TRUE;
462                         break;
463                 }
464         }
465
466         if (duplicate == TRUE) {
467                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to duplicates", __func__);
468
469                 return(WAIT_FAILED);
470         }
471
472         if (bogustype == TRUE) {
473                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to bogus type", __func__);
474
475                 return(WAIT_FAILED);
476         }
477
478         poll = FALSE;
479         for (i = 0; i < numobjects; ++i)
480                 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS)
481                         /* Can't wait for a process handle + another handle without polling */
482                         poll = TRUE;
483
484         done = test_and_own (numobjects, handles, waitall, &count, &lowest);
485         if (done == TRUE) {
486                 return(WAIT_OBJECT_0+lowest);
487         }
488         
489         if (timeout == 0) {
490                 return WAIT_TIMEOUT;
491         }
492
493         if (timeout != INFINITE) {
494                 wait_start = mono_100ns_ticks ();
495                 timeout_in_ticks = (gint64)timeout * 10 * 1000; //can't overflow as timeout is 32bits
496         }
497
498         /* Have to wait for some or all handles to become signalled
499          */
500
501         for (i = 0; i < numobjects; i++) {
502                 /* Add a reference, as we need to ensure the handle wont
503                  * disappear from under us while we're waiting in the loop
504                  * (not lock, as we don't want exclusive access here)
505                  */
506                 mono_w32handle_ref (handles[i]);
507         }
508
509         while(1) {
510                 /* Prod all handles with prewait methods and
511                  * special-wait handles that aren't already signalled
512                  */
513                 for (i = 0; i < numobjects; i++) {
514                         mono_w32handle_ops_prewait (handles[i]);
515                 
516                         if (mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT) == TRUE && mono_w32handle_issignalled (handles[i]) == FALSE) {
517                                 mono_w32handle_ops_specialwait (handles[i], 0, alertable ? &apc_pending : NULL);
518                         }
519                 }
520                 
521                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking signal mutex", __func__);
522
523                 thr_ret = mono_w32handle_lock_signal_mutex ();
524                 g_assert (thr_ret == 0);
525
526                 /* Check the signalled state of handles inside the critical section */
527                 if (waitall) {
528                         done = TRUE;
529                         for (i = 0; i < numobjects; i++)
530                                 if (!mono_w32handle_issignalled (handles [i]))
531                                         done = FALSE;
532                 } else {
533                         done = FALSE;
534                         for (i = 0; i < numobjects; i++)
535                                 if (mono_w32handle_issignalled (handles [i]))
536                                         done = TRUE;
537                 }
538                 
539                 if (!done) {
540                         /* Enter the wait */
541                         if (timeout == INFINITE) {
542                                 ret = mono_w32handle_timedwait_signal (INFINITE, poll, &apc_pending);
543                         } else {
544                                 gint64 elapsed = mono_100ns_ticks () - wait_start;
545                                 if (elapsed >= timeout_in_ticks) {
546                                         ret = WAIT_TIMEOUT;
547                                 } else {
548                                         ret = mono_w32handle_timedwait_signal ((timeout_in_ticks - elapsed) / 10 / 1000, poll, &apc_pending);
549                                 }
550                         }
551                 } else {
552                         /* No need to wait */
553                         ret = 0;
554                 }
555
556                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking signal mutex", __func__);
557
558                 thr_ret = mono_w32handle_unlock_signal_mutex ();
559                 g_assert (thr_ret == 0);
560                 
561                 if (alertable && apc_pending) {
562                         retval = WAIT_IO_COMPLETION;
563                         break;
564                 }
565         
566                 /* Check if everything is signalled, as we can't
567                  * guarantee to notice a shared signal even if the
568                  * wait timed out
569                  */
570                 done = test_and_own (numobjects, handles, waitall,
571                                      &count, &lowest);
572                 if (done == TRUE) {
573                         retval = WAIT_OBJECT_0+lowest;
574                         break;
575                 } else if (ret != 0) {
576                         /* Didn't get all handles, and there was a timeout */
577                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait returned error: %s", __func__,
578                                    strerror (ret));
579
580                         retval = WAIT_TIMEOUT;
581                         break;
582                 }
583         }
584
585         for (i = 0; i < numobjects; i++) {
586                 /* Unref everything we reffed above */
587                 mono_w32handle_unref (handles[i]);
588         }
589
590         return retval;
591 }
592
593 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
594                                gboolean waitall, guint32 timeout)
595 {
596         return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
597 }
598
599 /**
600  * WaitForInputIdle:
601  * @handle: a handle to the process to wait for
602  * @timeout: the maximum time in milliseconds to wait for
603  *
604  * This function returns when either @handle process is waiting
605  * for input, or @timeout ms elapses.  If @timeout is zero, the
606  * process state is tested and the function returns immediately.
607  * If @timeout is %INFINITE, the function waits forever.
608  *
609  * Return value: 0 - @handle process is waiting for input.
610  * %WAIT_TIMEOUT - The @timeout interval elapsed and
611  * @handle process is not waiting for input.  %WAIT_FAILED - an error
612  * occurred. 
613  */
614 guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
615 {
616         /*TODO: Not implemented*/
617         return WAIT_TIMEOUT;
618 }
619