2007-03-22 Chris Toshok <toshok@ximian.com>
[mono-project/dkf.git] / mono / io-layer / wait.c
blob228d17dd134694e3095656e7cc9f7e10148b3763
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 (_wapi_handle_test_capabilities (handle,
111 WAPI_HANDLE_CAP_WAIT) == FALSE) {
112 #ifdef DEBUG
113 g_message ("%s: handle %p can't be waited for", __func__,
114 handle);
115 #endif
117 return(WAIT_FAILED);
120 _wapi_handle_ops_prewait (handle);
122 if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
123 #ifdef DEBUG
124 g_message ("%s: handle %p has special wait", __func__, handle);
125 #endif
127 ret = _wapi_handle_ops_special_wait (handle, timeout);
129 if (alertable && _wapi_thread_apc_pending (current_thread)) {
130 apc_pending = TRUE;
131 ret = WAIT_IO_COMPLETION;
134 goto check_pending;
138 #ifdef DEBUG
139 g_message ("%s: locking handle %p", __func__, handle);
140 #endif
142 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
143 handle);
144 thr_ret = _wapi_handle_lock_handle (handle);
145 g_assert (thr_ret == 0);
147 if (_wapi_handle_test_capabilities (handle,
148 WAPI_HANDLE_CAP_OWN) == TRUE) {
149 if (own_if_owned (handle) == TRUE) {
150 #ifdef DEBUG
151 g_message ("%s: handle %p already owned", __func__,
152 handle);
153 #endif
154 ret = WAIT_OBJECT_0;
155 goto done;
159 if (alertable && _wapi_thread_apc_pending (current_thread)) {
160 apc_pending = TRUE;
161 ret = WAIT_IO_COMPLETION;
162 goto done;
165 if (own_if_signalled (handle) == TRUE) {
166 #ifdef DEBUG
167 g_message ("%s: handle %p already signalled", __func__,
168 handle);
169 #endif
171 ret=WAIT_OBJECT_0;
172 goto done;
175 if (timeout == 0) {
176 ret = WAIT_TIMEOUT;
177 goto done;
179 /* Have to wait for it */
180 if (timeout != INFINITE) {
181 _wapi_calc_timeout (&abstime, timeout);
184 do {
185 /* Check before waiting on the condition, just in case
187 _wapi_handle_ops_prewait (handle);
189 if (own_if_signalled (handle)) {
190 #ifdef DEBUG
191 g_message ("%s: handle %p signalled", __func__,
192 handle);
193 #endif
195 ret = WAIT_OBJECT_0;
196 goto done;
199 if (timeout == INFINITE) {
200 waited = _wapi_handle_wait_signal_handle (handle, alertable);
201 } else {
202 waited = _wapi_handle_timedwait_signal_handle (handle, &abstime, alertable);
205 if (alertable)
206 apc_pending = _wapi_thread_apc_pending (current_thread);
208 if(waited==0 && !apc_pending) {
209 /* Condition was signalled, so hopefully
210 * handle is signalled now. (It might not be
211 * if someone else got in before us.)
213 if (own_if_signalled (handle)) {
214 #ifdef DEBUG
215 g_message ("%s: handle %p signalled", __func__,
216 handle);
217 #endif
219 ret=WAIT_OBJECT_0;
220 goto done;
223 /* Better luck next time */
225 } while(waited == 0 && !apc_pending);
227 /* Timeout or other error */
228 #ifdef DEBUG
229 g_message ("%s: wait on handle %p error: %s", __func__, handle,
230 strerror (waited));
231 #endif
233 ret = WAIT_TIMEOUT;
235 done:
237 #ifdef DEBUG
238 g_message ("%s: unlocking handle %p", __func__, handle);
239 #endif
241 thr_ret = _wapi_handle_unlock_handle (handle);
242 g_assert (thr_ret == 0);
243 pthread_cleanup_pop (0);
245 check_pending:
246 if (apc_pending) {
247 _wapi_thread_dispatch_apc_queue (current_thread);
248 ret = WAIT_IO_COMPLETION;
251 return(ret);
254 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
256 return WaitForSingleObjectEx (handle, timeout, FALSE);
261 * SignalObjectAndWait:
262 * @signal_handle: An object to signal
263 * @wait: An object to wait for
264 * @timeout: The maximum time in milliseconds to wait for
265 * @alertable: Specifies whether the function returnes when the system
266 * queues an I/O completion routine or an APC for the calling thread.
268 * Atomically signals @signal and waits for @wait to become signalled,
269 * or @timeout ms elapses. If @timeout is zero, the object's state is
270 * tested and the function returns immediately. If @timeout is
271 * %INFINITE, the function waits forever.
273 * @signal can be a semaphore, mutex or event object.
275 * If @alertable is %TRUE and the system queues an I/O completion
276 * routine or an APC for the calling thread, the function returns and
277 * the thread calls the completion routine or APC function. If
278 * %FALSE, the function does not return, and the thread does not call
279 * the completion routine or APC function. A completion routine is
280 * queued when the ReadFileEx() or WriteFileEx() function in which it
281 * was specified has completed. The calling thread is the thread that
282 * initiated the read or write operation. An APC is queued when
283 * QueueUserAPC() is called. Currently completion routines and APC
284 * functions are not supported.
286 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
287 * released by the owning thread when it exited. Ownershop of the
288 * mutex object is granted to the calling thread and the mutex is set
289 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
290 * or more user-mode asynchronous procedure calls queued to the
291 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
292 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
293 * still not signalled. %WAIT_FAILED - an error occurred.
295 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
296 guint32 timeout, gboolean alertable)
298 guint32 ret, waited;
299 struct timespec abstime;
300 int thr_ret;
301 gboolean apc_pending = FALSE;
302 gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
304 if (current_thread == NULL) {
305 SetLastError (ERROR_INVALID_HANDLE);
306 return(WAIT_FAILED);
309 if (signal_handle == _WAPI_THREAD_CURRENT) {
310 signal_handle = _wapi_thread_handle_from_id (pthread_self ());
311 if (signal_handle == NULL) {
312 SetLastError (ERROR_INVALID_HANDLE);
313 return(WAIT_FAILED);
317 if (wait == _WAPI_THREAD_CURRENT) {
318 wait = _wapi_thread_handle_from_id (pthread_self ());
319 if (wait == NULL) {
320 SetLastError (ERROR_INVALID_HANDLE);
321 return(WAIT_FAILED);
325 if (_wapi_handle_test_capabilities (signal_handle,
326 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
327 return(WAIT_FAILED);
330 if (_wapi_handle_test_capabilities (wait,
331 WAPI_HANDLE_CAP_WAIT)==FALSE) {
332 return(WAIT_FAILED);
335 _wapi_handle_ops_prewait (wait);
337 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
338 g_warning ("%s: handle %p has special wait, implement me!!",
339 __func__, wait);
341 return (WAIT_FAILED);
344 #ifdef DEBUG
345 g_message ("%s: locking handle %p", __func__, wait);
346 #endif
348 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
349 wait);
350 thr_ret = _wapi_handle_lock_handle (wait);
351 g_assert (thr_ret == 0);
353 _wapi_handle_ops_signal (signal_handle);
355 if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
356 if (own_if_owned (wait)) {
357 #ifdef DEBUG
358 g_message ("%s: handle %p already owned", __func__,
359 wait);
360 #endif
361 ret = WAIT_OBJECT_0;
362 goto done;
366 if (alertable && _wapi_thread_apc_pending (current_thread)) {
367 apc_pending = TRUE;
368 ret = WAIT_IO_COMPLETION;
369 goto done;
372 if (own_if_signalled (wait)) {
373 #ifdef DEBUG
374 g_message ("%s: handle %p already signalled", __func__, wait);
375 #endif
377 ret = WAIT_OBJECT_0;
378 goto done;
381 /* Have to wait for it */
382 if (timeout != INFINITE) {
383 _wapi_calc_timeout (&abstime, timeout);
386 do {
387 /* Check before waiting on the condition, just in case
389 _wapi_handle_ops_prewait (wait);
391 if (own_if_signalled (wait)) {
392 #ifdef DEBUG
393 g_message ("%s: handle %p signalled", __func__, wait);
394 #endif
396 ret = WAIT_OBJECT_0;
397 goto done;
400 if (timeout == INFINITE) {
401 waited = _wapi_handle_wait_signal_handle (wait, alertable);
402 } else {
403 waited = _wapi_handle_timedwait_signal_handle (wait, &abstime, alertable);
406 if (alertable) {
407 apc_pending = _wapi_thread_apc_pending (current_thread);
410 if (waited==0 && !apc_pending) {
411 /* Condition was signalled, so hopefully
412 * handle is signalled now. (It might not be
413 * if someone else got in before us.)
415 if (own_if_signalled (wait)) {
416 #ifdef DEBUG
417 g_message ("%s: handle %p signalled", __func__,
418 wait);
419 #endif
421 ret = WAIT_OBJECT_0;
422 goto done;
425 /* Better luck next time */
427 } while(waited == 0 && !apc_pending);
429 /* Timeout or other error */
430 #ifdef DEBUG
431 g_message ("%s: wait on handle %p error: %s", __func__, wait,
432 strerror (ret));
433 #endif
435 ret = WAIT_TIMEOUT;
437 done:
439 #ifdef DEBUG
440 g_message ("%s: unlocking handle %p", __func__, wait);
441 #endif
443 thr_ret = _wapi_handle_unlock_handle (wait);
444 g_assert (thr_ret == 0);
445 pthread_cleanup_pop (0);
447 if (apc_pending) {
448 _wapi_thread_dispatch_apc_queue (current_thread);
449 ret = WAIT_IO_COMPLETION;
452 return(ret);
455 struct handle_cleanup_data
457 guint32 numobjects;
458 gpointer *handles;
461 static void handle_cleanup (void *data)
463 struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
465 _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
468 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
469 gboolean waitall, guint32 *count,
470 guint32 *lowest)
472 struct handle_cleanup_data cleanup_data;
473 gboolean done;
474 int i;
476 #ifdef DEBUG
477 g_message ("%s: locking handles", __func__);
478 #endif
479 cleanup_data.numobjects = numobjects;
480 cleanup_data.handles = handles;
482 pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
483 done = _wapi_handle_count_signalled_handles (numobjects, handles,
484 waitall, count, lowest);
485 if (done == TRUE) {
486 if (waitall == TRUE) {
487 for (i = 0; i < numobjects; i++) {
488 own_if_signalled (handles[i]);
490 } else {
491 own_if_signalled (handles[*lowest]);
495 #ifdef DEBUG
496 g_message ("%s: unlocking handles", __func__);
497 #endif
499 /* calls the unlock function */
500 pthread_cleanup_pop (1);
502 return(done);
508 * WaitForMultipleObjectsEx:
509 * @numobjects: The number of objects in @handles. The maximum allowed
510 * is %MAXIMUM_WAIT_OBJECTS.
511 * @handles: An array of object handles. Duplicates are not allowed.
512 * @waitall: If %TRUE, this function waits until all of the handles
513 * are signalled. If %FALSE, this function returns when any object is
514 * signalled.
515 * @timeout: The maximum time in milliseconds to wait for.
516 * @alertable: if TRUE, the wait can be interrupted by an APC call
518 * This function returns when either one or more of @handles is
519 * signalled, or @timeout ms elapses. If @timeout is zero, the state
520 * of each item of @handles is tested and the function returns
521 * immediately. If @timeout is %INFINITE, the function waits forever.
523 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
524 * if @waitall is %TRUE, indicates that all objects are signalled. If
525 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
526 * the first index into @handles of the objects that are signalled.
527 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
528 * @waitall is %TRUE, indicates that all objects are signalled, and at
529 * least one object is an abandoned mutex object (See
530 * WaitForSingleObject() for a description of abandoned mutexes.) If
531 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
532 * indicates the first index into @handles of an abandoned mutex.
533 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
534 * @handles are signalled. %WAIT_FAILED - an error occurred.
535 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
537 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
538 gboolean waitall, guint32 timeout,
539 gboolean alertable)
541 GHashTable *dups;
542 gboolean duplicate = FALSE, bogustype = FALSE, done;
543 guint32 count, lowest;
544 struct timespec abstime;
545 guint i;
546 guint32 ret;
547 int thr_ret;
548 gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
550 if (current_thread == NULL) {
551 SetLastError (ERROR_INVALID_HANDLE);
552 return(WAIT_FAILED);
555 if (numobjects > MAXIMUM_WAIT_OBJECTS) {
556 #ifdef DEBUG
557 g_message ("%s: Too many handles: %d", __func__, numobjects);
558 #endif
560 return(WAIT_FAILED);
563 if (numobjects == 1) {
564 return WaitForSingleObjectEx (handles [0], timeout, alertable);
567 /* Check for duplicates */
568 dups = g_hash_table_new (g_direct_hash, g_direct_equal);
569 for (i = 0; i < numobjects; i++) {
570 gpointer exists;
572 if (handles[i] == _WAPI_THREAD_CURRENT) {
573 handles[i] = _wapi_thread_handle_from_id (pthread_self ());
575 if (handles[i] == NULL) {
576 #ifdef DEBUG
577 g_message ("%s: Handle %d bogus", __func__, i);
578 #endif
580 bogustype = TRUE;
581 break;
585 exists = g_hash_table_lookup (dups, handles[i]);
586 if (exists != NULL) {
587 #ifdef DEBUG
588 g_message ("%s: Handle %p duplicated", __func__,
589 handles[i]);
590 #endif
592 duplicate = TRUE;
593 break;
596 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
597 #ifdef DEBUG
598 g_message ("%s: Handle %p can't be waited for",
599 __func__, handles[i]);
600 #endif
602 bogustype = TRUE;
605 g_hash_table_insert (dups, handles[i], handles[i]);
606 _wapi_handle_ops_prewait (handles[i]);
608 g_hash_table_destroy (dups);
610 if (duplicate == TRUE) {
611 #ifdef DEBUG
612 g_message ("%s: Returning due to duplicates", __func__);
613 #endif
615 return(WAIT_FAILED);
618 if (bogustype == TRUE) {
619 #ifdef DEBUG
620 g_message ("%s: Returning due to bogus type", __func__);
621 #endif
623 return(WAIT_FAILED);
626 done = test_and_own (numobjects, handles, waitall, &count, &lowest);
627 if (done == TRUE) {
628 return(WAIT_OBJECT_0+lowest);
631 if (timeout == 0) {
632 return WAIT_TIMEOUT;
634 /* Have to wait for some or all handles to become signalled
637 if(timeout!=INFINITE) {
638 _wapi_calc_timeout (&abstime, timeout);
641 if (alertable && _wapi_thread_apc_pending (current_thread)) {
642 _wapi_thread_dispatch_apc_queue (current_thread);
643 return WAIT_IO_COMPLETION;
646 while(1) {
647 /* Prod all handles with prewait methods and
648 * special-wait handles that aren't already signalled
650 for (i = 0; i < numobjects; i++) {
651 _wapi_handle_ops_prewait (handles[i]);
653 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
654 _wapi_handle_ops_special_wait (handles[i], 0);
658 /* Check before waiting on the condition, just in case
660 done = test_and_own (numobjects, handles, waitall,
661 &count, &lowest);
662 if (done == TRUE) {
663 return(WAIT_OBJECT_0 + lowest);
666 #ifdef DEBUG
667 g_message ("%s: locking signal mutex", __func__);
668 #endif
670 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
671 thr_ret = _wapi_handle_lock_signal_mutex ();
672 g_assert (thr_ret == 0);
674 if (timeout == INFINITE) {
675 ret = _wapi_handle_wait_signal ();
676 } else {
677 ret = _wapi_handle_timedwait_signal (&abstime);
680 #ifdef DEBUG
681 g_message ("%s: unlocking signal mutex", __func__);
682 #endif
684 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
685 g_assert (thr_ret == 0);
686 pthread_cleanup_pop (0);
688 if (alertable && _wapi_thread_apc_pending (current_thread)) {
689 _wapi_thread_dispatch_apc_queue (current_thread);
690 return WAIT_IO_COMPLETION;
693 /* Check if everything is signalled, as we can't
694 * guarantee to notice a shared signal even if the
695 * wait timed out
697 done = test_and_own (numobjects, handles, waitall,
698 &count, &lowest);
699 if (done == TRUE) {
700 return(WAIT_OBJECT_0+lowest);
701 } else if (ret != 0) {
702 /* Didn't get all handles, and there was a
703 * timeout or other error
705 #ifdef DEBUG
706 g_message ("%s: wait returned error: %s", __func__,
707 strerror (ret));
708 #endif
710 if(ret==ETIMEDOUT) {
711 return(WAIT_TIMEOUT);
712 } else {
713 return(WAIT_FAILED);
719 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
720 gboolean waitall, guint32 timeout)
722 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);