e1fd62e17dec0ea8addf3425a4f8ba2e0abbd960
[mono.git] / mono / io-layer / handles.c
1 /*
2  * handles.c:  Generic and internal operations on handles
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 <pthread.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <sys/un.h>
18
19 #if HAVE_BOEHM_GC
20 #include <gc/gc.h>
21 #endif
22
23 #include <mono/io-layer/wapi.h>
24 #include <mono/io-layer/wapi-private.h>
25 #include <mono/io-layer/handles-private.h>
26 #include <mono/io-layer/mono-mutex.h>
27 #include <mono/io-layer/shared.h>
28 #include <mono/io-layer/misc-private.h>
29 #include <mono/io-layer/daemon-messages.h>
30
31 #undef DEBUG
32
33 /*
34  * This flag _MUST_ remain set to FALSE in the daemon process.  When
35  * we exec()d a standalone daemon, that happened because shared_init()
36  * didnt get called in the daemon process.  Now we just fork() without
37  * exec(), we need to ensure that the fork() happens when shared is
38  * still FALSE.
39  *
40  * This is further complicated by the second attempt to start the
41  * daemon if the connect() fails.
42  */
43 static gboolean shared=FALSE;
44
45 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
46 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
47         NULL,
48         &_wapi_file_ops,
49         &_wapi_console_ops,
50         &_wapi_thread_ops,
51         &_wapi_sem_ops,
52         &_wapi_mutex_ops,
53         &_wapi_event_ops,
54         &_wapi_socket_ops,
55         &_wapi_find_ops,
56         &_wapi_process_ops,
57 };
58
59 static int daemon_sock;
60
61 static pthread_mutexattr_t mutex_shared_attr;
62 static pthread_condattr_t cond_shared_attr;
63
64 struct _WapiHandleShared_list *_wapi_shared_data=NULL;
65 struct _WapiHandlePrivate_list *_wapi_private_data=NULL;
66
67 int disable_shm = 0;
68 static void shared_init (void)
69 {
70         struct sockaddr_un shared_socket_address;
71         gboolean tried_once=FALSE;
72         int ret;
73
74         if (getenv ("MONO_ENABLE_SHM"))
75                 disable_shm = 0;
76         
77 attach_again:
78
79 #ifndef DISABLE_SHARED_HANDLES
80         if(getenv ("MONO_DISABLE_SHM") || disable_shm)
81 #endif
82         {
83                 shared=FALSE;
84 #ifndef DISABLE_SHARED_HANDLES
85         } else {
86                 int shm_id;
87                 gboolean success;
88                 
89                 /* Ensure that shared==FALSE while _wapi_shm_attach()
90                  * calls fork()
91                  */
92                 shared=FALSE;
93                 
94                 _wapi_shared_data=_wapi_shm_attach (FALSE, &success, &shm_id);
95                 shared=success;
96                 if(shared==FALSE) {
97                         g_warning ("Failed to attach shared memory! "
98                                    "(tried shared memory ID 0x%x). "
99                                    "Falling back to non-shared handles",
100                                    shm_id);
101                 }
102 #endif /* DISABLE_SHARED_HANDLES */
103         }
104         
105
106         if(shared==TRUE) {
107                 daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0);
108                 shared_socket_address.sun_family=AF_UNIX;
109                 memcpy (shared_socket_address.sun_path,
110                         _wapi_shared_data->daemon, MONO_SIZEOF_SUNPATH);
111                 ret=connect (daemon_sock, (struct sockaddr *)&shared_socket_address,
112                              sizeof(struct sockaddr_un));
113                 if(ret==-1) {
114                         if(tried_once==TRUE) {
115                                 g_warning (G_GNUC_PRETTY_FUNCTION
116                                            "connect to daemon failed: %s",
117                                            strerror (errno));
118                                 /* Fall back to private handles */
119                                 shared=FALSE;
120                         } else {
121                                 /* It's possible that the daemon
122                                  * crashed without destroying the
123                                  * shared memory segment (thus fooling
124                                  * subsequent processes into thinking
125                                  * the daemon is still active).
126                                  * 
127                                  * Destroy the shared memory segment
128                                  * and try once more.  This won't
129                                  * break running apps, but no new apps
130                                  * will be able to see the current
131                                  * shared memory segment.
132                                  */
133                                 tried_once=TRUE;
134                                 _wapi_shm_destroy ();
135                                 
136                                 goto attach_again;
137                         }
138                 }
139         }
140
141         if(shared==FALSE) {
142 #ifdef DEBUG
143                 g_message (G_GNUC_PRETTY_FUNCTION
144                            ": Using process-private handles");
145 #endif
146                 _wapi_shared_data=
147                         g_malloc0 (sizeof(struct _WapiHandleShared_list)+
148                                    _WAPI_SHM_SCRATCH_SIZE);
149         }
150         _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list, 1);
151
152         pthread_mutexattr_init (&mutex_shared_attr);
153         pthread_condattr_init (&cond_shared_attr);
154
155 #ifdef _POSIX_THREAD_PROCESS_SHARED
156         pthread_mutexattr_setpshared (&mutex_shared_attr,
157                                       PTHREAD_PROCESS_SHARED);
158         pthread_condattr_setpshared (&cond_shared_attr,
159                                      PTHREAD_PROCESS_SHARED);
160 #endif
161 }
162
163 /*
164  * _wapi_handle_new_internal:
165  * @type: Init handle to this type
166  *
167  * Search for a free handle and initialize it. Return the handle on
168  * succes and 0 on failure.
169  */
170 guint32 _wapi_handle_new_internal (WapiHandleType type)
171 {
172         guint32 i;
173         static guint32 last=1;
174         
175         /* A linear scan should be fast enough.  Start from the last
176          * allocation, assuming that handles are allocated more often
177          * than they're freed. Leave 0 (NULL) as a guard
178          */
179 again:
180         for(i=last; i<_WAPI_MAX_HANDLES; i++) {
181                 struct _WapiHandleShared *shared=&_wapi_shared_data->handles[i];
182                 
183                 if(shared->type==WAPI_HANDLE_UNUSED) {
184                         last=i+1;
185                         shared->type=type;
186                         shared->signalled=FALSE;
187                         mono_mutex_init (&_wapi_shared_data->handles[i].signal_mutex, &mutex_shared_attr);
188                         pthread_cond_init (&_wapi_shared_data->handles[i].signal_cond, &cond_shared_attr);
189
190                         return(i);
191                 }
192         }
193
194         if(last>1) {
195                 /* Try again from the beginning */
196                 last=1;
197                 goto again;
198         }
199
200         return(0);
201 }
202
203 gpointer _wapi_handle_new (WapiHandleType type)
204 {
205         static mono_once_t shared_init_once = MONO_ONCE_INIT;
206         static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER;
207         guint32 idx;
208         gpointer handle;
209         WapiHandleRequest new;
210         WapiHandleResponse new_resp;
211 #if HAVE_BOEHM_GC
212         gboolean tried_collect=FALSE;
213 #endif
214         
215         mono_once (&shared_init_once, shared_init);
216         
217 again:
218         if(shared==TRUE) {
219                 new.type=WapiHandleRequestType_New;
220                 new.u.new.type=type;
221         
222                 _wapi_daemon_request_response (daemon_sock, &new, &new_resp);
223         
224                 if (new_resp.type==WapiHandleResponseType_New) {
225                         idx=new_resp.u.new.handle;
226                 } else {
227                         g_warning (G_GNUC_PRETTY_FUNCTION
228                                    ": bogus daemon response, type %d",
229                                    new_resp.type);
230                         g_assert_not_reached ();
231                 }
232         } else {
233                 pthread_mutex_lock (&scan_mutex);
234                 idx=_wapi_handle_new_internal (type);
235                 pthread_mutex_unlock (&scan_mutex);
236         }
237                 
238         if(idx==0) {
239                 g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!");
240
241 #if HAVE_BOEHM_GC
242                 /* See if we can reclaim some handles by forcing a GC
243                  * collection
244                  */
245                 if(tried_collect==FALSE) {
246                         g_warning (G_GNUC_PRETTY_FUNCTION
247                                    ": Seeing if GC collection helps...");
248                         GC_gcollect ();
249                         tried_collect=TRUE;
250                         goto again;
251                 } else {
252                         g_warning (G_GNUC_PRETTY_FUNCTION
253                                    ": didn't help, returning error");
254                 }
255 #endif
256         
257                 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
258         }
259
260         handle=GUINT_TO_POINTER (idx);
261
262 #ifdef DEBUG
263         g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle);
264 #endif
265
266         return(handle);
267 }
268
269 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
270                               gpointer *shared, gpointer *private)
271 {
272         struct _WapiHandleShared *shared_handle_data;
273         struct _WapiHandlePrivate *private_handle_data;
274         guint32 idx=GPOINTER_TO_UINT (handle);
275
276         shared_handle_data=&_wapi_shared_data->handles[idx];
277
278         /* Allow WAPI_HANDLE_UNUSED to mean "dont care which
279          * type"
280          */
281         if(shared_handle_data->type!=type && type != WAPI_HANDLE_UNUSED) {
282                 return(FALSE);
283         }
284
285         if(shared!=NULL) {
286                 *shared=&shared_handle_data->u;
287         }
288         
289         if(private!=NULL) {
290                 private_handle_data=&_wapi_private_data->handles[idx];
291
292                 *private=&private_handle_data->u;
293         }
294         
295         return(TRUE);
296 }
297
298 gpointer _wapi_search_handle (WapiHandleType type,
299                               gboolean (*check)(gpointer test, gpointer user),
300                               gpointer user_data,
301                               gpointer *shared, gpointer *private)
302 {
303         struct _WapiHandleShared *shared_handle_data;
304         struct _WapiHandlePrivate *private_handle_data;
305         guint32 i;
306
307         for(i=1; i<_WAPI_MAX_HANDLES; i++) {
308                 struct _WapiHandleShared *shared=&_wapi_shared_data->handles[i];
309                 
310                 if(shared->type==type) {
311                         if(check (GUINT_TO_POINTER (i), user_data)==TRUE) {
312                                 break;
313                         }
314                 }
315         }
316
317         if(i==_WAPI_MAX_HANDLES) {
318                 return(GUINT_TO_POINTER (0));
319         }
320         
321         if(shared!=NULL) {
322                 shared_handle_data=&_wapi_shared_data->handles[i];
323
324                 *shared=&shared_handle_data->u;
325         }
326         
327         if(private!=NULL) {
328                 private_handle_data=&_wapi_private_data->handles[i];
329
330                 *private=&private_handle_data->u;
331         }
332         
333         return(GUINT_TO_POINTER (i));
334 }
335
336 void _wapi_handle_ref (gpointer handle)
337 {
338         guint32 idx=GPOINTER_TO_UINT (handle);
339
340         if(shared==TRUE) {
341                 WapiHandleRequest req;
342                 WapiHandleResponse resp;
343         
344                 req.type=WapiHandleRequestType_Open;
345                 req.u.open.handle=idx;
346         
347                 _wapi_daemon_request_response (daemon_sock, &req, &resp);
348                 if(resp.type!=WapiHandleResponseType_Open) {
349                         g_warning (G_GNUC_PRETTY_FUNCTION
350                                    ": bogus daemon response, type %d",
351                                    resp.type);
352                         g_assert_not_reached ();
353                 }
354         } else {
355                 _wapi_shared_data->handles[idx].ref++;
356         
357 #ifdef DEBUG
358                 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
359                            handle, _wapi_shared_data->handles[idx].ref);
360 #endif
361         }
362 }
363
364 void _wapi_handle_unref (gpointer handle)
365 {
366         guint32 idx=GPOINTER_TO_UINT (handle);
367         gboolean destroy;
368         
369         if(shared==TRUE) {
370                 WapiHandleRequest req;
371                 WapiHandleResponse resp;
372         
373                 req.type=WapiHandleRequestType_Close;
374                 req.u.close.handle=GPOINTER_TO_UINT (handle);
375         
376                 _wapi_daemon_request_response (daemon_sock, &req, &resp);
377                 if(resp.type!=WapiHandleResponseType_Close) {
378                         g_warning (G_GNUC_PRETTY_FUNCTION
379                                    ": bogus daemon response, type %d",
380                                    resp.type);
381                         g_assert_not_reached ();
382                 } else {
383                         destroy=resp.u.close.destroy;
384                 }
385         } else {
386                 _wapi_shared_data->handles[idx].ref--;
387         
388 #ifdef DEBUG
389                 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
390                            handle, _wapi_shared_data->handles[idx].ref);
391 #endif
392
393                 /* Possible race condition here if another thread refs
394                  * the handle between here and setting the type to
395                  * UNUSED.  I could lock a mutex, but I'm not sure
396                  * that allowing a handle reference to reach 0 isn't
397                  * an application bug anyway.
398                  */
399                 destroy=(_wapi_shared_data->handles[idx].ref==0);
400         }
401
402         if(destroy==TRUE) {
403 #ifdef DEBUG
404                 g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p",
405                            handle);
406 #endif
407                 
408                 if(shared==FALSE) {
409                         _wapi_handle_ops_close_shared (handle);
410                         _wapi_shared_data->handles[idx].type=WAPI_HANDLE_UNUSED;
411                         mono_mutex_destroy (&_wapi_shared_data->handles[idx].signal_mutex);
412                         pthread_cond_destroy (&_wapi_shared_data->handles[idx].signal_cond);
413                         memset (&_wapi_shared_data->handles[idx].u, '\0', sizeof(_wapi_shared_data->handles[idx].u));
414                 }
415                 
416                 _wapi_handle_ops_close_private (handle);
417         }
418 }
419
420 #define HDRSIZE sizeof(struct _WapiScratchHeader)
421
422 /*
423  * _wapi_handle_scratch_store_internal:
424  * @bytes: Allocate no. bytes
425  *
426  * Like malloc(3) except its for the shared memory segment's scratch
427  * part. Memory block returned is zeroed out.
428  */
429 guint32 _wapi_handle_scratch_store_internal (guint32 bytes)
430 {
431         guint32 idx=0, last_idx=0;
432         struct _WapiScratchHeader *hdr, *last_hdr;
433         gboolean last_was_free=FALSE;
434         guchar *storage=&_wapi_shared_data->scratch_base[0];
435         
436 #ifdef DEBUG
437         g_message (G_GNUC_PRETTY_FUNCTION
438                    ": looking for %d bytes of scratch space (%d bytes total)",
439                    bytes, _WAPI_SHM_SCRATCH_SIZE);
440 #endif
441
442         hdr=(struct _WapiScratchHeader *)&storage[0];
443         if(hdr->flags==0 && hdr->length==0) {
444                 /* Need to initialise scratch data */
445                 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
446                 hdr->length = _WAPI_SHM_SCRATCH_SIZE - HDRSIZE;
447         }
448         
449         while(idx< _WAPI_SHM_SCRATCH_SIZE) {
450                 hdr=(struct _WapiScratchHeader *)&storage[idx];
451                 
452                 /* Do a simple first-fit allocation, coalescing
453                  * adjacent free blocks as we progress through the
454                  * scratch space
455                  */
456                 if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
457                    hdr->length >= bytes + HDRSIZE) {
458                         /* found space */
459                         guint32 old_length=hdr->length;
460 #ifdef DEBUG
461                         g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length);
462 #endif
463
464                         hdr->flags &= ~WAPI_SHM_SCRATCH_FREE;
465                         hdr->length=bytes;
466                         idx += HDRSIZE;
467
468                         /* Put a new header in at the end of the used
469                          * space
470                          */
471                         hdr=(struct _WapiScratchHeader *)&storage[idx+bytes];
472                         hdr->flags |= WAPI_SHM_SCRATCH_FREE;
473                         hdr->length = old_length-bytes-HDRSIZE;
474
475 #ifdef DEBUG
476                         g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
477 #endif
478                         
479                         /*
480                          * It was memset(0..) when free/made so no need to do it here
481                          */
482
483                         return(idx);
484                 } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
485                           last_was_free == FALSE) {
486 #ifdef DEBUG
487                         g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length);
488 #endif
489
490                         /* save this point in case we can coalesce it with
491                          * the next block, if that is free.
492                          */
493                         last_was_free=TRUE;
494                         last_idx=idx;
495                         last_hdr=hdr;
496                         idx+=(hdr->length+HDRSIZE);
497                 } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE &&
498                            last_was_free == TRUE) {
499 #ifdef DEBUG
500                         g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length);
501 #endif
502
503                         /* This block and the previous are both free,
504                          * so coalesce them
505                          */
506                         last_hdr->length += (hdr->length + HDRSIZE);
507
508                         /* If the new block is now big enough, use it
509                          * (next time round the loop)
510                          */
511                         if(last_hdr->length >= bytes + HDRSIZE) {
512                                 idx=last_idx;
513                         } else {
514                                 /* leave the last free info as it is,
515                                  * in case the next block is also free
516                                  * and can be coalesced too
517                                  */
518                                 idx=last_idx+last_hdr->length+HDRSIZE;
519                         }
520                 } else {
521 #ifdef DEBUG
522                         g_message (G_GNUC_PRETTY_FUNCTION
523                                    ": found used block at %d, length %d", idx,
524                                    hdr->length);
525 #endif
526
527                         /* must be used, try next chunk */
528                         idx+=(hdr->length+HDRSIZE);
529
530                         /* Don't let the coalescing blow away this block */
531                         last_was_free=FALSE;
532                 }
533         }
534         
535         return(0);
536 }
537
538 guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
539 {
540         static pthread_mutex_t scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
541         guint32 idx;
542         
543         /* No point storing no data */
544         if(bytes==0) {
545                 return(0);
546         }
547         
548         if(shared==TRUE) {
549                 WapiHandleRequest scratch;
550                 WapiHandleResponse scratch_resp;
551         
552                 scratch.type=WapiHandleRequestType_Scratch;
553                 scratch.u.scratch.length=bytes;
554         
555                 _wapi_daemon_request_response (daemon_sock, &scratch,
556                                                &scratch_resp);
557         
558                 if(scratch_resp.type==WapiHandleResponseType_Scratch) {
559                         idx=scratch_resp.u.scratch.idx;
560                 } else {
561                         g_warning (G_GNUC_PRETTY_FUNCTION
562                                    ": bogus daemon response, type %d",
563                                    scratch_resp.type);
564                         g_assert_not_reached ();
565                 }
566         } else {
567                 pthread_mutex_lock (&scratch_mutex);
568                 idx=_wapi_handle_scratch_store_internal (bytes);
569                 pthread_mutex_unlock (&scratch_mutex);
570                 
571                 if(idx==0) {
572                         /* Failed to allocate space */
573                         return(0);
574                 }
575         }
576
577         memcpy (&_wapi_shared_data->scratch_base[idx], data, bytes);
578         
579         return(idx);
580 }
581
582 guchar *_wapi_handle_scratch_lookup_as_string (guint32 idx)
583 {
584         struct _WapiScratchHeader *hdr;
585         guchar *str;
586         guchar *storage=&_wapi_shared_data->scratch_base[0];
587         
588         if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) {
589                 return(NULL);
590         }
591         
592         hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
593         str=g_malloc0 (hdr->length+1);
594         memcpy (str, &storage[idx], hdr->length);
595
596         return(str);
597 }
598
599 /*
600  * _wapi_handle_scratch_delete_internal:
601  * @idx: Index to free block
602  *
603  * Like free(3) except its for the shared memory segment's scratch
604  * part.
605  */
606 void _wapi_handle_scratch_delete_internal (guint32 idx)
607 {
608         struct _WapiScratchHeader *hdr;
609         guchar *storage=&_wapi_shared_data->scratch_base[0];
610         
611         if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) {
612                 return;
613         }
614         
615         hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
616         memset (&storage[idx], '\0', hdr->length);
617         hdr->flags |= WAPI_SHM_SCRATCH_FREE;
618         
619         /* We could coalesce forwards here if the next block is also
620          * free, but the _store() function will do that anyway.
621          */
622 }
623
624 void _wapi_handle_scratch_delete (guint32 idx)
625 {
626         if(shared==TRUE) {
627                 WapiHandleRequest scratch_free;
628                 WapiHandleResponse scratch_free_resp;
629         
630                 scratch_free.type=WapiHandleRequestType_ScratchFree;
631                 scratch_free.u.scratch_free.idx=idx;
632         
633                 _wapi_daemon_request_response (daemon_sock, &scratch_free,
634                                                &scratch_free_resp);
635         
636                 if(scratch_free_resp.type!=WapiHandleResponseType_ScratchFree) {
637                         g_warning (G_GNUC_PRETTY_FUNCTION
638                                    ": bogus daemon response, type %d",
639                                    scratch_free_resp.type);
640                         g_assert_not_reached ();
641                 }
642         } else {
643                 _wapi_handle_scratch_delete_internal (idx);
644         }
645 }
646
647 void _wapi_handle_register_capabilities (WapiHandleType type,
648                                          WapiHandleCapability caps)
649 {
650         handle_caps[type]=caps;
651 }
652
653 gboolean _wapi_handle_test_capabilities (gpointer handle,
654                                          WapiHandleCapability caps)
655 {
656         guint32 idx=GPOINTER_TO_UINT (handle);
657         WapiHandleType type;
658
659         type=_wapi_shared_data->handles[idx].type;
660
661 #ifdef DEBUG
662         g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)",
663                    handle_caps[type], caps, handle_caps[type] & caps);
664 #endif
665         
666         return((handle_caps[type] & caps)!=0);
667 }
668
669 void _wapi_handle_ops_close_shared (gpointer handle)
670 {
671         guint32 idx=GPOINTER_TO_UINT (handle);
672         WapiHandleType type;
673
674         type=_wapi_shared_data->handles[idx].type;
675
676         if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) {
677                 handle_ops[type]->close_shared (handle);
678         }
679 }
680
681 void _wapi_handle_ops_close_private (gpointer handle)
682 {
683         guint32 idx=GPOINTER_TO_UINT (handle);
684         WapiHandleType type;
685
686         type=_wapi_shared_data->handles[idx].type;
687
688         if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) {
689                 handle_ops[type]->close_private (handle);
690         }
691 }
692
693 void _wapi_handle_ops_signal (gpointer handle)
694 {
695         guint32 idx=GPOINTER_TO_UINT (handle);
696         WapiHandleType type;
697
698         type=_wapi_shared_data->handles[idx].type;
699
700         if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) {
701                 handle_ops[type]->signal (handle);
702         }
703 }
704
705 void _wapi_handle_ops_own (gpointer handle)
706 {
707         guint32 idx=GPOINTER_TO_UINT (handle);
708         WapiHandleType type;
709
710         type=_wapi_shared_data->handles[idx].type;
711
712         if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) {
713                 handle_ops[type]->own_handle (handle);
714         }
715 }
716
717 gboolean _wapi_handle_ops_isowned (gpointer handle)
718 {
719         guint32 idx=GPOINTER_TO_UINT (handle);
720         WapiHandleType type;
721
722         type=_wapi_shared_data->handles[idx].type;
723
724         if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) {
725                 return(handle_ops[type]->is_owned (handle));
726         } else {
727                 return(FALSE);
728         }
729 }
730
731 /**
732  * CloseHandle:
733  * @handle: The handle to release
734  *
735  * Closes and invalidates @handle, releasing any resources it
736  * consumes.  When the last handle to a temporary or non-persistent
737  * object is closed, that object can be deleted.  Closing the same
738  * handle twice is an error.
739  *
740  * Return value: %TRUE on success, %FALSE otherwise.
741  */
742 gboolean CloseHandle(gpointer handle)
743 {
744         _wapi_handle_unref (handle);
745         
746         return(TRUE);
747 }
748
749 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
750                                                gpointer *handles,
751                                                gboolean waitall,
752                                                guint32 *retcount,
753                                                guint32 *lowest)
754 {
755         guint32 count, i, iter=0;
756         gboolean ret;
757         
758         /* Lock all the handles, with backoff */
759 again:
760         for(i=0; i<numhandles; i++) {
761                 guint32 idx=GPOINTER_TO_UINT (handles[i]);
762                 
763 #ifdef DEBUG
764                 g_message (G_GNUC_PRETTY_FUNCTION ": attempting to lock %p",
765                            handles[i]);
766 #endif
767
768                 ret=mono_mutex_trylock (&_wapi_shared_data->handles[idx].signal_mutex);
769                 if(ret!=0) {
770                         /* Bummer */
771                         struct timespec sleepytime;
772                         
773 #ifdef DEBUG
774                         g_message (G_GNUC_PRETTY_FUNCTION ": attempt failed for %p", handles[i]);
775 #endif
776
777                         while(i--) {
778                                 idx=GPOINTER_TO_UINT (handles[i]);
779                                 mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex);
780                         }
781
782                         /* If iter ever reaches 100 the nanosleep will
783                          * return EINVAL immediately, but we have a
784                          * design flaw if that happens.
785                          */
786                         iter++;
787                         if(iter==100) {
788                                 g_warning (G_GNUC_PRETTY_FUNCTION
789                                            ": iteration overflow!");
790                                 iter=1;
791                         }
792                         
793                         sleepytime.tv_sec=0;
794                         sleepytime.tv_nsec=10000000 * iter;     /* 10ms*iter */
795                         
796 #ifdef DEBUG
797                         g_message (G_GNUC_PRETTY_FUNCTION
798                                    ": Backing off for %d ms", iter*10);
799 #endif
800                         nanosleep (&sleepytime, NULL);
801                         
802                         goto again;
803                 }
804         }
805         
806 #ifdef DEBUG
807         g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles");
808 #endif
809
810         count=0;
811         *lowest=numhandles;
812         
813         for(i=0; i<numhandles; i++) {
814                 guint32 idx=GPOINTER_TO_UINT (handles[i]);
815                 
816 #ifdef DEBUG
817                 g_message (G_GNUC_PRETTY_FUNCTION ": Checking handle %p",
818                            handles[i]);
819 #endif
820
821                 if(((_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_OWN)==TRUE) &&
822                     (_wapi_handle_ops_isowned (handles[i])==TRUE)) ||
823                    (_wapi_shared_data->handles[idx].signalled==TRUE)) {
824                         count++;
825                         
826 #ifdef DEBUG
827                         g_message (G_GNUC_PRETTY_FUNCTION
828                                    ": Handle %p signalled", handles[i]);
829 #endif
830                         if(*lowest>i) {
831                                 *lowest=i;
832                         }
833                 }
834         }
835         
836 #ifdef DEBUG
837         g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
838                    count);
839 #endif
840
841         if((waitall==TRUE && count==numhandles) ||
842            (waitall==FALSE && count>0)) {
843                 ret=TRUE;
844         } else {
845                 ret=FALSE;
846         }
847         
848 #ifdef DEBUG
849         g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
850 #endif
851
852         *retcount=count;
853         
854         return(ret);
855 }
856
857 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
858 {
859         guint32 i;
860         
861         for(i=0; i<numhandles; i++) {
862                 guint32 idx=GPOINTER_TO_UINT (handles[i]);
863                 
864 #ifdef DEBUG
865                 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p",
866                            handles[i]);
867 #endif
868
869                 mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex);
870         }
871 }
872
873 /* Process-shared handles (currently only process and thread handles
874  * are allowed, and they only work because once signalled they can't
875  * become unsignalled) are waited for by one process and signalled by
876  * another.  Without process-shared conditions, the waiting process
877  * will block forever.  To get around this, the four handle waiting
878  * functions use a short timeout when _POSIX_THREAD_PROCESS_SHARED is
879  * not available.  They also return "success" if the fake timeout
880  * expired, and let the caller check signal status.
881  */
882 int _wapi_handle_wait_signal (void)
883 {
884 #ifdef _POSIX_THREAD_PROCESS_SHARED
885         return(mono_cond_wait (&_wapi_shared_data->signal_cond,
886                                &_wapi_shared_data->signal_mutex));
887 #else
888         struct timespec fake_timeout;
889         int ret;
890         
891         _wapi_calc_timeout (&fake_timeout, 100);
892
893         ret=mono_cond_timedwait (&_wapi_private_data->signal_cond,
894                                  &_wapi_private_data->signal_mutex,
895                                  &fake_timeout);
896         if(ret==ETIMEDOUT) {
897                 ret=0;
898         }
899
900         return(ret);
901 #endif /* _POSIX_THREAD_PROCESS_SHARED */
902 }
903
904 int _wapi_handle_timedwait_signal (struct timespec *timeout)
905 {
906 #ifdef _POSIX_THREAD_PROCESS_SHARED
907         return(mono_cond_timedwait (&_wapi_shared_data->signal_cond,
908                                     &_wapi_shared_data->signal_mutex,
909                                     timeout));
910 #else
911         struct timespec fake_timeout;
912         int ret;
913         
914         _wapi_calc_timeout (&fake_timeout, 100);
915         
916         if((fake_timeout.tv_sec>timeout->tv_sec) ||
917            (fake_timeout.tv_sec==timeout->tv_sec &&
918             fake_timeout.tv_nsec > timeout->tv_nsec)) {
919                 /* Real timeout is less than 100ms time */
920                 ret=mono_cond_timedwait (&_wapi_private_data->signal_cond,
921                                          &_wapi_private_data->signal_mutex,
922                                          timeout);
923         } else {
924                 ret=mono_cond_timedwait (&_wapi_private_data->signal_cond,
925                                          &_wapi_private_data->signal_mutex,
926                                          &fake_timeout);
927                 if(ret==ETIMEDOUT) {
928                         ret=0;
929                 }
930         }
931         
932         return(ret);
933 #endif /* _POSIX_THREAD_PROCESS_SHARED */
934 }
935
936 int _wapi_handle_wait_signal_handle (gpointer handle)
937 {
938 #ifdef _POSIX_THREAD_PROCESS_SHARED
939         guint32 idx=GPOINTER_TO_UINT (handle);
940         
941         return(mono_cond_wait (&_wapi_shared_data->handles[idx].signal_cond,
942                                &_wapi_shared_data->handles[idx].signal_mutex));
943 #else
944         guint32 idx=GPOINTER_TO_UINT (handle);
945         struct timespec fake_timeout;
946         int ret;
947         
948         _wapi_calc_timeout (&fake_timeout, 100);
949         
950         ret=mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
951                                  &_wapi_shared_data->handles[idx].signal_mutex,
952                                  &fake_timeout);
953         if(ret==ETIMEDOUT) {
954                 ret=0;
955         }
956
957         return(ret);
958 #endif /* _POSIX_THREAD_PROCESS_SHARED */
959 }
960
961 int _wapi_handle_timedwait_signal_handle (gpointer handle,
962                                           struct timespec *timeout)
963 {
964 #ifdef _POSIX_THREAD_PROCESS_SHARED
965         guint32 idx=GPOINTER_TO_UINT (handle);
966         
967         return(mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
968                                     &_wapi_shared_data->handles[idx].signal_mutex,
969                                     timeout));
970 #else
971         guint32 idx=GPOINTER_TO_UINT (handle);
972         struct timespec fake_timeout;
973         int ret;
974         
975         _wapi_calc_timeout (&fake_timeout, 100);
976         
977         if((fake_timeout.tv_sec>timeout->tv_sec) ||
978            (fake_timeout.tv_sec==timeout->tv_sec &&
979             fake_timeout.tv_nsec > timeout->tv_nsec)) {
980                 /* Real timeout is less than 100ms time */
981                 ret=mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
982                                          &_wapi_shared_data->handles[idx].signal_mutex,
983                                          timeout);
984         } else {
985                 ret=mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
986                                          &_wapi_shared_data->handles[idx].signal_mutex,
987                                          &fake_timeout);
988                 if(ret==ETIMEDOUT) {
989                         ret=0;
990                 }
991         }
992         
993         return(ret);
994 #endif /* _POSIX_THREAD_PROCESS_SHARED */
995 }
996
997 gboolean _wapi_handle_process_fork (guint32 cmd, guint32 args, guint32 env,
998                                     guint32 dir, gboolean inherit,
999                                     guint32 flags, gpointer stdin_handle,
1000                                     gpointer stdout_handle,
1001                                     gpointer stderr_handle,
1002                                     gpointer *process_handle,
1003                                     gpointer *thread_handle, guint32 *pid,
1004                                     guint32 *tid)
1005 {
1006         WapiHandleRequest fork_proc;
1007         WapiHandleResponse fork_proc_resp;
1008         int in_fd, out_fd, err_fd;
1009         
1010         if(shared!=TRUE) {
1011                 return(FALSE);
1012         }
1013
1014         fork_proc.type=WapiHandleRequestType_ProcessFork;
1015         fork_proc.u.process_fork.cmd=cmd;
1016         fork_proc.u.process_fork.args=args;
1017         fork_proc.u.process_fork.env=env;
1018         fork_proc.u.process_fork.dir=dir;
1019         fork_proc.u.process_fork.stdin_handle=GPOINTER_TO_UINT (stdin_handle);
1020         fork_proc.u.process_fork.stdout_handle=GPOINTER_TO_UINT (stdout_handle);
1021         fork_proc.u.process_fork.stderr_handle=GPOINTER_TO_UINT (stderr_handle);
1022         fork_proc.u.process_fork.inherit=inherit;
1023         fork_proc.u.process_fork.flags=flags;
1024         
1025         in_fd=_wapi_file_handle_to_fd (stdin_handle);
1026         out_fd=_wapi_file_handle_to_fd (stdout_handle);
1027         err_fd=_wapi_file_handle_to_fd (stderr_handle);
1028
1029         if(in_fd==-1 || out_fd==-1 || err_fd==-1) {
1030                 /* We were given duff handles */
1031                 /* FIXME: error code */
1032                 return(FALSE);
1033         }
1034         
1035         _wapi_daemon_request_response_with_fds (daemon_sock, &fork_proc,
1036                                                 &fork_proc_resp, in_fd,
1037                                                 out_fd, err_fd);
1038         if(fork_proc_resp.type==WapiHandleResponseType_ProcessFork) {
1039                 *process_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.process_handle);
1040                 *thread_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.thread_handle);
1041                 *pid=fork_proc_resp.u.process_fork.pid;
1042                 *tid=fork_proc_resp.u.process_fork.tid;
1043
1044                 /* If there was an internal error, the handles will be
1045                  * 0.  If there was an error forking or execing, the
1046                  * handles will have values, and process_handle's
1047                  * exec_errno will be set, and the handle will be
1048                  * signalled immediately.
1049                  */
1050                 if(process_handle==0 || thread_handle==0) {
1051                         return(FALSE);
1052                 } else {
1053                         return(TRUE);
1054                 }
1055         } else {
1056                 g_warning (G_GNUC_PRETTY_FUNCTION
1057                            ": bogus daemon response, type %d",
1058                            fork_proc_resp.type);
1059                 g_assert_not_reached ();
1060         }
1061         
1062         return(FALSE);
1063 }