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