2002-04-30 Dick Porter <dick@ximian.com>
[mono.git] / mono / io-layer / wait.c
1 #include <config.h>
2 #include <glib.h>
3 #include <string.h>
4 #include <errno.h>
5
6 #if HAVE_BOEHM_GC
7 #include <gc/gc.h>
8 #endif
9
10 #include <mono/io-layer/wapi.h>
11 #include <mono/io-layer/handles-private.h>
12 #include <mono/io-layer/wapi-private.h>
13 #include <mono/io-layer/mono-mutex.h>
14 #include <mono/io-layer/misc-private.h>
15
16 #undef DEBUG
17
18 /**
19  * WaitForSingleObject:
20  * @handle: an object to wait for
21  * @timeout: the maximum time in milliseconds to wait for
22  *
23  * This function returns when either @handle is signalled, or @timeout
24  * ms elapses.  If @timeout is zero, the object's state is tested and
25  * the function returns immediately.  If @timeout is %INFINITE, the
26  * function waits forever.
27  *
28  * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
29  * released by the owning thread when it exited.  Ownership of the
30  * mutex object is granted to the calling thread and the mutex is set
31  * to nonsignalled.  %WAIT_OBJECT_0 - The state of @handle is
32  * signalled.  %WAIT_TIMEOUT - The @timeout interval elapsed and
33  * @handle's state is still not signalled.  %WAIT_FAILED - an error
34  * occurred.
35  */
36 guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
37 {
38         guint32 ret, waited;
39         struct timespec abstime;
40         
41         if(_wapi_handle_test_capabilities (handle,
42                                            WAPI_HANDLE_CAP_WAIT)==FALSE) {
43 #ifdef DEBUG
44                 g_message (G_GNUC_PRETTY_FUNCTION
45                            ": handle %p can't be waited for", handle);
46 #endif
47
48                 return(WAIT_FAILED);
49         }
50         
51         _wapi_handle_lock_handle (handle);
52
53         if(_wapi_handle_test_capabilities (handle,
54                                            WAPI_HANDLE_CAP_OWN)==TRUE) {
55                 if(_wapi_handle_ops_isowned (handle)==TRUE) {
56 #ifdef DEBUG
57                         g_message (G_GNUC_PRETTY_FUNCTION
58                                    ": handle %p already owned", handle);
59 #endif
60                         _wapi_handle_ops_own (handle);
61                         ret=WAIT_OBJECT_0;
62                         goto done;
63                 }
64         }
65         
66         if(_wapi_handle_issignalled (handle)) {
67 #ifdef DEBUG
68                 g_message (G_GNUC_PRETTY_FUNCTION
69                            ": handle %p already signalled", handle);
70 #endif
71
72                 _wapi_handle_ops_own (handle);
73                 ret=WAIT_OBJECT_0;
74                 goto done;
75         }
76
77         /* Have to wait for it */
78         if(timeout!=INFINITE) {
79                 _wapi_calc_timeout (&abstime, timeout);
80         }
81         
82         do {
83                 if(timeout==INFINITE) {
84                         waited=_wapi_handle_wait_signal_handle (handle);
85                 } else {
86                         waited=_wapi_handle_timedwait_signal_handle (handle,
87                                                                      &abstime);
88                 }
89
90                 if(waited==0) {
91                         /* Condition was signalled, so hopefully
92                          * handle is signalled now.  (It might not be
93                          * if someone else got in before us.)
94                          */
95                         if(_wapi_handle_issignalled (handle)) {
96 #ifdef DEBUG
97                                 g_message (G_GNUC_PRETTY_FUNCTION
98                                            ": handle %p signalled", handle);
99 #endif
100
101                                 _wapi_handle_ops_own (handle);
102                                 ret=WAIT_OBJECT_0;
103                                 goto done;
104                         }
105                 
106                         /* Better luck next time */
107                 }
108         } while(waited==0);
109
110         /* Timeout or other error */
111 #ifdef DEBUG
112         g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s",
113                    handle, strerror (ret));
114 #endif
115
116         ret=WAIT_TIMEOUT;
117         
118 done:
119         _wapi_handle_unlock_handle (handle);
120         return(ret);
121 }
122
123 /**
124  * SignalObjectAndWait:
125  * @signal_handle: An object to signal
126  * @wait: An object to wait for
127  * @timeout: The maximum time in milliseconds to wait for
128  * @alertable: Specifies whether the function returnes when the system
129  * queues an I/O completion routine or an APC for the calling thread.
130  *
131  * Atomically signals @signal and waits for @wait to become signalled,
132  * or @timeout ms elapses.  If @timeout is zero, the object's state is
133  * tested and the function returns immediately.  If @timeout is
134  * %INFINITE, the function waits forever.
135  *
136  * @signal can be a semaphore, mutex or event object.
137  *
138  * If @alertable is %TRUE and the system queues an I/O completion
139  * routine or an APC for the calling thread, the function returns and
140  * the thread calls the completion routine or APC function.  If
141  * %FALSE, the function does not return, and the thread does not call
142  * the completion routine or APC function.  A completion routine is
143  * queued when the ReadFileEx() or WriteFileEx() function in which it
144  * was specified has completed.  The calling thread is the thread that
145  * initiated the read or write operation.  An APC is queued when
146  * QueueUserAPC() is called.  Currently completion routines and APC
147  * functions are not supported.
148  *
149  * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
150  * released by the owning thread when it exited.  Ownershop of the
151  * mutex object is granted to the calling thread and the mutex is set
152  * to nonsignalled.  %WAIT_IO_COMPLETION - the wait was ended by one
153  * or more user-mode asynchronous procedure calls queued to the
154  * thread.  %WAIT_OBJECT_0 - The state of @wait is signalled.
155  * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
156  * still not signalled.  %WAIT_FAILED - an error occurred.
157  */
158 guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
159                             guint32 timeout, gboolean alertable)
160 {
161         guint32 ret, waited;
162         struct timespec abstime;
163         
164         if(_wapi_handle_test_capabilities (signal_handle,
165                                            WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
166                 return(WAIT_FAILED);
167         }
168         
169         if(_wapi_handle_test_capabilities (wait,
170                                            WAPI_HANDLE_CAP_WAIT)==FALSE) {
171                 return(WAIT_FAILED);
172         }
173         
174         _wapi_handle_lock_handle (wait);
175
176         _wapi_handle_ops_signal (signal_handle);
177
178         if(_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
179                 if(_wapi_handle_ops_isowned (wait)==TRUE) {
180 #ifdef DEBUG
181                         g_message (G_GNUC_PRETTY_FUNCTION
182                                    ": handle %p already owned", wait);
183 #endif
184                         _wapi_handle_ops_own (wait);
185                         ret=WAIT_OBJECT_0;
186                         goto done;
187                 }
188         }
189         
190         if(_wapi_handle_issignalled (wait)) {
191 #ifdef DEBUG
192                 g_message (G_GNUC_PRETTY_FUNCTION
193                            ": handle %p already signalled", wait);
194 #endif
195
196                 _wapi_handle_ops_own (wait);
197                 ret=WAIT_OBJECT_0;
198                 goto done;
199         }
200
201         /* Have to wait for it */
202         if(timeout!=INFINITE) {
203                 _wapi_calc_timeout (&abstime, timeout);
204         }
205         
206         do {
207                 if(timeout==INFINITE) {
208                         waited=_wapi_handle_wait_signal_handle (wait);
209                 } else {
210                         waited=_wapi_handle_timedwait_signal_handle (wait,
211                                                                      &abstime);
212                 }
213
214                 if(waited==0) {
215                         /* Condition was signalled, so hopefully
216                          * handle is signalled now.  (It might not be
217                          * if someone else got in before us.)
218                          */
219                         if(_wapi_handle_issignalled (wait)) {
220 #ifdef DEBUG
221                                 g_message (G_GNUC_PRETTY_FUNCTION
222                                            ": handle %p signalled", wait);
223 #endif
224
225                                 _wapi_handle_ops_own (wait);
226                                 ret=WAIT_OBJECT_0;
227                                 goto done;
228                         }
229                 
230                         /* Better luck next time */
231                 }
232         } while(waited==0);
233
234         /* Timeout or other error */
235 #ifdef DEBUG
236         g_message (G_GNUC_PRETTY_FUNCTION ": wait on handle %p error: %s",
237                    wait, strerror (ret));
238 #endif
239
240         ret=WAIT_TIMEOUT;
241         
242 done:
243         _wapi_handle_unlock_handle (wait);
244
245         if(alertable==TRUE) {
246                 /* Deal with queued APC or IO completion routines */
247         }
248         
249         return(ret);
250 }
251
252 /**
253  * WaitForMultipleObjects:
254  * @numobjects: The number of objects in @handles. The maximum allowed
255  * is %MAXIMUM_WAIT_OBJECTS.
256  * @handles: An array of object handles.  Duplicates are not allowed.
257  * @waitall: If %TRUE, this function waits until all of the handles
258  * are signalled.  If %FALSE, this function returns when any object is
259  * signalled.
260  * @timeout: The maximum time in milliseconds to wait for.
261  * 
262  * This function returns when either one or more of @handles is
263  * signalled, or @timeout ms elapses.  If @timeout is zero, the state
264  * of each item of @handles is tested and the function returns
265  * immediately.  If @timeout is %INFINITE, the function waits forever.
266  *
267  * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
268  * if @waitall is %TRUE, indicates that all objects are signalled.  If
269  * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
270  * the first index into @handles of the objects that are signalled.
271  * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
272  * @waitall is %TRUE, indicates that all objects are signalled, and at
273  * least one object is an abandoned mutex object (See
274  * WaitForSingleObject() for a description of abandoned mutexes.)  If
275  * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
276  * indicates the first index into @handles of an abandoned mutex.
277  * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
278  * @handles are signalled.  %WAIT_FAILED - an error occurred.
279  */
280 guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
281                                gboolean waitall, guint32 timeout)
282 {
283         GHashTable *dups;
284         gboolean duplicate=FALSE, bogustype=FALSE, done;
285         guint32 count, lowest;
286         struct timespec abstime;
287         guint i;
288         guint32 ret;
289         
290         if(numobjects>MAXIMUM_WAIT_OBJECTS) {
291 #ifdef DEBUG
292                 g_message(G_GNUC_PRETTY_FUNCTION ": Too many handles: %d",
293                           numobjects);
294 #endif
295
296                 return(WAIT_FAILED);
297         }
298         
299         /* Check for duplicates */
300         dups=g_hash_table_new(g_direct_hash, g_direct_equal);
301         for(i=0; i<numobjects; i++) {
302                 gpointer exists=g_hash_table_lookup(dups, handles[i]);
303                 if(exists!=NULL) {
304 #ifdef DEBUG
305                         g_message(G_GNUC_PRETTY_FUNCTION
306                                   ": Handle %p duplicated", handles[i]);
307 #endif
308
309                         duplicate=TRUE;
310                         break;
311                 }
312
313                 if(_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT)==FALSE) {
314 #ifdef DEBUG
315                         g_message (G_GNUC_PRETTY_FUNCTION
316                                    ": Handle %p can't be waited for",
317                                    handles[i]);
318 #endif
319
320                         bogustype=TRUE;
321                 }
322
323                 g_hash_table_insert(dups, handles[i], handles[i]);
324         }
325         g_hash_table_destroy(dups);
326
327         if(duplicate==TRUE) {
328 #ifdef DEBUG
329                 g_message(G_GNUC_PRETTY_FUNCTION
330                           ": Returning due to duplicates");
331 #endif
332
333                 return(WAIT_FAILED);
334         }
335
336         if(bogustype==TRUE) {
337 #ifdef DEBUG
338                 g_message(G_GNUC_PRETTY_FUNCTION
339                           ": Returning due to bogus type");
340 #endif
341
342                 return(WAIT_FAILED);
343         }
344
345         done=_wapi_handle_count_signalled_handles (numobjects, handles,
346                                                    waitall, &count, &lowest);
347         if(done==TRUE) {
348                 for(i=0; i<numobjects; i++) {
349                         _wapi_handle_ops_own (handles[i]);
350                 }
351                 
352                 _wapi_handle_unlock_handles (numobjects, handles);
353                 return(WAIT_OBJECT_0+lowest);
354         }
355         
356         /* Have to wait for some or all handles to become signalled
357          */
358
359         _wapi_handle_unlock_handles (numobjects, handles);
360
361         if(timeout!=INFINITE) {
362                 _wapi_calc_timeout (&abstime, timeout);
363         }
364         
365         _wapi_handle_lock_signal_mutex ();
366
367         while(1) {
368                 if(timeout==INFINITE) {
369                         ret=_wapi_handle_wait_signal ();
370                 } else {
371                         ret=_wapi_handle_timedwait_signal (&abstime);
372                 }
373                 
374                 if(ret==0) {
375                         /* Something was signalled ... */
376                         done=_wapi_handle_count_signalled_handles (numobjects,
377                                                                    handles,
378                                                                    waitall,
379                                                                    &count,
380                                                                    &lowest);
381                         if(done==TRUE) {
382                                 for(i=0; i<numobjects; i++) {
383                                         _wapi_handle_ops_own (handles[i]);
384                                 }
385                                 
386                                 _wapi_handle_unlock_handles (numobjects,
387                                                              handles);
388                                 _wapi_handle_unlock_signal_mutex ();
389                                 return(WAIT_OBJECT_0+lowest);
390                         }
391                         _wapi_handle_unlock_handles (numobjects, handles);
392                 } else {
393                         /* Timeout or other error */
394 #ifdef DEBUG
395                         g_message (G_GNUC_PRETTY_FUNCTION ": wait returned error: %s", strerror (ret));
396 #endif
397
398                         _wapi_handle_unlock_signal_mutex ();
399                         if(ret==ETIMEDOUT) {
400                                 return(WAIT_TIMEOUT);
401                         } else {
402                                 return(WAIT_FAILED);
403                         }
404                 }
405         }
406 }