* daemon-messages.c: Retry if the communication with the daemon is
[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                 goto done;
88         }
89         
90         if(_wapi_handle_issignalled (handle)) {
91 #ifdef DEBUG
92                 g_message (G_GNUC_PRETTY_FUNCTION
93                            ": handle %p already signalled", handle);
94 #endif
95
96                 _wapi_handle_ops_own (handle);
97                 ret=WAIT_OBJECT_0;
98                 goto done;
99         }
100
101         /* Have to wait for it */
102         if(timeout!=INFINITE) {
103                 _wapi_calc_timeout (&abstime, timeout);
104         }
105         
106         do {
107                 if(timeout==INFINITE) {
108                         waited=_wapi_handle_wait_signal_handle (handle);
109                 } else {
110                         waited=_wapi_handle_timedwait_signal_handle (handle,
111                                                                      &abstime);
112                 }
113         
114                 if (alertable)
115                         apc_pending = _wapi_thread_apc_pending (current_thread);
116
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.)
121                          */
122                         if(_wapi_handle_issignalled (handle)) {
123 #ifdef DEBUG
124                                 g_message (G_GNUC_PRETTY_FUNCTION
125                                            ": handle %p signalled", handle);
126 #endif
127
128                                 _wapi_handle_ops_own (handle);
129                                 ret=WAIT_OBJECT_0;
130                                 goto done;
131                         }
132                 
133                         /* Better luck next time */
134                 }
135         } while(waited==0 && !apc_pending);
136
137         /* Timeout or other error */
138 #ifdef DEBUG
139         g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s",
140                    handle, strerror (ret));
141 #endif
142
143         ret=WAIT_TIMEOUT;
144         
145 done:
146
147 #ifdef DEBUG
148         g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p", handle);
149 #endif
150         
151         thr_ret = _wapi_handle_unlock_handle (handle);
152         g_assert (thr_ret == 0);
153         pthread_cleanup_pop (0);
154         
155         if (apc_pending) {
156                 _wapi_thread_dispatch_apc_queue (current_thread);
157                 ret = WAIT_IO_COMPLETION;
158         }
159                 
160         return(ret);
161 }
162
163 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
164 {
165         return WaitForSingleObjectEx (handle, timeout, FALSE);
166 }
167
168
169 /**
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.
176  *
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.
181  *
182  * @signal can be a semaphore, mutex or event object.
183  *
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.
194  *
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.
203  */
204 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
205                             guint32 timeout, gboolean alertable)
206 {
207         guint32 ret, waited;
208         struct timespec abstime;
209         int thr_ret;
210         gboolean apc_pending = FALSE;
211         gpointer current_thread = GetCurrentThread ();
212         
213         if(_wapi_handle_test_capabilities (signal_handle,
214                                            WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
215                 return(WAIT_FAILED);
216         }
217         
218         if(_wapi_handle_test_capabilities (wait,
219                                            WAPI_HANDLE_CAP_WAIT)==FALSE) {
220                 return(WAIT_FAILED);
221         }
222
223 #ifdef DEBUG
224         g_message (G_GNUC_PRETTY_FUNCTION ": locking handle %p", wait);
225 #endif
226
227         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
228                               wait);
229         thr_ret = _wapi_handle_lock_handle (wait);
230         g_assert (thr_ret == 0);
231
232         _wapi_handle_ops_signal (signal_handle);
233
234         if(_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
235                 if(_wapi_handle_ops_isowned (wait)==TRUE) {
236 #ifdef DEBUG
237                         g_message (G_GNUC_PRETTY_FUNCTION
238                                    ": handle %p already owned", wait);
239 #endif
240                         _wapi_handle_ops_own (wait);
241                         ret=WAIT_OBJECT_0;
242                         goto done;
243                 }
244         }
245         
246         if (alertable && _wapi_thread_apc_pending (current_thread)) {
247                 apc_pending = TRUE;
248                 goto done;
249         }
250         
251         if(_wapi_handle_issignalled (wait)) {
252 #ifdef DEBUG
253                 g_message (G_GNUC_PRETTY_FUNCTION
254                            ": handle %p already signalled", wait);
255 #endif
256
257                 _wapi_handle_ops_own (wait);
258                 ret=WAIT_OBJECT_0;
259                 goto done;
260         }
261
262         /* Have to wait for it */
263         if(timeout!=INFINITE) {
264                 _wapi_calc_timeout (&abstime, timeout);
265         }
266         
267         do {
268                 if(timeout==INFINITE) {
269                         waited=_wapi_handle_wait_signal_handle (wait);
270                 } else {
271                         waited=_wapi_handle_timedwait_signal_handle (wait,
272                                                                      &abstime);
273                 }
274
275                 if (alertable)
276                         apc_pending = _wapi_thread_apc_pending (current_thread);
277
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.)
282                          */
283                         if(_wapi_handle_issignalled (wait)) {
284 #ifdef DEBUG
285                                 g_message (G_GNUC_PRETTY_FUNCTION
286                                            ": handle %p signalled", wait);
287 #endif
288
289                                 _wapi_handle_ops_own (wait);
290                                 ret=WAIT_OBJECT_0;
291                                 goto done;
292                         }
293                 
294                         /* Better luck next time */
295                 }
296         } while(waited==0 && !apc_pending);
297
298         /* Timeout or other error */
299 #ifdef DEBUG
300         g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s",
301                    wait, strerror (ret));
302 #endif
303
304         ret=WAIT_TIMEOUT;
305         
306 done:
307
308 #ifdef DEBUG
309         g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p", wait);
310 #endif
311
312         thr_ret = _wapi_handle_unlock_handle (wait);
313         g_assert (thr_ret == 0);
314         pthread_cleanup_pop (0);
315
316         if (apc_pending) {
317                 _wapi_thread_dispatch_apc_queue (current_thread);
318                 ret = WAIT_IO_COMPLETION;
319         }
320         
321         return(ret);
322 }
323
324 struct handle_cleanup_data
325 {
326         guint32 numobjects;
327         gpointer *handles;
328 };
329
330 static void handle_cleanup (void *data)
331 {
332         struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
333
334         _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
335 }
336
337 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
338                               gboolean waitall, guint32 *count,
339                               guint32 *lowest)
340 {
341         struct handle_cleanup_data cleanup_data;
342         gboolean done;
343         int i;
344         
345 #ifdef DEBUG
346         g_message (G_GNUC_PRETTY_FUNCTION ": locking handles");
347 #endif
348         cleanup_data.numobjects = numobjects;
349         cleanup_data.handles = handles;
350         
351         pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
352         done = _wapi_handle_count_signalled_handles (numobjects, handles,
353                                                      waitall, count, lowest);
354         if (done == TRUE) {
355                 for (i = 0; i < numobjects; i++) {
356                         _wapi_handle_ops_own (handles[i]);
357                 }
358         }
359         
360 #ifdef DEBUG
361         g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handles");
362 #endif
363
364         /* calls the unlock function */
365         pthread_cleanup_pop (1);
366
367         return(done);
368 }
369
370
371
372 /**
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
379  * signalled.
380  * @timeout: The maximum time in milliseconds to wait for.
381  * @alertable: if TRUE, the wait can be interrupted by an APC call
382  * 
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.
387  *
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.
401  */
402 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
403                                gboolean waitall, guint32 timeout, gboolean alertable)
404 {
405         GHashTable *dups;
406         gboolean duplicate=FALSE, bogustype=FALSE, done;
407         guint32 count, lowest;
408         struct timespec abstime;
409         guint i;
410         guint32 ret;
411         int thr_ret;
412         gpointer current_thread = GetCurrentThread ();
413         
414         if(numobjects>MAXIMUM_WAIT_OBJECTS) {
415 #ifdef DEBUG
416                 g_message(G_GNUC_PRETTY_FUNCTION ": Too many handles: %d",
417                           numobjects);
418 #endif
419
420                 return(WAIT_FAILED);
421         }
422         
423         if (numobjects == 1) {
424                 return WaitForSingleObjectEx (handles [0], timeout, alertable);
425         }
426
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]);
431                 if(exists!=NULL) {
432 #ifdef DEBUG
433                         g_message(G_GNUC_PRETTY_FUNCTION
434                                   ": Handle %p duplicated", handles[i]);
435 #endif
436
437                         duplicate=TRUE;
438                         break;
439                 }
440
441                 if(_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT)==FALSE) {
442 #ifdef DEBUG
443                         g_message (G_GNUC_PRETTY_FUNCTION
444                                    ": Handle %p can't be waited for",
445                                    handles[i]);
446 #endif
447
448                         bogustype=TRUE;
449                 }
450
451                 g_hash_table_insert(dups, handles[i], handles[i]);
452         }
453         g_hash_table_destroy(dups);
454
455         if(duplicate==TRUE) {
456 #ifdef DEBUG
457                 g_message(G_GNUC_PRETTY_FUNCTION
458                           ": Returning due to duplicates");
459 #endif
460
461                 return(WAIT_FAILED);
462         }
463
464         if(bogustype==TRUE) {
465 #ifdef DEBUG
466                 g_message(G_GNUC_PRETTY_FUNCTION
467                           ": Returning due to bogus type");
468 #endif
469
470                 return(WAIT_FAILED);
471         }
472
473         done=test_and_own (numobjects, handles, waitall, &count, &lowest);
474         if(done==TRUE) {
475                 return(WAIT_OBJECT_0+lowest);
476         }
477         
478         /* Have to wait for some or all handles to become signalled
479          */
480
481         if(timeout!=INFINITE) {
482                 _wapi_calc_timeout (&abstime, timeout);
483         }
484
485         if (alertable && _wapi_thread_apc_pending (current_thread)) {
486                 _wapi_thread_dispatch_apc_queue (current_thread);
487                 return WAIT_IO_COMPLETION;
488         }
489         
490         while(1) {
491 #ifdef DEBUG
492                 g_message (G_GNUC_PRETTY_FUNCTION ": locking signal mutex");
493 #endif
494
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);
498                 
499                 if(timeout==INFINITE) {
500                         ret=_wapi_handle_wait_signal ();
501                 } else {
502                         ret=_wapi_handle_timedwait_signal (&abstime);
503                 }
504
505 #ifdef DEBUG
506                 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking signal mutex");
507 #endif
508
509                 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
510                 g_assert (thr_ret == 0);
511                 pthread_cleanup_pop (0);
512                 
513                 if (alertable && _wapi_thread_apc_pending (current_thread)) {
514                         _wapi_thread_dispatch_apc_queue (current_thread);
515                         return WAIT_IO_COMPLETION;
516                 }
517         
518                 if(ret==0) {
519                         /* Something was signalled ... */
520                         done = test_and_own (numobjects, handles, waitall,
521                                              &count, &lowest);
522                         if(done==TRUE) {
523                                 return(WAIT_OBJECT_0+lowest);
524                         }
525                 } else {
526                         /* Timeout or other error */
527 #ifdef DEBUG
528                         g_message (G_GNUC_PRETTY_FUNCTION ": wait returned error: %s", strerror (ret));
529 #endif
530
531                         if(ret==ETIMEDOUT) {
532                                 return(WAIT_TIMEOUT);
533                         } else {
534                                 return(WAIT_FAILED);
535                         }
536                 }
537         }
538 }
539
540 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
541                                gboolean waitall, guint32 timeout)
542 {
543         return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
544 }