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