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>
21 #include <mono/utils/mono-threads.h>
23 static gboolean own_if_signalled(gpointer handle)
27 if (mono_w32handle_issignalled (handle)) {
28 mono_w32handle_ops_own (handle);
35 static gboolean own_if_owned(gpointer handle)
39 if (mono_w32handle_ops_isowned (handle)) {
40 mono_w32handle_ops_own (handle);
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
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.
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.
66 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
71 gboolean apc_pending = FALSE;
72 gint64 wait_start, timeout_in_ticks;
74 if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
75 SetLastError (ERROR_INVALID_HANDLE);
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__,
87 mono_w32handle_ops_prewait (handle);
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);
92 ret = mono_w32handle_ops_specialwait (handle, timeout, alertable ? &apc_pending : NULL);
95 ret = WAIT_IO_COMPLETION;
101 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, handle);
103 thr_ret = mono_w32handle_lock_handle (handle);
104 g_assert (thr_ret == 0);
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__,
116 if (own_if_signalled (handle) == TRUE) {
117 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__,
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
135 /* Check before waiting on the condition, just in case
137 mono_w32handle_ops_prewait (handle);
139 if (own_if_signalled (handle)) {
140 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
147 if (timeout == INFINITE) {
148 waited = mono_w32handle_timedwait_signal_handle (handle, INFINITE, FALSE, alertable ? &apc_pending : NULL);
150 gint64 elapsed = mono_100ns_ticks () - wait_start;
151 if (elapsed >= timeout_in_ticks) {
156 waited = mono_w32handle_timedwait_signal_handle (handle, (timeout_in_ticks - elapsed) / 10 / 1000, FALSE, alertable ? &apc_pending : NULL);
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.)
164 if (own_if_signalled (handle)) {
165 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
172 /* Better luck next time */
174 } while(waited == 0 && !apc_pending);
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,
180 ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
184 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, handle);
186 thr_ret = mono_w32handle_unlock_handle (handle);
187 g_assert (thr_ret == 0);
192 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
194 return WaitForSingleObjectEx (handle, timeout, FALSE);
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.
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.
211 * @signal can be a semaphore, mutex or event object.
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.
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.
233 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
234 guint32 timeout, gboolean alertable)
236 guint32 ret = 0, waited;
238 gboolean apc_pending = FALSE;
239 gint64 wait_start, timeout_in_ticks;
241 if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
242 SetLastError (ERROR_INVALID_HANDLE);
246 if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
247 SetLastError (ERROR_INVALID_HANDLE);
251 if (mono_w32handle_test_capabilities (signal_handle,
252 MONO_W32HANDLE_CAP_SIGNAL)==FALSE) {
256 if (mono_w32handle_test_capabilities (wait,
257 MONO_W32HANDLE_CAP_WAIT)==FALSE) {
261 mono_w32handle_ops_prewait (wait);
263 if (mono_w32handle_test_capabilities (wait, MONO_W32HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
264 g_warning ("%s: handle %p has special wait, implement me!!",
267 return (WAIT_FAILED);
270 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, wait);
272 thr_ret = mono_w32handle_lock_handle (wait);
273 g_assert (thr_ret == 0);
275 mono_w32handle_ops_signal (signal_handle);
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__,
286 if (own_if_signalled (wait)) {
287 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__, wait);
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
298 /* Check before waiting on the condition, just in case
300 mono_w32handle_ops_prewait (wait);
302 if (own_if_signalled (wait)) {
303 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, wait);
309 if (timeout == INFINITE) {
310 waited = mono_w32handle_timedwait_signal_handle (wait, INFINITE, FALSE, alertable ? &apc_pending : NULL);
312 gint64 elapsed = mono_100ns_ticks () - wait_start;
313 if (elapsed >= timeout_in_ticks) {
318 waited = mono_w32handle_timedwait_signal_handle (wait, (timeout_in_ticks - elapsed) / 10 / 1000, FALSE, alertable ? &apc_pending : NULL);
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.)
326 if (own_if_signalled (wait)) {
327 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__,
334 /* Better luck next time */
336 } while(waited == 0 && !apc_pending);
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));
341 ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
345 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, wait);
347 thr_ret = mono_w32handle_unlock_handle (wait);
348 g_assert (thr_ret == 0);
353 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
354 gboolean waitall, guint32 *count,
360 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handles", __func__);
362 done = mono_w32handle_count_signalled_handles (numobjects, handles,
363 waitall, count, lowest);
365 if (waitall == TRUE) {
366 for (i = 0; i < numobjects; i++) {
367 own_if_signalled (handles[i]);
370 own_if_signalled (handles[*lowest]);
374 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handles", __func__);
376 mono_w32handle_unlock_handles (numobjects, handles);
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
389 * @timeout: The maximum time in milliseconds to wait for.
390 * @alertable: if TRUE, the wait can be interrupted by an APC call
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.
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.
411 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
412 gboolean waitall, guint32 timeout,
415 gboolean duplicate = FALSE, bogustype = FALSE, done;
416 guint32 count, lowest;
422 gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
423 gboolean apc_pending = FALSE;
424 gint64 wait_start, timeout_in_ticks;
426 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
427 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Too many handles: %d", __func__, numobjects);
432 if (numobjects == 1) {
433 return WaitForSingleObjectEx (handles [0], timeout, alertable);
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__,
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]);
454 sorted_handles [i] = handles [i];
455 mono_w32handle_ops_prewait (handles[i]);
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]) {
466 if (duplicate == TRUE) {
467 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to duplicates", __func__);
472 if (bogustype == TRUE) {
473 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning due to bogus type", __func__);
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 */
484 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
486 return(WAIT_OBJECT_0+lowest);
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
498 /* Have to wait for some or all handles to become signalled
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)
506 mono_w32handle_ref (handles[i]);
510 /* Prod all handles with prewait methods and
511 * special-wait handles that aren't already signalled
513 for (i = 0; i < numobjects; i++) {
514 mono_w32handle_ops_prewait (handles[i]);
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);
521 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking signal mutex", __func__);
523 thr_ret = mono_w32handle_lock_signal_mutex ();
524 g_assert (thr_ret == 0);
526 /* Check the signalled state of handles inside the critical section */
529 for (i = 0; i < numobjects; i++)
530 if (!mono_w32handle_issignalled (handles [i]))
534 for (i = 0; i < numobjects; i++)
535 if (mono_w32handle_issignalled (handles [i]))
541 if (timeout == INFINITE) {
542 ret = mono_w32handle_timedwait_signal (INFINITE, poll, &apc_pending);
544 gint64 elapsed = mono_100ns_ticks () - wait_start;
545 if (elapsed >= timeout_in_ticks) {
548 ret = mono_w32handle_timedwait_signal ((timeout_in_ticks - elapsed) / 10 / 1000, poll, &apc_pending);
552 /* No need to wait */
556 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking signal mutex", __func__);
558 thr_ret = mono_w32handle_unlock_signal_mutex ();
559 g_assert (thr_ret == 0);
561 if (alertable && apc_pending) {
562 retval = WAIT_IO_COMPLETION;
566 /* Check if everything is signalled, as we can't
567 * guarantee to notice a shared signal even if the
570 done = test_and_own (numobjects, handles, waitall,
573 retval = WAIT_OBJECT_0+lowest;
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__,
580 retval = WAIT_TIMEOUT;
585 for (i = 0; i < numobjects; i++) {
586 /* Unref everything we reffed above */
587 mono_w32handle_unref (handles[i]);
593 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
594 gboolean waitall, guint32 timeout)
596 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
601 * @handle: a handle to the process to wait for
602 * @timeout: the maximum time in milliseconds to wait for
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.
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
614 guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
616 /*TODO: Not implemented*/