Add System.Net.Http to build
[mono-project.git] / mono / io-layer / wait.c
blob7981ef23466f4fd0d47701202822d1eee19d83f7
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/io-layer/wapi.h>
16 #include <mono/io-layer/handles-private.h>
17 #include <mono/io-layer/wapi-private.h>
18 #include <mono/io-layer/mono-mutex.h>
19 #include <mono/io-layer/misc-private.h>
21 #if 0
22 #define DEBUG(...) g_message(__VA_ARGS__)
23 #else
24 #define DEBUG(...)
25 #endif
27 static gboolean own_if_signalled(gpointer handle)
29 gboolean ret = FALSE;
31 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
32 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
33 return (FALSE);
37 if (_wapi_handle_issignalled (handle)) {
38 _wapi_handle_ops_own (handle);
39 ret = TRUE;
42 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
43 _wapi_handle_unlock_shared_handles ();
46 return(ret);
49 static gboolean own_if_owned(gpointer handle)
51 gboolean ret = FALSE;
53 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
54 if (_wapi_handle_trylock_shared_handles () == EBUSY) {
55 return (FALSE);
59 if (_wapi_handle_ops_isowned (handle)) {
60 _wapi_handle_ops_own (handle);
61 ret = TRUE;
64 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
65 _wapi_handle_unlock_shared_handles ();
68 return(ret);
71 /**
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,
91 gboolean alertable)
93 guint32 ret, waited;
94 struct timespec abstime;
95 int thr_ret;
96 gboolean apc_pending = FALSE;
97 gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
99 if (current_thread == NULL) {
100 SetLastError (ERROR_INVALID_HANDLE);
101 return(WAIT_FAILED);
104 if (handle == _WAPI_THREAD_CURRENT) {
105 handle = _wapi_thread_handle_from_id (pthread_self ());
106 if (handle == NULL) {
107 SetLastError (ERROR_INVALID_HANDLE);
108 return(WAIT_FAILED);
112 if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
113 SetLastError (ERROR_INVALID_HANDLE);
114 return(WAIT_FAILED);
117 if (_wapi_handle_test_capabilities (handle,
118 WAPI_HANDLE_CAP_WAIT) == FALSE) {
119 DEBUG ("%s: handle %p can't be waited for", __func__,
120 handle);
122 return(WAIT_FAILED);
125 _wapi_handle_ops_prewait (handle);
127 if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
128 DEBUG ("%s: handle %p has special wait", __func__, handle);
130 ret = _wapi_handle_ops_special_wait (handle, timeout, alertable);
132 if (alertable && _wapi_thread_apc_pending (current_thread)) {
133 apc_pending = TRUE;
134 ret = WAIT_IO_COMPLETION;
137 goto check_pending;
141 DEBUG ("%s: locking handle %p", __func__, handle);
143 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
144 handle);
145 thr_ret = _wapi_handle_lock_handle (handle);
146 g_assert (thr_ret == 0);
148 if (_wapi_handle_test_capabilities (handle,
149 WAPI_HANDLE_CAP_OWN) == TRUE) {
150 if (own_if_owned (handle) == TRUE) {
151 DEBUG ("%s: handle %p already owned", __func__,
152 handle);
153 ret = WAIT_OBJECT_0;
154 goto done;
158 if (alertable && _wapi_thread_apc_pending (current_thread)) {
159 apc_pending = TRUE;
160 ret = WAIT_IO_COMPLETION;
161 goto done;
164 if (own_if_signalled (handle) == TRUE) {
165 DEBUG ("%s: handle %p already signalled", __func__,
166 handle);
168 ret=WAIT_OBJECT_0;
169 goto done;
172 if (timeout == 0) {
173 ret = WAIT_TIMEOUT;
174 goto done;
176 /* Have to wait for it */
177 if (timeout != INFINITE) {
178 _wapi_calc_timeout (&abstime, timeout);
181 do {
182 /* Check before waiting on the condition, just in case
184 _wapi_handle_ops_prewait (handle);
186 if (own_if_signalled (handle)) {
187 DEBUG ("%s: handle %p signalled", __func__,
188 handle);
190 ret = WAIT_OBJECT_0;
191 goto done;
194 if (timeout == INFINITE) {
195 waited = _wapi_handle_wait_signal_handle (handle, alertable);
196 } else {
197 waited = _wapi_handle_timedwait_signal_handle (handle, &abstime, alertable, FALSE);
200 if (alertable)
201 apc_pending = _wapi_thread_apc_pending (current_thread);
203 if(waited==0 && !apc_pending) {
204 /* Condition was signalled, so hopefully
205 * handle is signalled now. (It might not be
206 * if someone else got in before us.)
208 if (own_if_signalled (handle)) {
209 DEBUG ("%s: handle %p signalled", __func__,
210 handle);
212 ret=WAIT_OBJECT_0;
213 goto done;
216 /* Better luck next time */
218 } while(waited == 0 && !apc_pending);
220 /* Timeout or other error */
221 DEBUG ("%s: wait on handle %p error: %s", __func__, handle,
222 strerror (waited));
224 ret = WAIT_TIMEOUT;
226 done:
228 DEBUG ("%s: unlocking handle %p", __func__, handle);
230 thr_ret = _wapi_handle_unlock_handle (handle);
231 g_assert (thr_ret == 0);
232 pthread_cleanup_pop (0);
234 check_pending:
235 if (apc_pending) {
236 _wapi_thread_dispatch_apc_queue (current_thread);
237 ret = WAIT_IO_COMPLETION;
240 return(ret);
243 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
245 return WaitForSingleObjectEx (handle, timeout, FALSE);
250 * SignalObjectAndWait:
251 * @signal_handle: An object to signal
252 * @wait: An object to wait for
253 * @timeout: The maximum time in milliseconds to wait for
254 * @alertable: Specifies whether the function returnes when the system
255 * queues an I/O completion routine or an APC for the calling thread.
257 * Atomically signals @signal and waits for @wait to become signalled,
258 * or @timeout ms elapses. If @timeout is zero, the object's state is
259 * tested and the function returns immediately. If @timeout is
260 * %INFINITE, the function waits forever.
262 * @signal can be a semaphore, mutex or event object.
264 * If @alertable is %TRUE and the system queues an I/O completion
265 * routine or an APC for the calling thread, the function returns and
266 * the thread calls the completion routine or APC function. If
267 * %FALSE, the function does not return, and the thread does not call
268 * the completion routine or APC function. A completion routine is
269 * queued when the ReadFileEx() or WriteFileEx() function in which it
270 * was specified has completed. The calling thread is the thread that
271 * initiated the read or write operation. An APC is queued when
272 * QueueUserAPC() is called. Currently completion routines and APC
273 * functions are not supported.
275 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
276 * released by the owning thread when it exited. Ownershop of the
277 * mutex object is granted to the calling thread and the mutex is set
278 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
279 * or more user-mode asynchronous procedure calls queued to the
280 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
281 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
282 * still not signalled. %WAIT_FAILED - an error occurred.
284 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
285 guint32 timeout, gboolean alertable)
287 guint32 ret, waited;
288 struct timespec abstime;
289 int thr_ret;
290 gboolean apc_pending = FALSE;
291 gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
293 if (current_thread == NULL) {
294 SetLastError (ERROR_INVALID_HANDLE);
295 return(WAIT_FAILED);
298 if (signal_handle == _WAPI_THREAD_CURRENT) {
299 signal_handle = _wapi_thread_handle_from_id (pthread_self ());
300 if (signal_handle == NULL) {
301 SetLastError (ERROR_INVALID_HANDLE);
302 return(WAIT_FAILED);
306 if (wait == _WAPI_THREAD_CURRENT) {
307 wait = _wapi_thread_handle_from_id (pthread_self ());
308 if (wait == NULL) {
309 SetLastError (ERROR_INVALID_HANDLE);
310 return(WAIT_FAILED);
314 if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
315 SetLastError (ERROR_INVALID_HANDLE);
316 return(WAIT_FAILED);
319 if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
320 SetLastError (ERROR_INVALID_HANDLE);
321 return(WAIT_FAILED);
324 if (_wapi_handle_test_capabilities (signal_handle,
325 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
326 return(WAIT_FAILED);
329 if (_wapi_handle_test_capabilities (wait,
330 WAPI_HANDLE_CAP_WAIT)==FALSE) {
331 return(WAIT_FAILED);
334 _wapi_handle_ops_prewait (wait);
336 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
337 g_warning ("%s: handle %p has special wait, implement me!!",
338 __func__, wait);
340 return (WAIT_FAILED);
343 DEBUG ("%s: locking handle %p", __func__, wait);
345 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
346 wait);
347 thr_ret = _wapi_handle_lock_handle (wait);
348 g_assert (thr_ret == 0);
350 _wapi_handle_ops_signal (signal_handle);
352 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
353 if (own_if_owned (wait)) {
354 DEBUG ("%s: handle %p already owned", __func__,
355 wait);
356 ret = WAIT_OBJECT_0;
357 goto done;
361 if (alertable && _wapi_thread_apc_pending (current_thread)) {
362 apc_pending = TRUE;
363 ret = WAIT_IO_COMPLETION;
364 goto done;
367 if (own_if_signalled (wait)) {
368 DEBUG ("%s: handle %p already signalled", __func__, wait);
370 ret = WAIT_OBJECT_0;
371 goto done;
374 /* Have to wait for it */
375 if (timeout != INFINITE) {
376 _wapi_calc_timeout (&abstime, timeout);
379 do {
380 /* Check before waiting on the condition, just in case
382 _wapi_handle_ops_prewait (wait);
384 if (own_if_signalled (wait)) {
385 DEBUG ("%s: handle %p signalled", __func__, wait);
387 ret = WAIT_OBJECT_0;
388 goto done;
391 if (timeout == INFINITE) {
392 waited = _wapi_handle_wait_signal_handle (wait, alertable);
393 } else {
394 waited = _wapi_handle_timedwait_signal_handle (wait, &abstime, alertable, FALSE);
397 if (alertable) {
398 apc_pending = _wapi_thread_apc_pending (current_thread);
401 if (waited==0 && !apc_pending) {
402 /* Condition was signalled, so hopefully
403 * handle is signalled now. (It might not be
404 * if someone else got in before us.)
406 if (own_if_signalled (wait)) {
407 DEBUG ("%s: handle %p signalled", __func__,
408 wait);
410 ret = WAIT_OBJECT_0;
411 goto done;
414 /* Better luck next time */
416 } while(waited == 0 && !apc_pending);
418 /* Timeout or other error */
419 DEBUG ("%s: wait on handle %p error: %s", __func__, wait,
420 strerror (ret));
422 ret = WAIT_TIMEOUT;
424 done:
426 DEBUG ("%s: unlocking handle %p", __func__, wait);
428 thr_ret = _wapi_handle_unlock_handle (wait);
429 g_assert (thr_ret == 0);
430 pthread_cleanup_pop (0);
432 if (apc_pending) {
433 _wapi_thread_dispatch_apc_queue (current_thread);
434 ret = WAIT_IO_COMPLETION;
437 return(ret);
440 struct handle_cleanup_data
442 guint32 numobjects;
443 gpointer *handles;
446 static void handle_cleanup (void *data)
448 struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
450 _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
453 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
454 gboolean waitall, guint32 *count,
455 guint32 *lowest)
457 struct handle_cleanup_data cleanup_data;
458 gboolean done;
459 int i;
461 DEBUG ("%s: locking handles", __func__);
462 cleanup_data.numobjects = numobjects;
463 cleanup_data.handles = handles;
465 pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
466 done = _wapi_handle_count_signalled_handles (numobjects, handles,
467 waitall, count, lowest);
468 if (done == TRUE) {
469 if (waitall == TRUE) {
470 for (i = 0; i < numobjects; i++) {
471 own_if_signalled (handles[i]);
473 } else {
474 own_if_signalled (handles[*lowest]);
478 DEBUG ("%s: unlocking handles", __func__);
480 /* calls the unlock function */
481 pthread_cleanup_pop (1);
483 return(done);
487 * WaitForMultipleObjectsEx:
488 * @numobjects: The number of objects in @handles. The maximum allowed
489 * is %MAXIMUM_WAIT_OBJECTS.
490 * @handles: An array of object handles. Duplicates are not allowed.
491 * @waitall: If %TRUE, this function waits until all of the handles
492 * are signalled. If %FALSE, this function returns when any object is
493 * signalled.
494 * @timeout: The maximum time in milliseconds to wait for.
495 * @alertable: if TRUE, the wait can be interrupted by an APC call
497 * This function returns when either one or more of @handles is
498 * signalled, or @timeout ms elapses. If @timeout is zero, the state
499 * of each item of @handles is tested and the function returns
500 * immediately. If @timeout is %INFINITE, the function waits forever.
502 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
503 * if @waitall is %TRUE, indicates that all objects are signalled. If
504 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
505 * the first index into @handles of the objects that are signalled.
506 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
507 * @waitall is %TRUE, indicates that all objects are signalled, and at
508 * least one object is an abandoned mutex object (See
509 * WaitForSingleObject() for a description of abandoned mutexes.) If
510 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
511 * indicates the first index into @handles of an abandoned mutex.
512 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
513 * @handles are signalled. %WAIT_FAILED - an error occurred.
514 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
516 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
517 gboolean waitall, guint32 timeout,
518 gboolean alertable)
520 gboolean duplicate = FALSE, bogustype = FALSE, done;
521 guint32 count, lowest;
522 struct timespec abstime;
523 guint i;
524 guint32 ret;
525 int thr_ret;
526 gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
527 guint32 retval;
528 gboolean poll;
529 gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
531 if (current_thread == NULL) {
532 SetLastError (ERROR_INVALID_HANDLE);
533 return(WAIT_FAILED);
536 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
537 DEBUG ("%s: Too many handles: %d", __func__, numobjects);
539 return(WAIT_FAILED);
542 if (numobjects == 1) {
543 return WaitForSingleObjectEx (handles [0], timeout, alertable);
546 /* Check for duplicates */
547 for (i = 0; i < numobjects; i++) {
548 if (handles[i] == _WAPI_THREAD_CURRENT) {
549 handles[i] = _wapi_thread_handle_from_id (pthread_self ());
551 if (handles[i] == NULL) {
552 DEBUG ("%s: Handle %d bogus", __func__, i);
554 bogustype = TRUE;
555 break;
559 if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
560 DEBUG ("%s: Handle %d pseudo process", __func__,
563 bogustype = TRUE;
564 break;
567 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
568 DEBUG ("%s: Handle %p can't be waited for",
569 __func__, handles[i]);
571 bogustype = TRUE;
572 break;
575 sorted_handles [i] = handles [i];
576 _wapi_handle_ops_prewait (handles[i]);
579 qsort (sorted_handles, numobjects, sizeof (gpointer), g_direct_equal);
580 for (i = 1; i < numobjects; i++) {
581 if (sorted_handles [i - 1] == sorted_handles [i]) {
582 duplicate = TRUE;
583 break;
587 if (duplicate == TRUE) {
588 DEBUG ("%s: Returning due to duplicates", __func__);
590 return(WAIT_FAILED);
593 if (bogustype == TRUE) {
594 DEBUG ("%s: Returning due to bogus type", __func__);
596 return(WAIT_FAILED);
599 poll = FALSE;
600 for (i = 0; i < numobjects; ++i)
601 if (_wapi_handle_type (handles [i]) == WAPI_HANDLE_PROCESS || _WAPI_SHARED_HANDLE (_wapi_handle_type (handles[i])))
602 /* Can't wait for a process handle + another handle without polling */
603 poll = TRUE;
605 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
606 if (done == TRUE) {
607 return(WAIT_OBJECT_0+lowest);
610 if (timeout == 0) {
611 return WAIT_TIMEOUT;
613 /* Have to wait for some or all handles to become signalled
616 if(timeout!=INFINITE) {
617 _wapi_calc_timeout (&abstime, timeout);
620 if (alertable && _wapi_thread_apc_pending (current_thread)) {
621 _wapi_thread_dispatch_apc_queue (current_thread);
622 return WAIT_IO_COMPLETION;
625 for (i = 0; i < numobjects; i++) {
626 /* Add a reference, as we need to ensure the handle wont
627 * disappear from under us while we're waiting in the loop
628 * (not lock, as we don't want exclusive access here)
630 _wapi_handle_ref (handles[i]);
633 while(1) {
634 /* Prod all handles with prewait methods and
635 * special-wait handles that aren't already signalled
637 for (i = 0; i < numobjects; i++) {
638 _wapi_handle_ops_prewait (handles[i]);
640 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
641 _wapi_handle_ops_special_wait (handles[i], 0, alertable);
645 DEBUG ("%s: locking signal mutex", __func__);
647 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
648 thr_ret = _wapi_handle_lock_signal_mutex ();
649 g_assert (thr_ret == 0);
651 /* Check the signalled state of handles inside the critical section */
652 if (waitall) {
653 done = TRUE;
654 for (i = 0; i < numobjects; i++)
655 if (!_wapi_handle_issignalled (handles [i]))
656 done = FALSE;
657 } else {
658 done = FALSE;
659 for (i = 0; i < numobjects; i++)
660 if (_wapi_handle_issignalled (handles [i]))
661 done = TRUE;
664 if (!done) {
665 /* Enter the wait */
666 if (timeout == INFINITE) {
667 ret = _wapi_handle_wait_signal (poll);
668 } else {
669 ret = _wapi_handle_timedwait_signal (&abstime, poll);
671 } else {
672 /* No need to wait */
673 ret = 0;
676 DEBUG ("%s: unlocking signal mutex", __func__);
678 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
679 g_assert (thr_ret == 0);
680 pthread_cleanup_pop (0);
682 if (alertable && _wapi_thread_apc_pending (current_thread)) {
683 _wapi_thread_dispatch_apc_queue (current_thread);
684 retval = WAIT_IO_COMPLETION;
685 break;
688 /* Check if everything is signalled, as we can't
689 * guarantee to notice a shared signal even if the
690 * wait timed out
692 done = test_and_own (numobjects, handles, waitall,
693 &count, &lowest);
694 if (done == TRUE) {
695 retval = WAIT_OBJECT_0+lowest;
696 break;
697 } else if (ret != 0) {
698 /* Didn't get all handles, and there was a
699 * timeout or other error
701 DEBUG ("%s: wait returned error: %s", __func__,
702 strerror (ret));
704 if(ret==ETIMEDOUT) {
705 retval = WAIT_TIMEOUT;
706 } else {
707 retval = WAIT_FAILED;
709 break;
713 for (i = 0; i < numobjects; i++) {
714 /* Unref everything we reffed above */
715 _wapi_handle_unref (handles[i]);
718 return retval;
721 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
722 gboolean waitall, guint32 timeout)
724 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
728 * WaitForInputIdle:
729 * @handle: a handle to the process to wait for
730 * @timeout: the maximum time in milliseconds to wait for
732 * This function returns when either @handle process is waiting
733 * for input, or @timeout ms elapses. If @timeout is zero, the
734 * process state is tested and the function returns immediately.
735 * If @timeout is %INFINITE, the function waits forever.
737 * Return value: 0 - @handle process is waiting for input.
738 * %WAIT_TIMEOUT - The @timeout interval elapsed and
739 * @handle process is not waiting for input. %WAIT_FAILED - an error
740 * occurred.
742 guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
744 /*TODO: Not implemented*/
745 return WAIT_TIMEOUT;