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