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/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>
22 static gboolean own_if_signalled(gpointer handle)
26 if (mono_w32handle_issignalled (handle)) {
27 mono_w32handle_ops_own (handle);
34 static gboolean own_if_owned(gpointer handle)
38 if (mono_w32handle_ops_isowned (handle)) {
39 mono_w32handle_ops_own (handle);
47 * WaitForSingleObjectEx:
48 * @handle: an object to wait for
49 * @timeout: the maximum time in milliseconds to wait for
50 * @alertable: if TRUE, the wait can be interrupted by an APC call
52 * This function returns when either @handle is signalled, or @timeout
53 * ms elapses. If @timeout is zero, the object's state is tested and
54 * the function returns immediately. If @timeout is %INFINITE, the
55 * function waits forever.
57 * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
58 * released by the owning thread when it exited. Ownership of the
59 * mutex object is granted to the calling thread and the mutex is set
60 * to nonsignalled. %WAIT_OBJECT_0 - The state of @handle is
61 * signalled. %WAIT_TIMEOUT - The @timeout interval elapsed and
62 * @handle's state is still not signalled. %WAIT_FAILED - an error
63 * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
65 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
70 gboolean apc_pending = FALSE;
71 gpointer current_thread = wapi_get_current_thread_handle ();
72 gint64 wait_start, timeout_in_ticks;
74 if (current_thread == NULL) {
75 SetLastError (ERROR_INVALID_HANDLE);
79 if (handle == _WAPI_THREAD_CURRENT) {
80 handle = wapi_get_current_thread_handle ();
82 SetLastError (ERROR_INVALID_HANDLE);
87 if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
88 SetLastError (ERROR_INVALID_HANDLE);
92 if (mono_w32handle_test_capabilities (handle,
93 MONO_W32HANDLE_CAP_WAIT) == FALSE) {
94 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p can't be waited for", __func__,
100 mono_w32handle_ops_prewait (handle);
102 if (mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
103 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p has special wait", __func__, handle);
105 ret = mono_w32handle_ops_specialwait (handle, timeout, alertable);
107 if (alertable && _wapi_thread_cur_apc_pending ())
108 ret = WAIT_IO_COMPLETION;
114 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, handle);
116 thr_ret = mono_w32handle_lock_handle (handle);
117 g_assert (thr_ret == 0);
119 if (mono_w32handle_test_capabilities (handle,
120 MONO_W32HANDLE_CAP_OWN) == TRUE) {
121 if (own_if_owned (handle) == TRUE) {
122 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__,
129 if (own_if_signalled (handle) == TRUE) {
130 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__,
142 if (timeout != INFINITE) {
143 wait_start = mono_100ns_ticks ();
144 timeout_in_ticks = (gint64)timeout * 10 * 1000; //can't overflow as timeout is 32bits
148 /* Check before waiting on the condition, just in case
150 mono_w32handle_ops_prewait (handle);
152 if (own_if_signalled (handle)) {
153 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
160 if (timeout == INFINITE) {
161 waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &apc_pending : NULL);
163 gint64 elapsed = mono_100ns_ticks () - wait_start;
164 if (elapsed >= timeout_in_ticks) {
169 waited = mono_w32handle_timedwait_signal_handle (handle, (timeout_in_ticks - elapsed) / 10 / 1000, FALSE, alertable ? &apc_pending : NULL);
172 if(waited==0 && !apc_pending) {
173 /* Condition was signalled, so hopefully
174 * handle is signalled now. (It might not be
175 * if someone else got in before us.)
177 if (own_if_signalled (handle)) {
178 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
185 /* Better luck next time */
187 } while(waited == 0 && !apc_pending);
189 /* Timeout or other error */
190 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, handle,
193 ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
197 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, handle);
199 thr_ret = mono_w32handle_unlock_handle (handle);
200 g_assert (thr_ret == 0);
205 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
207 return WaitForSingleObjectEx (handle, timeout, FALSE);
212 * SignalObjectAndWait:
213 * @signal_handle: An object to signal
214 * @wait: An object to wait for
215 * @timeout: The maximum time in milliseconds to wait for
216 * @alertable: Specifies whether the function returnes when the system
217 * queues an I/O completion routine or an APC for the calling thread.
219 * Atomically signals @signal and waits for @wait to become signalled,
220 * or @timeout ms elapses. If @timeout is zero, the object's state is
221 * tested and the function returns immediately. If @timeout is
222 * %INFINITE, the function waits forever.
224 * @signal can be a semaphore, mutex or event object.
226 * If @alertable is %TRUE and the system queues an I/O completion
227 * routine or an APC for the calling thread, the function returns and
228 * the thread calls the completion routine or APC function. If
229 * %FALSE, the function does not return, and the thread does not call
230 * the completion routine or APC function. A completion routine is
231 * queued when the ReadFileEx() or WriteFileEx() function in which it
232 * was specified has completed. The calling thread is the thread that
233 * initiated the read or write operation. An APC is queued when
234 * QueueUserAPC() is called. Currently completion routines and APC
235 * functions are not supported.
237 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
238 * released by the owning thread when it exited. Ownershop of the
239 * mutex object is granted to the calling thread and the mutex is set
240 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
241 * or more user-mode asynchronous procedure calls queued to the
242 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
243 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
244 * still not signalled. %WAIT_FAILED - an error occurred.
246 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
247 guint32 timeout, gboolean alertable)
249 guint32 ret = 0, waited;
251 gboolean apc_pending = FALSE;
252 gpointer current_thread = wapi_get_current_thread_handle ();
253 gint64 wait_start, timeout_in_ticks;
255 if (current_thread == NULL) {
256 SetLastError (ERROR_INVALID_HANDLE);
260 if (signal_handle == _WAPI_THREAD_CURRENT) {
261 signal_handle = wapi_get_current_thread_handle ();
262 if (signal_handle == NULL) {
263 SetLastError (ERROR_INVALID_HANDLE);
268 if (wait == _WAPI_THREAD_CURRENT) {
269 wait = wapi_get_current_thread_handle ();
271 SetLastError (ERROR_INVALID_HANDLE);
276 if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
277 SetLastError (ERROR_INVALID_HANDLE);
281 if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
282 SetLastError (ERROR_INVALID_HANDLE);
286 if (mono_w32handle_test_capabilities (signal_handle,
287 MONO_W32HANDLE_CAP_SIGNAL)==FALSE) {
291 if (mono_w32handle_test_capabilities (wait,
292 MONO_W32HANDLE_CAP_WAIT)==FALSE) {
296 mono_w32handle_ops_prewait (wait);
298 if (mono_w32handle_test_capabilities (wait, MONO_W32HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
299 g_warning ("%s: handle %p has special wait, implement me!!",
302 return (WAIT_FAILED);
305 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, wait);
307 thr_ret = mono_w32handle_lock_handle (wait);
308 g_assert (thr_ret == 0);
310 mono_w32handle_ops_signal (signal_handle);
312 if (mono_w32handle_test_capabilities (wait, MONO_W32HANDLE_CAP_OWN)==TRUE) {
313 if (own_if_owned (wait)) {
314 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__,
321 if (own_if_signalled (wait)) {
322 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__, wait);
328 if (timeout != INFINITE) {
329 wait_start = mono_100ns_ticks ();
330 timeout_in_ticks = (gint64)timeout * 10 * 1000; //can't overflow as timeout is 32bits
333 /* Check before waiting on the condition, just in case
335 mono_w32handle_ops_prewait (wait);
337 if (own_if_signalled (wait)) {
338 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, wait);
344 if (timeout == INFINITE) {
345 waited = mono_w32handle_timedwait_signal_handle (wait, INFINITE, FALSE, alertable ? &apc_pending : NULL);
347 gint64 elapsed = mono_100ns_ticks () - wait_start;
348 if (elapsed >= timeout_in_ticks) {
353 waited = mono_w32handle_timedwait_signal_handle (wait, (timeout_in_ticks - elapsed) / 10 / 1000, FALSE, alertable ? &apc_pending : NULL);
356 if (waited==0 && !apc_pending) {
357 /* Condition was signalled, so hopefully
358 * handle is signalled now. (It might not be
359 * if someone else got in before us.)
361 if (own_if_signalled (wait)) {
362 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
369 /* Better luck next time */
371 } while(waited == 0 && !apc_pending);
373 /* Timeout or other error */
374 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, wait, strerror (ret));
376 ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
380 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, wait);
382 thr_ret = mono_w32handle_unlock_handle (wait);
383 g_assert (thr_ret == 0);
388 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
389 gboolean waitall, guint32 *count,
395 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handles", __func__);
397 done = mono_w32handle_count_signalled_handles (numobjects, handles,
398 waitall, count, lowest);
400 if (waitall == TRUE) {
401 for (i = 0; i < numobjects; i++) {
402 own_if_signalled (handles[i]);
405 own_if_signalled (handles[*lowest]);
409 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handles", __func__);
411 mono_w32handle_unlock_handles (numobjects, handles);
417 * WaitForMultipleObjectsEx:
418 * @numobjects: The number of objects in @handles. The maximum allowed
419 * is %MAXIMUM_WAIT_OBJECTS.
420 * @handles: An array of object handles. Duplicates are not allowed.
421 * @waitall: If %TRUE, this function waits until all of the handles
422 * are signalled. If %FALSE, this function returns when any object is
424 * @timeout: The maximum time in milliseconds to wait for.
425 * @alertable: if TRUE, the wait can be interrupted by an APC call
427 * This function returns when either one or more of @handles is
428 * signalled, or @timeout ms elapses. If @timeout is zero, the state
429 * of each item of @handles is tested and the function returns
430 * immediately. If @timeout is %INFINITE, the function waits forever.
432 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
433 * if @waitall is %TRUE, indicates that all objects are signalled. If
434 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
435 * the first index into @handles of the objects that are signalled.
436 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
437 * @waitall is %TRUE, indicates that all objects are signalled, and at
438 * least one object is an abandoned mutex object (See
439 * WaitForSingleObject() for a description of abandoned mutexes.) If
440 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
441 * indicates the first index into @handles of an abandoned mutex.
442 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
443 * @handles are signalled. %WAIT_FAILED - an error occurred.
444 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
446 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
447 gboolean waitall, guint32 timeout,
450 gboolean duplicate = FALSE, bogustype = FALSE, done;
451 guint32 count, lowest;
455 gpointer current_thread = wapi_get_current_thread_handle ();
458 gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
459 gboolean apc_pending = FALSE;
460 gint64 wait_start, timeout_in_ticks;
462 if (current_thread == NULL) {
463 SetLastError (ERROR_INVALID_HANDLE);
467 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
468 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Too many handles: %d", __func__, numobjects);
473 if (numobjects == 1) {
474 return WaitForSingleObjectEx (handles [0], timeout, alertable);
477 /* Check for duplicates */
478 for (i = 0; i < numobjects; i++) {
479 if (handles[i] == _WAPI_THREAD_CURRENT) {
480 handles[i] = wapi_get_current_thread_handle ();
482 if (handles[i] == NULL) {
483 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %d bogus", __func__, i);
490 if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
491 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %d pseudo process", __func__,
498 if (mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_WAIT) == FALSE) {
499 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %p can't be waited for",
500 __func__, handles[i]);
506 sorted_handles [i] = handles [i];
507 mono_w32handle_ops_prewait (handles[i]);
510 qsort (sorted_handles, numobjects, sizeof (gpointer), g_direct_equal);
511 for (i = 1; i < numobjects; i++) {
512 if (sorted_handles [i - 1] == sorted_handles [i]) {
518 if (duplicate == TRUE) {
519 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to duplicates", __func__);
524 if (bogustype == TRUE) {
525 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to bogus type", __func__);
531 for (i = 0; i < numobjects; ++i)
532 if (mono_w32handle_get_type (handles [i]) == MONO_W32HANDLE_PROCESS)
533 /* Can't wait for a process handle + another handle without polling */
536 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
538 return(WAIT_OBJECT_0+lowest);
545 if (timeout != INFINITE) {
546 wait_start = mono_100ns_ticks ();
547 timeout_in_ticks = (gint64)timeout * 10 * 1000; //can't overflow as timeout is 32bits
550 /* Have to wait for some or all handles to become signalled
553 for (i = 0; i < numobjects; i++) {
554 /* Add a reference, as we need to ensure the handle wont
555 * disappear from under us while we're waiting in the loop
556 * (not lock, as we don't want exclusive access here)
558 mono_w32handle_ref (handles[i]);
562 /* Prod all handles with prewait methods and
563 * special-wait handles that aren't already signalled
565 for (i = 0; i < numobjects; i++) {
566 mono_w32handle_ops_prewait (handles[i]);
568 if (mono_w32handle_test_capabilities (handles[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT) == TRUE && mono_w32handle_issignalled (handles[i]) == FALSE) {
569 mono_w32handle_ops_specialwait (handles[i], 0, alertable);
573 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking signal mutex", __func__);
575 thr_ret = mono_w32handle_lock_signal_mutex ();
576 g_assert (thr_ret == 0);
578 /* Check the signalled state of handles inside the critical section */
581 for (i = 0; i < numobjects; i++)
582 if (!mono_w32handle_issignalled (handles [i]))
586 for (i = 0; i < numobjects; i++)
587 if (mono_w32handle_issignalled (handles [i]))
593 if (timeout == INFINITE) {
594 ret = mono_w32handle_timedwait_signal (INFINITE, poll, &apc_pending);
596 gint64 elapsed = mono_100ns_ticks () - wait_start;
597 if (elapsed >= timeout_in_ticks) {
600 ret = mono_w32handle_timedwait_signal ((timeout_in_ticks - elapsed) / 10 / 1000, poll, &apc_pending);
604 /* No need to wait */
608 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking signal mutex", __func__);
610 thr_ret = mono_w32handle_unlock_signal_mutex ();
611 g_assert (thr_ret == 0);
613 if (alertable && apc_pending) {
614 retval = WAIT_IO_COMPLETION;
618 /* Check if everything is signalled, as we can't
619 * guarantee to notice a shared signal even if the
622 done = test_and_own (numobjects, handles, waitall,
625 retval = WAIT_OBJECT_0+lowest;
627 } else if (ret != 0) {
628 /* Didn't get all handles, and there was a
629 * timeout or other error
631 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait returned error: %s", __func__,
635 retval = WAIT_TIMEOUT;
637 retval = WAIT_FAILED;
643 for (i = 0; i < numobjects; i++) {
644 /* Unref everything we reffed above */
645 mono_w32handle_unref (handles[i]);
651 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
652 gboolean waitall, guint32 timeout)
654 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
659 * @handle: a handle to the process to wait for
660 * @timeout: the maximum time in milliseconds to wait for
662 * This function returns when either @handle process is waiting
663 * for input, or @timeout ms elapses. If @timeout is zero, the
664 * process state is tested and the function returns immediately.
665 * If @timeout is %INFINITE, the function waits forever.
667 * Return value: 0 - @handle process is waiting for input.
668 * %WAIT_TIMEOUT - The @timeout interval elapsed and
669 * @handle process is not waiting for input. %WAIT_FAILED - an error
672 guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
674 /*TODO: Not implemented*/