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/io-trace.h>
19 #include <mono/utils/mono-logger-internals.h>
21 static gboolean own_if_signalled(gpointer handle)
25 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
26 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
31 if (_wapi_handle_issignalled (handle)) {
32 _wapi_handle_ops_own (handle);
36 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
37 _wapi_handle_unlock_shared_handles ();
43 static gboolean own_if_owned(gpointer handle)
47 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
48 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
53 if (_wapi_handle_ops_isowned (handle)) {
54 _wapi_handle_ops_own (handle);
58 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
59 _wapi_handle_unlock_shared_handles ();
66 * WaitForSingleObjectEx:
67 * @handle: an object to wait for
68 * @timeout: the maximum time in milliseconds to wait for
69 * @alertable: if TRUE, the wait can be interrupted by an APC call
71 * This function returns when either @handle is signalled, or @timeout
72 * ms elapses. If @timeout is zero, the object's state is tested and
73 * the function returns immediately. If @timeout is %INFINITE, the
74 * function waits forever.
76 * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
77 * released by the owning thread when it exited. Ownership of the
78 * mutex object is granted to the calling thread and the mutex is set
79 * to nonsignalled. %WAIT_OBJECT_0 - The state of @handle is
80 * signalled. %WAIT_TIMEOUT - The @timeout interval elapsed and
81 * @handle's state is still not signalled. %WAIT_FAILED - an error
82 * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
84 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
89 gboolean apc_pending = FALSE;
90 gpointer current_thread = wapi_get_current_thread_handle ();
92 if (current_thread == NULL) {
93 SetLastError (ERROR_INVALID_HANDLE);
97 if (handle == _WAPI_THREAD_CURRENT) {
98 handle = wapi_get_current_thread_handle ();
100 SetLastError (ERROR_INVALID_HANDLE);
105 if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
106 SetLastError (ERROR_INVALID_HANDLE);
110 if (_wapi_handle_test_capabilities (handle,
111 WAPI_HANDLE_CAP_WAIT) == FALSE) {
112 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p can't be waited for", __func__,
118 _wapi_handle_ops_prewait (handle);
120 if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
121 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p has special wait", __func__, handle);
123 ret = _wapi_handle_ops_special_wait (handle, timeout, alertable);
125 if (alertable && _wapi_thread_cur_apc_pending ())
126 ret = WAIT_IO_COMPLETION;
132 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, handle);
134 thr_ret = _wapi_handle_lock_handle (handle);
135 g_assert (thr_ret == 0);
137 if (_wapi_handle_test_capabilities (handle,
138 WAPI_HANDLE_CAP_OWN) == TRUE) {
139 if (own_if_owned (handle) == TRUE) {
140 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__,
147 if (own_if_signalled (handle) == TRUE) {
148 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__,
161 /* Check before waiting on the condition, just in case
163 _wapi_handle_ops_prewait (handle);
165 if (own_if_signalled (handle)) {
166 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
173 waited = _wapi_handle_timedwait_signal_handle (handle, timeout, alertable, FALSE, &apc_pending);
175 if(waited==0 && !apc_pending) {
176 /* Condition was signalled, so hopefully
177 * handle is signalled now. (It might not be
178 * if someone else got in before us.)
180 if (own_if_signalled (handle)) {
181 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
188 /* Better luck next time */
190 } while(waited == 0 && !apc_pending);
192 /* Timeout or other error */
193 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, handle,
196 ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
200 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, handle);
202 thr_ret = _wapi_handle_unlock_handle (handle);
203 g_assert (thr_ret == 0);
208 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
210 return WaitForSingleObjectEx (handle, timeout, FALSE);
215 * SignalObjectAndWait:
216 * @signal_handle: An object to signal
217 * @wait: An object to wait for
218 * @timeout: The maximum time in milliseconds to wait for
219 * @alertable: Specifies whether the function returnes when the system
220 * queues an I/O completion routine or an APC for the calling thread.
222 * Atomically signals @signal and waits for @wait to become signalled,
223 * or @timeout ms elapses. If @timeout is zero, the object's state is
224 * tested and the function returns immediately. If @timeout is
225 * %INFINITE, the function waits forever.
227 * @signal can be a semaphore, mutex or event object.
229 * If @alertable is %TRUE and the system queues an I/O completion
230 * routine or an APC for the calling thread, the function returns and
231 * the thread calls the completion routine or APC function. If
232 * %FALSE, the function does not return, and the thread does not call
233 * the completion routine or APC function. A completion routine is
234 * queued when the ReadFileEx() or WriteFileEx() function in which it
235 * was specified has completed. The calling thread is the thread that
236 * initiated the read or write operation. An APC is queued when
237 * QueueUserAPC() is called. Currently completion routines and APC
238 * functions are not supported.
240 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
241 * released by the owning thread when it exited. Ownershop of the
242 * mutex object is granted to the calling thread and the mutex is set
243 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
244 * or more user-mode asynchronous procedure calls queued to the
245 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
246 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
247 * still not signalled. %WAIT_FAILED - an error occurred.
249 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
250 guint32 timeout, gboolean alertable)
252 guint32 ret = 0, waited;
254 gboolean apc_pending = FALSE;
255 gpointer current_thread = wapi_get_current_thread_handle ();
257 if (current_thread == NULL) {
258 SetLastError (ERROR_INVALID_HANDLE);
262 if (signal_handle == _WAPI_THREAD_CURRENT) {
263 signal_handle = wapi_get_current_thread_handle ();
264 if (signal_handle == NULL) {
265 SetLastError (ERROR_INVALID_HANDLE);
270 if (wait == _WAPI_THREAD_CURRENT) {
271 wait = wapi_get_current_thread_handle ();
273 SetLastError (ERROR_INVALID_HANDLE);
278 if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
279 SetLastError (ERROR_INVALID_HANDLE);
283 if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
284 SetLastError (ERROR_INVALID_HANDLE);
288 if (_wapi_handle_test_capabilities (signal_handle,
289 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
293 if (_wapi_handle_test_capabilities (wait,
294 WAPI_HANDLE_CAP_WAIT)==FALSE) {
298 _wapi_handle_ops_prewait (wait);
300 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
301 g_warning ("%s: handle %p has special wait, implement me!!",
304 return (WAIT_FAILED);
307 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, wait);
309 thr_ret = _wapi_handle_lock_handle (wait);
310 g_assert (thr_ret == 0);
312 _wapi_handle_ops_signal (signal_handle);
314 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
315 if (own_if_owned (wait)) {
316 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__,
323 if (own_if_signalled (wait)) {
324 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__, wait);
331 /* Check before waiting on the condition, just in case
333 _wapi_handle_ops_prewait (wait);
335 if (own_if_signalled (wait)) {
336 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, wait);
342 waited = _wapi_handle_timedwait_signal_handle (wait, timeout, alertable, FALSE, &apc_pending);
344 if (waited==0 && !apc_pending) {
345 /* Condition was signalled, so hopefully
346 * handle is signalled now. (It might not be
347 * if someone else got in before us.)
349 if (own_if_signalled (wait)) {
350 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
357 /* Better luck next time */
359 } while(waited == 0 && !apc_pending);
361 /* Timeout or other error */
362 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, wait, strerror (ret));
364 ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
368 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, wait);
370 thr_ret = _wapi_handle_unlock_handle (wait);
371 g_assert (thr_ret == 0);
376 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
377 gboolean waitall, guint32 *count,
383 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handles", __func__);
385 done = _wapi_handle_count_signalled_handles (numobjects, handles,
386 waitall, count, lowest);
388 if (waitall == TRUE) {
389 for (i = 0; i < numobjects; i++) {
390 own_if_signalled (handles[i]);
393 own_if_signalled (handles[*lowest]);
397 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handles", __func__);
399 _wapi_handle_unlock_handles (numobjects, handles);
405 * WaitForMultipleObjectsEx:
406 * @numobjects: The number of objects in @handles. The maximum allowed
407 * is %MAXIMUM_WAIT_OBJECTS.
408 * @handles: An array of object handles. Duplicates are not allowed.
409 * @waitall: If %TRUE, this function waits until all of the handles
410 * are signalled. If %FALSE, this function returns when any object is
412 * @timeout: The maximum time in milliseconds to wait for.
413 * @alertable: if TRUE, the wait can be interrupted by an APC call
415 * This function returns when either one or more of @handles is
416 * signalled, or @timeout ms elapses. If @timeout is zero, the state
417 * of each item of @handles is tested and the function returns
418 * immediately. If @timeout is %INFINITE, the function waits forever.
420 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
421 * if @waitall is %TRUE, indicates that all objects are signalled. If
422 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
423 * the first index into @handles of the objects that are signalled.
424 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
425 * @waitall is %TRUE, indicates that all objects are signalled, and at
426 * least one object is an abandoned mutex object (See
427 * WaitForSingleObject() for a description of abandoned mutexes.) If
428 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
429 * indicates the first index into @handles of an abandoned mutex.
430 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
431 * @handles are signalled. %WAIT_FAILED - an error occurred.
432 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
434 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
435 gboolean waitall, guint32 timeout,
438 gboolean duplicate = FALSE, bogustype = FALSE, done;
439 guint32 count, lowest;
443 gpointer current_thread = wapi_get_current_thread_handle ();
446 gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
447 gboolean apc_pending = FALSE;
449 if (current_thread == NULL) {
450 SetLastError (ERROR_INVALID_HANDLE);
454 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
455 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Too many handles: %d", __func__, numobjects);
460 if (numobjects == 1) {
461 return WaitForSingleObjectEx (handles [0], timeout, alertable);
464 /* Check for duplicates */
465 for (i = 0; i < numobjects; i++) {
466 if (handles[i] == _WAPI_THREAD_CURRENT) {
467 handles[i] = wapi_get_current_thread_handle ();
469 if (handles[i] == NULL) {
470 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %d bogus", __func__, i);
477 if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
478 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %d pseudo process", __func__,
485 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
486 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %p can't be waited for",
487 __func__, handles[i]);
493 sorted_handles [i] = handles [i];
494 _wapi_handle_ops_prewait (handles[i]);
497 qsort (sorted_handles, numobjects, sizeof (gpointer), g_direct_equal);
498 for (i = 1; i < numobjects; i++) {
499 if (sorted_handles [i - 1] == sorted_handles [i]) {
505 if (duplicate == TRUE) {
506 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to duplicates", __func__);
511 if (bogustype == TRUE) {
512 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to bogus type", __func__);
518 for (i = 0; i < numobjects; ++i)
519 if (_wapi_handle_type (handles [i]) == WAPI_HANDLE_PROCESS || _WAPI_SHARED_HANDLE (_wapi_handle_type (handles[i])))
520 /* Can't wait for a process handle + another handle without polling */
523 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
525 return(WAIT_OBJECT_0+lowest);
531 /* Have to wait for some or all handles to become signalled
534 for (i = 0; i < numobjects; i++) {
535 /* Add a reference, as we need to ensure the handle wont
536 * disappear from under us while we're waiting in the loop
537 * (not lock, as we don't want exclusive access here)
539 _wapi_handle_ref (handles[i]);
543 /* Prod all handles with prewait methods and
544 * special-wait handles that aren't already signalled
546 for (i = 0; i < numobjects; i++) {
547 _wapi_handle_ops_prewait (handles[i]);
549 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
550 _wapi_handle_ops_special_wait (handles[i], 0, alertable);
554 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking signal mutex", __func__);
556 thr_ret = _wapi_handle_lock_signal_mutex ();
557 g_assert (thr_ret == 0);
559 /* Check the signalled state of handles inside the critical section */
562 for (i = 0; i < numobjects; i++)
563 if (!_wapi_handle_issignalled (handles [i]))
567 for (i = 0; i < numobjects; i++)
568 if (_wapi_handle_issignalled (handles [i]))
574 ret = _wapi_handle_timedwait_signal (timeout, poll, &apc_pending);
576 /* No need to wait */
580 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking signal mutex", __func__);
582 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
583 g_assert (thr_ret == 0);
585 if (alertable && apc_pending) {
586 retval = WAIT_IO_COMPLETION;
590 /* Check if everything is signalled, as we can't
591 * guarantee to notice a shared signal even if the
594 done = test_and_own (numobjects, handles, waitall,
597 retval = WAIT_OBJECT_0+lowest;
599 } else if (ret != 0) {
600 /* Didn't get all handles, and there was a
601 * timeout or other error
603 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait returned error: %s", __func__,
607 retval = WAIT_TIMEOUT;
609 retval = WAIT_FAILED;
615 for (i = 0; i < numobjects; i++) {
616 /* Unref everything we reffed above */
617 _wapi_handle_unref (handles[i]);
623 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
624 gboolean waitall, guint32 timeout)
626 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
631 * @handle: a handle to the process to wait for
632 * @timeout: the maximum time in milliseconds to wait for
634 * This function returns when either @handle process is waiting
635 * for input, or @timeout ms elapses. If @timeout is zero, the
636 * process state is tested and the function returns immediately.
637 * If @timeout is %INFINITE, the function waits forever.
639 * Return value: 0 - @handle process is waiting for input.
640 * %WAIT_TIMEOUT - The @timeout interval elapsed and
641 * @handle process is not waiting for input. %WAIT_FAILED - an error
644 guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
646 /*TODO: Not implemented*/