2 * wait.c: wait for handles to become signalled
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
15 #include <mono/os/gc_wrapper.h>
17 #include <mono/io-layer/wapi.h>
18 #include <mono/io-layer/handles-private.h>
19 #include <mono/io-layer/wapi-private.h>
20 #include <mono/io-layer/mono-mutex.h>
21 #include <mono/io-layer/misc-private.h>
25 static gboolean own_if_signalled(gpointer handle)
28 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
30 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
31 if (_wapi_handle_shared_trylock_handle (handle, now) == EBUSY) {
36 if (_wapi_handle_issignalled (handle)) {
37 _wapi_handle_ops_own (handle);
41 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
42 _wapi_handle_shared_unlock_handle (handle, now);
48 static gboolean own_if_owned(gpointer handle)
51 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
53 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
54 if (_wapi_handle_shared_trylock_handle (handle, now) == EBUSY) {
59 if (_wapi_handle_ops_isowned (handle)) {
60 _wapi_handle_ops_own (handle);
64 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
65 _wapi_handle_shared_unlock_handle (handle, now);
72 * WaitForSingleObjectEx:
73 * @handle: an object to wait for
74 * @timeout: the maximum time in milliseconds to wait for
75 * @alertable: if TRUE, the wait can be interrupted by an APC call
77 * This function returns when either @handle is signalled, or @timeout
78 * ms elapses. If @timeout is zero, the object's state is tested and
79 * the function returns immediately. If @timeout is %INFINITE, the
80 * function waits forever.
82 * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
83 * released by the owning thread when it exited. Ownership of the
84 * mutex object is granted to the calling thread and the mutex is set
85 * to nonsignalled. %WAIT_OBJECT_0 - The state of @handle is
86 * signalled. %WAIT_TIMEOUT - The @timeout interval elapsed and
87 * @handle's state is still not signalled. %WAIT_FAILED - an error
88 * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
90 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
94 struct timespec abstime;
96 gboolean apc_pending = FALSE;
97 gpointer current_thread = GetCurrentThread ();
99 if (_wapi_handle_test_capabilities (handle,
100 WAPI_HANDLE_CAP_WAIT) == FALSE) {
102 g_message ("%s: handle %p can't be waited for", __func__,
109 if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
111 g_message ("%s: handle %p has special wait", __func__, handle);
114 ret = _wapi_handle_ops_special_wait (handle, timeout);
116 if (alertable && _wapi_thread_apc_pending (current_thread)) {
118 ret = WAIT_IO_COMPLETION;
126 g_message ("%s: locking handle %p", __func__, handle);
129 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
131 thr_ret = _wapi_handle_lock_handle (handle);
132 g_assert (thr_ret == 0);
134 if (_wapi_handle_test_capabilities (handle,
135 WAPI_HANDLE_CAP_OWN) == TRUE) {
136 if (own_if_owned (handle) == TRUE) {
138 g_message ("%s: handle %p already owned", __func__,
146 if (alertable && _wapi_thread_apc_pending (current_thread)) {
148 ret = WAIT_IO_COMPLETION;
152 if (own_if_signalled (handle) == TRUE) {
154 g_message ("%s: handle %p already signalled", __func__,
162 /* Have to wait for it */
163 if (timeout != INFINITE) {
164 _wapi_calc_timeout (&abstime, timeout);
168 /* Check before waiting on the condition, just in case
170 if (own_if_signalled (handle)) {
172 g_message ("%s: handle %p signalled", __func__,
180 if (timeout == INFINITE) {
181 waited = _wapi_handle_wait_signal_handle (handle);
183 waited = _wapi_handle_timedwait_signal_handle (handle, &abstime);
187 apc_pending = _wapi_thread_apc_pending (current_thread);
189 if(waited==0 && !apc_pending) {
190 /* Condition was signalled, so hopefully
191 * handle is signalled now. (It might not be
192 * if someone else got in before us.)
194 if (own_if_signalled (handle)) {
196 g_message ("%s: handle %p signalled", __func__,
204 /* Better luck next time */
206 } while(waited == 0 && !apc_pending);
208 /* Timeout or other error */
210 g_message ("%s: wait on handle %p error: %s", __func__, handle,
219 g_message ("%s: unlocking handle %p", __func__, handle);
222 thr_ret = _wapi_handle_unlock_handle (handle);
223 g_assert (thr_ret == 0);
224 pthread_cleanup_pop (0);
228 _wapi_thread_dispatch_apc_queue (current_thread);
229 ret = WAIT_IO_COMPLETION;
235 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
237 return WaitForSingleObjectEx (handle, timeout, FALSE);
242 * SignalObjectAndWait:
243 * @signal_handle: An object to signal
244 * @wait: An object to wait for
245 * @timeout: The maximum time in milliseconds to wait for
246 * @alertable: Specifies whether the function returnes when the system
247 * queues an I/O completion routine or an APC for the calling thread.
249 * Atomically signals @signal and waits for @wait to become signalled,
250 * or @timeout ms elapses. If @timeout is zero, the object's state is
251 * tested and the function returns immediately. If @timeout is
252 * %INFINITE, the function waits forever.
254 * @signal can be a semaphore, mutex or event object.
256 * If @alertable is %TRUE and the system queues an I/O completion
257 * routine or an APC for the calling thread, the function returns and
258 * the thread calls the completion routine or APC function. If
259 * %FALSE, the function does not return, and the thread does not call
260 * the completion routine or APC function. A completion routine is
261 * queued when the ReadFileEx() or WriteFileEx() function in which it
262 * was specified has completed. The calling thread is the thread that
263 * initiated the read or write operation. An APC is queued when
264 * QueueUserAPC() is called. Currently completion routines and APC
265 * functions are not supported.
267 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
268 * released by the owning thread when it exited. Ownershop of the
269 * mutex object is granted to the calling thread and the mutex is set
270 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
271 * or more user-mode asynchronous procedure calls queued to the
272 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
273 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
274 * still not signalled. %WAIT_FAILED - an error occurred.
276 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
277 guint32 timeout, gboolean alertable)
280 struct timespec abstime;
282 gboolean apc_pending = FALSE;
283 gpointer current_thread = GetCurrentThread ();
285 if (_wapi_handle_test_capabilities (signal_handle,
286 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
290 if (_wapi_handle_test_capabilities (wait,
291 WAPI_HANDLE_CAP_WAIT)==FALSE) {
295 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
296 g_warning ("%s: handle %p has special wait, implement me!!",
299 return (WAIT_FAILED);
303 g_message ("%s: locking handle %p", __func__, wait);
306 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
308 thr_ret = _wapi_handle_lock_handle (wait);
309 g_assert (thr_ret == 0);
311 _wapi_handle_ops_signal (signal_handle);
313 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
314 if (own_if_owned (wait)) {
316 g_message ("%s: handle %p already owned", __func__,
324 if (alertable && _wapi_thread_apc_pending (current_thread)) {
326 ret = WAIT_IO_COMPLETION;
330 if (own_if_signalled (wait)) {
332 g_message ("%s: handle %p already signalled", __func__, wait);
339 /* Have to wait for it */
340 if (timeout != INFINITE) {
341 _wapi_calc_timeout (&abstime, timeout);
345 /* Check before waiting on the condition, just in case
347 if (own_if_signalled (wait)) {
349 g_message ("%s: handle %p signalled", __func__, wait);
356 if (timeout == INFINITE) {
357 waited = _wapi_handle_wait_signal_handle (wait);
359 waited = _wapi_handle_timedwait_signal_handle (wait, &abstime);
363 apc_pending = _wapi_thread_apc_pending (current_thread);
366 if (waited==0 && !apc_pending) {
367 /* Condition was signalled, so hopefully
368 * handle is signalled now. (It might not be
369 * if someone else got in before us.)
371 if (own_if_signalled (wait)) {
373 g_message ("%s: handle %p signalled", __func__,
381 /* Better luck next time */
383 } while(waited == 0 && !apc_pending);
385 /* Timeout or other error */
387 g_message ("%s: wait on handle %p error: %s", __func__, wait,
396 g_message ("%s: unlocking handle %p", __func__, wait);
399 thr_ret = _wapi_handle_unlock_handle (wait);
400 g_assert (thr_ret == 0);
401 pthread_cleanup_pop (0);
404 _wapi_thread_dispatch_apc_queue (current_thread);
405 ret = WAIT_IO_COMPLETION;
411 struct handle_cleanup_data
418 static void handle_cleanup (void *data)
420 struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
422 _wapi_handle_unlock_handles (handles->numobjects, handles->handles,
426 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
427 gboolean waitall, guint32 *count,
430 struct handle_cleanup_data cleanup_data;
435 g_message ("%s: locking handles", __func__);
437 cleanup_data.numobjects = numobjects;
438 cleanup_data.handles = handles;
440 pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
441 done = _wapi_handle_count_signalled_handles (numobjects, handles,
442 waitall, count, lowest,
445 if (waitall == TRUE) {
446 for (i = 0; i < numobjects; i++) {
447 own_if_signalled (handles[i]);
450 own_if_signalled (handles[*lowest]);
455 g_message ("%s: unlocking handles", __func__);
458 /* calls the unlock function */
459 pthread_cleanup_pop (1);
467 * WaitForMultipleObjectsEx:
468 * @numobjects: The number of objects in @handles. The maximum allowed
469 * is %MAXIMUM_WAIT_OBJECTS.
470 * @handles: An array of object handles. Duplicates are not allowed.
471 * @waitall: If %TRUE, this function waits until all of the handles
472 * are signalled. If %FALSE, this function returns when any object is
474 * @timeout: The maximum time in milliseconds to wait for.
475 * @alertable: if TRUE, the wait can be interrupted by an APC call
477 * This function returns when either one or more of @handles is
478 * signalled, or @timeout ms elapses. If @timeout is zero, the state
479 * of each item of @handles is tested and the function returns
480 * immediately. If @timeout is %INFINITE, the function waits forever.
482 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
483 * if @waitall is %TRUE, indicates that all objects are signalled. If
484 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
485 * the first index into @handles of the objects that are signalled.
486 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
487 * @waitall is %TRUE, indicates that all objects are signalled, and at
488 * least one object is an abandoned mutex object (See
489 * WaitForSingleObject() for a description of abandoned mutexes.) If
490 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
491 * indicates the first index into @handles of an abandoned mutex.
492 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
493 * @handles are signalled. %WAIT_FAILED - an error occurred.
494 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
496 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
497 gboolean waitall, guint32 timeout,
501 gboolean duplicate = FALSE, bogustype = FALSE, done;
502 gboolean shared_wait = FALSE;
503 guint32 count, lowest;
504 struct timespec abstime;
508 gpointer current_thread = GetCurrentThread ();
510 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
512 g_message ("%s: Too many handles: %d", __func__, numobjects);
518 if (numobjects == 1) {
519 return WaitForSingleObjectEx (handles [0], timeout, alertable);
522 /* Check for duplicates */
523 dups = g_hash_table_new (g_direct_hash, g_direct_equal);
524 for (i = 0; i < numobjects; i++) {
525 gpointer exists = g_hash_table_lookup (dups, handles[i]);
526 if (exists != NULL) {
528 g_message ("%s: Handle %p duplicated", __func__,
536 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
538 g_message ("%s: Handle %p can't be waited for",
539 __func__, handles[i]);
545 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handles[i]))) {
549 g_hash_table_insert (dups, handles[i], handles[i]);
551 g_hash_table_destroy (dups);
553 if (duplicate == TRUE) {
555 g_message ("%s: Returning due to duplicates", __func__);
561 if (bogustype == TRUE) {
563 g_message ("%s: Returning due to bogus type", __func__);
569 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
571 return(WAIT_OBJECT_0+lowest);
574 /* Have to wait for some or all handles to become signalled
577 if(timeout!=INFINITE) {
578 _wapi_calc_timeout (&abstime, timeout);
581 if (alertable && _wapi_thread_apc_pending (current_thread)) {
582 _wapi_thread_dispatch_apc_queue (current_thread);
583 return WAIT_IO_COMPLETION;
587 /* Prod all special-wait handles that aren't already
590 for (i = 0; i < numobjects; i++) {
591 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
592 _wapi_handle_ops_special_wait (handles[i], 0);
596 /* Check before waiting on the condition, just in case
598 done = test_and_own (numobjects, handles, waitall,
601 return(WAIT_OBJECT_0 + lowest);
605 g_message ("%s: locking signal mutex", __func__);
608 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
609 thr_ret = _wapi_handle_lock_signal_mutex ();
610 g_assert (thr_ret == 0);
612 if (shared_wait == TRUE) {
613 if (timeout == INFINITE) {
614 ret = _wapi_handle_wait_signal_poll_share ();
616 ret = _wapi_handle_timedwait_signal_poll_share (&abstime);
619 if (timeout == INFINITE) {
620 ret = _wapi_handle_wait_signal ();
622 ret = _wapi_handle_timedwait_signal (&abstime);
627 g_message ("%s: unlocking signal mutex", __func__);
630 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
631 g_assert (thr_ret == 0);
632 pthread_cleanup_pop (0);
634 if (alertable && _wapi_thread_apc_pending (current_thread)) {
635 _wapi_thread_dispatch_apc_queue (current_thread);
636 return WAIT_IO_COMPLETION;
639 /* Check if everything is signalled, as we can't
640 * guarantee to notice a shared signal even if the
643 done = test_and_own (numobjects, handles, waitall,
646 return(WAIT_OBJECT_0+lowest);
647 } else if (ret != 0) {
648 /* Didn't get all handles, and there was a
649 * timeout or other error
652 g_message ("%s: wait returned error: %s", __func__,
657 return(WAIT_TIMEOUT);
665 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
666 gboolean waitall, guint32 timeout)
668 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);