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>
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,
48 struct timespec abstime;
50 gboolean apc_pending = FALSE;
51 gpointer current_thread = GetCurrentThread ();
53 if(_wapi_handle_test_capabilities (handle,
54 WAPI_HANDLE_CAP_WAIT)==FALSE) {
56 g_message (G_GNUC_PRETTY_FUNCTION
57 ": handle %p can't be waited for", handle);
64 g_message (G_GNUC_PRETTY_FUNCTION ": locking handle %p", handle);
67 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_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) {
76 g_message (G_GNUC_PRETTY_FUNCTION
77 ": handle %p already owned", handle);
79 _wapi_handle_ops_own (handle);
85 if (alertable && _wapi_thread_apc_pending (current_thread)) {
90 if(_wapi_handle_issignalled (handle)) {
92 g_message (G_GNUC_PRETTY_FUNCTION
93 ": handle %p already signalled", handle);
96 _wapi_handle_ops_own (handle);
101 /* Have to wait for it */
102 if(timeout!=INFINITE) {
103 _wapi_calc_timeout (&abstime, timeout);
107 if(timeout==INFINITE) {
108 waited=_wapi_handle_wait_signal_handle (handle);
110 waited=_wapi_handle_timedwait_signal_handle (handle,
115 apc_pending = _wapi_thread_apc_pending (current_thread);
117 if(waited==0 && !apc_pending) {
118 /* Condition was signalled, so hopefully
119 * handle is signalled now. (It might not be
120 * if someone else got in before us.)
122 if(_wapi_handle_issignalled (handle)) {
124 g_message (G_GNUC_PRETTY_FUNCTION
125 ": handle %p signalled", handle);
128 _wapi_handle_ops_own (handle);
133 /* Better luck next time */
135 } while(waited==0 && !apc_pending);
137 /* Timeout or other error */
139 g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s",
140 handle, strerror (ret));
148 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p", handle);
151 thr_ret = _wapi_handle_unlock_handle (handle);
152 g_assert (thr_ret == 0);
153 pthread_cleanup_pop (0);
156 _wapi_thread_dispatch_apc_queue (current_thread);
157 ret = WAIT_IO_COMPLETION;
163 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
165 return WaitForSingleObjectEx (handle, timeout, FALSE);
170 * SignalObjectAndWait:
171 * @signal_handle: An object to signal
172 * @wait: An object to wait for
173 * @timeout: The maximum time in milliseconds to wait for
174 * @alertable: Specifies whether the function returnes when the system
175 * queues an I/O completion routine or an APC for the calling thread.
177 * Atomically signals @signal and waits for @wait to become signalled,
178 * or @timeout ms elapses. If @timeout is zero, the object's state is
179 * tested and the function returns immediately. If @timeout is
180 * %INFINITE, the function waits forever.
182 * @signal can be a semaphore, mutex or event object.
184 * If @alertable is %TRUE and the system queues an I/O completion
185 * routine or an APC for the calling thread, the function returns and
186 * the thread calls the completion routine or APC function. If
187 * %FALSE, the function does not return, and the thread does not call
188 * the completion routine or APC function. A completion routine is
189 * queued when the ReadFileEx() or WriteFileEx() function in which it
190 * was specified has completed. The calling thread is the thread that
191 * initiated the read or write operation. An APC is queued when
192 * QueueUserAPC() is called. Currently completion routines and APC
193 * functions are not supported.
195 * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
196 * released by the owning thread when it exited. Ownershop of the
197 * mutex object is granted to the calling thread and the mutex is set
198 * to nonsignalled. %WAIT_IO_COMPLETION - the wait was ended by one
199 * or more user-mode asynchronous procedure calls queued to the
200 * thread. %WAIT_OBJECT_0 - The state of @wait is signalled.
201 * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
202 * still not signalled. %WAIT_FAILED - an error occurred.
204 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
205 guint32 timeout, gboolean alertable)
208 struct timespec abstime;
210 gboolean apc_pending = FALSE;
211 gpointer current_thread = GetCurrentThread ();
213 if(_wapi_handle_test_capabilities (signal_handle,
214 WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
218 if(_wapi_handle_test_capabilities (wait,
219 WAPI_HANDLE_CAP_WAIT)==FALSE) {
224 g_message (G_GNUC_PRETTY_FUNCTION ": locking handle %p", wait);
227 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
229 thr_ret = _wapi_handle_lock_handle (wait);
230 g_assert (thr_ret == 0);
232 _wapi_handle_ops_signal (signal_handle);
234 if(_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
235 if(_wapi_handle_ops_isowned (wait)==TRUE) {
237 g_message (G_GNUC_PRETTY_FUNCTION
238 ": handle %p already owned", wait);
240 _wapi_handle_ops_own (wait);
246 if (alertable && _wapi_thread_apc_pending (current_thread)) {
251 if(_wapi_handle_issignalled (wait)) {
253 g_message (G_GNUC_PRETTY_FUNCTION
254 ": handle %p already signalled", wait);
257 _wapi_handle_ops_own (wait);
262 /* Have to wait for it */
263 if(timeout!=INFINITE) {
264 _wapi_calc_timeout (&abstime, timeout);
268 if(timeout==INFINITE) {
269 waited=_wapi_handle_wait_signal_handle (wait);
271 waited=_wapi_handle_timedwait_signal_handle (wait,
276 apc_pending = _wapi_thread_apc_pending (current_thread);
278 if(waited==0 && !apc_pending) {
279 /* Condition was signalled, so hopefully
280 * handle is signalled now. (It might not be
281 * if someone else got in before us.)
283 if(_wapi_handle_issignalled (wait)) {
285 g_message (G_GNUC_PRETTY_FUNCTION
286 ": handle %p signalled", wait);
289 _wapi_handle_ops_own (wait);
294 /* Better luck next time */
296 } while(waited==0 && !apc_pending);
298 /* Timeout or other error */
300 g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s",
301 wait, strerror (ret));
309 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p", wait);
312 thr_ret = _wapi_handle_unlock_handle (wait);
313 g_assert (thr_ret == 0);
314 pthread_cleanup_pop (0);
317 _wapi_thread_dispatch_apc_queue (current_thread);
318 ret = WAIT_IO_COMPLETION;
324 struct handle_cleanup_data
330 static void handle_cleanup (void *data)
332 struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
334 _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
337 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
338 gboolean waitall, guint32 *count,
341 struct handle_cleanup_data cleanup_data;
346 g_message (G_GNUC_PRETTY_FUNCTION ": locking handles");
348 cleanup_data.numobjects = numobjects;
349 cleanup_data.handles = handles;
351 pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
352 done = _wapi_handle_count_signalled_handles (numobjects, handles,
353 waitall, count, lowest);
355 for (i = 0; i < numobjects; i++) {
356 _wapi_handle_ops_own (handles[i]);
361 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handles");
364 /* calls the unlock function */
365 pthread_cleanup_pop (1);
373 * WaitForMultipleObjectsEx:
374 * @numobjects: The number of objects in @handles. The maximum allowed
375 * is %MAXIMUM_WAIT_OBJECTS.
376 * @handles: An array of object handles. Duplicates are not allowed.
377 * @waitall: If %TRUE, this function waits until all of the handles
378 * are signalled. If %FALSE, this function returns when any object is
380 * @timeout: The maximum time in milliseconds to wait for.
381 * @alertable: if TRUE, the wait can be interrupted by an APC call
383 * This function returns when either one or more of @handles is
384 * signalled, or @timeout ms elapses. If @timeout is zero, the state
385 * of each item of @handles is tested and the function returns
386 * immediately. If @timeout is %INFINITE, the function waits forever.
388 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
389 * if @waitall is %TRUE, indicates that all objects are signalled. If
390 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
391 * the first index into @handles of the objects that are signalled.
392 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
393 * @waitall is %TRUE, indicates that all objects are signalled, and at
394 * least one object is an abandoned mutex object (See
395 * WaitForSingleObject() for a description of abandoned mutexes.) If
396 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
397 * indicates the first index into @handles of an abandoned mutex.
398 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
399 * @handles are signalled. %WAIT_FAILED - an error occurred.
400 * %WAIT_IO_COMPLETION - the wait was ended by an APC.
402 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
403 gboolean waitall, guint32 timeout, gboolean alertable)
406 gboolean duplicate=FALSE, bogustype=FALSE, done;
407 guint32 count, lowest;
408 struct timespec abstime;
412 gpointer current_thread = GetCurrentThread ();
414 if(numobjects>MAXIMUM_WAIT_OBJECTS) {
416 g_message(G_GNUC_PRETTY_FUNCTION ": Too many handles: %d",
423 if (numobjects == 1) {
424 return WaitForSingleObjectEx (handles [0], timeout, alertable);
427 /* Check for duplicates */
428 dups=g_hash_table_new(g_direct_hash, g_direct_equal);
429 for(i=0; i<numobjects; i++) {
430 gpointer exists=g_hash_table_lookup(dups, handles[i]);
433 g_message(G_GNUC_PRETTY_FUNCTION
434 ": Handle %p duplicated", handles[i]);
441 if(_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT)==FALSE) {
443 g_message (G_GNUC_PRETTY_FUNCTION
444 ": Handle %p can't be waited for",
451 g_hash_table_insert(dups, handles[i], handles[i]);
453 g_hash_table_destroy(dups);
455 if(duplicate==TRUE) {
457 g_message(G_GNUC_PRETTY_FUNCTION
458 ": Returning due to duplicates");
464 if(bogustype==TRUE) {
466 g_message(G_GNUC_PRETTY_FUNCTION
467 ": Returning due to bogus type");
473 done=test_and_own (numobjects, handles, waitall, &count, &lowest);
475 return(WAIT_OBJECT_0+lowest);
478 /* Have to wait for some or all handles to become signalled
481 if(timeout!=INFINITE) {
482 _wapi_calc_timeout (&abstime, timeout);
485 if (alertable && _wapi_thread_apc_pending (current_thread)) {
486 _wapi_thread_dispatch_apc_queue (current_thread);
487 return WAIT_IO_COMPLETION;
492 g_message (G_GNUC_PRETTY_FUNCTION ": locking signal mutex");
495 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
496 thr_ret = _wapi_handle_lock_signal_mutex ();
497 g_assert (thr_ret == 0);
499 if(timeout==INFINITE) {
500 ret=_wapi_handle_wait_signal ();
502 ret=_wapi_handle_timedwait_signal (&abstime);
506 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking signal mutex");
509 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
510 g_assert (thr_ret == 0);
511 pthread_cleanup_pop (0);
513 if (alertable && _wapi_thread_apc_pending (current_thread)) {
514 _wapi_thread_dispatch_apc_queue (current_thread);
515 return WAIT_IO_COMPLETION;
519 /* Something was signalled ... */
520 done = test_and_own (numobjects, handles, waitall,
523 return(WAIT_OBJECT_0+lowest);
526 /* Timeout or other error */
528 g_message (G_GNUC_PRETTY_FUNCTION ": wait returned error: %s", strerror (ret));
532 return(WAIT_TIMEOUT);
540 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
541 gboolean waitall, guint32 timeout)
543 return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);