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