d66f8994269b39154865c10e022a8da0ae68ca47
[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 static gboolean own_if_signalled(gpointer handle)
26 {
27         gboolean ret = FALSE;
28         guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
29         
30         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
31                 if (_wapi_handle_shared_trylock_handle (handle, now) == EBUSY) {
32                         return (FALSE);
33                 }
34         }
35         
36         if (_wapi_handle_issignalled (handle)) {
37                 _wapi_handle_ops_own (handle);
38                 ret = TRUE;
39         }
40
41         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
42                 _wapi_handle_shared_unlock_handle (handle, now);
43         }
44
45         return(ret);
46 }
47
48 static gboolean own_if_owned(gpointer handle)
49 {
50         gboolean ret = FALSE;
51         guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
52         
53         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
54                 if (_wapi_handle_shared_trylock_handle (handle, now) == EBUSY) {
55                         return (FALSE);
56                 }
57         }
58         
59         if (_wapi_handle_ops_isowned (handle)) {
60                 _wapi_handle_ops_own (handle);
61                 ret = TRUE;
62         }
63
64         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
65                 _wapi_handle_shared_unlock_handle (handle, now);
66         }
67
68         return(ret);
69 }
70
71 /**
72  * WaitForSingleObjectEx:
73  * @handle: an object to wait for
74  * @timeout: the maximum time in milliseconds to wait for
75  * @alertable: if TRUE, the wait can be interrupted by an APC call
76  *
77  * This function returns when either @handle is signalled, or @timeout
78  * ms elapses.  If @timeout is zero, the object's state is tested and
79  * the function returns immediately.  If @timeout is %INFINITE, the
80  * function waits forever.
81  *
82  * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
83  * released by the owning thread when it exited.  Ownership of the
84  * mutex object is granted to the calling thread and the mutex is set
85  * to nonsignalled.  %WAIT_OBJECT_0 - The state of @handle is
86  * signalled.  %WAIT_TIMEOUT - The @timeout interval elapsed and
87  * @handle's state is still not signalled.  %WAIT_FAILED - an error
88  * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
89  */
90 guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
91                               gboolean alertable)
92 {
93         guint32 ret, waited;
94         struct timespec abstime;
95         int thr_ret;
96         gboolean apc_pending = FALSE;
97         gpointer current_thread = GetCurrentThread ();
98         
99         if (_wapi_handle_test_capabilities (handle,
100                                             WAPI_HANDLE_CAP_WAIT) == FALSE) {
101 #ifdef DEBUG
102                 g_message ("%s: handle %p can't be waited for", __func__,
103                            handle);
104 #endif
105
106                 return(WAIT_FAILED);
107         }
108
109         if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
110 #ifdef DEBUG
111                 g_message ("%s: handle %p has special wait", __func__, handle);
112 #endif
113
114                 ret = _wapi_handle_ops_special_wait (handle, timeout);
115         
116                 if (alertable && _wapi_thread_apc_pending (current_thread)) {
117                         apc_pending = TRUE;
118                         ret = WAIT_IO_COMPLETION;
119                 }
120
121                 goto check_pending;
122         }
123         
124         
125 #ifdef DEBUG
126         g_message ("%s: locking handle %p", __func__, handle);
127 #endif
128
129         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
130                               handle);
131         thr_ret = _wapi_handle_lock_handle (handle);
132         g_assert (thr_ret == 0);
133
134         if (_wapi_handle_test_capabilities (handle,
135                                             WAPI_HANDLE_CAP_OWN) == TRUE) {
136                 if (own_if_owned (handle) == TRUE) {
137 #ifdef DEBUG
138                         g_message ("%s: handle %p already owned", __func__,
139                                    handle);
140 #endif
141                         ret = WAIT_OBJECT_0;
142                         goto done;
143                 }
144         }
145         
146         if (alertable && _wapi_thread_apc_pending (current_thread)) {
147                 apc_pending = TRUE;
148                 ret = WAIT_IO_COMPLETION;
149                 goto done;
150         }
151         
152         if (own_if_signalled (handle) == TRUE) {
153 #ifdef DEBUG
154                 g_message ("%s: handle %p already signalled", __func__,
155                            handle);
156 #endif
157
158                 ret=WAIT_OBJECT_0;
159                 goto done;
160         }
161
162         /* Have to wait for it */
163         if (timeout != INFINITE) {
164                 _wapi_calc_timeout (&abstime, timeout);
165         }
166         
167         do {
168                 /* Check before waiting on the condition, just in case
169                  */
170                 if (own_if_signalled (handle)) {
171 #ifdef DEBUG
172                         g_message ("%s: handle %p signalled", __func__,
173                                    handle);
174 #endif
175
176                         ret = WAIT_OBJECT_0;
177                         goto done;
178                 }
179                         
180                 if (timeout == INFINITE) {
181                         waited = _wapi_handle_wait_signal_handle (handle);
182                 } else {
183                         waited = _wapi_handle_timedwait_signal_handle (handle, &abstime);
184                 }
185         
186                 if (alertable)
187                         apc_pending = _wapi_thread_apc_pending (current_thread);
188
189                 if(waited==0 && !apc_pending) {
190                         /* Condition was signalled, so hopefully
191                          * handle is signalled now.  (It might not be
192                          * if someone else got in before us.)
193                          */
194                         if (own_if_signalled (handle)) {
195 #ifdef DEBUG
196                                 g_message ("%s: handle %p signalled", __func__,
197                                            handle);
198 #endif
199
200                                 ret=WAIT_OBJECT_0;
201                                 goto done;
202                         }
203                 
204                         /* Better luck next time */
205                 }
206         } while(waited == 0 && !apc_pending);
207
208         /* Timeout or other error */
209 #ifdef DEBUG
210         g_message ("%s: wait on handle %p error: %s", __func__, handle,
211                    strerror (waited));
212 #endif
213
214         ret = WAIT_TIMEOUT;
215         
216 done:
217
218 #ifdef DEBUG
219         g_message ("%s: unlocking handle %p", __func__, handle);
220 #endif
221         
222         thr_ret = _wapi_handle_unlock_handle (handle);
223         g_assert (thr_ret == 0);
224         pthread_cleanup_pop (0);
225         
226 check_pending:
227         if (apc_pending) {
228                 _wapi_thread_dispatch_apc_queue (current_thread);
229                 ret = WAIT_IO_COMPLETION;
230         }
231                 
232         return(ret);
233 }
234
235 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
236 {
237         return WaitForSingleObjectEx (handle, timeout, FALSE);
238 }
239
240
241 /**
242  * SignalObjectAndWait:
243  * @signal_handle: An object to signal
244  * @wait: An object to wait for
245  * @timeout: The maximum time in milliseconds to wait for
246  * @alertable: Specifies whether the function returnes when the system
247  * queues an I/O completion routine or an APC for the calling thread.
248  *
249  * Atomically signals @signal and waits for @wait to become signalled,
250  * or @timeout ms elapses.  If @timeout is zero, the object's state is
251  * tested and the function returns immediately.  If @timeout is
252  * %INFINITE, the function waits forever.
253  *
254  * @signal can be a semaphore, mutex or event object.
255  *
256  * If @alertable is %TRUE and the system queues an I/O completion
257  * routine or an APC for the calling thread, the function returns and
258  * the thread calls the completion routine or APC function.  If
259  * %FALSE, the function does not return, and the thread does not call
260  * the completion routine or APC function.  A completion routine is
261  * queued when the ReadFileEx() or WriteFileEx() function in which it
262  * was specified has completed.  The calling thread is the thread that
263  * initiated the read or write operation.  An APC is queued when
264  * QueueUserAPC() is called.  Currently completion routines and APC
265  * functions are not supported.
266  *
267  * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
268  * released by the owning thread when it exited.  Ownershop of the
269  * mutex object is granted to the calling thread and the mutex is set
270  * to nonsignalled.  %WAIT_IO_COMPLETION - the wait was ended by one
271  * or more user-mode asynchronous procedure calls queued to the
272  * thread.  %WAIT_OBJECT_0 - The state of @wait is signalled.
273  * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
274  * still not signalled.  %WAIT_FAILED - an error occurred.
275  */
276 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
277                             guint32 timeout, gboolean alertable)
278 {
279         guint32 ret, waited;
280         struct timespec abstime;
281         int thr_ret;
282         gboolean apc_pending = FALSE;
283         gpointer current_thread = GetCurrentThread ();
284         
285         if (_wapi_handle_test_capabilities (signal_handle,
286                                             WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
287                 return(WAIT_FAILED);
288         }
289         
290         if (_wapi_handle_test_capabilities (wait,
291                                             WAPI_HANDLE_CAP_WAIT)==FALSE) {
292                 return(WAIT_FAILED);
293         }
294
295         if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
296                 g_warning ("%s: handle %p has special wait, implement me!!",
297                            __func__, wait);
298
299                 return (WAIT_FAILED);
300         }
301
302 #ifdef DEBUG
303         g_message ("%s: locking handle %p", __func__, wait);
304 #endif
305
306         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
307                               wait);
308         thr_ret = _wapi_handle_lock_handle (wait);
309         g_assert (thr_ret == 0);
310
311         _wapi_handle_ops_signal (signal_handle);
312
313         if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
314                 if (own_if_owned (wait)) {
315 #ifdef DEBUG
316                         g_message ("%s: handle %p already owned", __func__,
317                                    wait);
318 #endif
319                         ret = WAIT_OBJECT_0;
320                         goto done;
321                 }
322         }
323         
324         if (alertable && _wapi_thread_apc_pending (current_thread)) {
325                 apc_pending = TRUE;
326                 ret = WAIT_IO_COMPLETION;
327                 goto done;
328         }
329         
330         if (own_if_signalled (wait)) {
331 #ifdef DEBUG
332                 g_message ("%s: handle %p already signalled", __func__, wait);
333 #endif
334
335                 ret = WAIT_OBJECT_0;
336                 goto done;
337         }
338
339         /* Have to wait for it */
340         if (timeout != INFINITE) {
341                 _wapi_calc_timeout (&abstime, timeout);
342         }
343         
344         do {
345                 /* Check before waiting on the condition, just in case
346                  */
347                 if (own_if_signalled (wait)) {
348 #ifdef DEBUG
349                         g_message ("%s: handle %p signalled", __func__, wait);
350 #endif
351
352                         ret = WAIT_OBJECT_0;
353                         goto done;
354                 }
355                 
356                 if (timeout == INFINITE) {
357                         waited = _wapi_handle_wait_signal_handle (wait);
358                 } else {
359                         waited = _wapi_handle_timedwait_signal_handle (wait, &abstime);
360                 }
361
362                 if (alertable) {
363                         apc_pending = _wapi_thread_apc_pending (current_thread);
364                 }
365
366                 if (waited==0 && !apc_pending) {
367                         /* Condition was signalled, so hopefully
368                          * handle is signalled now.  (It might not be
369                          * if someone else got in before us.)
370                          */
371                         if (own_if_signalled (wait)) {
372 #ifdef DEBUG
373                                 g_message ("%s: handle %p signalled", __func__,
374                                            wait);
375 #endif
376
377                                 ret = WAIT_OBJECT_0;
378                                 goto done;
379                         }
380                 
381                         /* Better luck next time */
382                 }
383         } while(waited == 0 && !apc_pending);
384
385         /* Timeout or other error */
386 #ifdef DEBUG
387         g_message ("%s: wait on handle %p error: %s", __func__, wait,
388                    strerror (ret));
389 #endif
390
391         ret = WAIT_TIMEOUT;
392         
393 done:
394
395 #ifdef DEBUG
396         g_message ("%s: unlocking handle %p", __func__, wait);
397 #endif
398
399         thr_ret = _wapi_handle_unlock_handle (wait);
400         g_assert (thr_ret == 0);
401         pthread_cleanup_pop (0);
402
403         if (apc_pending) {
404                 _wapi_thread_dispatch_apc_queue (current_thread);
405                 ret = WAIT_IO_COMPLETION;
406         }
407         
408         return(ret);
409 }
410
411 struct handle_cleanup_data
412 {
413         guint32 numobjects;
414         gpointer *handles;
415         guint32 now;
416 };
417
418 static void handle_cleanup (void *data)
419 {
420         struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
421
422         _wapi_handle_unlock_handles (handles->numobjects, handles->handles,
423                                      handles->now);
424 }
425
426 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
427                               gboolean waitall, guint32 *count,
428                               guint32 *lowest)
429 {
430         struct handle_cleanup_data cleanup_data;
431         gboolean done;
432         int i;
433         
434 #ifdef DEBUG
435         g_message ("%s: locking handles", __func__);
436 #endif
437         cleanup_data.numobjects = numobjects;
438         cleanup_data.handles = handles;
439         
440         pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
441         done = _wapi_handle_count_signalled_handles (numobjects, handles,
442                                                      waitall, count, lowest,
443                                                      &cleanup_data.now);
444         if (done == TRUE) {
445                 if (waitall == TRUE) {
446                         for (i = 0; i < numobjects; i++) {
447                                 own_if_signalled (handles[i]);
448                         }
449                 } else {
450                         own_if_signalled (handles[*lowest]);
451                 }
452         }
453         
454 #ifdef DEBUG
455         g_message ("%s: unlocking handles", __func__);
456 #endif
457
458         /* calls the unlock function */
459         pthread_cleanup_pop (1);
460
461         return(done);
462 }
463
464
465
466 /**
467  * WaitForMultipleObjectsEx:
468  * @numobjects: The number of objects in @handles. The maximum allowed
469  * is %MAXIMUM_WAIT_OBJECTS.
470  * @handles: An array of object handles.  Duplicates are not allowed.
471  * @waitall: If %TRUE, this function waits until all of the handles
472  * are signalled.  If %FALSE, this function returns when any object is
473  * signalled.
474  * @timeout: The maximum time in milliseconds to wait for.
475  * @alertable: if TRUE, the wait can be interrupted by an APC call
476  * 
477  * This function returns when either one or more of @handles is
478  * signalled, or @timeout ms elapses.  If @timeout is zero, the state
479  * of each item of @handles is tested and the function returns
480  * immediately.  If @timeout is %INFINITE, the function waits forever.
481  *
482  * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
483  * if @waitall is %TRUE, indicates that all objects are signalled.  If
484  * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
485  * the first index into @handles of the objects that are signalled.
486  * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
487  * @waitall is %TRUE, indicates that all objects are signalled, and at
488  * least one object is an abandoned mutex object (See
489  * WaitForSingleObject() for a description of abandoned mutexes.)  If
490  * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
491  * indicates the first index into @handles of an abandoned mutex.
492  * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
493  * @handles are signalled.  %WAIT_FAILED - an error occurred.
494  * %WAIT_IO_COMPLETION - the wait was ended by an APC.
495  */
496 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
497                                  gboolean waitall, guint32 timeout,
498                                  gboolean alertable)
499 {
500         GHashTable *dups;
501         gboolean duplicate = FALSE, bogustype = FALSE, done;
502         gboolean shared_wait = FALSE;
503         guint32 count, lowest;
504         struct timespec abstime;
505         guint i;
506         guint32 ret;
507         int thr_ret;
508         gpointer current_thread = GetCurrentThread ();
509         
510         if (numobjects > MAXIMUM_WAIT_OBJECTS) {
511 #ifdef DEBUG
512                 g_message ("%s: Too many handles: %d", __func__, numobjects);
513 #endif
514
515                 return(WAIT_FAILED);
516         }
517         
518         if (numobjects == 1) {
519                 return WaitForSingleObjectEx (handles [0], timeout, alertable);
520         }
521
522         /* Check for duplicates */
523         dups = g_hash_table_new (g_direct_hash, g_direct_equal);
524         for (i = 0; i < numobjects; i++) {
525                 gpointer exists = g_hash_table_lookup (dups, handles[i]);
526                 if (exists != NULL) {
527 #ifdef DEBUG
528                         g_message ("%s: Handle %p duplicated", __func__,
529                                    handles[i]);
530 #endif
531
532                         duplicate = TRUE;
533                         break;
534                 }
535
536                 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
537 #ifdef DEBUG
538                         g_message ("%s: Handle %p can't be waited for",
539                                    __func__, handles[i]);
540 #endif
541
542                         bogustype = TRUE;
543                 }
544
545                 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handles[i]))) {
546                         shared_wait = TRUE;
547                 }
548
549                 g_hash_table_insert (dups, handles[i], handles[i]);
550         }
551         g_hash_table_destroy (dups);
552
553         if (duplicate == TRUE) {
554 #ifdef DEBUG
555                 g_message ("%s: Returning due to duplicates", __func__);
556 #endif
557
558                 return(WAIT_FAILED);
559         }
560
561         if (bogustype == TRUE) {
562 #ifdef DEBUG
563                 g_message ("%s: Returning due to bogus type", __func__);
564 #endif
565
566                 return(WAIT_FAILED);
567         }
568
569         done = test_and_own (numobjects, handles, waitall, &count, &lowest);
570         if (done == TRUE) {
571                 return(WAIT_OBJECT_0+lowest);
572         }
573         
574         /* Have to wait for some or all handles to become signalled
575          */
576
577         if(timeout!=INFINITE) {
578                 _wapi_calc_timeout (&abstime, timeout);
579         }
580
581         if (alertable && _wapi_thread_apc_pending (current_thread)) {
582                 _wapi_thread_dispatch_apc_queue (current_thread);
583                 return WAIT_IO_COMPLETION;
584         }
585         
586         while(1) {
587                 /* Prod all special-wait handles that aren't already
588                  * signalled
589                  */
590                 for (i = 0; i < numobjects; i++) {
591                         if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
592                                 _wapi_handle_ops_special_wait (handles[i], 0);
593                         }
594                 }
595
596                 /* Check before waiting on the condition, just in case
597                  */
598                 done = test_and_own (numobjects, handles, waitall,
599                                      &count, &lowest);
600                 if (done == TRUE) {
601                         return(WAIT_OBJECT_0 + lowest);
602                 }
603                 
604 #ifdef DEBUG
605                 g_message ("%s: locking signal mutex", __func__);
606 #endif
607
608                 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
609                 thr_ret = _wapi_handle_lock_signal_mutex ();
610                 g_assert (thr_ret == 0);
611                 
612                 if (shared_wait == TRUE) {
613                         if (timeout == INFINITE) {
614                                 ret = _wapi_handle_wait_signal_poll_share ();
615                         } else {
616                                 ret = _wapi_handle_timedwait_signal_poll_share (&abstime);
617                         }
618                 } else {
619                         if (timeout == INFINITE) {
620                                 ret = _wapi_handle_wait_signal ();
621                         } else {
622                                 ret = _wapi_handle_timedwait_signal (&abstime);
623                         }
624                 }
625
626 #ifdef DEBUG
627                 g_message ("%s: unlocking signal mutex", __func__);
628 #endif
629
630                 thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
631                 g_assert (thr_ret == 0);
632                 pthread_cleanup_pop (0);
633                 
634                 if (alertable && _wapi_thread_apc_pending (current_thread)) {
635                         _wapi_thread_dispatch_apc_queue (current_thread);
636                         return WAIT_IO_COMPLETION;
637                 }
638         
639                 /* Check if everything is signalled, as we can't
640                  * guarantee to notice a shared signal even if the
641                  * wait timed out
642                  */
643                 done = test_and_own (numobjects, handles, waitall,
644                                      &count, &lowest);
645                 if (done == TRUE) {
646                         return(WAIT_OBJECT_0+lowest);
647                 } else if (ret != 0) {
648                         /* Didn't get all handles, and there was a
649                          * timeout or other error
650                          */
651 #ifdef DEBUG
652                         g_message ("%s: wait returned error: %s", __func__,
653                                    strerror (ret));
654 #endif
655
656                         if(ret==ETIMEDOUT) {
657                                 return(WAIT_TIMEOUT);
658                         } else {
659                                 return(WAIT_FAILED);
660                         }
661                 }
662         }
663 }
664
665 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
666                                gboolean waitall, guint32 timeout)
667 {
668         return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
669 }