2005-02-21 Ben Maurer <bmaurer@ximian.com>
[mono-project.git] / mono / io-layer / wait.c
bloba236d17ed3eadc232a66f4d047c45225ecadf463
1 /*
2 * wait.c: wait for handles to become signalled
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, 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 /**
26 * WaitForSingleObjectEx:
27 * @handle: an object to wait for
28 * @timeout: the maximum time in milliseconds to wait for
29 * @alertable: if TRUE, the wait can be interrupted by an APC call
31 * This function returns when either @handle is signalled, or @timeout
32 * ms elapses. If @timeout is zero, the object's state is tested and
33 * the function returns immediately. If @timeout is %INFINITE, the
34 * function waits forever.
36 * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
37 * released by the owning thread when it exited. Ownership of the
38 * mutex object is granted to the calling thread and the mutex is set
39 * to nonsignalled. %WAIT_OBJECT_0 - The state of @handle is
40 * signalled. %WAIT_TIMEOUT - The @timeout interval elapsed and
41 * @handle's state is still not signalled. %WAIT_FAILED - an error
42 * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
44 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
45 gboolean alertable)
47 guint32 ret, waited;
48 struct timespec abstime;
49 int thr_ret;
50 gboolean apc_pending = FALSE;
51 gpointer current_thread = GetCurrentThread ();
53 if(_wapi_handle_test_capabilities (handle,
54 WAPI_HANDLE_CAP_WAIT)==FALSE) {
55 #ifdef DEBUG
56 g_message (G_GNUC_PRETTY_FUNCTION
57 ": handle %p can't be waited for", handle);
58 #endif
60 return(WAIT_FAILED);
63 #ifdef DEBUG
64 g_message (G_GNUC_PRETTY_FUNCTION ": locking handle %p", handle);
65 #endif
67 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
68 handle);
69 thr_ret = _wapi_handle_lock_handle (handle);
70 g_assert (thr_ret == 0);
72 if(_wapi_handle_test_capabilities (handle,
73 WAPI_HANDLE_CAP_OWN)==TRUE) {
74 if(_wapi_handle_ops_isowned (handle)==TRUE) {
75 #ifdef DEBUG
76 g_message (G_GNUC_PRETTY_FUNCTION
77 ": handle %p already owned", handle);
78 #endif
79 _wapi_handle_ops_own (handle);
80 ret=WAIT_OBJECT_0;
81 goto done;
85 if (alertable && _wapi_thread_apc_pending (current_thread)) {
86 apc_pending = TRUE;
87 ret = WAIT_IO_COMPLETION;
88 goto done;
91 if(_wapi_handle_issignalled (handle)) {
92 #ifdef DEBUG
93 g_message (G_GNUC_PRETTY_FUNCTION
94 ": handle %p already signalled", handle);
95 #endif
97 _wapi_handle_ops_own (handle);
98 ret=WAIT_OBJECT_0;
99 goto done;
102 /* Have to wait for it */
103 if(timeout!=INFINITE) {
104 _wapi_calc_timeout (&abstime, timeout);
107 do {
108 if(timeout==INFINITE) {
109 waited=_wapi_handle_wait_signal_handle (handle);
110 } else {
111 waited=_wapi_handle_timedwait_signal_handle (handle,
112 &abstime);
115 if (alertable)
116 apc_pending = _wapi_thread_apc_pending (current_thread);
118 if(waited==0 && !apc_pending) {
119 /* Condition was signalled, so hopefully
120 * handle is signalled now. (It might not be
121 * if someone else got in before us.)
123 if(_wapi_handle_issignalled (handle)) {
124 #ifdef DEBUG
125 g_message (G_GNUC_PRETTY_FUNCTION
126 ": handle %p signalled", handle);
127 #endif
129 _wapi_handle_ops_own (handle);
130 ret=WAIT_OBJECT_0;
131 goto done;
134 /* Better luck next time */
136 } while(waited==0 && !apc_pending);
138 /* Timeout or other error */
139 #ifdef DEBUG
140 g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s",
141 handle, strerror (ret));
142 #endif
144 ret=WAIT_TIMEOUT;
146 done:
148 #ifdef DEBUG
149 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p", handle);
150 #endif
152 thr_ret = _wapi_handle_unlock_handle (handle);
153 g_assert (thr_ret == 0);
154 pthread_cleanup_pop (0);
156 if (apc_pending) {
157 _wapi_thread_dispatch_apc_queue (current_thread);
158 ret = WAIT_IO_COMPLETION;
161 return(ret);
164 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
166 return WaitForSingleObjectEx (handle, timeout, FALSE);
171 * SignalObjectAndWait:
172 * @signal_handle: An object to signal
173 * @wait: An object to wait for
174 * @timeout: The maximum time in milliseconds to wait for
175 * @alertable: Specifies whether the function returnes when the system
176 * queues an I/O completion routine or an APC for the calling thread.
178 * Atomically signals @signal and waits for @wait to become signalled,
179 * or @timeout ms elapses. If @timeout is zero, the object's state is
180 * tested and the function returns immediately. If @timeout is
181 * %INFINITE, the function waits forever.
183 * @signal can be a semaphore, mutex or event object.
185 * If @alertable is %TRUE and the system queues an I/O completion
186 * routine or an APC for the calling thread, the function returns and
187 * the thread calls the completion routine or APC function. If
188 * %FALSE, the function does not return, and the thread does not call
189 * the completion routine or APC function. A completion routine is
190 * queued when the ReadFileEx() or WriteFileEx() function in which it
191 * was specified has completed. The calling thread is the thread that
192 * initiated the read or write operation. An APC is queued when
193 * QueueUserAPC() is called. Currently completion routines and APC
194 * functions are not supported.
196 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
197 * released by the owning thread when it exited. Ownershop of the
198 * mutex object is granted to the calling thread and the mutex is set
199 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
200 * or more user-mode asynchronous procedure calls queued to the
201 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
202 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
203 * still not signalled. %WAIT_FAILED - an error occurred.
205 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
206 guint32 timeout, gboolean alertable)
208 guint32 ret, waited;
209 struct timespec abstime;
210 int thr_ret;
211 gboolean apc_pending = FALSE;
212 gpointer current_thread = GetCurrentThread ();
214 if(_wapi_handle_test_capabilities (signal_handle,
215 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
216 return(WAIT_FAILED);
219 if(_wapi_handle_test_capabilities (wait,
220 WAPI_HANDLE_CAP_WAIT)==FALSE) {
221 return(WAIT_FAILED);
224 #ifdef DEBUG
225 g_message (G_GNUC_PRETTY_FUNCTION ": locking handle %p", wait);
226 #endif
228 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
229 wait);
230 thr_ret = _wapi_handle_lock_handle (wait);
231 g_assert (thr_ret == 0);
233 _wapi_handle_ops_signal (signal_handle);
235 if(_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
236 if(_wapi_handle_ops_isowned (wait)==TRUE) {
237 #ifdef DEBUG
238 g_message (G_GNUC_PRETTY_FUNCTION
239 ": handle %p already owned", wait);
240 #endif
241 _wapi_handle_ops_own (wait);
242 ret=WAIT_OBJECT_0;
243 goto done;
247 if (alertable && _wapi_thread_apc_pending (current_thread)) {
248 apc_pending = TRUE;
249 ret = WAIT_IO_COMPLETION;
250 goto done;
253 if(_wapi_handle_issignalled (wait)) {
254 #ifdef DEBUG
255 g_message (G_GNUC_PRETTY_FUNCTION
256 ": handle %p already signalled", wait);
257 #endif
259 _wapi_handle_ops_own (wait);
260 ret=WAIT_OBJECT_0;
261 goto done;
264 /* Have to wait for it */
265 if(timeout!=INFINITE) {
266 _wapi_calc_timeout (&abstime, timeout);
269 do {
270 if(timeout==INFINITE) {
271 waited=_wapi_handle_wait_signal_handle (wait);
272 } else {
273 waited=_wapi_handle_timedwait_signal_handle (wait,
274 &abstime);
277 if (alertable)
278 apc_pending = _wapi_thread_apc_pending (current_thread);
280 if(waited==0 && !apc_pending) {
281 /* Condition was signalled, so hopefully
282 * handle is signalled now. (It might not be
283 * if someone else got in before us.)
285 if(_wapi_handle_issignalled (wait)) {
286 #ifdef DEBUG
287 g_message (G_GNUC_PRETTY_FUNCTION
288 ": handle %p signalled", wait);
289 #endif
291 _wapi_handle_ops_own (wait);
292 ret=WAIT_OBJECT_0;
293 goto done;
296 /* Better luck next time */
298 } while(waited==0 && !apc_pending);
300 /* Timeout or other error */
301 #ifdef DEBUG
302 g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s",
303 wait, strerror (ret));
304 #endif
306 ret=WAIT_TIMEOUT;
308 done:
310 #ifdef DEBUG
311 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p", wait);
312 #endif
314 thr_ret = _wapi_handle_unlock_handle (wait);
315 g_assert (thr_ret == 0);
316 pthread_cleanup_pop (0);
318 if (apc_pending) {
319 _wapi_thread_dispatch_apc_queue (current_thread);
320 ret = WAIT_IO_COMPLETION;
323 return(ret);
326 struct handle_cleanup_data
328 guint32 numobjects;
329 gpointer *handles;
332 static void handle_cleanup (void *data)
334 struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
336 _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
339 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
340 gboolean waitall, guint32 *count,
341 guint32 *lowest)
343 struct handle_cleanup_data cleanup_data;
344 gboolean done;
345 int i;
347 #ifdef DEBUG
348 g_message (G_GNUC_PRETTY_FUNCTION ": locking handles");
349 #endif
350 cleanup_data.numobjects = numobjects;
351 cleanup_data.handles = handles;
353 pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
354 done = _wapi_handle_count_signalled_handles (numobjects, handles,
355 waitall, count, lowest);
356 if (done == TRUE) {
357 if (waitall == TRUE) {
358 for (i = 0; i < numobjects; i++) {
359 if (_wapi_handle_issignalled (handles[i])) {
360 _wapi_handle_ops_own (handles[i]);
363 } else {
364 if (_wapi_handle_issignalled (handles[*lowest])) {
365 _wapi_handle_ops_own (handles[*lowest]);
370 #ifdef DEBUG
371 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handles");
372 #endif
374 /* calls the unlock function */
375 pthread_cleanup_pop (1);
377 return(done);
383 * WaitForMultipleObjectsEx:
384 * @numobjects: The number of objects in @handles. The maximum allowed
385 * is %MAXIMUM_WAIT_OBJECTS.
386 * @handles: An array of object handles. Duplicates are not allowed.
387 * @waitall: If %TRUE, this function waits until all of the handles
388 * are signalled. If %FALSE, this function returns when any object is
389 * signalled.
390 * @timeout: The maximum time in milliseconds to wait for.
391 * @alertable: if TRUE, the wait can be interrupted by an APC call
393 * This function returns when either one or more of @handles is
394 * signalled, or @timeout ms elapses. If @timeout is zero, the state
395 * of each item of @handles is tested and the function returns
396 * immediately. If @timeout is %INFINITE, the function waits forever.
398 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
399 * if @waitall is %TRUE, indicates that all objects are signalled. If
400 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
401 * the first index into @handles of the objects that are signalled.
402 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
403 * @waitall is %TRUE, indicates that all objects are signalled, and at
404 * least one object is an abandoned mutex object (See
405 * WaitForSingleObject() for a description of abandoned mutexes.) If
406 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
407 * indicates the first index into @handles of an abandoned mutex.
408 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
409 * @handles are signalled. %WAIT_FAILED - an error occurred.
410 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
412 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
413 gboolean waitall, guint32 timeout, gboolean alertable)
415 GHashTable *dups;
416 gboolean duplicate=FALSE, bogustype=FALSE, done;
417 guint32 count, lowest;
418 struct timespec abstime;
419 guint i;
420 guint32 ret;
421 int thr_ret;
422 gpointer current_thread = GetCurrentThread ();
424 if(numobjects>MAXIMUM_WAIT_OBJECTS) {
425 #ifdef DEBUG
426 g_message(G_GNUC_PRETTY_FUNCTION ": Too many handles: %d",
427 numobjects);
428 #endif
430 return(WAIT_FAILED);
433 if (numobjects == 1) {
434 return WaitForSingleObjectEx (handles [0], timeout, alertable);
437 /* Check for duplicates */
438 dups=g_hash_table_new(g_direct_hash, g_direct_equal);
439 for(i=0; i<numobjects; i++) {
440 gpointer exists=g_hash_table_lookup(dups, handles[i]);
441 if(exists!=NULL) {
442 #ifdef DEBUG
443 g_message(G_GNUC_PRETTY_FUNCTION
444 ": Handle %p duplicated", handles[i]);
445 #endif
447 duplicate=TRUE;
448 break;
451 if(_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT)==FALSE) {
452 #ifdef DEBUG
453 g_message (G_GNUC_PRETTY_FUNCTION
454 ": Handle %p can't be waited for",
455 handles[i]);
456 #endif
458 bogustype=TRUE;
461 g_hash_table_insert(dups, handles[i], handles[i]);
463 g_hash_table_destroy(dups);
465 if(duplicate==TRUE) {
466 #ifdef DEBUG
467 g_message(G_GNUC_PRETTY_FUNCTION
468 ": Returning due to duplicates");
469 #endif
471 return(WAIT_FAILED);
474 if(bogustype==TRUE) {
475 #ifdef DEBUG
476 g_message(G_GNUC_PRETTY_FUNCTION
477 ": Returning due to bogus type");
478 #endif
480 return(WAIT_FAILED);
483 done=test_and_own (numobjects, handles, waitall, &count, &lowest);
484 if(done==TRUE) {
485 return(WAIT_OBJECT_0+lowest);
488 /* Have to wait for some or all handles to become signalled
491 if(timeout!=INFINITE) {
492 _wapi_calc_timeout (&abstime, timeout);
495 if (alertable && _wapi_thread_apc_pending (current_thread)) {
496 _wapi_thread_dispatch_apc_queue (current_thread);
497 return WAIT_IO_COMPLETION;
500 while(1) {
501 #ifdef DEBUG
502 g_message (G_GNUC_PRETTY_FUNCTION ": locking signal mutex");
503 #endif
505 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
506 thr_ret = _wapi_handle_lock_signal_mutex ();
507 g_assert (thr_ret == 0);
509 if(timeout==INFINITE) {
510 ret=_wapi_handle_wait_signal ();
511 } else {
512 ret=_wapi_handle_timedwait_signal (&abstime);
515 #ifdef DEBUG
516 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking signal mutex");
517 #endif
519 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
520 g_assert (thr_ret == 0);
521 pthread_cleanup_pop (0);
523 if (alertable && _wapi_thread_apc_pending (current_thread)) {
524 _wapi_thread_dispatch_apc_queue (current_thread);
525 return WAIT_IO_COMPLETION;
528 if(ret==0) {
529 /* Something was signalled ... */
530 done = test_and_own (numobjects, handles, waitall,
531 &count, &lowest);
532 if(done==TRUE) {
533 return(WAIT_OBJECT_0+lowest);
535 } else {
536 /* Timeout or other error */
537 #ifdef DEBUG
538 g_message (G_GNUC_PRETTY_FUNCTION ": wait returned error: %s", strerror (ret));
539 #endif
541 if(ret==ETIMEDOUT) {
542 return(WAIT_TIMEOUT);
543 } else {
544 return(WAIT_FAILED);
550 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
551 gboolean waitall, guint32 timeout)
553 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);