* Makefile (MCS) [PROFILE=default]: Force testing of 'mcs'.
[mono.git] / mono / io-layer / handles.c
index 28bde54d177b3acad9b590824f29bd6c36180774..a1c3dde13efd889a11789b6069411d2e8b706dbb 100644 (file)
 #include <pthread.h>
 #include <errno.h>
 #include <unistd.h>
+#include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/mman.h>
 
-#if HAVE_BOEHM_GC
-#include <gc/gc.h>
-#endif
+#include <mono/os/gc_wrapper.h>
 
 #include <mono/io-layer/wapi.h>
 #include <mono/io-layer/wapi-private.h>
 #include <mono/io-layer/daemon-messages.h>
 
 #undef DEBUG
+#undef HEAVY_DEBUG /* This will print handle counts on every handle created */
+
+/* Shared threads don't seem to work yet */
+#undef _POSIX_THREAD_PROCESS_SHARED
 
 /*
  * This flag _MUST_ remain set to FALSE in the daemon process.  When
@@ -54,6 +58,7 @@ static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
        &_wapi_socket_ops,
        &_wapi_find_ops,
        &_wapi_process_ops,
+       &_wapi_pipe_ops,
 };
 
 static int daemon_sock;
@@ -61,43 +66,51 @@ static int daemon_sock;
 static pthread_mutexattr_t mutex_shared_attr;
 static pthread_condattr_t cond_shared_attr;
 
-struct _WapiHandleShared_list *_wapi_shared_data=NULL;
-struct _WapiHandlePrivate_list *_wapi_private_data=NULL;
+struct _WapiHandleShared_list **_wapi_shared_data=NULL;
+struct _WapiHandleScratch *_wapi_shared_scratch=NULL;
+struct _WapiHandlePrivate_list **_wapi_private_data=NULL;
+pthread_mutex_t _wapi_shared_mutex=PTHREAD_MUTEX_INITIALIZER;
+
+/* This holds the length of the _wapi_shared_data and
+ * _wapi_private_data arrays, so we know if a segment is off the end
+ * of the array, requiring a realloc
+ */
+guint32 _wapi_shm_mapped_segments;
+
+guint32 _wapi_fd_offset_table_size;
+gpointer *_wapi_fd_offset_table=NULL;
 
-int disable_shm = 0;
 static void shared_init (void)
 {
        struct sockaddr_un shared_socket_address;
        gboolean tried_once=FALSE;
        int ret;
-
-       if (getenv ("MONO_ENABLE_SHM"))
-               disable_shm = 0;
+       int thr_ret;
        
+       _wapi_shared_data=g_new0 (struct _WapiHandleShared_list *, 1);
+       _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list *, 1);
+
 attach_again:
 
 #ifndef DISABLE_SHARED_HANDLES
-       if(getenv ("MONO_DISABLE_SHM") || disable_shm)
+       if(getenv ("MONO_DISABLE_SHM"))
 #endif
        {
                shared=FALSE;
 #ifndef DISABLE_SHARED_HANDLES
        } else {
-               int shm_id;
-               gboolean success;
-               
                /* Ensure that shared==FALSE while _wapi_shm_attach()
                 * calls fork()
                 */
                shared=FALSE;
                
-               _wapi_shared_data=_wapi_shm_attach (FALSE, &success, &shm_id);
-               shared=success;
+               shared=_wapi_shm_attach (&_wapi_shared_data[0],
+                                        &_wapi_shared_scratch);
                if(shared==FALSE) {
-                       g_warning ("Failed to attach shared memory! "
-                                  "(tried shared memory ID 0x%x). "
-                                  "Falling back to non-shared handles",
-                                  shm_id);
+                       g_warning (
+                               "Failed to attach shared memory! "
+                               "Falling back to non-shared handles\n"
+                               "See: http://www.go-mono.com/issues.html#wapi for details");
                }
 #endif /* DISABLE_SHARED_HANDLES */
        }
@@ -106,14 +119,16 @@ attach_again:
        if(shared==TRUE) {
                daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0);
                shared_socket_address.sun_family=AF_UNIX;
-               memcpy (shared_socket_address.sun_path, _wapi_shared_data->daemon, 108);
-               ret=connect (daemon_sock, (struct sockaddr *)&shared_socket_address,
+               memcpy (shared_socket_address.sun_path,
+                       _wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH);
+               ret=connect (daemon_sock,
+                            (struct sockaddr *)&shared_socket_address,
                             sizeof(struct sockaddr_un));
                if(ret==-1) {
                        if(tried_once==TRUE) {
                                g_warning (G_GNUC_PRETTY_FUNCTION
-                                          "connect to daemon failed: %s",
-                                          strerror (errno));
+                                          "connect to daemon failed: %s",
+                                          g_strerror (errno));
                                /* Fall back to private handles */
                                shared=FALSE;
                        } else {
@@ -134,6 +149,9 @@ attach_again:
                                
                                goto attach_again;
                        }
+               } else {
+                       _wapi_fd_offset_table_size = _wapi_shared_data[0]->fd_offset_table_size;
+                       _wapi_fd_offset_table=g_new0 (gpointer, _wapi_fd_offset_table_size);
                }
        }
 
@@ -142,44 +160,154 @@ attach_again:
                g_message (G_GNUC_PRETTY_FUNCTION
                           ": Using process-private handles");
 #endif
-               _wapi_shared_data=
-                       g_malloc0 (sizeof(struct _WapiHandleShared_list)+
-                                  _WAPI_SHM_SCRATCH_SIZE);
-       }
-       _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list, 1);
+               _wapi_shared_data[0]=g_new0 (struct _WapiHandleShared_list, 1);
+               _wapi_shared_data[0]->num_segments=1;
+
+               _wapi_shared_scratch=g_new0 (struct _WapiHandleScratch, 1);
 
-       pthread_mutexattr_init (&mutex_shared_attr);
-       pthread_condattr_init (&cond_shared_attr);
+               _wapi_fd_offset_table_size=getdtablesize ();
+               _wapi_fd_offset_table=g_new0 (gpointer,
+                                             _wapi_fd_offset_table_size);
+       }
+       _wapi_private_data[0]=g_new0 (struct _WapiHandlePrivate_list, 1);
+       _wapi_shm_mapped_segments=1;
 
-#ifdef _POSIX_THREAD_PROCESS_SHARED
-       pthread_mutexattr_setpshared (&mutex_shared_attr,
-                                     PTHREAD_PROCESS_SHARED);
-       pthread_condattr_setpshared (&cond_shared_attr,
-                                    PTHREAD_PROCESS_SHARED);
+       thr_ret = pthread_mutexattr_init (&mutex_shared_attr);
+       g_assert (thr_ret == 0);
+       
+       thr_ret = pthread_condattr_init (&cond_shared_attr);
+       g_assert (thr_ret == 0);
+       
+#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
+       thr_ret = pthread_mutexattr_setpshared (&mutex_shared_attr,
+                                               PTHREAD_PROCESS_SHARED);
+       g_assert (thr_ret == 0);
+       
+       thr_ret = pthread_condattr_setpshared (&cond_shared_attr,
+                                              PTHREAD_PROCESS_SHARED);
+       g_assert (thr_ret == 0);
+#else
+       thr_ret = pthread_cond_init(&_wapi_private_data[0]->signal_cond, NULL);
+       g_assert (thr_ret == 0);
+       
+       thr_ret = mono_mutex_init(&_wapi_private_data[0]->signal_mutex, NULL);
+       g_assert (thr_ret == 0);
 #endif
 }
 
+#ifdef HEAVY_DEBUG
+static void
+print_handle_count (gint mask)
+{
+       gint *count, num_handles;
+       gint i;
+       static const gchar *names [] = {"WAPI_HANDLE_UNUSED",
+                                 "WAPI_HANDLE_FILE",
+                                 "WAPI_HANDLE_CONSOLE",
+                                 "WAPI_HANDLE_THREAD",
+                                 "WAPI_HANDLE_SEM",
+                                 "WAPI_HANDLE_MUTEX",
+                                 "WAPI_HANDLE_EVENT",
+                                 "WAPI_HANDLE_SOCKET",
+                                 "WAPI_HANDLE_FIND",
+                                 "WAPI_HANDLE_PROCESS",
+                                 "WAPI_HANDLE_PIPE"
+                               };
+
+
+       num_handles=_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT;
+       count=g_new0 (gint, num_handles);
+
+       for (i = 1; i < num_handles; i++) {
+               struct _WapiHandleShared *shared;
+               guint32 segment, idx;
+               
+               _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
+               _wapi_handle_ensure_mapped (segment);
+               
+               shared = &_wapi_handle_get_shared_segment (segment)->handles[idx];
+               count [shared->type]++;
+       }
+
+       for (i = 0; i < num_handles; i++)
+               if ((i & mask) == i) /* Always prints the UNUSED count */
+                       g_print ("%s: %d\n", names [i], count [i]);
+
+       g_free (count);
+}
+#endif /* HEAVY_DEBUG */
+
+/*
+ * _wapi_handle_new_internal:
+ * @type: Init handle to this type
+ *
+ * Search for a free handle and initialize it. Return the handle on
+ * success and 0 on failure.
+ */
 guint32 _wapi_handle_new_internal (WapiHandleType type)
 {
-       guint32 i;
+       guint32 segment, idx;
+       guint32 i, j;
        static guint32 last=1;
+       int thr_ret;
+       guint32 num_segments = _wapi_handle_get_shared_segment (0)->num_segments;
        
        /* A linear scan should be fast enough.  Start from the last
         * allocation, assuming that handles are allocated more often
         * than they're freed. Leave 0 (NULL) as a guard
         */
+#ifdef HEAVY_DEBUG
+       print_handle_count (0xFFF);
+#endif
 again:
-       for(i=last; i<_WAPI_MAX_HANDLES; i++) {
-               struct _WapiHandleShared *shared=&_wapi_shared_data->handles[i];
+       _wapi_handle_segment (GUINT_TO_POINTER (last), &segment, &idx);
+       for(i=segment; i < num_segments; i++) {
+               if(i!=segment) {
+                       idx=0;
+               }
                
-               if(shared->type==WAPI_HANDLE_UNUSED) {
-                       last=i;
-                       shared->type=type;
-                       shared->signalled=FALSE;
-                       mono_mutex_init (&_wapi_shared_data->handles[i].signal_mutex, &mutex_shared_attr);
-                       pthread_cond_init (&_wapi_shared_data->handles[i].signal_cond, &cond_shared_attr);
-
-                       return(i);
+               for(j=idx; j<_WAPI_HANDLES_PER_SEGMENT; j++) {
+                       struct _WapiHandleShared *shared;
+               
+                       /* Make sure we dont try and assign the
+                        * handles that would clash with fds
+                        */
+                       if ((i * _WAPI_HANDLES_PER_SEGMENT + j) < _wapi_fd_offset_table_size) {
+                               i = _wapi_fd_offset_table_size / _WAPI_HANDLES_PER_SEGMENT;
+                               j = _wapi_fd_offset_table_size - (i * _WAPI_HANDLES_PER_SEGMENT);
+                               
+                               if (i >= num_segments) {
+                                       /* Need to get the caller to
+                                        * add more shared segments
+                                        */
+                                       return(0);
+                               }
+                               
+                               continue;
+                       }
+                       
+                       shared=&_wapi_handle_get_shared_segment (i)->handles[j];
+               
+                       if(shared->type==WAPI_HANDLE_UNUSED) {
+                               last=(_wapi_handle_index (i, j)+1) % (_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT);
+                               shared->type=type;
+                               shared->signalled=FALSE;
+#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
+                               thr_ret = mono_mutex_init (&shared->signal_mutex, &mutex_shared_attr);
+                               g_assert (thr_ret == 0);
+                               
+                               thr_ret = pthread_cond_init (&shared->signal_cond, &cond_shared_attr);
+                               g_assert (thr_ret == 0);
+#else
+                               thr_ret = pthread_cond_init(&shared->signal_cond, NULL);
+                               g_assert (thr_ret == 0);
+                               
+                               thr_ret = mono_mutex_init(&shared->signal_mutex, NULL);
+                               g_assert (thr_ret == 0);
+#endif
+                               
+                               return(_wapi_handle_index (i, j));
+                       }
                }
        }
 
@@ -188,7 +316,8 @@ again:
                last=1;
                goto again;
        }
-       
+
+       /* Will need a new segment.  The caller will sort it out */
 
        return(0);
 }
@@ -197,13 +326,14 @@ gpointer _wapi_handle_new (WapiHandleType type)
 {
        static mono_once_t shared_init_once = MONO_ONCE_INIT;
        static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER;
-       guint32 idx;
+       guint32 handle_idx = 0, idx, segment;
        gpointer handle;
-       WapiHandleRequest new;
-       WapiHandleResponse new_resp;
+       WapiHandleRequest new={0};
+       WapiHandleResponse new_resp={0};
 #if HAVE_BOEHM_GC
        gboolean tried_collect=FALSE;
 #endif
+       int thr_ret;
        
        mono_once (&shared_init_once, shared_init);
        
@@ -215,7 +345,7 @@ again:
                _wapi_daemon_request_response (daemon_sock, &new, &new_resp);
        
                if (new_resp.type==WapiHandleResponseType_New) {
-                       idx=new_resp.u.new.handle;
+                       handle_idx=new_resp.u.new.handle;
                } else {
                        g_warning (G_GNUC_PRETTY_FUNCTION
                                   ": bogus daemon response, type %d",
@@ -223,12 +353,38 @@ again:
                        g_assert_not_reached ();
                }
        } else {
-               pthread_mutex_lock (&scan_mutex);
-               idx=_wapi_handle_new_internal (type);
-               pthread_mutex_unlock (&scan_mutex);
+               pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
+                                     (void *)&scan_mutex);
+               thr_ret = pthread_mutex_lock (&scan_mutex);
+               g_assert (thr_ret == 0);
+               
+               while ((handle_idx = _wapi_handle_new_internal (type)) == 0) {
+                       /* Try and get a new segment, and have another go */
+                       segment=_wapi_handle_get_shared_segment (0)->num_segments;
+                       _wapi_handle_ensure_mapped (segment);
+                       
+                       if(_wapi_handle_get_shared_segment (segment)!=NULL) {
+                               /* Got a new segment */
+                               _wapi_handle_get_shared_segment (0)->num_segments++;
+                       } else {
+                               /* Map failed.  Just return 0 meaning
+                                * "out of handles"
+                                */
+                       }
+               }
+               
+               _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
+               _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
+
+               thr_ret = pthread_mutex_unlock (&scan_mutex);
+               g_assert (thr_ret == 0);
+               pthread_cleanup_pop (0);
        }
                
-       if(idx==0) {
+       /* Make sure we left the space for fd mappings */
+       g_assert (handle_idx >= _wapi_fd_offset_table_size);
+       
+       if(handle_idx==0) {
                g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!");
 
 #if HAVE_BOEHM_GC
@@ -238,7 +394,7 @@ again:
                if(tried_collect==FALSE) {
                        g_warning (G_GNUC_PRETTY_FUNCTION
                                   ": Seeing if GC collection helps...");
-                       GC_gcollect ();
+                       GC_gcollect (); /* FIXME: we should wait for finalizers to be called */
                        tried_collect=TRUE;
                        goto again;
                } else {
@@ -250,12 +406,26 @@ again:
                return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
        }
 
-       handle=GUINT_TO_POINTER (idx);
+       _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
+       _wapi_handle_ensure_mapped (segment);
+
+       if(_wapi_private_data!=NULL) {
+               _wapi_handle_get_private_segment (segment)->handles[idx].type=type;
+       }
+       
+#if !defined(_POSIX_THREAD_PROCESS_SHARED) || _POSIX_THREAD_PROCESS_SHARED == -1
+       thr_ret = mono_mutex_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex, &mutex_shared_attr);
+       g_assert (thr_ret == 0);
+       
+       thr_ret = pthread_cond_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond, &cond_shared_attr);
+       g_assert (thr_ret == 0);
+#endif
+       handle=GUINT_TO_POINTER (handle_idx);
 
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle);
 #endif
-
+       
        return(handle);
 }
 
@@ -263,27 +433,41 @@ gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
                              gpointer *shared, gpointer *private)
 {
        struct _WapiHandleShared *shared_handle_data;
-       struct _WapiHandlePrivate *private_handle_data;
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       struct _WapiHandlePrivate *private_handle_data = NULL;
+       guint32 idx;
+       guint32 segment;
 
-       if(shared!=NULL) {
-               shared_handle_data=&_wapi_shared_data->handles[idx];
-               /* Allow WAPI_HANDLE_UNUSED to mean "dont care which
-                * type"
-                */
-               if(shared_handle_data->type!=type &&
-                  type != WAPI_HANDLE_UNUSED) {
-                       return(FALSE);
-               }
+       g_assert (GPOINTER_TO_UINT (handle) >= _wapi_fd_offset_table_size);
+       
+       _wapi_handle_segment (handle, &segment, &idx);
+       _wapi_handle_ensure_mapped (segment);
+       
+       shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
 
+       if(shared!=NULL) {
                *shared=&shared_handle_data->u;
        }
        
        if(private!=NULL) {
-               private_handle_data=&_wapi_private_data->handles[idx];
+               private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
 
                *private=&private_handle_data->u;
        }
+
+       if(shared_handle_data->type!=type) {
+               /* If shared type is UNUSED, see if the private type
+                * matches what we are looking for - this can happen
+                * when the handle is being destroyed and the
+                * close_private function is looking up the private
+                * data
+                */
+               if(shared_handle_data->type==WAPI_HANDLE_UNUSED &&
+                  (private!=NULL && private_handle_data->type==type)) {
+                       return(TRUE);
+               } else {
+                       return(FALSE);
+               }
+       }
        
        return(TRUE);
 }
@@ -295,10 +479,15 @@ gpointer _wapi_search_handle (WapiHandleType type,
 {
        struct _WapiHandleShared *shared_handle_data;
        struct _WapiHandlePrivate *private_handle_data;
-       guint32 i;
+       guint32 i, segment, idx;
 
-       for(i=1; i<_WAPI_MAX_HANDLES; i++) {
-               struct _WapiHandleShared *shared=&_wapi_shared_data->handles[i];
+       for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
+               struct _WapiHandleShared *shared;
+               
+               _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
+               _wapi_handle_ensure_mapped (segment);
+               
+               shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
                
                if(shared->type==type) {
                        if(check (GUINT_TO_POINTER (i), user_data)==TRUE) {
@@ -307,18 +496,18 @@ gpointer _wapi_search_handle (WapiHandleType type,
                }
        }
 
-       if(i==_WAPI_MAX_HANDLES) {
+       if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
                return(GUINT_TO_POINTER (0));
        }
        
        if(shared!=NULL) {
-               shared_handle_data=&_wapi_shared_data->handles[i];
+               shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
 
                *shared=&shared_handle_data->u;
        }
        
        if(private!=NULL) {
-               private_handle_data=&_wapi_private_data->handles[i];
+               private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
 
                *private=&private_handle_data->u;
        }
@@ -326,16 +515,116 @@ gpointer _wapi_search_handle (WapiHandleType type,
        return(GUINT_TO_POINTER (i));
 }
 
-void _wapi_handle_ref (gpointer handle)
+gpointer _wapi_search_handle_namespace (WapiHandleType type,
+                                       gchar *utf8_name, gpointer *shared,
+                                       gpointer *private)
 {
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       struct _WapiHandleShared *shared_handle_data;
+       struct _WapiHandlePrivate *private_handle_data;
+       guint32 i, segment, idx;
+
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": Lookup for handle named [%s] type %d", utf8_name, type);
+#endif
+
+       for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
+               struct _WapiHandleShared *shared;
+               
+               _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
+               _wapi_handle_ensure_mapped (segment);
+               
+               shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
+               
+               /* Check mutex, event, semaphore, timer, job and file-mapping
+                * object names.  So far only mutex is implemented.
+                */
+               if(_WAPI_SHARED_NAMESPACE (shared->type)) {
+                       gchar *lookup_name;
+                       WapiSharedNamespace *sharedns;
+                       
+#ifdef DEBUG
+                       g_message (G_GNUC_PRETTY_FUNCTION ": found a shared namespace handle at 0x%x (type %d)", i, shared->type);
+#endif
 
+                       shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
+                       sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
+                       
+                       
+                       if(sharedns->name) {
+                               lookup_name=_wapi_handle_scratch_lookup (
+                                       sharedns->name);
+                       } else {
+#ifdef DEBUG
+                               g_message (G_GNUC_PRETTY_FUNCTION
+                                          ": handle 0x%x is unnamed", i);
+#endif
+                               continue;
+                       }
+
+                       if(lookup_name==NULL) {
+#ifdef DEBUG
+                               g_message (G_GNUC_PRETTY_FUNCTION
+                                          ": couldn't find handle 0x%x name",
+                                          i);
+#endif
+                               continue;
+                       }
+
+#ifdef DEBUG
+                       g_message (G_GNUC_PRETTY_FUNCTION ": name is [%s]",
+                                  lookup_name);
+#endif
+
+                       if(strcmp (lookup_name, utf8_name)==0) {
+                               if(shared->type!=type) {
+                                       /* Its the wrong type, so fail now */
+#ifdef DEBUG
+                                       g_message (G_GNUC_PRETTY_FUNCTION ": handle 0x%x matches name but is wrong type: %d", i, shared->type);
+#endif
+                                       return(_WAPI_HANDLE_INVALID);
+                               } else {
+                                       /* fall through so we can fill
+                                        * in the data
+                                        */
+#ifdef DEBUG
+                                       g_message (G_GNUC_PRETTY_FUNCTION ": handle 0x%x matches name and type", i);
+#endif
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
+               return(GUINT_TO_POINTER (0));
+       }
+       
+       if(shared!=NULL) {
+               shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
+
+               *shared=&shared_handle_data->u;
+       }
+       
+       if(private!=NULL) {
+               private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
+
+               *private=&private_handle_data->u;
+       }
+       
+       return(GUINT_TO_POINTER (i));
+}
+
+void _wapi_handle_ref (gpointer handle)
+{
+       g_assert (GPOINTER_TO_UINT (handle) >= _wapi_fd_offset_table_size);
+       
        if(shared==TRUE) {
-               WapiHandleRequest req;
-               WapiHandleResponse resp;
+               WapiHandleRequest req={0};
+               WapiHandleResponse resp={0};
        
                req.type=WapiHandleRequestType_Open;
-               req.u.open.handle=idx;
+               req.u.open.handle=GPOINTER_TO_UINT (handle);
        
                _wapi_daemon_request_response (daemon_sock, &req, &resp);
                if(resp.type!=WapiHandleResponseType_Open) {
@@ -345,23 +634,34 @@ void _wapi_handle_ref (gpointer handle)
                        g_assert_not_reached ();
                }
        } else {
-               _wapi_shared_data->handles[idx].ref++;
+               guint32 idx, segment;
+
+               _wapi_handle_segment (handle, &segment, &idx);
+               
+               _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
        
 #ifdef DEBUG
                g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
-                          handle, _wapi_shared_data->handles[idx].ref);
+                          handle,
+                          _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
 #endif
        }
 }
 
+/* The handle must not be locked on entry to this function */
 void _wapi_handle_unref (gpointer handle)
 {
-       guint32 idx=GPOINTER_TO_UINT (handle);
-       gboolean destroy;
+       guint32 idx, segment;
+       gboolean destroy = FALSE;
+       int thr_ret;
+       
+       g_assert (GPOINTER_TO_UINT (handle) >= _wapi_fd_offset_table_size);
+       
+       _wapi_handle_segment (handle, &segment, &idx);
        
        if(shared==TRUE) {
-               WapiHandleRequest req;
-               WapiHandleResponse resp;
+               WapiHandleRequest req={0};
+               WapiHandleResponse resp={0};
        
                req.type=WapiHandleRequestType_Close;
                req.u.close.handle=GPOINTER_TO_UINT (handle);
@@ -376,11 +676,10 @@ void _wapi_handle_unref (gpointer handle)
                        destroy=resp.u.close.destroy;
                }
        } else {
-               _wapi_shared_data->handles[idx].ref--;
+               _wapi_handle_get_shared_segment (segment)->handles[idx].ref--;
        
 #ifdef DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
-                          handle, _wapi_shared_data->handles[idx].ref);
+               g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d", handle, _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
 #endif
 
                /* Possible race condition here if another thread refs
@@ -389,7 +688,7 @@ void _wapi_handle_unref (gpointer handle)
                 * that allowing a handle reference to reach 0 isn't
                 * an application bug anyway.
                 */
-               destroy=(_wapi_shared_data->handles[idx].ref==0);
+               destroy=(_wapi_handle_get_shared_segment (segment)->handles[idx].ref==0);
        }
 
        if(destroy==TRUE) {
@@ -400,39 +699,68 @@ void _wapi_handle_unref (gpointer handle)
                
                if(shared==FALSE) {
                        _wapi_handle_ops_close_shared (handle);
-                       _wapi_shared_data->handles[idx].type=WAPI_HANDLE_UNUSED;
-                       mono_mutex_destroy (&_wapi_shared_data->handles[idx].signal_mutex);
-                       pthread_cond_destroy (&_wapi_shared_data->handles[idx].signal_cond);
-                       memset (&_wapi_shared_data->handles[idx].u, '\0', sizeof(_wapi_shared_data->handles[idx].u));
+
+                       memset (&_wapi_handle_get_shared_segment (segment)->handles[idx].u, '\0', sizeof(_wapi_handle_get_shared_segment (segment)->handles[idx].u));
                }
-               
+
                _wapi_handle_ops_close_private (handle);
+               _wapi_handle_get_shared_segment (segment)->handles[idx].type=WAPI_HANDLE_UNUSED;
+               _wapi_handle_get_private_segment (segment)->handles[idx].type=WAPI_HANDLE_UNUSED;
+               
+               /* Destroy the mutex and cond var.  We hope nobody
+                * tried to grab them between the handle unlock and
+                * now, but pthreads doesn't have a
+                * "unlock_and_destroy" atomic function.
+                */
+               thr_ret = mono_mutex_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
+               g_assert (thr_ret == 0);
+                       
+               thr_ret = pthread_cond_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond);
+               g_assert (thr_ret == 0);
        }
 }
 
 #define HDRSIZE sizeof(struct _WapiScratchHeader)
 
-guint32 _wapi_handle_scratch_store_internal (guint32 bytes)
+static pthread_mutex_t _wapi_scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
+
+/* _wapi_scratch_mutex must be held when this function is called in
+ * the non-shared case
+ */
+static void _wapi_handle_scratch_expand (void)
+{
+       guint32 old_len, new_len;
+               
+       old_len=sizeof(struct _WapiHandleScratch) +
+               _wapi_shared_scratch->data_len;
+       new_len=old_len+_WAPI_SHM_SCRATCH_SIZE;
+               
+       if(_wapi_shared_scratch->is_shared==TRUE) {
+               /* expand via mmap() */
+               _wapi_shared_scratch=_wapi_shm_file_expand (_wapi_shared_scratch, WAPI_SHM_SCRATCH, 0, old_len, new_len);
+       } else {
+               _wapi_shared_scratch=_wapi_g_renew0 (_wapi_shared_scratch, old_len, new_len);
+       }
+       _wapi_shared_scratch->data_len+=_WAPI_SHM_SCRATCH_SIZE;
+}
+
+/* _wapi_scratch_mutex must be held when this function is called in
+ * the non-shared case
+ */
+static guint32 _wapi_handle_scratch_locate_space (guint32 bytes)
 {
        guint32 idx=0, last_idx=0;
-       struct _WapiScratchHeader *hdr, *last_hdr;
+       struct _WapiScratchHeader *hdr, *last_hdr = NULL;
        gboolean last_was_free=FALSE;
-       guchar *storage=&_wapi_shared_data->scratch_base[0];
+       guchar *storage=_wapi_shared_scratch->scratch_data;
        
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION
                   ": looking for %d bytes of scratch space (%d bytes total)",
-                  bytes, _WAPI_SHM_SCRATCH_SIZE);
+                  bytes, _wapi_shared_scratch->data_len);
 #endif
-
-       hdr=(struct _WapiScratchHeader *)&storage[0];
-       if(hdr->flags==0 && hdr->length==0) {
-               /* Need to initialise scratch data */
-               hdr->flags |= WAPI_SHM_SCRATCH_FREE;
-               hdr->length = _WAPI_SHM_SCRATCH_SIZE - HDRSIZE;
-       }
        
-       while(idx< _WAPI_SHM_SCRATCH_SIZE) {
+       while(idx< _wapi_shared_scratch->data_len) {
                hdr=(struct _WapiScratchHeader *)&storage[idx];
                
                /* Do a simple first-fit allocation, coalescing
@@ -462,6 +790,10 @@ guint32 _wapi_handle_scratch_store_internal (guint32 bytes)
                        g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
 #endif
                        
+                       /*
+                        * It was memset(0..) when free/made so no need to do it here
+                        */
+
                        return(idx);
                } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
                          last_was_free == FALSE) {
@@ -511,81 +843,295 @@ guint32 _wapi_handle_scratch_store_internal (guint32 bytes)
 
                        /* Don't let the coalescing blow away this block */
                        last_was_free=FALSE;
+
+                       /* But remember where the last block started */
+                       last_idx=idx;
                }
        }
+
+       /* Not enough free space.  last_idx points to the last block.
+        * If it's free, just tack on more space and update the
+        * length.  If it's allocated, it must have fit right into the
+        * available space, so add more space and add a new header
+        * after this block.
+        */
+       _wapi_handle_scratch_expand ();
+       storage=_wapi_shared_scratch->scratch_data;
        
+       hdr=(struct _WapiScratchHeader *)&storage[last_idx];
+       if(hdr->flags & WAPI_SHM_SCRATCH_FREE) {
+               hdr->length+=_WAPI_SHM_SCRATCH_SIZE;
+       } else {
+               idx=(hdr->length+HDRSIZE);
+               hdr=(struct _WapiScratchHeader *)&storage[idx];
+               hdr->flags |= WAPI_SHM_SCRATCH_FREE;
+               hdr->length = _WAPI_SHM_SCRATCH_SIZE-HDRSIZE;
+       }
+
+       /* The caller will try again */
        return(0);
 }
 
-guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
+/*
+ * _wapi_handle_scratch_store_internal:
+ * @bytes: Allocate no. bytes
+ *
+ * Like malloc(3) except its for the shared memory segment's scratch
+ * part. Memory block returned is zeroed out.
+ */
+guint32 _wapi_handle_scratch_store_internal (guint32 bytes, gboolean *remap)
 {
-       static pthread_mutex_t scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
+       guchar *storage;
        guint32 idx;
+       struct _WapiScratchHeader *hdr;
+
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
+#endif
        
+       *remap=FALSE;
+       
+       if(_wapi_shared_scratch->data_len==0) {
+               /* Need to expand the data array for the first use */
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION
+                          ": setting up scratch space");
+#endif
+
+               _wapi_handle_scratch_expand ();
+               *remap=TRUE;
+       }
+
+       storage=_wapi_shared_scratch->scratch_data;
+       hdr=(struct _WapiScratchHeader *)&storage[0];
+       if(hdr->flags==0 && hdr->length==0) {
+               /* Need to initialise scratch data */
+               hdr->flags |= WAPI_SHM_SCRATCH_FREE;
+               hdr->length = _wapi_shared_scratch->data_len - HDRSIZE;
+       }
+
+       idx=_wapi_handle_scratch_locate_space (bytes);
+       if(idx==0) {
+               /* Some more space will have been allocated, so try again */
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": trying again");
+#endif
+
+               idx=_wapi_handle_scratch_locate_space (bytes);
+               *remap=TRUE;
+       }
+       
+       return(idx);
+}
+
+guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
+{
+       guint32 idx = 0, store_bytes;
+       gboolean remap;
+       int thr_ret;
+       guint32 ret = 0;
+       
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
+#endif
+
        /* No point storing no data */
        if(bytes==0) {
                return(0);
        }
+
+       /* Align bytes to 32 bits (needed for sparc at least) */
+       store_bytes = (((bytes) + 3) & (~3));
+
+       pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
+                             (void *)&_wapi_scratch_mutex);
+       thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
+       g_assert (thr_ret == 0);
        
        if(shared==TRUE) {
-               WapiHandleRequest scratch;
-               WapiHandleResponse scratch_resp;
-       
+               WapiHandleRequest scratch={0};
+               WapiHandleResponse scratch_resp={0};
+               guint32 old_len=sizeof(struct _WapiHandleScratch) +
+                       _wapi_shared_scratch->data_len;
+               
                scratch.type=WapiHandleRequestType_Scratch;
-               scratch.u.scratch.length=bytes;
+               scratch.u.scratch.length=store_bytes;
        
                _wapi_daemon_request_response (daemon_sock, &scratch,
                                               &scratch_resp);
        
                if(scratch_resp.type==WapiHandleResponseType_Scratch) {
                        idx=scratch_resp.u.scratch.idx;
+                       remap=scratch_resp.u.scratch.remap;
                } else {
                        g_warning (G_GNUC_PRETTY_FUNCTION
                                   ": bogus daemon response, type %d",
                                   scratch_resp.type);
                        g_assert_not_reached ();
                }
+       
+               if(remap==TRUE) {
+                       munmap (_wapi_shared_scratch, old_len);
+                       _wapi_shared_scratch=_wapi_shm_file_map (WAPI_SHM_SCRATCH, 0, NULL, NULL);
+               }
        } else {
-               pthread_mutex_lock (&scratch_mutex);
-               idx=_wapi_handle_scratch_store_internal (bytes);
-               pthread_mutex_unlock (&scratch_mutex);
-               
+               idx=_wapi_handle_scratch_store_internal (store_bytes, &remap);
                if(idx==0) {
                        /* Failed to allocate space */
-                       return(0);
+                       goto cleanup;
                }
        }
+       ret = idx;
+       
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": stored [%s] at %d (len %d, aligned len %d)",
+                  (char *)data, idx, bytes, store_bytes);
+#endif
+       
+       memcpy (&_wapi_shared_scratch->scratch_data[idx], data, bytes);
+
+cleanup:
+       thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
+       
+       return(ret);
+}
+
+guint32 _wapi_handle_scratch_store_string_array (gchar **data)
+{
+       guint32 *stored_strings, count=0, i, idx;
+       gchar **strings;
+       
+       /* No point storing no data */
+       if(data==NULL) {
+               return(0);
+       }
 
-       memcpy (&_wapi_shared_data->scratch_base[idx], data, bytes);
+       strings=data;
+       while(*strings!=NULL) {
+               count++;
+               strings++;
+       }
+       
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": %d strings to store", count);
+#endif
+       
+       if(count==0) {
+               return(0);
+       }
+
+       /* stored_strings[0] is the count */
+       stored_strings=g_new0 (guint32, count+1);
+       stored_strings[0]=count;
+       
+       strings=data;
+       for(i=0; i<count; i++) {
+               stored_strings[i+1]=_wapi_handle_scratch_store (strings[i], strlen (strings[i]));
+       }
+
+       idx=_wapi_handle_scratch_store (stored_strings,
+                                       sizeof(guint32)*(count+1));
        
        return(idx);
 }
 
-guchar *_wapi_handle_scratch_lookup_as_string (guint32 idx)
+gpointer _wapi_handle_scratch_lookup (guint32 idx)
 {
        struct _WapiScratchHeader *hdr;
-       guchar *str;
-       guchar *storage=&_wapi_shared_data->scratch_base[0];
+       gpointer ret;
+       guchar *storage;
+       int thr_ret;
        
-       if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) {
+       if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
                return(NULL);
        }
+
+       pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
+                             (void *)&_wapi_scratch_mutex);
+       thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
+       g_assert (thr_ret == 0);
+       
+       storage=_wapi_shared_scratch->scratch_data;
        
        hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
-       str=g_malloc0 (hdr->length+1);
-       memcpy (str, &storage[idx], hdr->length);
+       ret=g_malloc0 (hdr->length+1);
+       memcpy (ret, &storage[idx], hdr->length);
+
+       thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
 
-       return(str);
+       return(ret);
 }
 
+gchar **_wapi_handle_scratch_lookup_string_array (guint32 idx)
+{
+       gchar **strings;
+       guint32 *stored_strings;
+       guint32 count, i;
+       
+       if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
+               return(NULL);
+       }
+
+       stored_strings=_wapi_handle_scratch_lookup (idx);
+       if(stored_strings==NULL) {
+               return(NULL);
+       }
+       
+       /* stored_strings[0] is the number of strings, the index of
+        * each string follows
+        */
+       count=stored_strings[0];
+       
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": looking up an array of %d strings", count);
+#endif
+       
+       /* NULL-terminate the array */
+       strings=g_new0 (gchar *, count+1);
+       
+       for(i=0; i<count; i++) {
+               strings[i]=_wapi_handle_scratch_lookup (stored_strings[i+1]);
+
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": string %d is [%s]", i,
+                          strings[i]);
+#endif
+       }
+
+       g_free (stored_strings);
+       
+       return(strings);
+}
+
+/*
+ * _wapi_handle_scratch_delete_internal:
+ * @idx: Index to free block
+ *
+ * Like free(3) except its for the shared memory segment's scratch
+ * part.
+ */
 void _wapi_handle_scratch_delete_internal (guint32 idx)
 {
        struct _WapiScratchHeader *hdr;
-       guchar *storage=&_wapi_shared_data->scratch_base[0];
+       guchar *storage;
+       int thr_ret;
        
-       if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) {
+       if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
                return;
        }
+
+       pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
+                             (void *)&_wapi_scratch_mutex);
+       thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
+       g_assert (thr_ret == 0);
+       
+       storage=_wapi_shared_scratch->scratch_data;
        
        hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
        memset (&storage[idx], '\0', hdr->length);
@@ -594,13 +1140,17 @@ void _wapi_handle_scratch_delete_internal (guint32 idx)
        /* We could coalesce forwards here if the next block is also
         * free, but the _store() function will do that anyway.
         */
+
+       thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
 }
 
 void _wapi_handle_scratch_delete (guint32 idx)
 {
        if(shared==TRUE) {
-               WapiHandleRequest scratch_free;
-               WapiHandleResponse scratch_free_resp;
+               WapiHandleRequest scratch_free={0};
+               WapiHandleResponse scratch_free_resp={0};
        
                scratch_free.type=WapiHandleRequestType_ScratchFree;
                scratch_free.u.scratch_free.idx=idx;
@@ -619,6 +1169,35 @@ void _wapi_handle_scratch_delete (guint32 idx)
        }
 }
 
+void _wapi_handle_scratch_delete_string_array (guint32 idx)
+{
+       guint32 *stored_strings;
+       guint32 count, i;
+       
+       stored_strings=_wapi_handle_scratch_lookup (idx);
+       if(stored_strings==NULL) {
+               return;
+       }
+       
+       /* stored_strings[0] is the number of strings, the index of
+        * each string follows
+        */
+       count=stored_strings[0];
+       
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": deleting an array of %d strings",
+                  count);
+#endif
+       
+       for(i=1; i<count; i++) {
+               _wapi_handle_scratch_delete (stored_strings[i]);
+       }
+       
+       _wapi_handle_scratch_delete (idx);
+
+       g_free (stored_strings);
+}
+
 void _wapi_handle_register_capabilities (WapiHandleType type,
                                         WapiHandleCapability caps)
 {
@@ -628,10 +1207,16 @@ void _wapi_handle_register_capabilities (WapiHandleType type,
 gboolean _wapi_handle_test_capabilities (gpointer handle,
                                         WapiHandleCapability caps)
 {
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       guint32 idx, segment;
        WapiHandleType type;
 
-       type=_wapi_shared_data->handles[idx].type;
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+       
+       _wapi_handle_segment (handle, &segment, &idx);
+       
+       type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
 
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)",
@@ -643,10 +1228,16 @@ gboolean _wapi_handle_test_capabilities (gpointer handle,
 
 void _wapi_handle_ops_close_shared (gpointer handle)
 {
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       guint32 idx, segment;
        WapiHandleType type;
 
-       type=_wapi_shared_data->handles[idx].type;
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
+       
+       type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
 
        if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) {
                handle_ops[type]->close_shared (handle);
@@ -655,10 +1246,23 @@ void _wapi_handle_ops_close_shared (gpointer handle)
 
 void _wapi_handle_ops_close_private (gpointer handle)
 {
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       guint32 idx, segment;
        WapiHandleType type;
 
-       type=_wapi_shared_data->handles[idx].type;
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
+
+       type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
+
+       /* When a handle in the process of being destroyed the shared
+        * type has already been set to UNUSED
+        */
+       if(type==WAPI_HANDLE_UNUSED && _wapi_private_data!=NULL) {
+               type=_wapi_handle_get_private_segment (segment)->handles[idx].type;
+       }
 
        if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) {
                handle_ops[type]->close_private (handle);
@@ -667,10 +1271,16 @@ void _wapi_handle_ops_close_private (gpointer handle)
 
 void _wapi_handle_ops_signal (gpointer handle)
 {
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       guint32 idx, segment;
        WapiHandleType type;
 
-       type=_wapi_shared_data->handles[idx].type;
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
+
+       type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
 
        if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) {
                handle_ops[type]->signal (handle);
@@ -679,10 +1289,16 @@ void _wapi_handle_ops_signal (gpointer handle)
 
 void _wapi_handle_ops_own (gpointer handle)
 {
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       guint32 idx, segment;
        WapiHandleType type;
 
-       type=_wapi_shared_data->handles[idx].type;
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
+       
+       type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
 
        if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) {
                handle_ops[type]->own_handle (handle);
@@ -691,10 +1307,16 @@ void _wapi_handle_ops_own (gpointer handle)
 
 gboolean _wapi_handle_ops_isowned (gpointer handle)
 {
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       guint32 idx, segment;
        WapiHandleType type;
 
-       type=_wapi_shared_data->handles[idx].type;
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
+       
+       type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
 
        if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) {
                return(handle_ops[type]->is_owned (handle));
@@ -716,6 +1338,14 @@ gboolean _wapi_handle_ops_isowned (gpointer handle)
  */
 gboolean CloseHandle(gpointer handle)
 {
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       if (handle == NULL) {
+               return(FALSE);
+       }
+
        _wapi_handle_unref (handle);
        
        return(TRUE);
@@ -729,29 +1359,44 @@ gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
 {
        guint32 count, i, iter=0;
        gboolean ret;
+       int thr_ret;
        
        /* Lock all the handles, with backoff */
 again:
        for(i=0; i<numhandles; i++) {
-               guint32 idx=GPOINTER_TO_UINT (handles[i]);
+               guint32 idx, segment;
+               gpointer handle = handles[i];
+               
+               if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+                       handle = _wapi_handle_fd_offset_to_handle (handle);
+               }
+               
+               _wapi_handle_segment (handle, &segment, &idx);
                
 #ifdef DEBUG
                g_message (G_GNUC_PRETTY_FUNCTION ": attempting to lock %p",
-                          handles[i]);
+                          handle);
 #endif
 
-               ret=mono_mutex_trylock (&_wapi_shared_data->handles[idx].signal_mutex);
+               ret=mono_mutex_trylock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
                if(ret!=0) {
                        /* Bummer */
                        struct timespec sleepytime;
                        
 #ifdef DEBUG
-                       g_message (G_GNUC_PRETTY_FUNCTION ": attempt failed for %p", handles[i]);
+                       g_message (G_GNUC_PRETTY_FUNCTION ": attempt failed for %p: %s", handle, strerror (ret));
 #endif
 
                        while(i--) {
-                               idx=GPOINTER_TO_UINT (handles[i]);
-                               mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex);
+                               handle = handles[i];
+               
+                               if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+                                       handle = _wapi_handle_fd_offset_to_handle (handle);
+                               }
+
+                               _wapi_handle_segment (handle, &segment, &idx);
+                               thr_ret = mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
+                               g_assert (thr_ret == 0);
                        }
 
                        /* If iter ever reaches 100 the nanosleep will
@@ -786,21 +1431,30 @@ again:
        *lowest=numhandles;
        
        for(i=0; i<numhandles; i++) {
-               guint32 idx=GPOINTER_TO_UINT (handles[i]);
+               guint32 idx, segment;
+               gpointer handle = handles[i];
+               
+               if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+                       handle = _wapi_handle_fd_offset_to_handle (handle);
+               }
+               
+               _wapi_handle_ref (handle);
+               
+               _wapi_handle_segment (handle, &segment, &idx);
                
 #ifdef DEBUG
                g_message (G_GNUC_PRETTY_FUNCTION ": Checking handle %p",
-                          handles[i]);
+                          handle);
 #endif
 
-               if(((_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_OWN)==TRUE) &&
-                   (_wapi_handle_ops_isowned (handles[i])==TRUE)) ||
-                  (_wapi_shared_data->handles[idx].signalled==TRUE)) {
+               if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
+                   (_wapi_handle_ops_isowned (handle)==TRUE)) ||
+                  (_wapi_handle_get_shared_segment (segment)->handles[idx].signalled==TRUE)) {
                        count++;
                        
 #ifdef DEBUG
                        g_message (G_GNUC_PRETTY_FUNCTION
-                                  ": Handle %p signalled", handles[i]);
+                                  ": Handle %p signalled", handle);
 #endif
                        if(*lowest>i) {
                                *lowest=i;
@@ -832,16 +1486,27 @@ again:
 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
 {
        guint32 i;
+       int thr_ret;
        
        for(i=0; i<numhandles; i++) {
-               guint32 idx=GPOINTER_TO_UINT (handles[i]);
+               guint32 idx, segment;
+               gpointer handle = handles[i];
+               
+               if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+                       handle = _wapi_handle_fd_offset_to_handle (handle);
+               }
                
+               _wapi_handle_segment (handle, &segment, &idx);
+
 #ifdef DEBUG
                g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p",
-                          handles[i]);
+                          handle);
 #endif
 
-               mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex);
+               thr_ret = mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
+               g_assert (thr_ret == 0);
+
+               _wapi_handle_unref (handle);
        }
 }
 
@@ -856,17 +1521,17 @@ void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
  */
 int _wapi_handle_wait_signal (void)
 {
-#ifdef _POSIX_THREAD_PROCESS_SHARED
-       return(mono_cond_wait (&_wapi_shared_data->signal_cond,
-                              &_wapi_shared_data->signal_mutex));
+#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
+       return(mono_cond_wait (&_wapi_handle_get_shared_segment (0)->signal_cond,
+                              &_wapi_handle_get_shared_segment (0)->signal_mutex));
 #else
        struct timespec fake_timeout;
        int ret;
        
        _wapi_calc_timeout (&fake_timeout, 100);
 
-       ret=mono_cond_timedwait (&_wapi_private_data->signal_cond,
-                                &_wapi_private_data->signal_mutex,
+       ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
+                                &_wapi_handle_get_private_segment (0)->signal_mutex,
                                 &fake_timeout);
        if(ret==ETIMEDOUT) {
                ret=0;
@@ -878,9 +1543,9 @@ int _wapi_handle_wait_signal (void)
 
 int _wapi_handle_timedwait_signal (struct timespec *timeout)
 {
-#ifdef _POSIX_THREAD_PROCESS_SHARED
-       return(mono_cond_timedwait (&_wapi_shared_data->signal_cond,
-                                   &_wapi_shared_data->signal_mutex,
+#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
+       return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (0)->signal_cond,
+                                   &_wapi_handle_get_shared_segment (0)->signal_mutex,
                                    timeout));
 #else
        struct timespec fake_timeout;
@@ -892,12 +1557,12 @@ int _wapi_handle_timedwait_signal (struct timespec *timeout)
           (fake_timeout.tv_sec==timeout->tv_sec &&
            fake_timeout.tv_nsec > timeout->tv_nsec)) {
                /* Real timeout is less than 100ms time */
-               ret=mono_cond_timedwait (&_wapi_private_data->signal_cond,
-                                        &_wapi_private_data->signal_mutex,
+               ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
+                                        &_wapi_handle_get_private_segment (0)->signal_mutex,
                                         timeout);
        } else {
-               ret=mono_cond_timedwait (&_wapi_private_data->signal_cond,
-                                        &_wapi_private_data->signal_mutex,
+               ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
+                                        &_wapi_handle_get_private_segment (0)->signal_mutex,
                                         &fake_timeout);
                if(ret==ETIMEDOUT) {
                        ret=0;
@@ -910,20 +1575,31 @@ int _wapi_handle_timedwait_signal (struct timespec *timeout)
 
 int _wapi_handle_wait_signal_handle (gpointer handle)
 {
-#ifdef _POSIX_THREAD_PROCESS_SHARED
-       guint32 idx=GPOINTER_TO_UINT (handle);
+#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
+       guint32 idx, segment;
+
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
        
-       return(mono_cond_wait (&_wapi_shared_data->handles[idx].signal_cond,
-                              &_wapi_shared_data->handles[idx].signal_mutex));
+       return(mono_cond_wait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
+                              &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex));
 #else
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       guint32 idx, segment;
        struct timespec fake_timeout;
        int ret;
        
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
        _wapi_calc_timeout (&fake_timeout, 100);
        
-       ret=mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
-                                &_wapi_shared_data->handles[idx].signal_mutex,
+       ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
+                                &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
                                 &fake_timeout);
        if(ret==ETIMEDOUT) {
                ret=0;
@@ -936,29 +1612,40 @@ int _wapi_handle_wait_signal_handle (gpointer handle)
 int _wapi_handle_timedwait_signal_handle (gpointer handle,
                                          struct timespec *timeout)
 {
-#ifdef _POSIX_THREAD_PROCESS_SHARED
-       guint32 idx=GPOINTER_TO_UINT (handle);
+#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
+       guint32 idx, segment;
+
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
        
-       return(mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
-                                   &_wapi_shared_data->handles[idx].signal_mutex,
+       return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
+                                   &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
                                    timeout));
 #else
-       guint32 idx=GPOINTER_TO_UINT (handle);
+       guint32 idx, segment;
        struct timespec fake_timeout;
        int ret;
        
+       if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
+               handle = _wapi_handle_fd_offset_to_handle (handle);
+       }
+
+       _wapi_handle_segment (handle, &segment, &idx);
        _wapi_calc_timeout (&fake_timeout, 100);
        
        if((fake_timeout.tv_sec>timeout->tv_sec) ||
           (fake_timeout.tv_sec==timeout->tv_sec &&
            fake_timeout.tv_nsec > timeout->tv_nsec)) {
                /* Real timeout is less than 100ms time */
-               ret=mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
-                                        &_wapi_shared_data->handles[idx].signal_mutex,
+               ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
+                                        &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
                                         timeout);
        } else {
-               ret=mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
-                                        &_wapi_shared_data->handles[idx].signal_mutex,
+               ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
+                                        &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
                                         &fake_timeout);
                if(ret==ETIMEDOUT) {
                        ret=0;
@@ -969,17 +1656,18 @@ int _wapi_handle_timedwait_signal_handle (gpointer handle,
 #endif /* _POSIX_THREAD_PROCESS_SHARED */
 }
 
-gboolean _wapi_handle_process_fork (guint32 cmd, guint32 args, guint32 env,
-                                   guint32 dir, gboolean inherit,
-                                   guint32 flags, gpointer stdin_handle,
+gboolean _wapi_handle_process_fork (guint32 cmd, guint32 env, guint32 dir,
+                                   gboolean inherit, guint32 flags,
+                                   gpointer stdin_handle,
                                    gpointer stdout_handle,
                                    gpointer stderr_handle,
                                    gpointer *process_handle,
                                    gpointer *thread_handle, guint32 *pid,
                                    guint32 *tid)
 {
-       WapiHandleRequest fork_proc;
-       WapiHandleResponse fork_proc_resp;
+       WapiHandleRequest fork_proc={0};
+       WapiHandleResponse fork_proc_resp={0};
+       int in_fd, out_fd, err_fd;
        
        if(shared!=TRUE) {
                return(FALSE);
@@ -987,7 +1675,6 @@ gboolean _wapi_handle_process_fork (guint32 cmd, guint32 args, guint32 env,
 
        fork_proc.type=WapiHandleRequestType_ProcessFork;
        fork_proc.u.process_fork.cmd=cmd;
-       fork_proc.u.process_fork.args=args;
        fork_proc.u.process_fork.env=env;
        fork_proc.u.process_fork.dir=dir;
        fork_proc.u.process_fork.stdin_handle=GPOINTER_TO_UINT (stdin_handle);
@@ -996,8 +1683,19 @@ gboolean _wapi_handle_process_fork (guint32 cmd, guint32 args, guint32 env,
        fork_proc.u.process_fork.inherit=inherit;
        fork_proc.u.process_fork.flags=flags;
        
-       _wapi_daemon_request_response (daemon_sock, &fork_proc,
-                                      &fork_proc_resp);
+       in_fd=_wapi_file_handle_to_fd (stdin_handle);
+       out_fd=_wapi_file_handle_to_fd (stdout_handle);
+       err_fd=_wapi_file_handle_to_fd (stderr_handle);
+
+       if(in_fd==-1 || out_fd==-1 || err_fd==-1) {
+               /* We were given duff handles */
+               /* FIXME: error code */
+               return(FALSE);
+       }
+       
+       _wapi_daemon_request_response_with_fds (daemon_sock, &fork_proc,
+                                               &fork_proc_resp, in_fd,
+                                               out_fd, err_fd);
        if(fork_proc_resp.type==WapiHandleResponseType_ProcessFork) {
                *process_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.process_handle);
                *thread_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.thread_handle);
@@ -1010,9 +1708,26 @@ gboolean _wapi_handle_process_fork (guint32 cmd, guint32 args, guint32 env,
                 * exec_errno will be set, and the handle will be
                 * signalled immediately.
                 */
-               if(process_handle==0 || thread_handle==0) {
+               if(*process_handle==0 || *thread_handle==0) {
                        return(FALSE);
                } else {
+                       /* This call returns new handles, so we need to do
+                        * a little bookkeeping
+                        */
+                       if (_wapi_private_data != NULL) {
+                               guint32 segment, idx;
+
+                               _wapi_handle_segment (*process_handle,
+                                                     &segment, &idx);
+                               _wapi_handle_ensure_mapped (segment);
+                               _wapi_handle_get_private_segment (segment)->handles[idx].type = WAPI_HANDLE_PROCESS;
+
+                               _wapi_handle_segment (*thread_handle,
+                                                     &segment, &idx);
+                               _wapi_handle_ensure_mapped (segment);
+                               _wapi_handle_get_private_segment (segment)->handles[idx].type = WAPI_HANDLE_THREAD;
+                       }
+
                        return(TRUE);
                }
        } else {
@@ -1024,3 +1739,99 @@ gboolean _wapi_handle_process_fork (guint32 cmd, guint32 args, guint32 env,
        
        return(FALSE);
 }
+
+gboolean
+_wapi_handle_process_kill (pid_t process, guint32 signo, gint *errnum)
+{
+       WapiHandleRequest killproc = {0};
+       WapiHandleResponse killprocresp = {0};
+       gint result;
+       
+       if (shared != TRUE) {
+               if (errnum) *errnum = EINVAL;
+               return FALSE;
+       }
+
+       killproc.type = WapiHandleRequestType_ProcessKill;
+       killproc.u.process_kill.pid = process;
+       killproc.u.process_kill.signo = signo;
+       
+       _wapi_daemon_request_response (daemon_sock, &killproc, &killprocresp);
+
+       if (killprocresp.type != WapiHandleResponseType_ProcessKill) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": bogus daemon response, type %d",
+                          killprocresp.type);
+               g_assert_not_reached ();
+       }
+
+       result = killprocresp.u.process_kill.err;
+       if (result != 0 && errnum != NULL)
+               *errnum = (result == FALSE) ? result : 0;
+       
+       return (result == 0);
+}
+
+gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
+                                       guint32 new_sharemode,
+                                       guint32 new_access,
+                                       guint32 *old_sharemode,
+                                       guint32 *old_access)
+{
+       WapiHandleRequest req = {0};
+       WapiHandleResponse resp = {0};
+       
+       if(shared != TRUE) {
+               /* No daemon means we don't know if a file is sharable.
+                * We're running in our own little world if this is
+                * the case, so there's no point in pretending that
+                * the file isn't sharable.
+                */
+               return(FALSE);
+       }
+       
+       req.type = WapiHandleRequestType_GetOrSetShare;
+       req.u.get_or_set_share.device = device;
+       req.u.get_or_set_share.inode = inode;
+       req.u.get_or_set_share.new_sharemode = new_sharemode;
+       req.u.get_or_set_share.new_access = new_access;
+       
+       _wapi_daemon_request_response (daemon_sock, &req, &resp);
+       if (resp.type != WapiHandleResponseType_GetOrSetShare) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": bogus daemon response, type %d", resp.type);
+               g_assert_not_reached ();
+       }
+       
+       *old_sharemode = resp.u.get_or_set_share.sharemode;
+       *old_access = resp.u.get_or_set_share.access;
+
+       return(resp.u.get_or_set_share.exists);
+}
+
+void _wapi_handle_set_share (dev_t device, ino_t inode, guint32 sharemode,
+                            guint32 access)
+{
+       WapiHandleRequest req = {0};
+       WapiHandleResponse resp = {0};
+       
+       if(shared != TRUE) {
+               /* No daemon, so there's no one else to tell about
+                * file sharing.
+                */
+               return;
+       }
+
+       req.type = WapiHandleRequestType_SetShare;
+       req.u.set_share.device = device;
+       req.u.set_share.inode = inode;
+       req.u.set_share.sharemode = sharemode;
+       req.u.set_share.access = access;
+       
+       _wapi_daemon_request_response (daemon_sock, &req, &resp);
+       if (resp.type != WapiHandleResponseType_SetShare) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": bogus daemon response, type %d", resp.type);
+               g_assert_not_reached ();
+       }
+}