* Makefile: Suppress compiler warning cs:618 for test dll.
[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 <string.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/un.h>
19 #include <sys/mman.h>
20
21 #include <mono/os/gc_wrapper.h>
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 #undef HEAVY_DEBUG /* This will print handle counts on every handle created */
33
34 /* Shared threads don't seem to work yet */
35 #undef _POSIX_THREAD_PROCESS_SHARED
36
37 /*
38  * This flag _MUST_ remain set to FALSE in the daemon process.  When
39  * we exec()d a standalone daemon, that happened because shared_init()
40  * didnt get called in the daemon process.  Now we just fork() without
41  * exec(), we need to ensure that the fork() happens when shared is
42  * still FALSE.
43  *
44  * This is further complicated by the second attempt to start the
45  * daemon if the connect() fails.
46  */
47 static gboolean shared=FALSE;
48
49 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
50 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
51         NULL,
52         &_wapi_file_ops,
53         &_wapi_console_ops,
54         &_wapi_thread_ops,
55         &_wapi_sem_ops,
56         &_wapi_mutex_ops,
57         &_wapi_event_ops,
58         &_wapi_socket_ops,
59         &_wapi_find_ops,
60         &_wapi_process_ops,
61         &_wapi_pipe_ops,
62 };
63
64 static int daemon_sock;
65
66 static pthread_mutexattr_t mutex_shared_attr;
67 static pthread_condattr_t cond_shared_attr;
68
69 struct _WapiHandleShared_list **_wapi_shared_data=NULL;
70 struct _WapiHandleScratch *_wapi_shared_scratch=NULL;
71 struct _WapiHandlePrivate_list **_wapi_private_data=NULL;
72 pthread_mutex_t _wapi_shared_mutex=PTHREAD_MUTEX_INITIALIZER;
73
74 /* This holds the length of the _wapi_shared_data and
75  * _wapi_private_data arrays, so we know if a segment is off the end
76  * of the array, requiring a realloc
77  */
78 guint32 _wapi_shm_mapped_segments;
79
80 static void shared_init (void)
81 {
82         struct sockaddr_un shared_socket_address;
83         gboolean tried_once=FALSE;
84         int ret;
85         int thr_ret;
86         
87         _wapi_shared_data=g_new0 (struct _WapiHandleShared_list *, 1);
88         _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list *, 1);
89         
90 attach_again:
91
92 #ifndef DISABLE_SHARED_HANDLES
93         if(getenv ("MONO_DISABLE_SHM"))
94 #endif
95         {
96                 shared=FALSE;
97 #ifndef DISABLE_SHARED_HANDLES
98         } else {
99                 /* Ensure that shared==FALSE while _wapi_shm_attach()
100                  * calls fork()
101                  */
102                 shared=FALSE;
103                 
104                 shared=_wapi_shm_attach (&_wapi_shared_data[0],
105                                          &_wapi_shared_scratch);
106                 if(shared==FALSE) {
107                         g_warning (
108                                 "Failed to attach shared memory! "
109                                 "Falling back to non-shared handles\n"
110                                 "See: http://www.go-mono.com/issues.html#wapi for details");
111                 }
112 #endif /* DISABLE_SHARED_HANDLES */
113         }
114         
115
116         if(shared==TRUE) {
117                 daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0);
118                 shared_socket_address.sun_family=AF_UNIX;
119                 memcpy (shared_socket_address.sun_path,
120                         _wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH);
121                 ret=connect (daemon_sock,
122                              (struct sockaddr *)&shared_socket_address,
123                              sizeof(struct sockaddr_un));
124                 if(ret==-1) {
125                         if(tried_once==TRUE) {
126                                 g_warning (G_GNUC_PRETTY_FUNCTION
127                                            ": connect to daemon failed: %s",
128                                            g_strerror (errno));
129                                 /* Fall back to private handles */
130                                 shared=FALSE;
131                         } else {
132                                 /* It's possible that the daemon
133                                  * crashed without destroying the
134                                  * shared memory segment (thus fooling
135                                  * subsequent processes into thinking
136                                  * the daemon is still active).
137                                  * 
138                                  * Destroy the shared memory segment
139                                  * and try once more.  This won't
140                                  * break running apps, but no new apps
141                                  * will be able to see the current
142                                  * shared memory segment.
143                                  */
144                                 tried_once=TRUE;
145                                 _wapi_shm_destroy ();
146                                 
147                                 goto attach_again;
148                         }
149                 }
150         }
151
152         if(shared==FALSE) {
153 #ifdef DEBUG
154                 g_message (G_GNUC_PRETTY_FUNCTION
155                            ": Using process-private handles");
156 #endif
157                 _wapi_shared_data[0]=g_new0 (struct _WapiHandleShared_list, 1);
158                 _wapi_shared_data[0]->num_segments=1;
159
160                 _wapi_shared_scratch=g_new0 (struct _WapiHandleScratch, 1);
161         }
162         _wapi_private_data[0]=g_new0 (struct _WapiHandlePrivate_list, 1);
163         _wapi_shm_mapped_segments=1;
164
165         thr_ret = pthread_mutexattr_init (&mutex_shared_attr);
166         g_assert (thr_ret == 0);
167         
168         thr_ret = pthread_condattr_init (&cond_shared_attr);
169         g_assert (thr_ret == 0);
170         
171 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
172         thr_ret = pthread_mutexattr_setpshared (&mutex_shared_attr,
173                                                 PTHREAD_PROCESS_SHARED);
174         g_assert (thr_ret == 0);
175         
176         thr_ret = pthread_condattr_setpshared (&cond_shared_attr,
177                                                PTHREAD_PROCESS_SHARED);
178         g_assert (thr_ret == 0);
179 #else
180         thr_ret = pthread_cond_init(&_wapi_private_data[0]->signal_cond, NULL);
181         g_assert (thr_ret == 0);
182         
183         thr_ret = mono_mutex_init(&_wapi_private_data[0]->signal_mutex, NULL);
184         g_assert (thr_ret == 0);
185 #endif
186 }
187
188 #ifdef HEAVY_DEBUG
189 static void
190 print_handle_count (gint mask)
191 {
192         gint *count, num_handles;
193         gint i;
194         static const gchar *names [] = {"WAPI_HANDLE_UNUSED",
195                                   "WAPI_HANDLE_FILE",
196                                   "WAPI_HANDLE_CONSOLE",
197                                   "WAPI_HANDLE_THREAD",
198                                   "WAPI_HANDLE_SEM",
199                                   "WAPI_HANDLE_MUTEX",
200                                   "WAPI_HANDLE_EVENT",
201                                   "WAPI_HANDLE_SOCKET",
202                                   "WAPI_HANDLE_FIND",
203                                   "WAPI_HANDLE_PROCESS",
204                                   "WAPI_HANDLE_PIPE"
205                                 };
206
207
208         num_handles=_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT;
209         count=g_new0 (gint, num_handles);
210
211         for (i = 1; i < num_handles; i++) {
212                 struct _WapiHandleShared *shared;
213                 guint32 segment, idx;
214                 
215                 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
216                 _wapi_handle_ensure_mapped (segment);
217                 
218                 shared = &_wapi_handle_get_shared_segment (segment)->handles[idx];
219                 count [shared->type]++;
220         }
221
222         for (i = 0; i < num_handles; i++)
223                 if ((i & mask) == i) /* Always prints the UNUSED count */
224                         g_print ("%s: %d\n", names [i], count [i]);
225
226         g_free (count);
227 }
228 #endif /* HEAVY_DEBUG */
229
230 /*
231  * _wapi_handle_new_internal:
232  * @type: Init handle to this type
233  *
234  * Search for a free handle and initialize it. Return the handle on
235  * success and 0 on failure.
236  */
237 guint32 _wapi_handle_new_internal (WapiHandleType type)
238 {
239         guint32 segment, idx;
240         guint32 i, j;
241         static guint32 last=1;
242         int thr_ret;
243         
244         /* A linear scan should be fast enough.  Start from the last
245          * allocation, assuming that handles are allocated more often
246          * than they're freed. Leave 0 (NULL) as a guard
247          */
248 #ifdef HEAVY_DEBUG
249         print_handle_count (0xFFF);
250 #endif
251 again:
252         _wapi_handle_segment (GUINT_TO_POINTER (last), &segment, &idx);
253         for(i=segment; i<_wapi_handle_get_shared_segment (0)->num_segments;
254             i++) {
255                 if(i!=segment) {
256                         idx=0;
257                 }
258                 
259                 for(j=idx; j<_WAPI_HANDLES_PER_SEGMENT; j++) {
260                         struct _WapiHandleShared *shared;
261                 
262                         /* Make sure we dont try and assign handle 0 */
263                         if (i==0 && j==0) {
264                                 continue;
265                         }
266                         
267                         shared=&_wapi_handle_get_shared_segment (i)->handles[j];
268                 
269                         if(shared->type==WAPI_HANDLE_UNUSED) {
270                                 last=(_wapi_handle_index (i, j)+1) % (_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT);
271                                 shared->type=type;
272                                 shared->signalled=FALSE;
273 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
274                                 thr_ret = mono_mutex_init (&shared->signal_mutex, &mutex_shared_attr);
275                                 g_assert (thr_ret == 0);
276                                 
277                                 thr_ret = pthread_cond_init (&shared->signal_cond, &cond_shared_attr);
278                                 g_assert (thr_ret == 0);
279 #else
280                                 thr_ret = pthread_cond_init(&shared->signal_cond, NULL);
281                                 g_assert (thr_ret == 0);
282                                 
283                                 thr_ret = mono_mutex_init(&shared->signal_mutex, NULL);
284                                 g_assert (thr_ret == 0);
285 #endif
286                                 
287                                 return(_wapi_handle_index (i, j));
288                         }
289                 }
290         }
291
292         if(last>1) {
293                 /* Try again from the beginning */
294                 last=1;
295                 goto again;
296         }
297
298         /* Will need a new segment.  The caller will sort it out */
299
300         return(0);
301 }
302
303 gpointer _wapi_handle_new (WapiHandleType type)
304 {
305         static mono_once_t shared_init_once = MONO_ONCE_INIT;
306         static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER;
307         guint32 handle_idx = 0, idx, segment;
308         gpointer handle;
309         WapiHandleRequest new={0};
310         WapiHandleResponse new_resp={0};
311 #if HAVE_BOEHM_GC
312         gboolean tried_collect=FALSE;
313 #endif
314         int thr_ret;
315         
316         mono_once (&shared_init_once, shared_init);
317
318 again:
319         if(shared==TRUE) {
320                 new.type=WapiHandleRequestType_New;
321                 new.u.new.type=type;
322         
323                 _wapi_daemon_request_response (daemon_sock, &new, &new_resp);
324         
325                 if (new_resp.type==WapiHandleResponseType_New) {
326                         handle_idx=new_resp.u.new.handle;
327                 } else {
328                         g_warning (G_GNUC_PRETTY_FUNCTION
329                                    ": bogus daemon response, type %d",
330                                    new_resp.type);
331                         g_assert_not_reached ();
332                 }
333         } else {
334                 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
335                                       (void *)&scan_mutex);
336                 thr_ret = pthread_mutex_lock (&scan_mutex);
337                 g_assert (thr_ret == 0);
338                 
339                 handle_idx=_wapi_handle_new_internal (type);
340                 if(handle_idx==0) {
341                         /* Try and get a new segment, and have another go */
342                         segment=_wapi_handle_get_shared_segment (0)->num_segments;
343                         _wapi_handle_ensure_mapped (segment);
344                         
345                         if(_wapi_handle_get_shared_segment (segment)!=NULL) {
346                                 /* Got a new segment */
347                                 _wapi_handle_get_shared_segment (0)->num_segments++;
348                                 handle_idx=_wapi_handle_new_internal (type);
349                         } else {
350                                 /* Map failed.  Just return 0 meaning
351                                  * "out of handles"
352                                  */
353                         }
354                 }
355                 
356                 _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
357                 _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
358
359                 thr_ret = pthread_mutex_unlock (&scan_mutex);
360                 g_assert (thr_ret == 0);
361                 pthread_cleanup_pop (0);
362         }
363                 
364         if(handle_idx==0) {
365                 g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!");
366
367 #if HAVE_BOEHM_GC
368                 /* See if we can reclaim some handles by forcing a GC
369                  * collection
370                  */
371                 if(tried_collect==FALSE) {
372                         g_warning (G_GNUC_PRETTY_FUNCTION
373                                    ": Seeing if GC collection helps...");
374                         GC_gcollect (); /* FIXME: we should wait for finalizers to be called */
375                         tried_collect=TRUE;
376                         goto again;
377                 } else {
378                         g_warning (G_GNUC_PRETTY_FUNCTION
379                                    ": didn't help, returning error");
380                 }
381 #endif
382         
383                 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
384         }
385
386         _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
387         _wapi_handle_ensure_mapped (segment);
388
389         if(_wapi_private_data!=NULL) {
390                 _wapi_handle_get_private_segment (segment)->handles[idx].type=type;
391         }
392         
393 #if !defined(_POSIX_THREAD_PROCESS_SHARED) || _POSIX_THREAD_PROCESS_SHARED == -1
394         thr_ret = mono_mutex_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex, &mutex_shared_attr);
395         g_assert (thr_ret == 0);
396         
397         thr_ret = pthread_cond_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond, &cond_shared_attr);
398         g_assert (thr_ret == 0);
399 #endif
400         handle=GUINT_TO_POINTER (handle_idx);
401
402 #ifdef DEBUG
403         g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle);
404 #endif
405         
406         return(handle);
407 }
408
409 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
410                               gpointer *shared, gpointer *private)
411 {
412         struct _WapiHandleShared *shared_handle_data;
413         struct _WapiHandlePrivate *private_handle_data = NULL;
414         guint32 idx;
415         guint32 segment;
416
417         _wapi_handle_segment (handle, &segment, &idx);
418         _wapi_handle_ensure_mapped (segment);
419         
420         shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
421
422         if(shared!=NULL) {
423                 *shared=&shared_handle_data->u;
424         }
425         
426         if(private!=NULL) {
427                 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
428
429                 *private=&private_handle_data->u;
430         }
431
432         if(shared_handle_data->type!=type) {
433                 /* If shared type is UNUSED, see if the private type
434                  * matches what we are looking for - this can happen
435                  * when the handle is being destroyed and the
436                  * close_private function is looking up the private
437                  * data
438                  */
439                 if(shared_handle_data->type==WAPI_HANDLE_UNUSED &&
440                    (private!=NULL && private_handle_data->type==type)) {
441                         return(TRUE);
442                 } else {
443                         return(FALSE);
444                 }
445         }
446         
447         return(TRUE);
448 }
449
450 gpointer _wapi_search_handle (WapiHandleType type,
451                               gboolean (*check)(gpointer test, gpointer user),
452                               gpointer user_data,
453                               gpointer *shared, gpointer *private)
454 {
455         struct _WapiHandleShared *shared_handle_data;
456         struct _WapiHandlePrivate *private_handle_data;
457         guint32 i, segment, idx;
458
459         for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
460                 struct _WapiHandleShared *shared;
461                 
462                 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
463                 _wapi_handle_ensure_mapped (segment);
464                 
465                 shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
466                 
467                 if(shared->type==type) {
468                         if(check (GUINT_TO_POINTER (i), user_data)==TRUE) {
469                                 break;
470                         }
471                 }
472         }
473
474         if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
475                 return(GUINT_TO_POINTER (0));
476         }
477         
478         if(shared!=NULL) {
479                 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
480
481                 *shared=&shared_handle_data->u;
482         }
483         
484         if(private!=NULL) {
485                 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
486
487                 *private=&private_handle_data->u;
488         }
489         
490         return(GUINT_TO_POINTER (i));
491 }
492
493 gpointer _wapi_search_handle_namespace (WapiHandleType type,
494                                         gchar *utf8_name, gpointer *shared,
495                                         gpointer *private)
496 {
497         struct _WapiHandleShared *shared_handle_data;
498         struct _WapiHandlePrivate *private_handle_data;
499         guint32 i, segment, idx;
500
501 #ifdef DEBUG
502         g_message (G_GNUC_PRETTY_FUNCTION
503                    ": Lookup for handle named [%s] type %d", utf8_name, type);
504 #endif
505
506         for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
507                 struct _WapiHandleShared *shared;
508                 
509                 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
510                 _wapi_handle_ensure_mapped (segment);
511                 
512                 shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
513                 
514                 /* Check mutex, event, semaphore, timer, job and file-mapping
515                  * object names.  So far only mutex is implemented.
516                  */
517                 if(_WAPI_SHARED_NAMESPACE (shared->type)) {
518                         gchar *lookup_name;
519                         WapiSharedNamespace *sharedns;
520                         
521 #ifdef DEBUG
522                         g_message (G_GNUC_PRETTY_FUNCTION ": found a shared namespace handle at 0x%x (type %d)", i, shared->type);
523 #endif
524
525                         shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
526                         sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
527                         
528                         
529                         if(sharedns->name) {
530                                 lookup_name=_wapi_handle_scratch_lookup (
531                                         sharedns->name);
532                         } else {
533 #ifdef DEBUG
534                                 g_message (G_GNUC_PRETTY_FUNCTION
535                                            ": handle 0x%x is unnamed", i);
536 #endif
537                                 continue;
538                         }
539
540                         if(lookup_name==NULL) {
541 #ifdef DEBUG
542                                 g_message (G_GNUC_PRETTY_FUNCTION
543                                            ": couldn't find handle 0x%x name",
544                                            i);
545 #endif
546                                 continue;
547                         }
548
549 #ifdef DEBUG
550                         g_message (G_GNUC_PRETTY_FUNCTION ": name is [%s]",
551                                    lookup_name);
552 #endif
553
554                         if(strcmp (lookup_name, utf8_name)==0) {
555                                 if(shared->type!=type) {
556                                         /* Its the wrong type, so fail now */
557 #ifdef DEBUG
558                                         g_message (G_GNUC_PRETTY_FUNCTION ": handle 0x%x matches name but is wrong type: %d", i, shared->type);
559 #endif
560                                         return(_WAPI_HANDLE_INVALID);
561                                 } else {
562                                         /* fall through so we can fill
563                                          * in the data
564                                          */
565 #ifdef DEBUG
566                                         g_message (G_GNUC_PRETTY_FUNCTION ": handle 0x%x matches name and type", i);
567 #endif
568                                         break;
569                                 }
570                         }
571                 }
572         }
573
574         if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
575                 return(GUINT_TO_POINTER (0));
576         }
577         
578         if(shared!=NULL) {
579                 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
580
581                 *shared=&shared_handle_data->u;
582         }
583         
584         if(private!=NULL) {
585                 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
586
587                 *private=&private_handle_data->u;
588         }
589         
590         return(GUINT_TO_POINTER (i));
591 }
592
593 void _wapi_handle_ref (gpointer handle)
594 {
595         if(shared==TRUE) {
596                 WapiHandleRequest req={0};
597                 WapiHandleResponse resp={0};
598         
599                 req.type=WapiHandleRequestType_Open;
600                 req.u.open.handle=GPOINTER_TO_UINT (handle);
601         
602                 _wapi_daemon_request_response (daemon_sock, &req, &resp);
603                 if(resp.type!=WapiHandleResponseType_Open) {
604                         g_warning (G_GNUC_PRETTY_FUNCTION
605                                    ": bogus daemon response, type %d",
606                                    resp.type);
607                         g_assert_not_reached ();
608                 }
609         } else {
610                 guint32 idx, segment;
611
612                 _wapi_handle_segment (handle, &segment, &idx);
613                 
614                 _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
615         
616 #ifdef DEBUG
617                 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
618                            handle,
619                            _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
620 #endif
621         }
622 }
623
624 /* The handle must not be locked on entry to this function */
625 void _wapi_handle_unref (gpointer handle)
626 {
627         guint32 idx, segment;
628         gboolean destroy = FALSE;
629         int thr_ret;
630         
631         _wapi_handle_segment (handle, &segment, &idx);
632         
633         if(shared==TRUE) {
634                 WapiHandleRequest req={0};
635                 WapiHandleResponse resp={0};
636         
637                 req.type=WapiHandleRequestType_Close;
638                 req.u.close.handle=GPOINTER_TO_UINT (handle);
639         
640                 _wapi_daemon_request_response (daemon_sock, &req, &resp);
641                 if(resp.type!=WapiHandleResponseType_Close) {
642                         g_warning (G_GNUC_PRETTY_FUNCTION
643                                    ": bogus daemon response, type %d",
644                                    resp.type);
645                         g_assert_not_reached ();
646                 } else {
647                         destroy=resp.u.close.destroy;
648                 }
649         } else {
650                 _wapi_handle_get_shared_segment (segment)->handles[idx].ref--;
651         
652 #ifdef DEBUG
653                 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d", handle, _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
654 #endif
655
656                 /* Possible race condition here if another thread refs
657                  * the handle between here and setting the type to
658                  * UNUSED.  I could lock a mutex, but I'm not sure
659                  * that allowing a handle reference to reach 0 isn't
660                  * an application bug anyway.
661                  */
662                 destroy=(_wapi_handle_get_shared_segment (segment)->handles[idx].ref==0);
663         }
664
665         if(destroy==TRUE) {
666 #ifdef DEBUG
667                 g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p",
668                            handle);
669 #endif
670                 
671                 if(shared==FALSE) {
672                         _wapi_handle_ops_close_shared (handle);
673
674                         memset (&_wapi_handle_get_shared_segment (segment)->handles[idx].u, '\0', sizeof(_wapi_handle_get_shared_segment (segment)->handles[idx].u));
675                 }
676
677                 _wapi_handle_ops_close_private (handle);
678                 _wapi_handle_get_shared_segment (segment)->handles[idx].type=WAPI_HANDLE_UNUSED;
679                 
680                 /* Destroy the mutex and cond var.  We hope nobody
681                  * tried to grab them between the handle unlock and
682                  * now, but pthreads doesn't have a
683                  * "unlock_and_destroy" atomic function.
684                  */
685                 thr_ret = mono_mutex_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
686                 g_assert (thr_ret == 0);
687                         
688                 thr_ret = pthread_cond_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond);
689                 g_assert (thr_ret == 0);
690         }
691 }
692
693 #define HDRSIZE sizeof(struct _WapiScratchHeader)
694
695 static pthread_mutex_t _wapi_scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
696
697 /* _wapi_scratch_mutex must be held when this function is called in
698  * the non-shared case
699  */
700 static void _wapi_handle_scratch_expand (void)
701 {
702         guint32 old_len, new_len;
703                 
704         old_len=sizeof(struct _WapiHandleScratch) +
705                 _wapi_shared_scratch->data_len;
706         new_len=old_len+_WAPI_SHM_SCRATCH_SIZE;
707                 
708         if(_wapi_shared_scratch->is_shared==TRUE) {
709                 /* expand via mmap() */
710                 _wapi_shared_scratch=_wapi_shm_file_expand (_wapi_shared_scratch, WAPI_SHM_SCRATCH, 0, old_len, new_len);
711         } else {
712                 _wapi_shared_scratch=_wapi_g_renew0 (_wapi_shared_scratch, old_len, new_len);
713         }
714         _wapi_shared_scratch->data_len+=_WAPI_SHM_SCRATCH_SIZE;
715 }
716
717 /* _wapi_scratch_mutex must be held when this function is called in
718  * the non-shared case
719  */
720 static guint32 _wapi_handle_scratch_locate_space (guint32 bytes)
721 {
722         guint32 idx=0, last_idx=0;
723         struct _WapiScratchHeader *hdr, *last_hdr = NULL;
724         gboolean last_was_free=FALSE;
725         guchar *storage=_wapi_shared_scratch->scratch_data;
726         
727 #ifdef DEBUG
728         g_message (G_GNUC_PRETTY_FUNCTION
729                    ": looking for %d bytes of scratch space (%d bytes total)",
730                    bytes, _wapi_shared_scratch->data_len);
731 #endif
732         
733         while(idx< _wapi_shared_scratch->data_len) {
734                 hdr=(struct _WapiScratchHeader *)&storage[idx];
735                 
736                 /* Do a simple first-fit allocation, coalescing
737                  * adjacent free blocks as we progress through the
738                  * scratch space
739                  */
740                 if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
741                    hdr->length >= bytes + HDRSIZE) {
742                         /* found space */
743                         guint32 old_length=hdr->length;
744 #ifdef DEBUG
745                         g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length);
746 #endif
747
748                         hdr->flags &= ~WAPI_SHM_SCRATCH_FREE;
749                         hdr->length=bytes;
750                         idx += HDRSIZE;
751
752                         /* Put a new header in at the end of the used
753                          * space
754                          */
755                         hdr=(struct _WapiScratchHeader *)&storage[idx+bytes];
756                         hdr->flags |= WAPI_SHM_SCRATCH_FREE;
757                         hdr->length = old_length-bytes-HDRSIZE;
758
759 #ifdef DEBUG
760                         g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
761 #endif
762                         
763                         /*
764                          * It was memset(0..) when free/made so no need to do it here
765                          */
766
767                         return(idx);
768                 } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
769                           last_was_free == FALSE) {
770 #ifdef DEBUG
771                         g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length);
772 #endif
773
774                         /* save this point in case we can coalesce it with
775                          * the next block, if that is free.
776                          */
777                         last_was_free=TRUE;
778                         last_idx=idx;
779                         last_hdr=hdr;
780                         idx+=(hdr->length+HDRSIZE);
781                 } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE &&
782                            last_was_free == TRUE) {
783 #ifdef DEBUG
784                         g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length);
785 #endif
786
787                         /* This block and the previous are both free,
788                          * so coalesce them
789                          */
790                         last_hdr->length += (hdr->length + HDRSIZE);
791
792                         /* If the new block is now big enough, use it
793                          * (next time round the loop)
794                          */
795                         if(last_hdr->length >= bytes + HDRSIZE) {
796                                 idx=last_idx;
797                         } else {
798                                 /* leave the last free info as it is,
799                                  * in case the next block is also free
800                                  * and can be coalesced too
801                                  */
802                                 idx=last_idx+last_hdr->length+HDRSIZE;
803                         }
804                 } else {
805 #ifdef DEBUG
806                         g_message (G_GNUC_PRETTY_FUNCTION
807                                    ": found used block at %d, length %d", idx,
808                                    hdr->length);
809 #endif
810
811                         /* must be used, try next chunk */
812                         idx+=(hdr->length+HDRSIZE);
813
814                         /* Don't let the coalescing blow away this block */
815                         last_was_free=FALSE;
816
817                         /* But remember where the last block started */
818                         last_idx=idx;
819                 }
820         }
821
822         /* Not enough free space.  last_idx points to the last block.
823          * If it's free, just tack on more space and update the
824          * length.  If it's allocated, it must have fit right into the
825          * available space, so add more space and add a new header
826          * after this block.
827          */
828         _wapi_handle_scratch_expand ();
829         storage=_wapi_shared_scratch->scratch_data;
830         
831         hdr=(struct _WapiScratchHeader *)&storage[last_idx];
832         if(hdr->flags & WAPI_SHM_SCRATCH_FREE) {
833                 hdr->length+=_WAPI_SHM_SCRATCH_SIZE;
834         } else {
835                 idx=(hdr->length+HDRSIZE);
836                 hdr=(struct _WapiScratchHeader *)&storage[idx];
837                 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
838                 hdr->length = _WAPI_SHM_SCRATCH_SIZE-HDRSIZE;
839         }
840
841         /* The caller will try again */
842         return(0);
843 }
844
845 /*
846  * _wapi_handle_scratch_store_internal:
847  * @bytes: Allocate no. bytes
848  *
849  * Like malloc(3) except its for the shared memory segment's scratch
850  * part. Memory block returned is zeroed out.
851  */
852 guint32 _wapi_handle_scratch_store_internal (guint32 bytes, gboolean *remap)
853 {
854         guchar *storage;
855         guint32 idx;
856         struct _WapiScratchHeader *hdr;
857
858 #ifdef DEBUG
859         g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
860 #endif
861         
862         *remap=FALSE;
863         
864         if(_wapi_shared_scratch->data_len==0) {
865                 /* Need to expand the data array for the first use */
866 #ifdef DEBUG
867                 g_message (G_GNUC_PRETTY_FUNCTION
868                            ": setting up scratch space");
869 #endif
870
871                 _wapi_handle_scratch_expand ();
872                 *remap=TRUE;
873         }
874
875         storage=_wapi_shared_scratch->scratch_data;
876         hdr=(struct _WapiScratchHeader *)&storage[0];
877         if(hdr->flags==0 && hdr->length==0) {
878                 /* Need to initialise scratch data */
879                 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
880                 hdr->length = _wapi_shared_scratch->data_len - HDRSIZE;
881         }
882
883         idx=_wapi_handle_scratch_locate_space (bytes);
884         if(idx==0) {
885                 /* Some more space will have been allocated, so try again */
886 #ifdef DEBUG
887                 g_message (G_GNUC_PRETTY_FUNCTION ": trying again");
888 #endif
889
890                 idx=_wapi_handle_scratch_locate_space (bytes);
891                 *remap=TRUE;
892         }
893         
894         return(idx);
895 }
896
897 guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
898 {
899         guint32 idx = 0, store_bytes;
900         gboolean remap;
901         int thr_ret;
902         guint32 ret = 0;
903         
904 #ifdef DEBUG
905         g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
906 #endif
907
908         /* No point storing no data */
909         if(bytes==0) {
910                 return(0);
911         }
912
913         /* Align bytes to 32 bits (needed for sparc at least) */
914         store_bytes = (((bytes) + 3) & (~3));
915
916         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
917                               (void *)&_wapi_scratch_mutex);
918         thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
919         g_assert (thr_ret == 0);
920         
921         if(shared==TRUE) {
922                 WapiHandleRequest scratch={0};
923                 WapiHandleResponse scratch_resp={0};
924                 guint32 old_len=sizeof(struct _WapiHandleScratch) +
925                         _wapi_shared_scratch->data_len;
926                 
927                 scratch.type=WapiHandleRequestType_Scratch;
928                 scratch.u.scratch.length=store_bytes;
929         
930                 _wapi_daemon_request_response (daemon_sock, &scratch,
931                                                &scratch_resp);
932         
933                 if(scratch_resp.type==WapiHandleResponseType_Scratch) {
934                         idx=scratch_resp.u.scratch.idx;
935                         remap=scratch_resp.u.scratch.remap;
936                 } else {
937                         g_warning (G_GNUC_PRETTY_FUNCTION
938                                    ": bogus daemon response, type %d",
939                                    scratch_resp.type);
940                         g_assert_not_reached ();
941                 }
942         
943                 if(remap==TRUE) {
944                         munmap (_wapi_shared_scratch, old_len);
945                         _wapi_shared_scratch=_wapi_shm_file_map (WAPI_SHM_SCRATCH, 0, NULL, NULL);
946                 }
947         } else {
948                 idx=_wapi_handle_scratch_store_internal (store_bytes, &remap);
949                 if(idx==0) {
950                         /* Failed to allocate space */
951                         goto cleanup;
952                 }
953         }
954         ret = idx;
955         
956 #ifdef DEBUG
957         g_message (G_GNUC_PRETTY_FUNCTION
958                    ": stored [%s] at %d (len %d, aligned len %d)",
959                    (char *)data, idx, bytes, store_bytes);
960 #endif
961         
962         memcpy (&_wapi_shared_scratch->scratch_data[idx], data, bytes);
963
964 cleanup:
965         thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
966         g_assert (thr_ret == 0);
967         pthread_cleanup_pop (0);
968         
969         return(ret);
970 }
971
972 guint32 _wapi_handle_scratch_store_string_array (gchar **data)
973 {
974         guint32 *stored_strings, count=0, i, idx;
975         gchar **strings;
976         
977         /* No point storing no data */
978         if(data==NULL) {
979                 return(0);
980         }
981
982         strings=data;
983         while(*strings!=NULL) {
984                 count++;
985                 strings++;
986         }
987         
988 #ifdef DEBUG
989         g_message (G_GNUC_PRETTY_FUNCTION ": %d strings to store", count);
990 #endif
991         
992         if(count==0) {
993                 return(0);
994         }
995
996         /* stored_strings[0] is the count */
997         stored_strings=g_new0 (guint32, count+1);
998         stored_strings[0]=count;
999         
1000         strings=data;
1001         for(i=0; i<count; i++) {
1002                 stored_strings[i+1]=_wapi_handle_scratch_store (strings[i], strlen (strings[i]));
1003         }
1004
1005         idx=_wapi_handle_scratch_store (stored_strings,
1006                                         sizeof(guint32)*(count+1));
1007         
1008         return(idx);
1009 }
1010
1011 gpointer _wapi_handle_scratch_lookup (guint32 idx)
1012 {
1013         struct _WapiScratchHeader *hdr;
1014         gpointer ret;
1015         guchar *storage;
1016         int thr_ret;
1017         
1018         if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
1019                 return(NULL);
1020         }
1021
1022         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
1023                               (void *)&_wapi_scratch_mutex);
1024         thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
1025         g_assert (thr_ret == 0);
1026         
1027         storage=_wapi_shared_scratch->scratch_data;
1028         
1029         hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
1030         ret=g_malloc0 (hdr->length+1);
1031         memcpy (ret, &storage[idx], hdr->length);
1032
1033         thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
1034         g_assert (thr_ret == 0);
1035         pthread_cleanup_pop (0);
1036
1037         return(ret);
1038 }
1039
1040 gchar **_wapi_handle_scratch_lookup_string_array (guint32 idx)
1041 {
1042         gchar **strings;
1043         guint32 *stored_strings;
1044         guint32 count, i;
1045         
1046         if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
1047                 return(NULL);
1048         }
1049
1050         stored_strings=_wapi_handle_scratch_lookup (idx);
1051         if(stored_strings==NULL) {
1052                 return(NULL);
1053         }
1054         
1055         /* stored_strings[0] is the number of strings, the index of
1056          * each string follows
1057          */
1058         count=stored_strings[0];
1059         
1060 #ifdef DEBUG
1061         g_message (G_GNUC_PRETTY_FUNCTION
1062                    ": looking up an array of %d strings", count);
1063 #endif
1064         
1065         /* NULL-terminate the array */
1066         strings=g_new0 (gchar *, count+1);
1067         
1068         for(i=0; i<count; i++) {
1069                 strings[i]=_wapi_handle_scratch_lookup (stored_strings[i+1]);
1070
1071 #ifdef DEBUG
1072                 g_message (G_GNUC_PRETTY_FUNCTION ": string %d is [%s]", i,
1073                            strings[i]);
1074 #endif
1075         }
1076
1077         g_free (stored_strings);
1078         
1079         return(strings);
1080 }
1081
1082 /*
1083  * _wapi_handle_scratch_delete_internal:
1084  * @idx: Index to free block
1085  *
1086  * Like free(3) except its for the shared memory segment's scratch
1087  * part.
1088  */
1089 void _wapi_handle_scratch_delete_internal (guint32 idx)
1090 {
1091         struct _WapiScratchHeader *hdr;
1092         guchar *storage;
1093         int thr_ret;
1094         
1095         if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
1096                 return;
1097         }
1098
1099         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
1100                               (void *)&_wapi_scratch_mutex);
1101         thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
1102         g_assert (thr_ret == 0);
1103         
1104         storage=_wapi_shared_scratch->scratch_data;
1105         
1106         hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
1107         memset (&storage[idx], '\0', hdr->length);
1108         hdr->flags |= WAPI_SHM_SCRATCH_FREE;
1109         
1110         /* We could coalesce forwards here if the next block is also
1111          * free, but the _store() function will do that anyway.
1112          */
1113
1114         thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
1115         g_assert (thr_ret == 0);
1116         pthread_cleanup_pop (0);
1117 }
1118
1119 void _wapi_handle_scratch_delete (guint32 idx)
1120 {
1121         if(shared==TRUE) {
1122                 WapiHandleRequest scratch_free={0};
1123                 WapiHandleResponse scratch_free_resp={0};
1124         
1125                 scratch_free.type=WapiHandleRequestType_ScratchFree;
1126                 scratch_free.u.scratch_free.idx=idx;
1127         
1128                 _wapi_daemon_request_response (daemon_sock, &scratch_free,
1129                                                &scratch_free_resp);
1130         
1131                 if(scratch_free_resp.type!=WapiHandleResponseType_ScratchFree) {
1132                         g_warning (G_GNUC_PRETTY_FUNCTION
1133                                    ": bogus daemon response, type %d",
1134                                    scratch_free_resp.type);
1135                         g_assert_not_reached ();
1136                 }
1137         } else {
1138                 _wapi_handle_scratch_delete_internal (idx);
1139         }
1140 }
1141
1142 void _wapi_handle_scratch_delete_string_array (guint32 idx)
1143 {
1144         guint32 *stored_strings;
1145         guint32 count, i;
1146         
1147         stored_strings=_wapi_handle_scratch_lookup (idx);
1148         if(stored_strings==NULL) {
1149                 return;
1150         }
1151         
1152         /* stored_strings[0] is the number of strings, the index of
1153          * each string follows
1154          */
1155         count=stored_strings[0];
1156         
1157 #ifdef DEBUG
1158         g_message (G_GNUC_PRETTY_FUNCTION ": deleting an array of %d strings",
1159                    count);
1160 #endif
1161         
1162         for(i=1; i<count; i++) {
1163                 _wapi_handle_scratch_delete (stored_strings[i]);
1164         }
1165         
1166         _wapi_handle_scratch_delete (idx);
1167
1168         g_free (stored_strings);
1169 }
1170
1171 void _wapi_handle_register_capabilities (WapiHandleType type,
1172                                          WapiHandleCapability caps)
1173 {
1174         handle_caps[type]=caps;
1175 }
1176
1177 gboolean _wapi_handle_test_capabilities (gpointer handle,
1178                                          WapiHandleCapability caps)
1179 {
1180         guint32 idx, segment;
1181         WapiHandleType type;
1182
1183         _wapi_handle_segment (handle, &segment, &idx);
1184         
1185         type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1186
1187 #ifdef DEBUG
1188         g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)",
1189                    handle_caps[type], caps, handle_caps[type] & caps);
1190 #endif
1191         
1192         return((handle_caps[type] & caps)!=0);
1193 }
1194
1195 void _wapi_handle_ops_close_shared (gpointer handle)
1196 {
1197         guint32 idx, segment;
1198         WapiHandleType type;
1199
1200         _wapi_handle_segment (handle, &segment, &idx);
1201         
1202         type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1203
1204         if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) {
1205                 handle_ops[type]->close_shared (handle);
1206         }
1207 }
1208
1209 void _wapi_handle_ops_close_private (gpointer handle)
1210 {
1211         guint32 idx, segment;
1212         WapiHandleType type;
1213
1214         _wapi_handle_segment (handle, &segment, &idx);
1215
1216         type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1217
1218         /* When a handle in the process of being destroyed the shared
1219          * type has already been set to UNUSED
1220          */
1221         if(type==WAPI_HANDLE_UNUSED && _wapi_private_data!=NULL) {
1222                 type=_wapi_handle_get_private_segment (segment)->handles[idx].type;
1223         }
1224
1225         if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) {
1226                 handle_ops[type]->close_private (handle);
1227         }
1228 }
1229
1230 void _wapi_handle_ops_signal (gpointer handle)
1231 {
1232         guint32 idx, segment;
1233         WapiHandleType type;
1234
1235         _wapi_handle_segment (handle, &segment, &idx);
1236
1237         type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1238
1239         if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) {
1240                 handle_ops[type]->signal (handle);
1241         }
1242 }
1243
1244 void _wapi_handle_ops_own (gpointer handle)
1245 {
1246         guint32 idx, segment;
1247         WapiHandleType type;
1248
1249         _wapi_handle_segment (handle, &segment, &idx);
1250         
1251         type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1252
1253         if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) {
1254                 handle_ops[type]->own_handle (handle);
1255         }
1256 }
1257
1258 gboolean _wapi_handle_ops_isowned (gpointer handle)
1259 {
1260         guint32 idx, segment;
1261         WapiHandleType type;
1262
1263         _wapi_handle_segment (handle, &segment, &idx);
1264         
1265         type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1266
1267         if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) {
1268                 return(handle_ops[type]->is_owned (handle));
1269         } else {
1270                 return(FALSE);
1271         }
1272 }
1273
1274 /**
1275  * CloseHandle:
1276  * @handle: The handle to release
1277  *
1278  * Closes and invalidates @handle, releasing any resources it
1279  * consumes.  When the last handle to a temporary or non-persistent
1280  * object is closed, that object can be deleted.  Closing the same
1281  * handle twice is an error.
1282  *
1283  * Return value: %TRUE on success, %FALSE otherwise.
1284  */
1285 gboolean CloseHandle(gpointer handle)
1286 {
1287         _wapi_handle_unref (handle);
1288         
1289         return(TRUE);
1290 }
1291
1292 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1293                                                gpointer *handles,
1294                                                gboolean waitall,
1295                                                guint32 *retcount,
1296                                                guint32 *lowest)
1297 {
1298         guint32 count, i, iter=0;
1299         gboolean ret;
1300         int thr_ret;
1301         
1302         /* Lock all the handles, with backoff */
1303 again:
1304         for(i=0; i<numhandles; i++) {
1305                 guint32 idx, segment;
1306                 
1307                 _wapi_handle_segment (handles[i], &segment, &idx);
1308                 
1309 #ifdef DEBUG
1310                 g_message (G_GNUC_PRETTY_FUNCTION ": attempting to lock %p",
1311                            handles[i]);
1312 #endif
1313
1314                 ret=mono_mutex_trylock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1315                 if(ret!=0) {
1316                         /* Bummer */
1317                         struct timespec sleepytime;
1318                         
1319 #ifdef DEBUG
1320                         g_message (G_GNUC_PRETTY_FUNCTION ": attempt failed for %p", handles[i]);
1321 #endif
1322
1323                         while(i--) {
1324                                 _wapi_handle_segment (handles[i], &segment, &idx);
1325                                 thr_ret = mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1326                                 g_assert (thr_ret == 0);
1327                         }
1328
1329                         /* If iter ever reaches 100 the nanosleep will
1330                          * return EINVAL immediately, but we have a
1331                          * design flaw if that happens.
1332                          */
1333                         iter++;
1334                         if(iter==100) {
1335                                 g_warning (G_GNUC_PRETTY_FUNCTION
1336                                            ": iteration overflow!");
1337                                 iter=1;
1338                         }
1339                         
1340                         sleepytime.tv_sec=0;
1341                         sleepytime.tv_nsec=10000000 * iter;     /* 10ms*iter */
1342                         
1343 #ifdef DEBUG
1344                         g_message (G_GNUC_PRETTY_FUNCTION
1345                                    ": Backing off for %d ms", iter*10);
1346 #endif
1347                         nanosleep (&sleepytime, NULL);
1348                         
1349                         goto again;
1350                 }
1351         }
1352         
1353 #ifdef DEBUG
1354         g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles");
1355 #endif
1356
1357         count=0;
1358         *lowest=numhandles;
1359         
1360         for(i=0; i<numhandles; i++) {
1361                 guint32 idx, segment;
1362
1363                 _wapi_handle_ref (handles[i]);
1364                 
1365                 _wapi_handle_segment (handles[i], &segment, &idx);
1366                 
1367 #ifdef DEBUG
1368                 g_message (G_GNUC_PRETTY_FUNCTION ": Checking handle %p",
1369                            handles[i]);
1370 #endif
1371
1372                 if(((_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_OWN)==TRUE) &&
1373                     (_wapi_handle_ops_isowned (handles[i])==TRUE)) ||
1374                    (_wapi_handle_get_shared_segment (segment)->handles[idx].signalled==TRUE)) {
1375                         count++;
1376                         
1377 #ifdef DEBUG
1378                         g_message (G_GNUC_PRETTY_FUNCTION
1379                                    ": Handle %p signalled", handles[i]);
1380 #endif
1381                         if(*lowest>i) {
1382                                 *lowest=i;
1383                         }
1384                 }
1385         }
1386         
1387 #ifdef DEBUG
1388         g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
1389                    count);
1390 #endif
1391
1392         if((waitall==TRUE && count==numhandles) ||
1393            (waitall==FALSE && count>0)) {
1394                 ret=TRUE;
1395         } else {
1396                 ret=FALSE;
1397         }
1398         
1399 #ifdef DEBUG
1400         g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
1401 #endif
1402
1403         *retcount=count;
1404         
1405         return(ret);
1406 }
1407
1408 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1409 {
1410         guint32 i;
1411         int thr_ret;
1412         
1413         for(i=0; i<numhandles; i++) {
1414                 guint32 idx, segment;
1415
1416                 _wapi_handle_segment (handles[i], &segment, &idx);
1417
1418 #ifdef DEBUG
1419                 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p",
1420                            handles[i]);
1421 #endif
1422
1423                 thr_ret = mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1424                 g_assert (thr_ret == 0);
1425
1426                 _wapi_handle_unref (handles[i]);
1427         }
1428 }
1429
1430 /* Process-shared handles (currently only process and thread handles
1431  * are allowed, and they only work because once signalled they can't
1432  * become unsignalled) are waited for by one process and signalled by
1433  * another.  Without process-shared conditions, the waiting process
1434  * will block forever.  To get around this, the four handle waiting
1435  * functions use a short timeout when _POSIX_THREAD_PROCESS_SHARED is
1436  * not available.  They also return "success" if the fake timeout
1437  * expired, and let the caller check signal status.
1438  */
1439 int _wapi_handle_wait_signal (void)
1440 {
1441 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1442         return(mono_cond_wait (&_wapi_handle_get_shared_segment (0)->signal_cond,
1443                                &_wapi_handle_get_shared_segment (0)->signal_mutex));
1444 #else
1445         struct timespec fake_timeout;
1446         int ret;
1447         
1448         _wapi_calc_timeout (&fake_timeout, 100);
1449
1450         ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1451                                  &_wapi_handle_get_private_segment (0)->signal_mutex,
1452                                  &fake_timeout);
1453         if(ret==ETIMEDOUT) {
1454                 ret=0;
1455         }
1456
1457         return(ret);
1458 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1459 }
1460
1461 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1462 {
1463 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1464         return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (0)->signal_cond,
1465                                     &_wapi_handle_get_shared_segment (0)->signal_mutex,
1466                                     timeout));
1467 #else
1468         struct timespec fake_timeout;
1469         int ret;
1470         
1471         _wapi_calc_timeout (&fake_timeout, 100);
1472         
1473         if((fake_timeout.tv_sec>timeout->tv_sec) ||
1474            (fake_timeout.tv_sec==timeout->tv_sec &&
1475             fake_timeout.tv_nsec > timeout->tv_nsec)) {
1476                 /* Real timeout is less than 100ms time */
1477                 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1478                                          &_wapi_handle_get_private_segment (0)->signal_mutex,
1479                                          timeout);
1480         } else {
1481                 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1482                                          &_wapi_handle_get_private_segment (0)->signal_mutex,
1483                                          &fake_timeout);
1484                 if(ret==ETIMEDOUT) {
1485                         ret=0;
1486                 }
1487         }
1488         
1489         return(ret);
1490 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1491 }
1492
1493 int _wapi_handle_wait_signal_handle (gpointer handle)
1494 {
1495 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1496         guint32 idx, segment;
1497
1498         _wapi_handle_segment (handle, &segment, &idx);
1499         
1500         return(mono_cond_wait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1501                                &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex));
1502 #else
1503         guint32 idx, segment;
1504         struct timespec fake_timeout;
1505         int ret;
1506         
1507         _wapi_handle_segment (handle, &segment, &idx);
1508         _wapi_calc_timeout (&fake_timeout, 100);
1509         
1510         ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1511                                  &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1512                                  &fake_timeout);
1513         if(ret==ETIMEDOUT) {
1514                 ret=0;
1515         }
1516
1517         return(ret);
1518 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1519 }
1520
1521 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1522                                           struct timespec *timeout)
1523 {
1524 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1525         guint32 idx, segment;
1526
1527         _wapi_handle_segment (handle, &segment, &idx);
1528         
1529         return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1530                                     &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1531                                     timeout));
1532 #else
1533         guint32 idx, segment;
1534         struct timespec fake_timeout;
1535         int ret;
1536         
1537         _wapi_handle_segment (handle, &segment, &idx);
1538         _wapi_calc_timeout (&fake_timeout, 100);
1539         
1540         if((fake_timeout.tv_sec>timeout->tv_sec) ||
1541            (fake_timeout.tv_sec==timeout->tv_sec &&
1542             fake_timeout.tv_nsec > timeout->tv_nsec)) {
1543                 /* Real timeout is less than 100ms time */
1544                 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1545                                          &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1546                                          timeout);
1547         } else {
1548                 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1549                                          &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1550                                          &fake_timeout);
1551                 if(ret==ETIMEDOUT) {
1552                         ret=0;
1553                 }
1554         }
1555         
1556         return(ret);
1557 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1558 }
1559
1560 gboolean _wapi_handle_process_fork (guint32 cmd, guint32 env, guint32 dir,
1561                                     gboolean inherit, guint32 flags,
1562                                     gpointer stdin_handle,
1563                                     gpointer stdout_handle,
1564                                     gpointer stderr_handle,
1565                                     gpointer *process_handle,
1566                                     gpointer *thread_handle, guint32 *pid,
1567                                     guint32 *tid)
1568 {
1569         WapiHandleRequest fork_proc={0};
1570         WapiHandleResponse fork_proc_resp={0};
1571         int in_fd, out_fd, err_fd;
1572         
1573         if(shared!=TRUE) {
1574                 return(FALSE);
1575         }
1576
1577         fork_proc.type=WapiHandleRequestType_ProcessFork;
1578         fork_proc.u.process_fork.cmd=cmd;
1579         fork_proc.u.process_fork.env=env;
1580         fork_proc.u.process_fork.dir=dir;
1581         fork_proc.u.process_fork.stdin_handle=GPOINTER_TO_UINT (stdin_handle);
1582         fork_proc.u.process_fork.stdout_handle=GPOINTER_TO_UINT (stdout_handle);
1583         fork_proc.u.process_fork.stderr_handle=GPOINTER_TO_UINT (stderr_handle);
1584         fork_proc.u.process_fork.inherit=inherit;
1585         fork_proc.u.process_fork.flags=flags;
1586         
1587         in_fd=_wapi_file_handle_to_fd (stdin_handle);
1588         out_fd=_wapi_file_handle_to_fd (stdout_handle);
1589         err_fd=_wapi_file_handle_to_fd (stderr_handle);
1590
1591         if(in_fd==-1 || out_fd==-1 || err_fd==-1) {
1592                 /* We were given duff handles */
1593                 /* FIXME: error code */
1594                 return(FALSE);
1595         }
1596         
1597         _wapi_daemon_request_response_with_fds (daemon_sock, &fork_proc,
1598                                                 &fork_proc_resp, in_fd,
1599                                                 out_fd, err_fd);
1600         if(fork_proc_resp.type==WapiHandleResponseType_ProcessFork) {
1601                 *process_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.process_handle);
1602                 *thread_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.thread_handle);
1603                 *pid=fork_proc_resp.u.process_fork.pid;
1604                 *tid=fork_proc_resp.u.process_fork.tid;
1605
1606                 /* If there was an internal error, the handles will be
1607                  * 0.  If there was an error forking or execing, the
1608                  * handles will have values, and process_handle's
1609                  * exec_errno will be set, and the handle will be
1610                  * signalled immediately.
1611                  */
1612                 if(process_handle==0 || thread_handle==0) {
1613                         return(FALSE);
1614                 } else {
1615                         return(TRUE);
1616                 }
1617         } else {
1618                 g_warning (G_GNUC_PRETTY_FUNCTION
1619                            ": bogus daemon response, type %d",
1620                            fork_proc_resp.type);
1621                 g_assert_not_reached ();
1622         }
1623         
1624         return(FALSE);
1625 }
1626
1627 gboolean
1628 _wapi_handle_process_kill (pid_t process, guint32 signo, gint *errnum)
1629 {
1630         WapiHandleRequest killproc = {0};
1631         WapiHandleResponse killprocresp = {0};
1632         gint result;
1633         
1634         if (shared != TRUE) {
1635                 if (errnum) *errnum = EINVAL;
1636                 return FALSE;
1637         }
1638
1639         killproc.type = WapiHandleRequestType_ProcessKill;
1640         killproc.u.process_kill.pid = process;
1641         killproc.u.process_kill.signo = signo;
1642         
1643         _wapi_daemon_request_response (daemon_sock, &killproc, &killprocresp);
1644
1645         if (killprocresp.type != WapiHandleResponseType_ProcessKill) {
1646                 g_warning (G_GNUC_PRETTY_FUNCTION
1647                            ": bogus daemon response, type %d",
1648                            killprocresp.type);
1649                 g_assert_not_reached ();
1650         }
1651
1652         result = killprocresp.u.process_kill.err;
1653         if (result != 0 && errnum != NULL)
1654                 *errnum = (result == FALSE) ? result : 0;
1655         
1656         return (result == 0);
1657 }
1658
1659 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1660                                         guint32 new_sharemode,
1661                                         guint32 new_access,
1662                                         guint32 *old_sharemode,
1663                                         guint32 *old_access)
1664 {
1665         WapiHandleRequest req = {0};
1666         WapiHandleResponse resp = {0};
1667         
1668         if(shared != TRUE) {
1669                 /* No daemon means we don't know if a file is sharable.
1670                  * We're running in our own little world if this is
1671                  * the case, so there's no point in pretending that
1672                  * the file isn't sharable.
1673                  */
1674                 return(FALSE);
1675         }
1676         
1677         req.type = WapiHandleRequestType_GetOrSetShare;
1678         req.u.get_or_set_share.device = device;
1679         req.u.get_or_set_share.inode = inode;
1680         req.u.get_or_set_share.new_sharemode = new_sharemode;
1681         req.u.get_or_set_share.new_access = new_access;
1682         
1683         _wapi_daemon_request_response (daemon_sock, &req, &resp);
1684         if (resp.type != WapiHandleResponseType_GetOrSetShare) {
1685                 g_warning (G_GNUC_PRETTY_FUNCTION
1686                            ": bogus daemon response, type %d", resp.type);
1687                 g_assert_not_reached ();
1688         }
1689         
1690         *old_sharemode = resp.u.get_or_set_share.sharemode;
1691         *old_access = resp.u.get_or_set_share.access;
1692
1693         return(resp.u.get_or_set_share.exists);
1694 }
1695
1696 void _wapi_handle_set_share (dev_t device, ino_t inode, guint32 sharemode,
1697                              guint32 access)
1698 {
1699         WapiHandleRequest req = {0};
1700         WapiHandleResponse resp = {0};
1701         
1702         if(shared != TRUE) {
1703                 /* No daemon, so there's no one else to tell about
1704                  * file sharing.
1705                  */
1706                 return;
1707         }
1708
1709         req.type = WapiHandleRequestType_SetShare;
1710         req.u.set_share.device = device;
1711         req.u.set_share.inode = inode;
1712         req.u.set_share.sharemode = sharemode;
1713         req.u.set_share.access = access;
1714         
1715         _wapi_daemon_request_response (daemon_sock, &req, &resp);
1716         if (resp.type != WapiHandleResponseType_SetShare) {
1717                 g_warning (G_GNUC_PRETTY_FUNCTION
1718                            ": bogus daemon response, type %d", resp.type);
1719                 g_assert_not_reached ();
1720         }
1721 }