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