2006-04-06 Dick Porter <dick@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         _wapi_handle_ops_prewait (handle);
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         if (timeout == 0) {
163                 ret = WAIT_TIMEOUT;
164                 goto done;
165         }
166         /* Have to wait for it */
167         if (timeout != INFINITE) {
168                 _wapi_calc_timeout (&abstime, timeout);
169         }
170         
171         do {
172                 /* Check before waiting on the condition, just in case
173                  */
174                 _wapi_handle_ops_prewait (handle);
175
176                 if (own_if_signalled (handle)) {
177 #ifdef DEBUG
178                         g_message ("%s: handle %p signalled", __func__,
179                                    handle);
180 #endif
181
182                         ret = WAIT_OBJECT_0;
183                         goto done;
184                 }
185                         
186                 if (timeout == INFINITE) {
187                         waited = _wapi_handle_wait_signal_handle (handle);
188                 } else {
189                         waited = _wapi_handle_timedwait_signal_handle (handle, &abstime);
190                 }
191         
192                 if (alertable)
193                         apc_pending = _wapi_thread_apc_pending (current_thread);
194
195                 if(waited==0 && !apc_pending) {
196                         /* Condition was signalled, so hopefully
197                          * handle is signalled now.  (It might not be
198                          * if someone else got in before us.)
199                          */
200                         if (own_if_signalled (handle)) {
201 #ifdef DEBUG
202                                 g_message ("%s: handle %p signalled", __func__,
203                                            handle);
204 #endif
205
206                                 ret=WAIT_OBJECT_0;
207                                 goto done;
208                         }
209                 
210                         /* Better luck next time */
211                 }
212         } while(waited == 0 && !apc_pending);
213
214         /* Timeout or other error */
215 #ifdef DEBUG
216         g_message ("%s: wait on handle %p error: %s", __func__, handle,
217                    strerror (waited));
218 #endif
219
220         ret = WAIT_TIMEOUT;
221         
222 done:
223
224 #ifdef DEBUG
225         g_message ("%s: unlocking handle %p", __func__, handle);
226 #endif
227         
228         thr_ret = _wapi_handle_unlock_handle (handle);
229         g_assert (thr_ret == 0);
230         pthread_cleanup_pop (0);
231         
232 check_pending:
233         if (apc_pending) {
234                 _wapi_thread_dispatch_apc_queue (current_thread);
235                 ret = WAIT_IO_COMPLETION;
236         }
237                 
238         return(ret);
239 }
240
241 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
242 {
243         return WaitForSingleObjectEx (handle, timeout, FALSE);
244 }
245
246
247 /**
248  * SignalObjectAndWait:
249  * @signal_handle: An object to signal
250  * @wait: An object to wait for
251  * @timeout: The maximum time in milliseconds to wait for
252  * @alertable: Specifies whether the function returnes when the system
253  * queues an I/O completion routine or an APC for the calling thread.
254  *
255  * Atomically signals @signal and waits for @wait to become signalled,
256  * or @timeout ms elapses.  If @timeout is zero, the object's state is
257  * tested and the function returns immediately.  If @timeout is
258  * %INFINITE, the function waits forever.
259  *
260  * @signal can be a semaphore, mutex or event object.
261  *
262  * If @alertable is %TRUE and the system queues an I/O completion
263  * routine or an APC for the calling thread, the function returns and
264  * the thread calls the completion routine or APC function.  If
265  * %FALSE, the function does not return, and the thread does not call
266  * the completion routine or APC function.  A completion routine is
267  * queued when the ReadFileEx() or WriteFileEx() function in which it
268  * was specified has completed.  The calling thread is the thread that
269  * initiated the read or write operation.  An APC is queued when
270  * QueueUserAPC() is called.  Currently completion routines and APC
271  * functions are not supported.
272  *
273  * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
274  * released by the owning thread when it exited.  Ownershop of the
275  * mutex object is granted to the calling thread and the mutex is set
276  * to nonsignalled.  %WAIT_IO_COMPLETION - the wait was ended by one
277  * or more user-mode asynchronous procedure calls queued to the
278  * thread.  %WAIT_OBJECT_0 - The state of @wait is signalled.
279  * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
280  * still not signalled.  %WAIT_FAILED - an error occurred.
281  */
282 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
283                             guint32 timeout, gboolean alertable)
284 {
285         guint32 ret, waited;
286         struct timespec abstime;
287         int thr_ret;
288         gboolean apc_pending = FALSE;
289         gpointer current_thread = GetCurrentThread ();
290         
291         if (_wapi_handle_test_capabilities (signal_handle,
292                                             WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
293                 return(WAIT_FAILED);
294         }
295         
296         if (_wapi_handle_test_capabilities (wait,
297                                             WAPI_HANDLE_CAP_WAIT)==FALSE) {
298                 return(WAIT_FAILED);
299         }
300
301         _wapi_handle_ops_prewait (wait);
302         
303         if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
304                 g_warning ("%s: handle %p has special wait, implement me!!",
305                            __func__, wait);
306
307                 return (WAIT_FAILED);
308         }
309
310 #ifdef DEBUG
311         g_message ("%s: locking handle %p", __func__, wait);
312 #endif
313
314         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
315                               wait);
316         thr_ret = _wapi_handle_lock_handle (wait);
317         g_assert (thr_ret == 0);
318
319         _wapi_handle_ops_signal (signal_handle);
320
321         if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
322                 if (own_if_owned (wait)) {
323 #ifdef DEBUG
324                         g_message ("%s: handle %p already owned", __func__,
325                                    wait);
326 #endif
327                         ret = WAIT_OBJECT_0;
328                         goto done;
329                 }
330         }
331         
332         if (alertable && _wapi_thread_apc_pending (current_thread)) {
333                 apc_pending = TRUE;
334                 ret = WAIT_IO_COMPLETION;
335                 goto done;
336         }
337         
338         if (own_if_signalled (wait)) {
339 #ifdef DEBUG
340                 g_message ("%s: handle %p already signalled", __func__, wait);
341 #endif
342
343                 ret = WAIT_OBJECT_0;
344                 goto done;
345         }
346
347         /* Have to wait for it */
348         if (timeout != INFINITE) {
349                 _wapi_calc_timeout (&abstime, timeout);
350         }
351         
352         do {
353                 /* Check before waiting on the condition, just in case
354                  */
355                 _wapi_handle_ops_prewait (wait);
356         
357                 if (own_if_signalled (wait)) {
358 #ifdef DEBUG
359                         g_message ("%s: handle %p signalled", __func__, wait);
360 #endif
361
362                         ret = WAIT_OBJECT_0;
363                         goto done;
364                 }
365                 
366                 if (timeout == INFINITE) {
367                         waited = _wapi_handle_wait_signal_handle (wait);
368                 } else {
369                         waited = _wapi_handle_timedwait_signal_handle (wait, &abstime);
370                 }
371
372                 if (alertable) {
373                         apc_pending = _wapi_thread_apc_pending (current_thread);
374                 }
375
376                 if (waited==0 && !apc_pending) {
377                         /* Condition was signalled, so hopefully
378                          * handle is signalled now.  (It might not be
379                          * if someone else got in before us.)
380                          */
381                         if (own_if_signalled (wait)) {
382 #ifdef DEBUG
383                                 g_message ("%s: handle %p signalled", __func__,
384                                            wait);
385 #endif
386
387                                 ret = WAIT_OBJECT_0;
388                                 goto done;
389                         }
390                 
391                         /* Better luck next time */
392                 }
393         } while(waited == 0 && !apc_pending);
394
395         /* Timeout or other error */
396 #ifdef DEBUG
397         g_message ("%s: wait on handle %p error: %s", __func__, wait,
398                    strerror (ret));
399 #endif
400
401         ret = WAIT_TIMEOUT;
402         
403 done:
404
405 #ifdef DEBUG
406         g_message ("%s: unlocking handle %p", __func__, wait);
407 #endif
408
409         thr_ret = _wapi_handle_unlock_handle (wait);
410         g_assert (thr_ret == 0);
411         pthread_cleanup_pop (0);
412
413         if (apc_pending) {
414                 _wapi_thread_dispatch_apc_queue (current_thread);
415                 ret = WAIT_IO_COMPLETION;
416         }
417         
418         return(ret);
419 }
420
421 struct handle_cleanup_data
422 {
423         guint32 numobjects;
424         gpointer *handles;
425 };
426
427 static void handle_cleanup (void *data)
428 {
429         struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
430
431         _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
432 }
433
434 static gboolean test_and_own (guint32 numobjects, gpointer *handles,
435                               gboolean waitall, guint32 *count,
436                               guint32 *lowest)
437 {
438         struct handle_cleanup_data cleanup_data;
439         gboolean done;
440         int i;
441         
442 #ifdef DEBUG
443         g_message ("%s: locking handles", __func__);
444 #endif
445         cleanup_data.numobjects = numobjects;
446         cleanup_data.handles = handles;
447         
448         pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
449         done = _wapi_handle_count_signalled_handles (numobjects, handles,
450                                                      waitall, count, lowest);
451         if (done == TRUE) {
452                 if (waitall == TRUE) {
453                         for (i = 0; i < numobjects; i++) {
454                                 own_if_signalled (handles[i]);
455                         }
456                 } else {
457                         own_if_signalled (handles[*lowest]);
458                 }
459         }
460         
461 #ifdef DEBUG
462         g_message ("%s: unlocking handles", __func__);
463 #endif
464
465         /* calls the unlock function */
466         pthread_cleanup_pop (1);
467
468         return(done);
469 }
470
471
472
473 /**
474  * WaitForMultipleObjectsEx:
475  * @numobjects: The number of objects in @handles. The maximum allowed
476  * is %MAXIMUM_WAIT_OBJECTS.
477  * @handles: An array of object handles.  Duplicates are not allowed.
478  * @waitall: If %TRUE, this function waits until all of the handles
479  * are signalled.  If %FALSE, this function returns when any object is
480  * signalled.
481  * @timeout: The maximum time in milliseconds to wait for.
482  * @alertable: if TRUE, the wait can be interrupted by an APC call
483  * 
484  * This function returns when either one or more of @handles is
485  * signalled, or @timeout ms elapses.  If @timeout is zero, the state
486  * of each item of @handles is tested and the function returns
487  * immediately.  If @timeout is %INFINITE, the function waits forever.
488  *
489  * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
490  * if @waitall is %TRUE, indicates that all objects are signalled.  If
491  * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
492  * the first index into @handles of the objects that are signalled.
493  * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
494  * @waitall is %TRUE, indicates that all objects are signalled, and at
495  * least one object is an abandoned mutex object (See
496  * WaitForSingleObject() for a description of abandoned mutexes.)  If
497  * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
498  * indicates the first index into @handles of an abandoned mutex.
499  * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
500  * @handles are signalled.  %WAIT_FAILED - an error occurred.
501  * %WAIT_IO_COMPLETION - the wait was ended by an APC.
502  */
503 guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
504                                  gboolean waitall, guint32 timeout,
505                                  gboolean alertable)
506 {
507         GHashTable *dups;
508         gboolean duplicate = FALSE, bogustype = FALSE, done;
509         guint32 count, lowest;
510         struct timespec abstime;
511         guint i;
512         guint32 ret;
513         int thr_ret;
514         gpointer current_thread = GetCurrentThread ();
515         
516         if (numobjects > MAXIMUM_WAIT_OBJECTS) {
517 #ifdef DEBUG
518                 g_message ("%s: Too many handles: %d", __func__, numobjects);
519 #endif
520
521                 return(WAIT_FAILED);
522         }
523         
524         if (numobjects == 1) {
525                 return WaitForSingleObjectEx (handles [0], timeout, alertable);
526         }
527
528         /* Check for duplicates */
529         dups = g_hash_table_new (g_direct_hash, g_direct_equal);
530         for (i = 0; i < numobjects; i++) {
531                 gpointer exists = g_hash_table_lookup (dups, handles[i]);
532                 if (exists != NULL) {
533 #ifdef DEBUG
534                         g_message ("%s: Handle %p duplicated", __func__,
535                                    handles[i]);
536 #endif
537
538                         duplicate = TRUE;
539                         break;
540                 }
541
542                 if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
543 #ifdef DEBUG
544                         g_message ("%s: Handle %p can't be waited for",
545                                    __func__, handles[i]);
546 #endif
547
548                         bogustype = TRUE;
549                 }
550
551                 g_hash_table_insert (dups, handles[i], handles[i]);
552                 _wapi_handle_ops_prewait (handles[i]);
553         }
554         g_hash_table_destroy (dups);
555
556         if (duplicate == TRUE) {
557 #ifdef DEBUG
558                 g_message ("%s: Returning due to duplicates", __func__);
559 #endif
560
561                 return(WAIT_FAILED);
562         }
563
564         if (bogustype == TRUE) {
565 #ifdef DEBUG
566                 g_message ("%s: Returning due to bogus type", __func__);
567 #endif
568
569                 return(WAIT_FAILED);
570         }
571
572         done = test_and_own (numobjects, handles, waitall, &count, &lowest);
573         if (done == TRUE) {
574                 return(WAIT_OBJECT_0+lowest);
575         }
576         
577         if (timeout == 0) {
578                 return WAIT_TIMEOUT;
579         }
580         /* Have to wait for some or all handles to become signalled
581          */
582
583         if(timeout!=INFINITE) {
584                 _wapi_calc_timeout (&abstime, timeout);
585         }
586
587         if (alertable && _wapi_thread_apc_pending (current_thread)) {
588                 _wapi_thread_dispatch_apc_queue (current_thread);
589                 return WAIT_IO_COMPLETION;
590         }
591         
592         while(1) {
593                 /* Prod all handles with prewait methods and
594                  * special-wait handles that aren't already signalled
595                  */
596                 for (i = 0; i < numobjects; i++) {
597                         _wapi_handle_ops_prewait (handles[i]);
598                 
599                         if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
600                                 _wapi_handle_ops_special_wait (handles[i], 0);
601                         }
602                 }
603
604                 /* Check before waiting on the condition, just in case
605                  */
606                 done = test_and_own (numobjects, handles, waitall,
607                                      &count, &lowest);
608                 if (done == TRUE) {
609                         return(WAIT_OBJECT_0 + lowest);
610                 }
611                 
612 #ifdef DEBUG
613                 g_message ("%s: locking signal mutex", __func__);
614 #endif
615
616                 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
617                 thr_ret = _wapi_handle_lock_signal_mutex ();
618                 g_assert (thr_ret == 0);
619                 
620                 if (timeout == INFINITE) {
621                         ret = _wapi_handle_wait_signal ();
622                 } else {
623                         ret = _wapi_handle_timedwait_signal (&abstime);
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 }