Don't run test-318 with gmcs.
[mono.git] / mono / io-layer / wait.c
1 /*
2  * wait.c:  wait for handles to become signalled
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <string.h>
13 #include <errno.h>
14
15 #include <mono/os/gc_wrapper.h>
16
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>
22
23 #undef DEBUG
24
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
30  *
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.
35  *
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.
43  */
44 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
45                                                 gboolean alertable)
46 {
47         guint32 ret, waited;
48         struct timespec abstime;
49         int thr_ret;
50         gboolean apc_pending = FALSE;
51         gpointer current_thread = GetCurrentThread ();
52         
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
59
60                 return(WAIT_FAILED);
61         }
62         
63 #ifdef DEBUG
64         g_message (G_GNUC_PRETTY_FUNCTION ": locking handle %p", handle);
65 #endif
66
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);
71
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;
82                 }
83         }
84         
85         if (alertable && _wapi_thread_apc_pending (current_thread)) {
86                 apc_pending = TRUE;
87                 ret = WAIT_IO_COMPLETION;
88                 goto done;
89         }
90         
91         if(_wapi_handle_issignalled (handle)) {
92 #ifdef DEBUG
93                 g_message (G_GNUC_PRETTY_FUNCTION
94                            ": handle %p already signalled", handle);
95 #endif
96
97                 _wapi_handle_ops_own (handle);
98                 ret=WAIT_OBJECT_0;
99                 goto done;
100         }
101
102         /* Have to wait for it */
103         if(timeout!=INFINITE) {
104                 _wapi_calc_timeout (&abstime, timeout);
105         }
106         
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);
113                 }
114         
115                 if (alertable)
116                         apc_pending = _wapi_thread_apc_pending (current_thread);
117
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.)
122                          */
123                         if(_wapi_handle_issignalled (handle)) {
124 #ifdef DEBUG
125                                 g_message (G_GNUC_PRETTY_FUNCTION
126                                            ": handle %p signalled", handle);
127 #endif
128
129                                 _wapi_handle_ops_own (handle);
130                                 ret=WAIT_OBJECT_0;
131                                 goto done;
132                         }
133                 
134                         /* Better luck next time */
135                 }
136         } while(waited==0 && !apc_pending);
137
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
143
144         ret=WAIT_TIMEOUT;
145         
146 done:
147
148 #ifdef DEBUG
149         g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p", handle);
150 #endif
151         
152         thr_ret = _wapi_handle_unlock_handle (handle);
153         g_assert (thr_ret == 0);
154         pthread_cleanup_pop (0);
155         
156         if (apc_pending) {
157                 _wapi_thread_dispatch_apc_queue (current_thread);
158                 ret = WAIT_IO_COMPLETION;
159         }
160                 
161         return(ret);
162 }
163
164 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
165 {
166         return WaitForSingleObjectEx (handle, timeout, FALSE);
167 }
168
169
170 /**
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.
177  *
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.
182  *
183  * @signal can be a semaphore, mutex or event object.
184  *
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.
195  *
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.
204  */
205 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
206                             guint32 timeout, gboolean alertable)
207 {
208         guint32 ret, waited;
209         struct timespec abstime;
210         int thr_ret;
211         gboolean apc_pending = FALSE;
212         gpointer current_thread = GetCurrentThread ();
213         
214         if(_wapi_handle_test_capabilities (signal_handle,
215                                            WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
216                 return(WAIT_FAILED);
217         }
218         
219         if(_wapi_handle_test_capabilities (wait,
220                                            WAPI_HANDLE_CAP_WAIT)==FALSE) {
221                 return(WAIT_FAILED);
222         }
223
224 #ifdef DEBUG
225         g_message (G_GNUC_PRETTY_FUNCTION ": locking handle %p", wait);
226 #endif
227
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);
232
233         _wapi_handle_ops_signal (signal_handle);
234
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;
244                 }
245         }
246         
247         if (alertable && _wapi_thread_apc_pending (current_thread)) {
248                 apc_pending = TRUE;
249                 ret = WAIT_IO_COMPLETION;
250                 goto done;
251         }
252         
253         if(_wapi_handle_issignalled (wait)) {
254 #ifdef DEBUG
255                 g_message (G_GNUC_PRETTY_FUNCTION
256                            ": handle %p already signalled", wait);
257 #endif
258
259                 _wapi_handle_ops_own (wait);
260                 ret=WAIT_OBJECT_0;
261                 goto done;
262         }
263
264         /* Have to wait for it */
265         if(timeout!=INFINITE) {
266                 _wapi_calc_timeout (&abstime, timeout);
267         }
268         
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);
275                 }
276
277                 if (alertable)
278                         apc_pending = _wapi_thread_apc_pending (current_thread);
279
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.)
284                          */
285                         if(_wapi_handle_issignalled (wait)) {
286 #ifdef DEBUG
287                                 g_message (G_GNUC_PRETTY_FUNCTION
288                                            ": handle %p signalled", wait);
289 #endif
290
291                                 _wapi_handle_ops_own (wait);
292                                 ret=WAIT_OBJECT_0;
293                                 goto done;
294                         }
295                 
296                         /* Better luck next time */
297                 }
298         } while(waited==0 && !apc_pending);
299
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
305
306         ret=WAIT_TIMEOUT;
307         
308 done:
309
310 #ifdef DEBUG
311         g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p", wait);
312 #endif
313
314         thr_ret = _wapi_handle_unlock_handle (wait);
315         g_assert (thr_ret == 0);
316         pthread_cleanup_pop (0);
317
318         if (apc_pending) {
319                 _wapi_thread_dispatch_apc_queue (current_thread);
320                 ret = WAIT_IO_COMPLETION;
321         }
322         
323         return(ret);
324 }
325
326 struct handle_cleanup_data
327 {
328         guint32 numobjects;
329         gpointer *handles;
330 };
331
332 static void handle_cleanup (void *data)
333 {
334         struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
335
336         _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
337 }
338
339 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
340                               gboolean waitall, guint32 *count,
341                               guint32 *lowest)
342 {
343         struct handle_cleanup_data cleanup_data;
344         gboolean done;
345         int i;
346         
347 #ifdef DEBUG
348         g_message (G_GNUC_PRETTY_FUNCTION ": locking handles");
349 #endif
350         cleanup_data.numobjects = numobjects;
351         cleanup_data.handles = handles;
352         
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]);
361                                 }
362                         }
363                 } else {
364                         if (_wapi_handle_issignalled (handles[*lowest])) {
365                                 _wapi_handle_ops_own (handles[*lowest]);
366                         }
367                 }
368         }
369         
370 #ifdef DEBUG
371         g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handles");
372 #endif
373
374         /* calls the unlock function */
375         pthread_cleanup_pop (1);
376
377         return(done);
378 }
379
380
381
382 /**
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
392  * 
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.
397  *
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.
411  */
412 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
413                                gboolean waitall, guint32 timeout, gboolean alertable)
414 {
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 ();
423         
424         if(numobjects>MAXIMUM_WAIT_OBJECTS) {
425 #ifdef DEBUG
426                 g_message(G_GNUC_PRETTY_FUNCTION ": Too many handles: %d",
427                           numobjects);
428 #endif
429
430                 return(WAIT_FAILED);
431         }
432         
433         if (numobjects == 1) {
434                 return WaitForSingleObjectEx (handles [0], timeout, alertable);
435         }
436
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
446
447                         duplicate=TRUE;
448                         break;
449                 }
450
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
457
458                         bogustype=TRUE;
459                 }
460
461                 g_hash_table_insert(dups, handles[i], handles[i]);
462         }
463         g_hash_table_destroy(dups);
464
465         if(duplicate==TRUE) {
466 #ifdef DEBUG
467                 g_message(G_GNUC_PRETTY_FUNCTION
468                           ": Returning due to duplicates");
469 #endif
470
471                 return(WAIT_FAILED);
472         }
473
474         if(bogustype==TRUE) {
475 #ifdef DEBUG
476                 g_message(G_GNUC_PRETTY_FUNCTION
477                           ": Returning due to bogus type");
478 #endif
479
480                 return(WAIT_FAILED);
481         }
482
483         done=test_and_own (numobjects, handles, waitall, &count, &lowest);
484         if(done==TRUE) {
485                 return(WAIT_OBJECT_0+lowest);
486         }
487         
488         /* Have to wait for some or all handles to become signalled
489          */
490
491         if(timeout!=INFINITE) {
492                 _wapi_calc_timeout (&abstime, timeout);
493         }
494
495         if (alertable && _wapi_thread_apc_pending (current_thread)) {
496                 _wapi_thread_dispatch_apc_queue (current_thread);
497                 return WAIT_IO_COMPLETION;
498         }
499         
500         while(1) {
501 #ifdef DEBUG
502                 g_message (G_GNUC_PRETTY_FUNCTION ": locking signal mutex");
503 #endif
504
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);
508                 
509                 if(timeout==INFINITE) {
510                         ret=_wapi_handle_wait_signal ();
511                 } else {
512                         ret=_wapi_handle_timedwait_signal (&abstime);
513                 }
514
515 #ifdef DEBUG
516                 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking signal mutex");
517 #endif
518
519                 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
520                 g_assert (thr_ret == 0);
521                 pthread_cleanup_pop (0);
522                 
523                 if (alertable && _wapi_thread_apc_pending (current_thread)) {
524                         _wapi_thread_dispatch_apc_queue (current_thread);
525                         return WAIT_IO_COMPLETION;
526                 }
527         
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);
534                         }
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
540
541                         if(ret==ETIMEDOUT) {
542                                 return(WAIT_TIMEOUT);
543                         } else {
544                                 return(WAIT_FAILED);
545                         }
546                 }
547         }
548 }
549
550 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
551                                gboolean waitall, guint32 timeout)
552 {
553         return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
554 }