2002-05-07 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mono / io-layer / handles.c
1 #include <config.h>
2 #include <glib.h>
3 #include <pthread.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <sys/un.h>
9
10 #if HAVE_BOEHM_GC
11 #include <gc/gc.h>
12 #endif
13
14 #include <mono/io-layer/wapi.h>
15 #include <mono/io-layer/wapi-private.h>
16 #include <mono/io-layer/handles-private.h>
17 #include <mono/io-layer/mono-mutex.h>
18 #include <mono/io-layer/shared.h>
19 #include <mono/io-layer/misc-private.h>
20 #include <mono/io-layer/daemon-messages.h>
21
22 #undef DEBUG
23
24 static pthread_once_t shared_data_once=PTHREAD_ONCE_INIT;
25 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
26 static gboolean shared=FALSE;
27 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
28         NULL,
29         &_wapi_file_ops,
30         &_wapi_console_ops,
31         &_wapi_thread_ops,
32         &_wapi_sem_ops,
33         &_wapi_mutex_ops,
34         &_wapi_event_ops,
35         &_wapi_socket_ops,
36         &_wapi_find_ops,
37         &_wapi_process_ops,
38 };
39
40 static int daemon_sock;
41
42 static pthread_mutexattr_t mutex_shared_attr;
43 static pthread_condattr_t cond_shared_attr;
44
45 struct _WapiHandleShared_list *_wapi_shared_data=NULL;
46 struct _WapiHandlePrivate_list *_wapi_private_data=NULL;
47
48 int disable_shm = 1;
49 static void shared_init (void)
50 {
51         struct sockaddr_un sun;
52         int ret;
53
54         if (getenv ("MONO_ENABLE_SHM"))
55                 disable_shm = 0;
56         
57 #ifndef DISABLE_SHARED_HANDLES
58         if(getenv ("MONO_DISABLE_SHM") || disable_shm)
59 #endif
60         {
61 #ifdef DEBUG
62                 g_message (G_GNUC_PRETTY_FUNCTION
63                            ": Using process-private handles");
64 #endif
65                 shared=FALSE;
66
67                 _wapi_shared_data=
68                         g_malloc0 (sizeof(struct _WapiHandleShared_list)+
69                                    _WAPI_SHM_SCRATCH_SIZE);
70                 _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list, 1);
71 #ifndef DISABLE_SHARED_HANDLES
72         } else {
73                 shared=TRUE;
74                 
75                 _wapi_shared_data=_wapi_shm_attach (FALSE);
76                 _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list, 1);
77
78                 daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0);
79                 sun.sun_family=AF_UNIX;
80                 memcpy (sun.sun_path, _wapi_shared_data->daemon, 108);
81                 ret=connect (daemon_sock, (struct sockaddr *)&sun,
82                              sizeof(struct sockaddr_un));
83                 if(ret==-1) {
84                         g_error (G_GNUC_PRETTY_FUNCTION
85                                  "connect to daemon failed: %s",
86                                  strerror (errno));
87                         g_assert_not_reached ();
88                 }
89 #endif /* DISABLE_SHARED_HANDLES */
90         }
91
92         pthread_mutexattr_init (&mutex_shared_attr);
93         pthread_condattr_init (&cond_shared_attr);
94
95 #ifdef _POSIX_THREAD_PROCESS_SHARED
96         pthread_mutexattr_setpshared (&mutex_shared_attr,
97                                       PTHREAD_PROCESS_SHARED);
98         pthread_condattr_setpshared (&cond_shared_attr,
99                                      PTHREAD_PROCESS_SHARED);
100 #endif
101 }
102
103 void _wapi_handle_init (void)
104 {
105         /* Create our own process and main thread handles (assume this
106          * function is being called from the main thread)
107          */
108         /* No need to make these variables visible outside this
109          * function, the handles will be cleaned up at process exit.
110          */
111         gpointer process_handle;
112         gpointer thread_handle;
113
114         process_handle=_wapi_handle_new (WAPI_HANDLE_PROCESS);
115         
116         thread_handle=_wapi_handle_new (WAPI_HANDLE_THREAD);
117 }
118
119 guint32 _wapi_handle_new_internal (WapiHandleType type)
120 {
121         guint32 i;
122         
123         /* A linear scan should be fast enough.  Start from 1, leaving
124          * 0 (NULL) as a guard
125          */
126         for(i=1; i<_WAPI_MAX_HANDLES; i++) {
127                 struct _WapiHandleShared *shared=&_wapi_shared_data->handles[i];
128                 
129                 if(shared->type==WAPI_HANDLE_UNUSED) {
130                         shared->type=type;
131                         shared->signalled=FALSE;
132                         mono_mutex_init (&_wapi_shared_data->handles[i].signal_mutex, &mutex_shared_attr);
133                         pthread_cond_init (&_wapi_shared_data->handles[i].signal_cond, &cond_shared_attr);
134
135                         return(i);
136                 }
137         }
138
139         return(0);
140 }
141
142 gpointer _wapi_handle_new (WapiHandleType type)
143 {
144         static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER;
145         guint32 idx;
146         gpointer handle;
147         WapiHandleRequest new;
148         WapiHandleResponse new_resp;
149 #if HAVE_BOEHM_GC
150         gboolean tried_collect=FALSE;
151 #endif
152
153         pthread_once (&shared_data_once, shared_init);
154         
155 again:
156         if(shared==TRUE) {
157                 new.type=WapiHandleRequestType_New;
158                 new.u.new.type=type;
159         
160                 _wapi_daemon_request_response (daemon_sock, &new, &new_resp);
161         
162                 if (new_resp.type==WapiHandleResponseType_New) {
163                         idx=new_resp.u.new.handle;
164                 } else {
165                         g_warning (G_GNUC_PRETTY_FUNCTION
166                                    ": bogus daemon response, type %d",
167                                    new_resp.type);
168                         g_assert_not_reached ();
169                 }
170         } else {
171                 pthread_mutex_lock (&scan_mutex);
172                 idx=_wapi_handle_new_internal (type);
173                 pthread_mutex_unlock (&scan_mutex);
174         }
175                 
176         if(idx==0) {
177                 g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!");
178
179 #if HAVE_BOEHM_GC
180                 /* See if we can reclaim some handles by forcing a GC
181                  * collection
182                  */
183                 if(tried_collect==FALSE) {
184                         g_warning (G_GNUC_PRETTY_FUNCTION
185                                    ": Seeing if GC collection helps...");
186                         GC_gcollect ();
187                         tried_collect=TRUE;
188                         goto again;
189                 } else {
190                         g_warning (G_GNUC_PRETTY_FUNCTION
191                                    ": didn't help, returning error");
192                 }
193 #endif
194         
195                 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
196         }
197
198         handle=GUINT_TO_POINTER (idx);
199
200 #ifdef DEBUG
201         g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle);
202 #endif
203
204         return(handle);
205 }
206
207 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
208                               gpointer *shared, gpointer *private)
209 {
210         struct _WapiHandleShared *shared_handle_data;
211         struct _WapiHandlePrivate *private_handle_data;
212         guint32 idx=GPOINTER_TO_UINT (handle);
213
214         if(shared!=NULL) {
215                 shared_handle_data=&_wapi_shared_data->handles[idx];
216                 /* Allow WAPI_HANDLE_UNUSED to mean "dont care which
217                  * type"
218                  */
219                 if(shared_handle_data->type!=type &&
220                    type != WAPI_HANDLE_UNUSED) {
221                         return(FALSE);
222                 }
223
224                 *shared=&shared_handle_data->u;
225         }
226         
227         if(private!=NULL) {
228                 private_handle_data=&_wapi_private_data->handles[idx];
229
230                 *private=&private_handle_data->u;
231         }
232         
233         return(TRUE);
234 }
235
236 void _wapi_handle_ref (gpointer handle)
237 {
238         guint32 idx=GPOINTER_TO_UINT (handle);
239
240         if(shared==TRUE) {
241                 WapiHandleRequest req;
242                 WapiHandleResponse resp;
243         
244                 req.type=WapiHandleRequestType_Open;
245                 req.u.open.handle=idx;
246         
247                 _wapi_daemon_request_response (daemon_sock, &req, &resp);
248                 if(resp.type!=WapiHandleResponseType_Open) {
249                         g_warning (G_GNUC_PRETTY_FUNCTION
250                                    ": bogus daemon response, type %d",
251                                    resp.type);
252                         g_assert_not_reached ();
253                 }
254         } else {
255                 _wapi_shared_data->handles[idx].ref++;
256         
257 #ifdef DEBUG
258                 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
259                            handle, _wapi_shared_data->handles[idx].ref);
260 #endif
261         }
262 }
263
264 void _wapi_handle_unref (gpointer handle)
265 {
266         guint32 idx=GPOINTER_TO_UINT (handle);
267         gboolean destroy;
268         
269         if(shared==TRUE) {
270                 WapiHandleRequest req;
271                 WapiHandleResponse resp;
272         
273                 req.type=WapiHandleRequestType_Close;
274                 req.u.close.handle=GPOINTER_TO_UINT (handle);
275         
276                 _wapi_daemon_request_response (daemon_sock, &req, &resp);
277                 if(resp.type!=WapiHandleResponseType_Close) {
278                         g_warning (G_GNUC_PRETTY_FUNCTION
279                                    ": bogus daemon response, type %d",
280                                    resp.type);
281                         g_assert_not_reached ();
282                 } else {
283                         destroy=resp.u.close.destroy;
284                 }
285         } else {
286                 _wapi_shared_data->handles[idx].ref--;
287         
288 #ifdef DEBUG
289                 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
290                            handle, _wapi_shared_data->handles[idx].ref);
291 #endif
292
293                 /* Possible race condition here if another thread refs
294                  * the handle between here and setting the type to
295                  * UNUSED.  I could lock a mutex, but I'm not sure
296                  * that allowing a handle reference to reach 0 isn't
297                  * an application bug anyway.
298                  */
299                 destroy=(_wapi_shared_data->handles[idx].ref==0);
300         }
301
302         if(destroy==TRUE) {
303 #ifdef DEBUG
304                 g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p",
305                            handle);
306 #endif
307                 
308                 if(shared==FALSE) {
309                         _wapi_handle_ops_close_shared (handle);
310                         _wapi_shared_data->handles[idx].type=WAPI_HANDLE_UNUSED;
311                         mono_mutex_destroy (&_wapi_shared_data->handles[idx].signal_mutex);
312                         pthread_cond_destroy (&_wapi_shared_data->handles[idx].signal_cond);
313                         memset (&_wapi_shared_data->handles[idx].u, '\0', sizeof(_wapi_shared_data->handles[idx].u));
314                 }
315                 
316                 _wapi_handle_ops_close_private (handle);
317         }
318 }
319
320 #define HDRSIZE sizeof(struct _WapiScratchHeader)
321
322 guint32 _wapi_handle_scratch_store_internal (guint32 bytes)
323 {
324         guint32 idx=0, last_idx=0;
325         struct _WapiScratchHeader *hdr, *last_hdr;
326         gboolean last_was_free=FALSE;
327         guchar *storage=&_wapi_shared_data->scratch_base[0];
328         
329 #ifdef DEBUG
330         g_message (G_GNUC_PRETTY_FUNCTION
331                    ": looking for %d bytes of scratch space (%d bytes total)",
332                    bytes, _WAPI_SHM_SCRATCH_SIZE);
333 #endif
334
335         hdr=(struct _WapiScratchHeader *)&storage[0];
336         if(hdr->flags==0 && hdr->length==0) {
337                 /* Need to initialise scratch data */
338                 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
339                 hdr->length = _WAPI_SHM_SCRATCH_SIZE - HDRSIZE;
340         }
341         
342         while(idx< _WAPI_SHM_SCRATCH_SIZE) {
343                 hdr=(struct _WapiScratchHeader *)&storage[idx];
344                 
345                 /* Do a simple first-fit allocation, coalescing
346                  * adjacent free blocks as we progress through the
347                  * scratch space
348                  */
349                 if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
350                    hdr->length >= bytes + HDRSIZE) {
351                         /* found space */
352                         guint32 old_length=hdr->length;
353 #ifdef DEBUG
354                         g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length);
355 #endif
356
357                         hdr->flags &= ~WAPI_SHM_SCRATCH_FREE;
358                         hdr->length=bytes;
359                         idx += HDRSIZE;
360
361                         /* Put a new header in at the end of the used
362                          * space
363                          */
364                         hdr=(struct _WapiScratchHeader *)&storage[idx+bytes];
365                         hdr->flags |= WAPI_SHM_SCRATCH_FREE;
366                         hdr->length = old_length-bytes-HDRSIZE;
367
368 #ifdef DEBUG
369                         g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
370 #endif
371                         
372                         return(idx);
373                 } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
374                           last_was_free == FALSE) {
375 #ifdef DEBUG
376                         g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length);
377 #endif
378
379                         /* save this point in case we can coalesce it with
380                          * the next block, if that is free.
381                          */
382                         last_was_free=TRUE;
383                         last_idx=idx;
384                         last_hdr=hdr;
385                         idx+=(hdr->length+HDRSIZE);
386                 } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE &&
387                            last_was_free == TRUE) {
388 #ifdef DEBUG
389                         g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length);
390 #endif
391
392                         /* This block and the previous are both free,
393                          * so coalesce them
394                          */
395                         last_hdr->length += (hdr->length + HDRSIZE);
396
397                         /* If the new block is now big enough, use it
398                          * (next time round the loop)
399                          */
400                         if(last_hdr->length >= bytes + HDRSIZE) {
401                                 idx=last_idx;
402                         } else {
403                                 /* leave the last free info as it is,
404                                  * in case the next block is also free
405                                  * and can be coalesced too
406                                  */
407                                 idx=last_idx+last_hdr->length+HDRSIZE;
408                         }
409                 } else {
410 #ifdef DEBUG
411                         g_message (G_GNUC_PRETTY_FUNCTION
412                                    ": found used block at %d, length %d", idx,
413                                    hdr->length);
414 #endif
415
416                         /* must be used, try next chunk */
417                         idx+=(hdr->length+HDRSIZE);
418                 }
419         }
420         
421         return(0);
422 }
423
424 guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
425 {
426         static pthread_mutex_t scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
427         guint32 idx;
428         
429         if(shared==TRUE) {
430                 WapiHandleRequest scratch;
431                 WapiHandleResponse scratch_resp;
432         
433                 scratch.type=WapiHandleRequestType_Scratch;
434                 scratch.u.scratch.length=bytes;
435         
436                 _wapi_daemon_request_response (daemon_sock, &scratch,
437                                                &scratch_resp);
438         
439                 if(scratch_resp.type==WapiHandleResponseType_Scratch) {
440                         idx=scratch_resp.u.scratch.idx;
441                 } else {
442                         g_warning (G_GNUC_PRETTY_FUNCTION
443                                    ": bogus daemon response, type %d",
444                                    scratch_resp.type);
445                         g_assert_not_reached ();
446                 }
447         } else {
448                 pthread_mutex_lock (&scratch_mutex);
449                 idx=_wapi_handle_scratch_store_internal (bytes);
450                 pthread_mutex_unlock (&scratch_mutex);
451                 
452                 if(idx==0) {
453                         /* Failed to allocate space */
454                         return(0);
455                 }
456         }
457
458         memcpy (&_wapi_shared_data->scratch_base[idx], data, bytes);
459         
460         return(idx);
461 }
462
463 guchar *_wapi_handle_scratch_lookup_as_string (guint32 idx)
464 {
465         struct _WapiScratchHeader *hdr;
466         guchar *str;
467         guchar *storage=&_wapi_shared_data->scratch_base[0];
468         
469         if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) {
470                 return(NULL);
471         }
472         
473         hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
474         str=g_malloc0 (hdr->length+1);
475         memcpy (str, &storage[idx], hdr->length);
476
477         return(str);
478 }
479
480 void _wapi_handle_scratch_delete_internal (guint32 idx)
481 {
482         struct _WapiScratchHeader *hdr;
483         guchar *storage=&_wapi_shared_data->scratch_base[0];
484         
485         if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) {
486                 return;
487         }
488         
489         hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
490         memset (&storage[idx], '\0', hdr->length);
491         hdr->flags |= WAPI_SHM_SCRATCH_FREE;
492         
493         /* We could coalesce forwards here if the next block is also
494          * free, but the _store() function will do that anyway.
495          */
496 }
497
498 void _wapi_handle_scratch_delete (guint32 idx)
499 {
500         if(shared==TRUE) {
501                 WapiHandleRequest scratch_free;
502                 WapiHandleResponse scratch_free_resp;
503         
504                 scratch_free.type=WapiHandleRequestType_ScratchFree;
505                 scratch_free.u.scratch_free.idx=idx;
506         
507                 _wapi_daemon_request_response (daemon_sock, &scratch_free,
508                                                &scratch_free_resp);
509         
510                 if(scratch_free_resp.type!=WapiHandleResponseType_ScratchFree) {
511                         g_warning (G_GNUC_PRETTY_FUNCTION
512                                    ": bogus daemon response, type %d",
513                                    scratch_free_resp.type);
514                         g_assert_not_reached ();
515                 }
516         } else {
517                 _wapi_handle_scratch_delete_internal (idx);
518         }
519 }
520
521 void _wapi_handle_register_capabilities (WapiHandleType type,
522                                          WapiHandleCapability caps)
523 {
524         handle_caps[type]=caps;
525 }
526
527 gboolean _wapi_handle_test_capabilities (gpointer handle,
528                                          WapiHandleCapability caps)
529 {
530         guint32 idx=GPOINTER_TO_UINT (handle);
531         WapiHandleType type;
532
533         type=_wapi_shared_data->handles[idx].type;
534
535 #ifdef DEBUG
536         g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)",
537                    handle_caps[type], caps, handle_caps[type] & caps);
538 #endif
539         
540         return((handle_caps[type] & caps)!=0);
541 }
542
543 void _wapi_handle_ops_close_shared (gpointer handle)
544 {
545         guint32 idx=GPOINTER_TO_UINT (handle);
546         WapiHandleType type;
547
548         type=_wapi_shared_data->handles[idx].type;
549
550         if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) {
551                 handle_ops[type]->close_shared (handle);
552         }
553 }
554
555 void _wapi_handle_ops_close_private (gpointer handle)
556 {
557         guint32 idx=GPOINTER_TO_UINT (handle);
558         WapiHandleType type;
559
560         type=_wapi_shared_data->handles[idx].type;
561
562         if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) {
563                 handle_ops[type]->close_private (handle);
564         }
565 }
566
567 void _wapi_handle_ops_signal (gpointer handle)
568 {
569         guint32 idx=GPOINTER_TO_UINT (handle);
570         WapiHandleType type;
571
572         type=_wapi_shared_data->handles[idx].type;
573
574         if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) {
575                 handle_ops[type]->signal (handle);
576         }
577 }
578
579 void _wapi_handle_ops_own (gpointer handle)
580 {
581         guint32 idx=GPOINTER_TO_UINT (handle);
582         WapiHandleType type;
583
584         type=_wapi_shared_data->handles[idx].type;
585
586         if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) {
587                 handle_ops[type]->own_handle (handle);
588         }
589 }
590
591 gboolean _wapi_handle_ops_isowned (gpointer handle)
592 {
593         guint32 idx=GPOINTER_TO_UINT (handle);
594         WapiHandleType type;
595
596         type=_wapi_shared_data->handles[idx].type;
597
598         if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) {
599                 return(handle_ops[type]->is_owned (handle));
600         } else {
601                 return(FALSE);
602         }
603 }
604
605 /**
606  * CloseHandle:
607  * @handle: The handle to release
608  *
609  * Closes and invalidates @handle, releasing any resources it
610  * consumes.  When the last handle to a temporary or non-persistent
611  * object is closed, that object can be deleted.  Closing the same
612  * handle twice is an error.
613  *
614  * Return value: %TRUE on success, %FALSE otherwise.
615  */
616 gboolean CloseHandle(gpointer handle)
617 {
618         _wapi_handle_unref (handle);
619         
620         return(TRUE);
621 }
622
623 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
624                                                gpointer *handles,
625                                                gboolean waitall,
626                                                guint32 *retcount,
627                                                guint32 *lowest)
628 {
629         guint32 count, i, iter=0;
630         gboolean ret;
631         
632         /* Lock all the handles, with backoff */
633 again:
634         for(i=0; i<numhandles; i++) {
635                 guint32 idx=GPOINTER_TO_UINT (handles[i]);
636                 
637                 ret=mono_mutex_trylock (&_wapi_shared_data->handles[idx].signal_mutex);
638                 if(ret!=0) {
639                         /* Bummer */
640                         struct timespec sleepytime;
641                         
642                         while(i--) {
643                                 idx=GPOINTER_TO_UINT (handles[i]);
644                                 mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex);
645                         }
646
647                         /* If iter ever reaches 100 the nanosleep will
648                          * return EINVAL immediately, but we have a
649                          * design flaw if that happens.
650                          */
651                         iter++;
652                         if(iter==100) {
653                                 g_warning (G_GNUC_PRETTY_FUNCTION
654                                            ": iteration overflow!");
655                                 iter=1;
656                         }
657                         
658                         sleepytime.tv_sec=0;
659                         sleepytime.tv_nsec=10000000 * iter;     /* 10ms*iter */
660                         
661 #ifdef DEBUG
662                         g_message (G_GNUC_PRETTY_FUNCTION
663                                    ": Backing off for %d ms", iter*10);
664 #endif
665                         nanosleep (&sleepytime, NULL);
666                         
667                         goto again;
668                 }
669         }
670         
671 #ifdef DEBUG
672         g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles");
673 #endif
674
675         count=0;
676         *lowest=numhandles;
677         
678         for(i=0; i<numhandles; i++) {
679                 guint32 idx=GPOINTER_TO_UINT (handles[i]);
680                 
681 #ifdef DEBUG
682                 g_message (G_GNUC_PRETTY_FUNCTION ": Checking handle %p",
683                            handles[i]);
684 #endif
685
686                 if(((_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_OWN)==TRUE) &&
687                     (_wapi_handle_ops_isowned (handles[i])==TRUE)) ||
688                    (_wapi_shared_data->handles[idx].signalled==TRUE)) {
689                         count++;
690                         
691 #ifdef DEBUG
692                         g_message (G_GNUC_PRETTY_FUNCTION
693                                    ": Handle %p signalled", handles[i]);
694 #endif
695                         if(*lowest>i) {
696                                 *lowest=i;
697                         }
698                 }
699         }
700         
701 #ifdef DEBUG
702         g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
703                    count);
704 #endif
705
706         if((waitall==TRUE && count==numhandles) ||
707            (waitall==FALSE && count>0)) {
708                 ret=TRUE;
709         } else {
710                 ret=FALSE;
711         }
712         
713 #ifdef DEBUG
714         g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
715 #endif
716
717         *retcount=count;
718         
719         return(ret);
720 }
721
722 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
723 {
724         guint32 i;
725         
726         for(i=0; i<numhandles; i++) {
727                 guint32 idx=GPOINTER_TO_UINT (handles[i]);
728                 
729                 mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex);
730         }
731 }
732
733 int _wapi_handle_wait_signal (void)
734 {
735 #ifdef _POSIX_THREAD_PROCESS_SHARED
736         return(mono_cond_wait (&_wapi_shared_data->signal_cond,
737                                &_wapi_shared_data->signal_mutex));
738 #else
739         return(mono_cond_wait (&_wapi_private_data->signal_cond,
740                                &_wapi_private_data->signal_mutex));
741 #endif /* _POSIX_THREAD_PROCESS_SHARED */
742 }
743
744 int _wapi_handle_timedwait_signal (struct timespec *timeout)
745 {
746 #ifdef _POSIX_THREAD_PROCESS_SHARED
747         return(mono_cond_timedwait (&_wapi_shared_data->signal_cond,
748                                     &_wapi_shared_data->signal_mutex,
749                                     timeout));
750 #else
751         return(mono_cond_timedwait (&_wapi_private_data->signal_cond,
752                                     &_wapi_private_data->signal_mutex,
753                                     timeout));
754 #endif /* _POSIX_THREAD_PROCESS_SHARED */
755 }
756
757 int _wapi_handle_wait_signal_handle (gpointer handle)
758 {
759         guint32 idx=GPOINTER_TO_UINT (handle);
760         
761         return(mono_cond_wait (&_wapi_shared_data->handles[idx].signal_cond,
762                                &_wapi_shared_data->handles[idx].signal_mutex));
763 }
764
765 int _wapi_handle_timedwait_signal_handle (gpointer handle,
766                                           struct timespec *timeout)
767 {
768         guint32 idx=GPOINTER_TO_UINT (handle);
769         
770         return(mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
771                                     &_wapi_shared_data->handles[idx].signal_mutex,
772                                     timeout));
773 }