2008-10-26 Zoltan Varga <vargaz@gmail.com>
[mono-project.git] / mono / io-layer / wait.c
blob6a24d83b5184ee62a74043fcb19e2ea715e9de7c
1 /*
2 * wait.c: wait for handles to become signalled
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002-2006 Novell, Inc.
8 */
10 #include <config.h>
11 #include <glib.h>
12 #include <string.h>
13 #include <errno.h>
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>
23 #undef DEBUG
25 static gboolean own_if_signalled(gpointer handle)
27 gboolean ret = FALSE;
29 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
30 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
31 return (FALSE);
35 if (_wapi_handle_issignalled (handle)) {
36 _wapi_handle_ops_own (handle);
37 ret = TRUE;
40 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
41 _wapi_handle_unlock_shared_handles ();
44 return(ret);
47 static gboolean own_if_owned(gpointer handle)
49 gboolean ret = FALSE;
51 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
52 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
53 return (FALSE);
57 if (_wapi_handle_ops_isowned (handle)) {
58 _wapi_handle_ops_own (handle);
59 ret = TRUE;
62 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
63 _wapi_handle_unlock_shared_handles ();
66 return(ret);
69 /**
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,
89 gboolean alertable)
91 guint32 ret, waited;
92 struct timespec abstime;
93 int thr_ret;
94 gboolean apc_pending = FALSE;
95 gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
97 if (current_thread == NULL) {
98 SetLastError (ERROR_INVALID_HANDLE);
99 return(WAIT_FAILED);
102 if (handle == _WAPI_THREAD_CURRENT) {
103 handle = _wapi_thread_handle_from_id (pthread_self ());
104 if (handle == NULL) {
105 SetLastError (ERROR_INVALID_HANDLE);
106 return(WAIT_FAILED);
110 if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
111 SetLastError (ERROR_INVALID_HANDLE);
112 return(WAIT_FAILED);
115 if (_wapi_handle_test_capabilities (handle,
116 WAPI_HANDLE_CAP_WAIT) == FALSE) {
117 #ifdef DEBUG
118 g_message ("%s: handle %p can't be waited for", __func__,
119 handle);
120 #endif
122 return(WAIT_FAILED);
125 _wapi_handle_ops_prewait (handle);
127 if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
128 #ifdef DEBUG
129 g_message ("%s: handle %p has special wait", __func__, handle);
130 #endif
132 ret = _wapi_handle_ops_special_wait (handle, timeout);
134 if (alertable && _wapi_thread_apc_pending (current_thread)) {
135 apc_pending = TRUE;
136 ret = WAIT_IO_COMPLETION;
139 goto check_pending;
143 #ifdef DEBUG
144 g_message ("%s: locking handle %p", __func__, handle);
145 #endif
147 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
148 handle);
149 thr_ret = _wapi_handle_lock_handle (handle);
150 g_assert (thr_ret == 0);
152 if (_wapi_handle_test_capabilities (handle,
153 WAPI_HANDLE_CAP_OWN) == TRUE) {
154 if (own_if_owned (handle) == TRUE) {
155 #ifdef DEBUG
156 g_message ("%s: handle %p already owned", __func__,
157 handle);
158 #endif
159 ret = WAIT_OBJECT_0;
160 goto done;
164 if (alertable && _wapi_thread_apc_pending (current_thread)) {
165 apc_pending = TRUE;
166 ret = WAIT_IO_COMPLETION;
167 goto done;
170 if (own_if_signalled (handle) == TRUE) {
171 #ifdef DEBUG
172 g_message ("%s: handle %p already signalled", __func__,
173 handle);
174 #endif
176 ret=WAIT_OBJECT_0;
177 goto done;
180 if (timeout == 0) {
181 ret = WAIT_TIMEOUT;
182 goto done;
184 /* Have to wait for it */
185 if (timeout != INFINITE) {
186 _wapi_calc_timeout (&abstime, timeout);
189 do {
190 /* Check before waiting on the condition, just in case
192 _wapi_handle_ops_prewait (handle);
194 if (own_if_signalled (handle)) {
195 #ifdef DEBUG
196 g_message ("%s: handle %p signalled", __func__,
197 handle);
198 #endif
200 ret = WAIT_OBJECT_0;
201 goto done;
204 if (timeout == INFINITE) {
205 waited = _wapi_handle_wait_signal_handle (handle, alertable);
206 } else {
207 waited = _wapi_handle_timedwait_signal_handle (handle, &abstime, alertable);
210 if (alertable)
211 apc_pending = _wapi_thread_apc_pending (current_thread);
213 if(waited==0 && !apc_pending) {
214 /* Condition was signalled, so hopefully
215 * handle is signalled now. (It might not be
216 * if someone else got in before us.)
218 if (own_if_signalled (handle)) {
219 #ifdef DEBUG
220 g_message ("%s: handle %p signalled", __func__,
221 handle);
222 #endif
224 ret=WAIT_OBJECT_0;
225 goto done;
228 /* Better luck next time */
230 } while(waited == 0 && !apc_pending);
232 /* Timeout or other error */
233 #ifdef DEBUG
234 g_message ("%s: wait on handle %p error: %s", __func__, handle,
235 strerror (waited));
236 #endif
238 ret = WAIT_TIMEOUT;
240 done:
242 #ifdef DEBUG
243 g_message ("%s: unlocking handle %p", __func__, handle);
244 #endif
246 thr_ret = _wapi_handle_unlock_handle (handle);
247 g_assert (thr_ret == 0);
248 pthread_cleanup_pop (0);
250 check_pending:
251 if (apc_pending) {
252 _wapi_thread_dispatch_apc_queue (current_thread);
253 ret = WAIT_IO_COMPLETION;
256 return(ret);
259 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
261 return WaitForSingleObjectEx (handle, timeout, FALSE);
266 * SignalObjectAndWait:
267 * @signal_handle: An object to signal
268 * @wait: An object to wait for
269 * @timeout: The maximum time in milliseconds to wait for
270 * @alertable: Specifies whether the function returnes when the system
271 * queues an I/O completion routine or an APC for the calling thread.
273 * Atomically signals @signal and waits for @wait to become signalled,
274 * or @timeout ms elapses. If @timeout is zero, the object's state is
275 * tested and the function returns immediately. If @timeout is
276 * %INFINITE, the function waits forever.
278 * @signal can be a semaphore, mutex or event object.
280 * If @alertable is %TRUE and the system queues an I/O completion
281 * routine or an APC for the calling thread, the function returns and
282 * the thread calls the completion routine or APC function. If
283 * %FALSE, the function does not return, and the thread does not call
284 * the completion routine or APC function. A completion routine is
285 * queued when the ReadFileEx() or WriteFileEx() function in which it
286 * was specified has completed. The calling thread is the thread that
287 * initiated the read or write operation. An APC is queued when
288 * QueueUserAPC() is called. Currently completion routines and APC
289 * functions are not supported.
291 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
292 * released by the owning thread when it exited. Ownershop of the
293 * mutex object is granted to the calling thread and the mutex is set
294 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
295 * or more user-mode asynchronous procedure calls queued to the
296 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
297 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
298 * still not signalled. %WAIT_FAILED - an error occurred.
300 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
301 guint32 timeout, gboolean alertable)
303 guint32 ret, waited;
304 struct timespec abstime;
305 int thr_ret;
306 gboolean apc_pending = FALSE;
307 gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
309 if (current_thread == NULL) {
310 SetLastError (ERROR_INVALID_HANDLE);
311 return(WAIT_FAILED);
314 if (signal_handle == _WAPI_THREAD_CURRENT) {
315 signal_handle = _wapi_thread_handle_from_id (pthread_self ());
316 if (signal_handle == NULL) {
317 SetLastError (ERROR_INVALID_HANDLE);
318 return(WAIT_FAILED);
322 if (wait == _WAPI_THREAD_CURRENT) {
323 wait = _wapi_thread_handle_from_id (pthread_self ());
324 if (wait == NULL) {
325 SetLastError (ERROR_INVALID_HANDLE);
326 return(WAIT_FAILED);
330 if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
331 SetLastError (ERROR_INVALID_HANDLE);
332 return(WAIT_FAILED);
335 if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
336 SetLastError (ERROR_INVALID_HANDLE);
337 return(WAIT_FAILED);
340 if (_wapi_handle_test_capabilities (signal_handle,
341 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
342 return(WAIT_FAILED);
345 if (_wapi_handle_test_capabilities (wait,
346 WAPI_HANDLE_CAP_WAIT)==FALSE) {
347 return(WAIT_FAILED);
350 _wapi_handle_ops_prewait (wait);
352 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
353 g_warning ("%s: handle %p has special wait, implement me!!",
354 __func__, wait);
356 return (WAIT_FAILED);
359 #ifdef DEBUG
360 g_message ("%s: locking handle %p", __func__, wait);
361 #endif
363 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
364 wait);
365 thr_ret = _wapi_handle_lock_handle (wait);
366 g_assert (thr_ret == 0);
368 _wapi_handle_ops_signal (signal_handle);
370 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
371 if (own_if_owned (wait)) {
372 #ifdef DEBUG
373 g_message ("%s: handle %p already owned", __func__,
374 wait);
375 #endif
376 ret = WAIT_OBJECT_0;
377 goto done;
381 if (alertable && _wapi_thread_apc_pending (current_thread)) {
382 apc_pending = TRUE;
383 ret = WAIT_IO_COMPLETION;
384 goto done;
387 if (own_if_signalled (wait)) {
388 #ifdef DEBUG
389 g_message ("%s: handle %p already signalled", __func__, wait);
390 #endif
392 ret = WAIT_OBJECT_0;
393 goto done;
396 /* Have to wait for it */
397 if (timeout != INFINITE) {
398 _wapi_calc_timeout (&abstime, timeout);
401 do {
402 /* Check before waiting on the condition, just in case
404 _wapi_handle_ops_prewait (wait);
406 if (own_if_signalled (wait)) {
407 #ifdef DEBUG
408 g_message ("%s: handle %p signalled", __func__, wait);
409 #endif
411 ret = WAIT_OBJECT_0;
412 goto done;
415 if (timeout == INFINITE) {
416 waited = _wapi_handle_wait_signal_handle (wait, alertable);
417 } else {
418 waited = _wapi_handle_timedwait_signal_handle (wait, &abstime, alertable);
421 if (alertable) {
422 apc_pending = _wapi_thread_apc_pending (current_thread);
425 if (waited==0 && !apc_pending) {
426 /* Condition was signalled, so hopefully
427 * handle is signalled now. (It might not be
428 * if someone else got in before us.)
430 if (own_if_signalled (wait)) {
431 #ifdef DEBUG
432 g_message ("%s: handle %p signalled", __func__,
433 wait);
434 #endif
436 ret = WAIT_OBJECT_0;
437 goto done;
440 /* Better luck next time */
442 } while(waited == 0 && !apc_pending);
444 /* Timeout or other error */
445 #ifdef DEBUG
446 g_message ("%s: wait on handle %p error: %s", __func__, wait,
447 strerror (ret));
448 #endif
450 ret = WAIT_TIMEOUT;
452 done:
454 #ifdef DEBUG
455 g_message ("%s: unlocking handle %p", __func__, wait);
456 #endif
458 thr_ret = _wapi_handle_unlock_handle (wait);
459 g_assert (thr_ret == 0);
460 pthread_cleanup_pop (0);
462 if (apc_pending) {
463 _wapi_thread_dispatch_apc_queue (current_thread);
464 ret = WAIT_IO_COMPLETION;
467 return(ret);
470 struct handle_cleanup_data
472 guint32 numobjects;
473 gpointer *handles;
476 static void handle_cleanup (void *data)
478 struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
480 _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
483 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
484 gboolean waitall, guint32 *count,
485 guint32 *lowest)
487 struct handle_cleanup_data cleanup_data;
488 gboolean done;
489 int i;
491 #ifdef DEBUG
492 g_message ("%s: locking handles", __func__);
493 #endif
494 cleanup_data.numobjects = numobjects;
495 cleanup_data.handles = handles;
497 pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
498 done = _wapi_handle_count_signalled_handles (numobjects, handles,
499 waitall, count, lowest);
500 if (done == TRUE) {
501 if (waitall == TRUE) {
502 for (i = 0; i < numobjects; i++) {
503 own_if_signalled (handles[i]);
505 } else {
506 own_if_signalled (handles[*lowest]);
510 #ifdef DEBUG
511 g_message ("%s: unlocking handles", __func__);
512 #endif
514 /* calls the unlock function */
515 pthread_cleanup_pop (1);
517 return(done);
523 * WaitForMultipleObjectsEx:
524 * @numobjects: The number of objects in @handles. The maximum allowed
525 * is %MAXIMUM_WAIT_OBJECTS.
526 * @handles: An array of object handles. Duplicates are not allowed.
527 * @waitall: If %TRUE, this function waits until all of the handles
528 * are signalled. If %FALSE, this function returns when any object is
529 * signalled.
530 * @timeout: The maximum time in milliseconds to wait for.
531 * @alertable: if TRUE, the wait can be interrupted by an APC call
533 * This function returns when either one or more of @handles is
534 * signalled, or @timeout ms elapses. If @timeout is zero, the state
535 * of each item of @handles is tested and the function returns
536 * immediately. If @timeout is %INFINITE, the function waits forever.
538 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
539 * if @waitall is %TRUE, indicates that all objects are signalled. If
540 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
541 * the first index into @handles of the objects that are signalled.
542 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
543 * @waitall is %TRUE, indicates that all objects are signalled, and at
544 * least one object is an abandoned mutex object (See
545 * WaitForSingleObject() for a description of abandoned mutexes.) If
546 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
547 * indicates the first index into @handles of an abandoned mutex.
548 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
549 * @handles are signalled. %WAIT_FAILED - an error occurred.
550 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
552 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
553 gboolean waitall, guint32 timeout,
554 gboolean alertable)
556 GHashTable *dups;
557 gboolean duplicate = FALSE, bogustype = FALSE, done;
558 guint32 count, lowest;
559 struct timespec abstime;
560 guint i;
561 guint32 ret;
562 int thr_ret;
563 gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
564 guint32 retval;
566 if (current_thread == NULL) {
567 SetLastError (ERROR_INVALID_HANDLE);
568 return(WAIT_FAILED);
571 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
572 #ifdef DEBUG
573 g_message ("%s: Too many handles: %d", __func__, numobjects);
574 #endif
576 return(WAIT_FAILED);
579 if (numobjects == 1) {
580 return WaitForSingleObjectEx (handles [0], timeout, alertable);
583 /* Check for duplicates */
584 dups = g_hash_table_new (g_direct_hash, g_direct_equal);
585 for (i = 0; i < numobjects; i++) {
586 gpointer exists;
588 if (handles[i] == _WAPI_THREAD_CURRENT) {
589 handles[i] = _wapi_thread_handle_from_id (pthread_self ());
591 if (handles[i] == NULL) {
592 #ifdef DEBUG
593 g_message ("%s: Handle %d bogus", __func__, i);
594 #endif
596 bogustype = TRUE;
597 break;
601 if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
602 #ifdef DEBUG
603 g_message ("%s: Handle %d pseudo process", __func__,
605 #endif
607 bogustype = TRUE;
608 break;
611 exists = g_hash_table_lookup (dups, handles[i]);
612 if (exists != NULL) {
613 #ifdef DEBUG
614 g_message ("%s: Handle %p duplicated", __func__,
615 handles[i]);
616 #endif
618 duplicate = TRUE;
619 break;
622 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
623 #ifdef DEBUG
624 g_message ("%s: Handle %p can't be waited for",
625 __func__, handles[i]);
626 #endif
628 bogustype = TRUE;
631 g_hash_table_insert (dups, handles[i], handles[i]);
632 _wapi_handle_ops_prewait (handles[i]);
634 g_hash_table_destroy (dups);
636 if (duplicate == TRUE) {
637 #ifdef DEBUG
638 g_message ("%s: Returning due to duplicates", __func__);
639 #endif
641 return(WAIT_FAILED);
644 if (bogustype == TRUE) {
645 #ifdef DEBUG
646 g_message ("%s: Returning due to bogus type", __func__);
647 #endif
649 return(WAIT_FAILED);
652 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
653 if (done == TRUE) {
654 return(WAIT_OBJECT_0+lowest);
657 if (timeout == 0) {
658 return WAIT_TIMEOUT;
660 /* Have to wait for some or all handles to become signalled
663 if(timeout!=INFINITE) {
664 _wapi_calc_timeout (&abstime, timeout);
667 if (alertable && _wapi_thread_apc_pending (current_thread)) {
668 _wapi_thread_dispatch_apc_queue (current_thread);
669 return WAIT_IO_COMPLETION;
672 for (i = 0; i < numobjects; i++) {
673 /* Add a reference, as we need to ensure the handle wont
674 * disappear from under us while we're waiting in the loop
675 * (not lock, as we don't want exclusive access here)
677 _wapi_handle_ref (handles[i]);
680 while(1) {
681 /* Prod all handles with prewait methods and
682 * special-wait handles that aren't already signalled
684 for (i = 0; i < numobjects; i++) {
685 _wapi_handle_ops_prewait (handles[i]);
687 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
688 _wapi_handle_ops_special_wait (handles[i], 0);
692 /* Check before waiting on the condition, just in case
694 done = test_and_own (numobjects, handles, waitall,
695 &count, &lowest);
696 if (done == TRUE) {
697 retval = WAIT_OBJECT_0 + lowest;
698 break;
701 #ifdef DEBUG
702 g_message ("%s: locking signal mutex", __func__);
703 #endif
705 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
706 thr_ret = _wapi_handle_lock_signal_mutex ();
707 g_assert (thr_ret == 0);
709 if (timeout == INFINITE) {
710 ret = _wapi_handle_wait_signal ();
711 } else {
712 ret = _wapi_handle_timedwait_signal (&abstime);
715 #ifdef DEBUG
716 g_message ("%s: unlocking signal mutex", __func__);
717 #endif
719 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
720 g_assert (thr_ret == 0);
721 pthread_cleanup_pop (0);
723 if (alertable && _wapi_thread_apc_pending (current_thread)) {
724 _wapi_thread_dispatch_apc_queue (current_thread);
725 retval = WAIT_IO_COMPLETION;
726 break;
729 /* Check if everything is signalled, as we can't
730 * guarantee to notice a shared signal even if the
731 * wait timed out
733 done = test_and_own (numobjects, handles, waitall,
734 &count, &lowest);
735 if (done == TRUE) {
736 retval = WAIT_OBJECT_0+lowest;
737 break;
738 } else if (ret != 0) {
739 /* Didn't get all handles, and there was a
740 * timeout or other error
742 #ifdef DEBUG
743 g_message ("%s: wait returned error: %s", __func__,
744 strerror (ret));
745 #endif
747 if(ret==ETIMEDOUT) {
748 retval = WAIT_TIMEOUT;
749 } else {
750 retval = WAIT_FAILED;
752 break;
756 for (i = 0; i < numobjects; i++) {
757 /* Unref everything we reffed above */
758 _wapi_handle_unref (handles[i]);
761 return retval;
764 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
765 gboolean waitall, guint32 timeout)
767 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);