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)
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,
92 struct timespec abstime;
94 gboolean apc_pending = FALSE;
95 gpointer current_thread = GetCurrentThread ();
97 if (_wapi_handle_test_capabilities (handle,
98 WAPI_HANDLE_CAP_WAIT) == FALSE) {
100 g_message ("%s: handle %p can't be waited for", __func__,
107 _wapi_handle_ops_prewait (handle);
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__,
166 /* Have to wait for it */
167 if (timeout != INFINITE) {
168 _wapi_calc_timeout (&abstime, timeout);
172 /* Check before waiting on the condition, just in case
174 _wapi_handle_ops_prewait (handle);
176 if (own_if_signalled (handle)) {
178 g_message ("%s: handle %p signalled", __func__,
186 if (timeout == INFINITE) {
187 waited = _wapi_handle_wait_signal_handle (handle);
189 waited = _wapi_handle_timedwait_signal_handle (handle, &abstime);
193 apc_pending = _wapi_thread_apc_pending (current_thread);
195 if(waited==0 && !apc_pending) {
196 /* Condition was signalled, so hopefully
197 * handle is signalled now. (It might not be
198 * if someone else got in before us.)
200 if (own_if_signalled (handle)) {
202 g_message ("%s: handle %p signalled", __func__,
210 /* Better luck next time */
212 } while(waited == 0 && !apc_pending);
214 /* Timeout or other error */
216 g_message ("%s: wait on handle %p error: %s", __func__, handle,
225 g_message ("%s: unlocking handle %p", __func__, handle);
228 thr_ret = _wapi_handle_unlock_handle (handle);
229 g_assert (thr_ret == 0);
230 pthread_cleanup_pop (0);
234 _wapi_thread_dispatch_apc_queue (current_thread);
235 ret = WAIT_IO_COMPLETION;
241 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
243 return WaitForSingleObjectEx (handle, timeout, FALSE);
248 * SignalObjectAndWait:
249 * @signal_handle: An object to signal
250 * @wait: An object to wait for
251 * @timeout: The maximum time in milliseconds to wait for
252 * @alertable: Specifies whether the function returnes when the system
253 * queues an I/O completion routine or an APC for the calling thread.
255 * Atomically signals @signal and waits for @wait to become signalled,
256 * or @timeout ms elapses. If @timeout is zero, the object's state is
257 * tested and the function returns immediately. If @timeout is
258 * %INFINITE, the function waits forever.
260 * @signal can be a semaphore, mutex or event object.
262 * If @alertable is %TRUE and the system queues an I/O completion
263 * routine or an APC for the calling thread, the function returns and
264 * the thread calls the completion routine or APC function. If
265 * %FALSE, the function does not return, and the thread does not call
266 * the completion routine or APC function. A completion routine is
267 * queued when the ReadFileEx() or WriteFileEx() function in which it
268 * was specified has completed. The calling thread is the thread that
269 * initiated the read or write operation. An APC is queued when
270 * QueueUserAPC() is called. Currently completion routines and APC
271 * functions are not supported.
273 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
274 * released by the owning thread when it exited. Ownershop of the
275 * mutex object is granted to the calling thread and the mutex is set
276 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
277 * or more user-mode asynchronous procedure calls queued to the
278 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
279 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
280 * still not signalled. %WAIT_FAILED - an error occurred.
282 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
283 guint32 timeout, gboolean alertable)
286 struct timespec abstime;
288 gboolean apc_pending = FALSE;
289 gpointer current_thread = GetCurrentThread ();
291 if (_wapi_handle_test_capabilities (signal_handle,
292 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
296 if (_wapi_handle_test_capabilities (wait,
297 WAPI_HANDLE_CAP_WAIT)==FALSE) {
301 _wapi_handle_ops_prewait (wait);
303 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
304 g_warning ("%s: handle %p has special wait, implement me!!",
307 return (WAIT_FAILED);
311 g_message ("%s: locking handle %p", __func__, wait);
314 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
316 thr_ret = _wapi_handle_lock_handle (wait);
317 g_assert (thr_ret == 0);
319 _wapi_handle_ops_signal (signal_handle);
321 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
322 if (own_if_owned (wait)) {
324 g_message ("%s: handle %p already owned", __func__,
332 if (alertable && _wapi_thread_apc_pending (current_thread)) {
334 ret = WAIT_IO_COMPLETION;
338 if (own_if_signalled (wait)) {
340 g_message ("%s: handle %p already signalled", __func__, wait);
347 /* Have to wait for it */
348 if (timeout != INFINITE) {
349 _wapi_calc_timeout (&abstime, timeout);
353 /* Check before waiting on the condition, just in case
355 _wapi_handle_ops_prewait (wait);
357 if (own_if_signalled (wait)) {
359 g_message ("%s: handle %p signalled", __func__, wait);
366 if (timeout == INFINITE) {
367 waited = _wapi_handle_wait_signal_handle (wait);
369 waited = _wapi_handle_timedwait_signal_handle (wait, &abstime);
373 apc_pending = _wapi_thread_apc_pending (current_thread);
376 if (waited==0 && !apc_pending) {
377 /* Condition was signalled, so hopefully
378 * handle is signalled now. (It might not be
379 * if someone else got in before us.)
381 if (own_if_signalled (wait)) {
383 g_message ("%s: handle %p signalled", __func__,
391 /* Better luck next time */
393 } while(waited == 0 && !apc_pending);
395 /* Timeout or other error */
397 g_message ("%s: wait on handle %p error: %s", __func__, wait,
406 g_message ("%s: unlocking handle %p", __func__, wait);
409 thr_ret = _wapi_handle_unlock_handle (wait);
410 g_assert (thr_ret == 0);
411 pthread_cleanup_pop (0);
414 _wapi_thread_dispatch_apc_queue (current_thread);
415 ret = WAIT_IO_COMPLETION;
421 struct handle_cleanup_data
427 static void handle_cleanup (void *data)
429 struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
431 _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
434 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
435 gboolean waitall, guint32 *count,
438 struct handle_cleanup_data cleanup_data;
443 g_message ("%s: locking handles", __func__);
445 cleanup_data.numobjects = numobjects;
446 cleanup_data.handles = handles;
448 pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
449 done = _wapi_handle_count_signalled_handles (numobjects, handles,
450 waitall, count, lowest);
452 if (waitall == TRUE) {
453 for (i = 0; i < numobjects; i++) {
454 own_if_signalled (handles[i]);
457 own_if_signalled (handles[*lowest]);
462 g_message ("%s: unlocking handles", __func__);
465 /* calls the unlock function */
466 pthread_cleanup_pop (1);
474 * WaitForMultipleObjectsEx:
475 * @numobjects: The number of objects in @handles. The maximum allowed
476 * is %MAXIMUM_WAIT_OBJECTS.
477 * @handles: An array of object handles. Duplicates are not allowed.
478 * @waitall: If %TRUE, this function waits until all of the handles
479 * are signalled. If %FALSE, this function returns when any object is
481 * @timeout: The maximum time in milliseconds to wait for.
482 * @alertable: if TRUE, the wait can be interrupted by an APC call
484 * This function returns when either one or more of @handles is
485 * signalled, or @timeout ms elapses. If @timeout is zero, the state
486 * of each item of @handles is tested and the function returns
487 * immediately. If @timeout is %INFINITE, the function waits forever.
489 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
490 * if @waitall is %TRUE, indicates that all objects are signalled. If
491 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
492 * the first index into @handles of the objects that are signalled.
493 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
494 * @waitall is %TRUE, indicates that all objects are signalled, and at
495 * least one object is an abandoned mutex object (See
496 * WaitForSingleObject() for a description of abandoned mutexes.) If
497 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
498 * indicates the first index into @handles of an abandoned mutex.
499 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
500 * @handles are signalled. %WAIT_FAILED - an error occurred.
501 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
503 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
504 gboolean waitall, guint32 timeout,
508 gboolean duplicate = FALSE, bogustype = FALSE, done;
509 guint32 count, lowest;
510 struct timespec abstime;
514 gpointer current_thread = GetCurrentThread ();
516 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
518 g_message ("%s: Too many handles: %d", __func__, numobjects);
524 if (numobjects == 1) {
525 return WaitForSingleObjectEx (handles [0], timeout, alertable);
528 /* Check for duplicates */
529 dups = g_hash_table_new (g_direct_hash, g_direct_equal);
530 for (i = 0; i < numobjects; i++) {
531 gpointer exists = g_hash_table_lookup (dups, handles[i]);
532 if (exists != NULL) {
534 g_message ("%s: Handle %p duplicated", __func__,
542 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
544 g_message ("%s: Handle %p can't be waited for",
545 __func__, handles[i]);
551 g_hash_table_insert (dups, handles[i], handles[i]);
552 _wapi_handle_ops_prewait (handles[i]);
554 g_hash_table_destroy (dups);
556 if (duplicate == TRUE) {
558 g_message ("%s: Returning due to duplicates", __func__);
564 if (bogustype == TRUE) {
566 g_message ("%s: Returning due to bogus type", __func__);
572 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
574 return(WAIT_OBJECT_0+lowest);
580 /* Have to wait for some or all handles to become signalled
583 if(timeout!=INFINITE) {
584 _wapi_calc_timeout (&abstime, timeout);
587 if (alertable && _wapi_thread_apc_pending (current_thread)) {
588 _wapi_thread_dispatch_apc_queue (current_thread);
589 return WAIT_IO_COMPLETION;
593 /* Prod all handles with prewait methods and
594 * special-wait handles that aren't already signalled
596 for (i = 0; i < numobjects; i++) {
597 _wapi_handle_ops_prewait (handles[i]);
599 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
600 _wapi_handle_ops_special_wait (handles[i], 0);
604 /* Check before waiting on the condition, just in case
606 done = test_and_own (numobjects, handles, waitall,
609 return(WAIT_OBJECT_0 + lowest);
613 g_message ("%s: locking signal mutex", __func__);
616 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
617 thr_ret = _wapi_handle_lock_signal_mutex ();
618 g_assert (thr_ret == 0);
620 if (timeout == INFINITE) {
621 ret = _wapi_handle_wait_signal ();
623 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);