2 * wait.c: wait for handles to become signalled
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002-2006 Novell, Inc.
15 #include <mono/io-layer/wapi.h>
16 #include <mono/io-layer/handles-private.h>
17 #include <mono/io-layer/wapi-private.h>
18 #include <mono/io-layer/misc-private.h>
20 #include <mono/utils/mono-mutex.h>
21 #include <mono/utils/mono-threads.h>
24 #define DEBUG(...) g_message(__VA_ARGS__)
29 static gboolean own_if_signalled(gpointer handle)
33 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
34 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
39 if (_wapi_handle_issignalled (handle)) {
40 _wapi_handle_ops_own (handle);
44 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
45 _wapi_handle_unlock_shared_handles ();
51 static gboolean own_if_owned(gpointer handle)
55 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
56 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
61 if (_wapi_handle_ops_isowned (handle)) {
62 _wapi_handle_ops_own (handle);
66 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
67 _wapi_handle_unlock_shared_handles ();
73 static guint32 WaitForSingleObjectEx_inner(gpointer handle, guint32 timeout, gboolean alertable);
76 * WaitForSingleObjectEx:
77 * @handle: an object to wait for
78 * @timeout: the maximum time in milliseconds to wait for
79 * @alertable: if TRUE, the wait can be interrupted by an APC call
81 * This function returns when either @handle is signalled, or @timeout
82 * ms elapses. If @timeout is zero, the object's state is tested and
83 * the function returns immediately. If @timeout is %INFINITE, the
84 * function waits forever.
86 * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
87 * released by the owning thread when it exited. Ownership of the
88 * mutex object is granted to the calling thread and the mutex is set
89 * to nonsignalled. %WAIT_OBJECT_0 - The state of @handle is
90 * signalled. %WAIT_TIMEOUT - The @timeout interval elapsed and
91 * @handle's state is still not signalled. %WAIT_FAILED - an error
92 * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
94 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
99 res = WaitForSingleObjectEx_inner (handle, timeout, alertable);
104 static guint32 WaitForSingleObjectEx_inner(gpointer handle, guint32 timeout,
108 struct timespec abstime;
110 gboolean apc_pending = FALSE;
111 gpointer current_thread = wapi_get_current_thread_handle ();
113 if (current_thread == NULL) {
114 SetLastError (ERROR_INVALID_HANDLE);
118 if (handle == _WAPI_THREAD_CURRENT) {
119 handle = wapi_get_current_thread_handle ();
120 if (handle == NULL) {
121 SetLastError (ERROR_INVALID_HANDLE);
126 if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
127 SetLastError (ERROR_INVALID_HANDLE);
131 if (_wapi_handle_test_capabilities (handle,
132 WAPI_HANDLE_CAP_WAIT) == FALSE) {
133 DEBUG ("%s: handle %p can't be waited for", __func__,
139 _wapi_handle_ops_prewait (handle);
141 if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
142 DEBUG ("%s: handle %p has special wait", __func__, handle);
144 ret = _wapi_handle_ops_special_wait (handle, timeout, alertable);
146 if (alertable && _wapi_thread_apc_pending (current_thread)) {
148 ret = WAIT_IO_COMPLETION;
155 DEBUG ("%s: locking handle %p", __func__, handle);
157 thr_ret = _wapi_handle_lock_handle (handle);
158 g_assert (thr_ret == 0);
160 if (_wapi_handle_test_capabilities (handle,
161 WAPI_HANDLE_CAP_OWN) == TRUE) {
162 if (own_if_owned (handle) == TRUE) {
163 DEBUG ("%s: handle %p already owned", __func__,
170 if (alertable && _wapi_thread_apc_pending (current_thread)) {
172 ret = WAIT_IO_COMPLETION;
176 if (own_if_signalled (handle) == TRUE) {
177 DEBUG ("%s: handle %p already signalled", __func__,
188 /* Have to wait for it */
189 if (timeout != INFINITE) {
190 _wapi_calc_timeout (&abstime, timeout);
194 /* Check before waiting on the condition, just in case
196 _wapi_handle_ops_prewait (handle);
198 if (own_if_signalled (handle)) {
199 DEBUG ("%s: handle %p signalled", __func__,
206 if (timeout == INFINITE) {
207 waited = _wapi_handle_wait_signal_handle (handle, alertable);
209 waited = _wapi_handle_timedwait_signal_handle (handle, &abstime, alertable, FALSE);
213 apc_pending = _wapi_thread_apc_pending (current_thread);
215 if(waited==0 && !apc_pending) {
216 /* Condition was signalled, so hopefully
217 * handle is signalled now. (It might not be
218 * if someone else got in before us.)
220 if (own_if_signalled (handle)) {
221 DEBUG ("%s: handle %p signalled", __func__,
228 /* Better luck next time */
230 } while(waited == 0 && !apc_pending);
232 /* Timeout or other error */
233 DEBUG ("%s: wait on handle %p error: %s", __func__, handle,
240 DEBUG ("%s: unlocking handle %p", __func__, handle);
242 thr_ret = _wapi_handle_unlock_handle (handle);
243 g_assert (thr_ret == 0);
247 ret = WAIT_IO_COMPLETION;
252 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
254 return WaitForSingleObjectEx (handle, timeout, FALSE);
259 * SignalObjectAndWait:
260 * @signal_handle: An object to signal
261 * @wait: An object to wait for
262 * @timeout: The maximum time in milliseconds to wait for
263 * @alertable: Specifies whether the function returnes when the system
264 * queues an I/O completion routine or an APC for the calling thread.
266 * Atomically signals @signal and waits for @wait to become signalled,
267 * or @timeout ms elapses. If @timeout is zero, the object's state is
268 * tested and the function returns immediately. If @timeout is
269 * %INFINITE, the function waits forever.
271 * @signal can be a semaphore, mutex or event object.
273 * If @alertable is %TRUE and the system queues an I/O completion
274 * routine or an APC for the calling thread, the function returns and
275 * the thread calls the completion routine or APC function. If
276 * %FALSE, the function does not return, and the thread does not call
277 * the completion routine or APC function. A completion routine is
278 * queued when the ReadFileEx() or WriteFileEx() function in which it
279 * was specified has completed. The calling thread is the thread that
280 * initiated the read or write operation. An APC is queued when
281 * QueueUserAPC() is called. Currently completion routines and APC
282 * functions are not supported.
284 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
285 * released by the owning thread when it exited. Ownershop of the
286 * mutex object is granted to the calling thread and the mutex is set
287 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
288 * or more user-mode asynchronous procedure calls queued to the
289 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
290 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
291 * still not signalled. %WAIT_FAILED - an error occurred.
293 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
294 guint32 timeout, gboolean alertable)
297 struct timespec abstime;
299 gboolean apc_pending = FALSE;
300 gpointer current_thread = wapi_get_current_thread_handle ();
302 if (current_thread == NULL) {
303 SetLastError (ERROR_INVALID_HANDLE);
307 if (signal_handle == _WAPI_THREAD_CURRENT) {
308 signal_handle = wapi_get_current_thread_handle ();
309 if (signal_handle == NULL) {
310 SetLastError (ERROR_INVALID_HANDLE);
315 if (wait == _WAPI_THREAD_CURRENT) {
316 wait = wapi_get_current_thread_handle ();
318 SetLastError (ERROR_INVALID_HANDLE);
323 if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
324 SetLastError (ERROR_INVALID_HANDLE);
328 if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
329 SetLastError (ERROR_INVALID_HANDLE);
333 if (_wapi_handle_test_capabilities (signal_handle,
334 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
338 if (_wapi_handle_test_capabilities (wait,
339 WAPI_HANDLE_CAP_WAIT)==FALSE) {
343 _wapi_handle_ops_prewait (wait);
345 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
346 g_warning ("%s: handle %p has special wait, implement me!!",
349 return (WAIT_FAILED);
352 DEBUG ("%s: locking handle %p", __func__, wait);
354 thr_ret = _wapi_handle_lock_handle (wait);
355 g_assert (thr_ret == 0);
357 _wapi_handle_ops_signal (signal_handle);
359 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
360 if (own_if_owned (wait)) {
361 DEBUG ("%s: handle %p already owned", __func__,
368 if (alertable && _wapi_thread_apc_pending (current_thread)) {
370 ret = WAIT_IO_COMPLETION;
374 if (own_if_signalled (wait)) {
375 DEBUG ("%s: handle %p already signalled", __func__, wait);
381 /* Have to wait for it */
382 if (timeout != INFINITE) {
383 _wapi_calc_timeout (&abstime, timeout);
387 /* Check before waiting on the condition, just in case
389 _wapi_handle_ops_prewait (wait);
391 if (own_if_signalled (wait)) {
392 DEBUG ("%s: handle %p signalled", __func__, wait);
398 if (timeout == INFINITE) {
399 waited = _wapi_handle_wait_signal_handle (wait, alertable);
401 waited = _wapi_handle_timedwait_signal_handle (wait, &abstime, alertable, FALSE);
405 apc_pending = _wapi_thread_apc_pending (current_thread);
408 if (waited==0 && !apc_pending) {
409 /* Condition was signalled, so hopefully
410 * handle is signalled now. (It might not be
411 * if someone else got in before us.)
413 if (own_if_signalled (wait)) {
414 DEBUG ("%s: handle %p signalled", __func__,
421 /* Better luck next time */
423 } while(waited == 0 && !apc_pending);
425 /* Timeout or other error */
426 DEBUG ("%s: wait on handle %p error: %s", __func__, wait,
433 DEBUG ("%s: unlocking handle %p", __func__, wait);
435 thr_ret = _wapi_handle_unlock_handle (wait);
436 g_assert (thr_ret == 0);
439 ret = WAIT_IO_COMPLETION;
444 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
445 gboolean waitall, guint32 *count,
451 DEBUG ("%s: locking handles", __func__);
453 done = _wapi_handle_count_signalled_handles (numobjects, handles,
454 waitall, count, lowest);
456 if (waitall == TRUE) {
457 for (i = 0; i < numobjects; i++) {
458 own_if_signalled (handles[i]);
461 own_if_signalled (handles[*lowest]);
465 DEBUG ("%s: unlocking handles", __func__);
467 _wapi_handle_unlock_handles (numobjects, handles);
472 static guint32 WaitForMultipleObjectsEx_inner(guint32 numobjects, gpointer *handles,
473 gboolean waitall, guint32 timeout,
477 * WaitForMultipleObjectsEx:
478 * @numobjects: The number of objects in @handles. The maximum allowed
479 * is %MAXIMUM_WAIT_OBJECTS.
480 * @handles: An array of object handles. Duplicates are not allowed.
481 * @waitall: If %TRUE, this function waits until all of the handles
482 * are signalled. If %FALSE, this function returns when any object is
484 * @timeout: The maximum time in milliseconds to wait for.
485 * @alertable: if TRUE, the wait can be interrupted by an APC call
487 * This function returns when either one or more of @handles is
488 * signalled, or @timeout ms elapses. If @timeout is zero, the state
489 * of each item of @handles is tested and the function returns
490 * immediately. If @timeout is %INFINITE, the function waits forever.
492 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
493 * if @waitall is %TRUE, indicates that all objects are signalled. If
494 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
495 * the first index into @handles of the objects that are signalled.
496 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
497 * @waitall is %TRUE, indicates that all objects are signalled, and at
498 * least one object is an abandoned mutex object (See
499 * WaitForSingleObject() for a description of abandoned mutexes.) If
500 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
501 * indicates the first index into @handles of an abandoned mutex.
502 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
503 * @handles are signalled. %WAIT_FAILED - an error occurred.
504 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
506 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
507 gboolean waitall, guint32 timeout,
511 MONO_PREPARE_BLOCKING
512 res = WaitForMultipleObjectsEx_inner (numobjects, handles, waitall, timeout, alertable);
517 static guint32 WaitForMultipleObjectsEx_inner(guint32 numobjects, gpointer *handles,
518 gboolean waitall, guint32 timeout,
521 gboolean duplicate = FALSE, bogustype = FALSE, done;
522 guint32 count, lowest;
523 struct timespec abstime;
527 gpointer current_thread = wapi_get_current_thread_handle ();
530 gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
532 if (current_thread == NULL) {
533 SetLastError (ERROR_INVALID_HANDLE);
537 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
538 DEBUG ("%s: Too many handles: %d", __func__, numobjects);
543 if (numobjects == 1) {
544 return WaitForSingleObjectEx_inner (handles [0], timeout, alertable);
547 /* Check for duplicates */
548 for (i = 0; i < numobjects; i++) {
549 if (handles[i] == _WAPI_THREAD_CURRENT) {
550 handles[i] = wapi_get_current_thread_handle ();
552 if (handles[i] == NULL) {
553 DEBUG ("%s: Handle %d bogus", __func__, i);
560 if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
561 DEBUG ("%s: Handle %d pseudo process", __func__,
568 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
569 DEBUG ("%s: Handle %p can't be waited for",
570 __func__, handles[i]);
576 sorted_handles [i] = handles [i];
577 _wapi_handle_ops_prewait (handles[i]);
580 qsort (sorted_handles, numobjects, sizeof (gpointer), g_direct_equal);
581 for (i = 1; i < numobjects; i++) {
582 if (sorted_handles [i - 1] == sorted_handles [i]) {
588 if (duplicate == TRUE) {
589 DEBUG ("%s: Returning due to duplicates", __func__);
594 if (bogustype == TRUE) {
595 DEBUG ("%s: Returning due to bogus type", __func__);
601 for (i = 0; i < numobjects; ++i)
602 if (_wapi_handle_type (handles [i]) == WAPI_HANDLE_PROCESS || _WAPI_SHARED_HANDLE (_wapi_handle_type (handles[i])))
603 /* Can't wait for a process handle + another handle without polling */
606 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
608 return(WAIT_OBJECT_0+lowest);
614 /* Have to wait for some or all handles to become signalled
617 if(timeout!=INFINITE) {
618 _wapi_calc_timeout (&abstime, timeout);
621 if (alertable && _wapi_thread_apc_pending (current_thread))
622 return WAIT_IO_COMPLETION;
624 for (i = 0; i < numobjects; i++) {
625 /* Add a reference, as we need to ensure the handle wont
626 * disappear from under us while we're waiting in the loop
627 * (not lock, as we don't want exclusive access here)
629 _wapi_handle_ref (handles[i]);
633 /* Prod all handles with prewait methods and
634 * special-wait handles that aren't already signalled
636 for (i = 0; i < numobjects; i++) {
637 _wapi_handle_ops_prewait (handles[i]);
639 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
640 _wapi_handle_ops_special_wait (handles[i], 0, alertable);
644 DEBUG ("%s: locking signal mutex", __func__);
646 thr_ret = _wapi_handle_lock_signal_mutex ();
647 g_assert (thr_ret == 0);
649 /* Check the signalled state of handles inside the critical section */
652 for (i = 0; i < numobjects; i++)
653 if (!_wapi_handle_issignalled (handles [i]))
657 for (i = 0; i < numobjects; i++)
658 if (_wapi_handle_issignalled (handles [i]))
664 if (timeout == INFINITE) {
665 ret = _wapi_handle_wait_signal (poll);
667 ret = _wapi_handle_timedwait_signal (&abstime, poll);
670 /* No need to wait */
674 DEBUG ("%s: unlocking signal mutex", __func__);
676 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
677 g_assert (thr_ret == 0);
679 if (alertable && _wapi_thread_apc_pending (current_thread)) {
680 retval = WAIT_IO_COMPLETION;
684 /* Check if everything is signalled, as we can't
685 * guarantee to notice a shared signal even if the
688 done = test_and_own (numobjects, handles, waitall,
691 retval = WAIT_OBJECT_0+lowest;
693 } else if (ret != 0) {
694 /* Didn't get all handles, and there was a
695 * timeout or other error
697 DEBUG ("%s: wait returned error: %s", __func__,
701 retval = WAIT_TIMEOUT;
703 retval = WAIT_FAILED;
709 for (i = 0; i < numobjects; i++) {
710 /* Unref everything we reffed above */
711 _wapi_handle_unref (handles[i]);
717 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
718 gboolean waitall, guint32 timeout)
720 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
725 * @handle: a handle to the process to wait for
726 * @timeout: the maximum time in milliseconds to wait for
728 * This function returns when either @handle process is waiting
729 * for input, or @timeout ms elapses. If @timeout is zero, the
730 * process state is tested and the function returns immediately.
731 * If @timeout is %INFINITE, the function waits forever.
733 * Return value: 0 - @handle process is waiting for input.
734 * %WAIT_TIMEOUT - The @timeout interval elapsed and
735 * @handle process is not waiting for input. %WAIT_FAILED - an error
738 guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
740 /*TODO: Not implemented*/