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>
20 #define DEBUG(...) g_message(__VA_ARGS__)
25 static gboolean own_if_signalled(gpointer handle)
29 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
30 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
35 if (_wapi_handle_issignalled (handle)) {
36 _wapi_handle_ops_own (handle);
40 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
41 _wapi_handle_unlock_shared_handles ();
47 static gboolean own_if_owned(gpointer handle)
51 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
52 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
57 if (_wapi_handle_ops_isowned (handle)) {
58 _wapi_handle_ops_own (handle);
62 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
63 _wapi_handle_unlock_shared_handles ();
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
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.
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.
88 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
93 gboolean apc_pending = FALSE;
94 gpointer current_thread = wapi_get_current_thread_handle ();
96 if (current_thread == NULL) {
97 SetLastError (ERROR_INVALID_HANDLE);
101 if (handle == _WAPI_THREAD_CURRENT) {
102 handle = wapi_get_current_thread_handle ();
103 if (handle == NULL) {
104 SetLastError (ERROR_INVALID_HANDLE);
109 if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
110 SetLastError (ERROR_INVALID_HANDLE);
114 if (_wapi_handle_test_capabilities (handle,
115 WAPI_HANDLE_CAP_WAIT) == FALSE) {
116 DEBUG ("%s: handle %p can't be waited for", __func__,
122 _wapi_handle_ops_prewait (handle);
124 if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
125 DEBUG ("%s: handle %p has special wait", __func__, handle);
127 ret = _wapi_handle_ops_special_wait (handle, timeout, alertable);
129 if (alertable && _wapi_thread_cur_apc_pending ())
130 ret = WAIT_IO_COMPLETION;
136 DEBUG ("%s: locking handle %p", __func__, handle);
138 thr_ret = _wapi_handle_lock_handle (handle);
139 g_assert (thr_ret == 0);
141 if (_wapi_handle_test_capabilities (handle,
142 WAPI_HANDLE_CAP_OWN) == TRUE) {
143 if (own_if_owned (handle) == TRUE) {
144 DEBUG ("%s: handle %p already owned", __func__,
151 if (own_if_signalled (handle) == TRUE) {
152 DEBUG ("%s: handle %p already signalled", __func__,
165 /* Check before waiting on the condition, just in case
167 _wapi_handle_ops_prewait (handle);
169 if (own_if_signalled (handle)) {
170 DEBUG ("%s: handle %p signalled", __func__,
177 waited = _wapi_handle_timedwait_signal_handle (handle, timeout, alertable, FALSE, &apc_pending);
179 if(waited==0 && !apc_pending) {
180 /* Condition was signalled, so hopefully
181 * handle is signalled now. (It might not be
182 * if someone else got in before us.)
184 if (own_if_signalled (handle)) {
185 DEBUG ("%s: handle %p signalled", __func__,
192 /* Better luck next time */
194 } while(waited == 0 && !apc_pending);
196 /* Timeout or other error */
197 DEBUG ("%s: wait on handle %p error: %s", __func__, handle,
200 ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
204 DEBUG ("%s: unlocking handle %p", __func__, handle);
206 thr_ret = _wapi_handle_unlock_handle (handle);
207 g_assert (thr_ret == 0);
212 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
214 return WaitForSingleObjectEx (handle, timeout, FALSE);
219 * SignalObjectAndWait:
220 * @signal_handle: An object to signal
221 * @wait: An object to wait for
222 * @timeout: The maximum time in milliseconds to wait for
223 * @alertable: Specifies whether the function returnes when the system
224 * queues an I/O completion routine or an APC for the calling thread.
226 * Atomically signals @signal and waits for @wait to become signalled,
227 * or @timeout ms elapses. If @timeout is zero, the object's state is
228 * tested and the function returns immediately. If @timeout is
229 * %INFINITE, the function waits forever.
231 * @signal can be a semaphore, mutex or event object.
233 * If @alertable is %TRUE and the system queues an I/O completion
234 * routine or an APC for the calling thread, the function returns and
235 * the thread calls the completion routine or APC function. If
236 * %FALSE, the function does not return, and the thread does not call
237 * the completion routine or APC function. A completion routine is
238 * queued when the ReadFileEx() or WriteFileEx() function in which it
239 * was specified has completed. The calling thread is the thread that
240 * initiated the read or write operation. An APC is queued when
241 * QueueUserAPC() is called. Currently completion routines and APC
242 * functions are not supported.
244 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
245 * released by the owning thread when it exited. Ownershop of the
246 * mutex object is granted to the calling thread and the mutex is set
247 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
248 * or more user-mode asynchronous procedure calls queued to the
249 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
250 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
251 * still not signalled. %WAIT_FAILED - an error occurred.
253 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
254 guint32 timeout, gboolean alertable)
258 gboolean apc_pending = FALSE;
259 gpointer current_thread = wapi_get_current_thread_handle ();
261 if (current_thread == NULL) {
262 SetLastError (ERROR_INVALID_HANDLE);
266 if (signal_handle == _WAPI_THREAD_CURRENT) {
267 signal_handle = wapi_get_current_thread_handle ();
268 if (signal_handle == NULL) {
269 SetLastError (ERROR_INVALID_HANDLE);
274 if (wait == _WAPI_THREAD_CURRENT) {
275 wait = wapi_get_current_thread_handle ();
277 SetLastError (ERROR_INVALID_HANDLE);
282 if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
283 SetLastError (ERROR_INVALID_HANDLE);
287 if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
288 SetLastError (ERROR_INVALID_HANDLE);
292 if (_wapi_handle_test_capabilities (signal_handle,
293 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
297 if (_wapi_handle_test_capabilities (wait,
298 WAPI_HANDLE_CAP_WAIT)==FALSE) {
302 _wapi_handle_ops_prewait (wait);
304 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
305 g_warning ("%s: handle %p has special wait, implement me!!",
308 return (WAIT_FAILED);
311 DEBUG ("%s: locking handle %p", __func__, wait);
313 thr_ret = _wapi_handle_lock_handle (wait);
314 g_assert (thr_ret == 0);
316 _wapi_handle_ops_signal (signal_handle);
318 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
319 if (own_if_owned (wait)) {
320 DEBUG ("%s: handle %p already owned", __func__,
327 if (own_if_signalled (wait)) {
328 DEBUG ("%s: handle %p already signalled", __func__, wait);
335 /* Check before waiting on the condition, just in case
337 _wapi_handle_ops_prewait (wait);
339 if (own_if_signalled (wait)) {
340 DEBUG ("%s: handle %p signalled", __func__, wait);
346 waited = _wapi_handle_timedwait_signal_handle (wait, timeout, alertable, FALSE, &apc_pending);
348 if (waited==0 && !apc_pending) {
349 /* Condition was signalled, so hopefully
350 * handle is signalled now. (It might not be
351 * if someone else got in before us.)
353 if (own_if_signalled (wait)) {
354 DEBUG ("%s: handle %p signalled", __func__,
361 /* Better luck next time */
363 } while(waited == 0 && !apc_pending);
365 /* Timeout or other error */
366 DEBUG ("%s: wait on handle %p error: %s", __func__, wait,
369 ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
373 DEBUG ("%s: unlocking handle %p", __func__, wait);
375 thr_ret = _wapi_handle_unlock_handle (wait);
376 g_assert (thr_ret == 0);
381 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
382 gboolean waitall, guint32 *count,
388 DEBUG ("%s: locking handles", __func__);
390 done = _wapi_handle_count_signalled_handles (numobjects, handles,
391 waitall, count, lowest);
393 if (waitall == TRUE) {
394 for (i = 0; i < numobjects; i++) {
395 own_if_signalled (handles[i]);
398 own_if_signalled (handles[*lowest]);
402 DEBUG ("%s: unlocking handles", __func__);
404 _wapi_handle_unlock_handles (numobjects, handles);
410 * WaitForMultipleObjectsEx:
411 * @numobjects: The number of objects in @handles. The maximum allowed
412 * is %MAXIMUM_WAIT_OBJECTS.
413 * @handles: An array of object handles. Duplicates are not allowed.
414 * @waitall: If %TRUE, this function waits until all of the handles
415 * are signalled. If %FALSE, this function returns when any object is
417 * @timeout: The maximum time in milliseconds to wait for.
418 * @alertable: if TRUE, the wait can be interrupted by an APC call
420 * This function returns when either one or more of @handles is
421 * signalled, or @timeout ms elapses. If @timeout is zero, the state
422 * of each item of @handles is tested and the function returns
423 * immediately. If @timeout is %INFINITE, the function waits forever.
425 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
426 * if @waitall is %TRUE, indicates that all objects are signalled. If
427 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
428 * the first index into @handles of the objects that are signalled.
429 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
430 * @waitall is %TRUE, indicates that all objects are signalled, and at
431 * least one object is an abandoned mutex object (See
432 * WaitForSingleObject() for a description of abandoned mutexes.) If
433 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
434 * indicates the first index into @handles of an abandoned mutex.
435 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
436 * @handles are signalled. %WAIT_FAILED - an error occurred.
437 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
439 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
440 gboolean waitall, guint32 timeout,
443 gboolean duplicate = FALSE, bogustype = FALSE, done;
444 guint32 count, lowest;
448 gpointer current_thread = wapi_get_current_thread_handle ();
451 gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
452 gboolean apc_pending = FALSE;
454 if (current_thread == NULL) {
455 SetLastError (ERROR_INVALID_HANDLE);
459 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
460 DEBUG ("%s: Too many handles: %d", __func__, numobjects);
465 if (numobjects == 1) {
466 return WaitForSingleObjectEx (handles [0], timeout, alertable);
469 /* Check for duplicates */
470 for (i = 0; i < numobjects; i++) {
471 if (handles[i] == _WAPI_THREAD_CURRENT) {
472 handles[i] = wapi_get_current_thread_handle ();
474 if (handles[i] == NULL) {
475 DEBUG ("%s: Handle %d bogus", __func__, i);
482 if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
483 DEBUG ("%s: Handle %d pseudo process", __func__,
490 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
491 DEBUG ("%s: Handle %p can't be waited for",
492 __func__, handles[i]);
498 sorted_handles [i] = handles [i];
499 _wapi_handle_ops_prewait (handles[i]);
502 qsort (sorted_handles, numobjects, sizeof (gpointer), g_direct_equal);
503 for (i = 1; i < numobjects; i++) {
504 if (sorted_handles [i - 1] == sorted_handles [i]) {
510 if (duplicate == TRUE) {
511 DEBUG ("%s: Returning due to duplicates", __func__);
516 if (bogustype == TRUE) {
517 DEBUG ("%s: Returning due to bogus type", __func__);
523 for (i = 0; i < numobjects; ++i)
524 if (_wapi_handle_type (handles [i]) == WAPI_HANDLE_PROCESS || _WAPI_SHARED_HANDLE (_wapi_handle_type (handles[i])))
525 /* Can't wait for a process handle + another handle without polling */
528 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
530 return(WAIT_OBJECT_0+lowest);
536 /* Have to wait for some or all handles to become signalled
539 for (i = 0; i < numobjects; i++) {
540 /* Add a reference, as we need to ensure the handle wont
541 * disappear from under us while we're waiting in the loop
542 * (not lock, as we don't want exclusive access here)
544 _wapi_handle_ref (handles[i]);
548 /* Prod all handles with prewait methods and
549 * special-wait handles that aren't already signalled
551 for (i = 0; i < numobjects; i++) {
552 _wapi_handle_ops_prewait (handles[i]);
554 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
555 _wapi_handle_ops_special_wait (handles[i], 0, alertable);
559 DEBUG ("%s: locking signal mutex", __func__);
561 thr_ret = _wapi_handle_lock_signal_mutex ();
562 g_assert (thr_ret == 0);
564 /* Check the signalled state of handles inside the critical section */
567 for (i = 0; i < numobjects; i++)
568 if (!_wapi_handle_issignalled (handles [i]))
572 for (i = 0; i < numobjects; i++)
573 if (_wapi_handle_issignalled (handles [i]))
579 ret = _wapi_handle_timedwait_signal (timeout, poll, &apc_pending);
581 /* No need to wait */
585 DEBUG ("%s: unlocking signal mutex", __func__);
587 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
588 g_assert (thr_ret == 0);
590 if (alertable && apc_pending) {
591 retval = WAIT_IO_COMPLETION;
595 /* Check if everything is signalled, as we can't
596 * guarantee to notice a shared signal even if the
599 done = test_and_own (numobjects, handles, waitall,
602 retval = WAIT_OBJECT_0+lowest;
604 } else if (ret != 0) {
605 /* Didn't get all handles, and there was a
606 * timeout or other error
608 DEBUG ("%s: wait returned error: %s", __func__,
612 retval = WAIT_TIMEOUT;
614 retval = WAIT_FAILED;
620 for (i = 0; i < numobjects; i++) {
621 /* Unref everything we reffed above */
622 _wapi_handle_unref (handles[i]);
628 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
629 gboolean waitall, guint32 timeout)
631 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
636 * @handle: a handle to the process to wait for
637 * @timeout: the maximum time in milliseconds to wait for
639 * This function returns when either @handle process is waiting
640 * for input, or @timeout ms elapses. If @timeout is zero, the
641 * process state is tested and the function returns immediately.
642 * If @timeout is %INFINITE, the function waits forever.
644 * Return value: 0 - @handle process is waiting for input.
645 * %WAIT_TIMEOUT - The @timeout interval elapsed and
646 * @handle process is not waiting for input. %WAIT_FAILED - an error
649 guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
651 /*TODO: Not implemented*/