From d549f6909edff44ed671d80bd066aeb4cd5f3fa3 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Fri, 22 Dec 2000 02:04:15 +0000
Subject: [PATCH] Use a separate FIFO pair for server requests that don't need
 to pass a file descriptor. Associate file descriptors with handles on the
 server side so that we don't need to pass the fd every time the client wants
 to use it.

---
 dlls/ntdll/om.c    |   2 +
 files/file.c       |  23 +++++--
 include/server.h   |  22 +++----
 include/thread.h   |  10 +--
 loader/ne/module.c |   8 ++-
 memory/virtual.c   |  43 +++++++++----
 scheduler/client.c | 157 +++++++++++++++++++++++++++++----------------
 scheduler/handle.c |  17 +++--
 scheduler/thread.c |  11 +++-
 server/console.c   |   2 +-
 server/debugger.c  |  16 ++---
 server/file.c      |   8 +--
 server/handle.c    |  58 ++++++++++++-----
 server/handle.h    |   3 +-
 server/mapping.c   |  24 +++++--
 server/object.h    |   1 -
 server/pipe.c      |   2 +-
 server/request.c   | 144 +++++++++++++++++++++++++++++++++--------
 server/request.h   |   5 +-
 server/thread.c    |  23 +++++--
 server/thread.h    |   4 +-
 server/trace.c     |  31 ++++-----
 22 files changed, 427 insertions(+), 187 deletions(-)

diff --git a/dlls/ntdll/om.c b/dlls/ntdll/om.c
index 3e31fc8c3aa..e8a70cff6a1 100644
--- a/dlls/ntdll/om.c
+++ b/dlls/ntdll/om.c
@@ -4,6 +4,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include "debugtools.h"
 
 #include "ntddk.h"
@@ -221,6 +222,7 @@ NTSTATUS WINAPI NtClose( HANDLE Handle )
         struct close_handle_request *req = server_alloc_req( sizeof(*req), 0 );
         req->handle = Handle;
         ret = server_call_noerr( REQ_CLOSE_HANDLE );
+        if (!ret && req->fd != -1) close( req->fd );
     }
     SERVER_END_REQ;
     return ret;
diff --git a/files/file.c b/files/file.c
index 3d957aac431..3d5e0e475e9 100644
--- a/files/file.c
+++ b/files/file.c
@@ -185,7 +185,7 @@ HFILE FILE_DupUnixHandle( int fd, DWORD access )
 {
     struct alloc_file_handle_request *req = get_req_buffer();
     req->access  = access;
-    server_call_fd( REQ_ALLOC_FILE_HANDLE, fd, NULL );
+    server_call_fd( REQ_ALLOC_FILE_HANDLE, fd );
     return req->handle;
 }
 
@@ -197,12 +197,21 @@ HFILE FILE_DupUnixHandle( int fd, DWORD access )
  */
 int FILE_GetUnixHandle( HANDLE handle, DWORD access )
 {
-    int unix_handle = -1;
-    struct get_handle_fd_request *req = get_req_buffer();
-    req->handle = handle;
-    req->access = access;
-    server_call_fd( REQ_GET_HANDLE_FD, -1, &unix_handle );
-    return unix_handle;
+    int ret, fd = -1;
+    SERVER_START_REQ
+    {
+        struct get_handle_fd_request *req = wine_server_alloc_req( sizeof(*req), 0 );
+        req->handle = handle;
+        req->access = access;
+        if (!(ret = server_call( REQ_GET_HANDLE_FD ))) fd = req->fd;
+    }
+    SERVER_END_REQ;
+    if (!ret)
+    {
+        if (fd == -1) return wine_server_recv_fd( handle, 1 );
+        fd = dup(fd);
+    }
+    return fd;
 }
 
 
diff --git a/include/server.h b/include/server.h
index f0a614d6a48..d7c7075f393 100644
--- a/include/server.h
+++ b/include/server.h
@@ -359,15 +359,7 @@ struct close_handle_request
 {
     REQUEST_HEADER;                /* request header */
     IN  int          handle;       /* handle to close */
-};
-
-
-/* Get information about a handle */
-struct get_handle_info_request
-{
-    REQUEST_HEADER;                /* request header */
-    IN  int          handle;       /* handle we are interested in */
-    OUT int          flags;        /* handle flags */
+    OUT int          fd;           /* associated fd to close */
 };
 
 
@@ -378,6 +370,9 @@ struct set_handle_info_request
     IN  int          handle;       /* handle we are interested in */
     IN  int          flags;        /* new handle flags */
     IN  int          mask;         /* mask for flags to set */
+    IN  int          fd;           /* file descriptor or -1 */
+    OUT int          old_flags;    /* old flag value */
+    OUT int          cur_fd;       /* current file descriptor */
 };
 
 
@@ -392,6 +387,7 @@ struct dup_handle_request
     IN  int          inherit;      /* inherit flag */
     IN  int          options;      /* duplicate options (see below) */
     OUT int          handle;       /* duplicated handle in dst process */
+    OUT int          fd;           /* associated fd to close */
 };
 #define DUP_HANDLE_CLOSE_SOURCE  DUPLICATE_CLOSE_SOURCE
 #define DUP_HANDLE_SAME_ACCESS   DUPLICATE_SAME_ACCESS
@@ -860,6 +856,7 @@ struct get_mapping_info_request
     OUT void*        base;          /* default base addr (for VPROT_IMAGE mapping) */
     OUT int          shared_file;   /* shared mapping file handle */
     OUT int          shared_size;   /* shared mapping size */
+    OUT int          anonymous;     /* anonymous mapping? */
 };
 
 
@@ -1373,7 +1370,6 @@ enum request
     REQ_QUEUE_APC,
     REQ_GET_APC,
     REQ_CLOSE_HANDLE,
-    REQ_GET_HANDLE_INFO,
     REQ_SET_HANDLE_INFO,
     REQ_DUP_HANDLE,
     REQ_OPEN_PROCESS,
@@ -1489,7 +1485,6 @@ union generic_request
     struct queue_apc_request queue_apc;
     struct get_apc_request get_apc;
     struct close_handle_request close_handle;
-    struct get_handle_info_request get_handle_info;
     struct set_handle_info_request set_handle_info;
     struct dup_handle_request dup_handle;
     struct open_process_request open_process;
@@ -1579,7 +1574,7 @@ union generic_request
     struct async_result_request async_result;
 };
 
-#define SERVER_PROTOCOL_VERSION 30
+#define SERVER_PROTOCOL_VERSION 31
 
 /* ### make_requests end ### */
 /* Everything above this line is generated automatically by tools/make_requests */
@@ -1605,9 +1600,10 @@ struct server_buffer_info
 /* client communication functions */
 
 extern unsigned int wine_server_call( enum request req );
-extern unsigned int server_call_fd( enum request req, int fd_out, int *fd_in );
+extern unsigned int server_call_fd( enum request req, int fd_out );
 extern void server_protocol_error( const char *err, ... ) WINE_NORETURN;
 extern void *wine_server_alloc_req( size_t fixed_size, size_t var_size );
+extern int wine_server_recv_fd( int handle, int cache );
 extern const char *get_config_dir(void);
 
 /* compatibility macros */
diff --git a/include/thread.h b/include/thread.h
index 72a3ea800b5..572b981f8fb 100644
--- a/include/thread.h
+++ b/include/thread.h
@@ -94,13 +94,15 @@ typedef struct _TEB
     DWORD        cleanup;        /* --3 1fc Cleanup service handle */
     int          socket;         /* --3 200 Socket for server communication */
     void        *buffer;         /* --3 204 Buffer shared with server */
-    struct server_buffer_info *buffer_info;   /* --3 208 Buffer information */
-    void        *debug_info;     /* --3 20c Info for debugstr functions */
-    void        *pthread_data;   /* --3 210 Data for pthread emulation */
+    int          request_fd;     /* --3 208 fd for sending server requests */
+    int          reply_fd;       /* --3 20c fd for receiving server replies */
+    struct server_buffer_info *buffer_info;   /* --3 210 Buffer information */
+    void        *debug_info;     /* --3 214 Info for debugstr functions */
+    void        *pthread_data;   /* --3 218 Data for pthread emulation */
     /* here is plenty space for wine specific fields (don't forget to change pad6!!) */
 
     /* the following are nt specific fields */
-    DWORD        pad6[633];                  /* --n 214 */
+    DWORD        pad6[631];                  /* --n 21c */
     UNICODE_STRING StaticUnicodeString;      /* -2- bf8 used by advapi32 */
     USHORT       StaticUnicodeBuffer[261];   /* -2- c00 used by advapi32 */
     DWORD        pad7;                       /* --n e0c */
diff --git a/loader/ne/module.c b/loader/ne/module.c
index 3cc10d79938..f4fc76a8905 100644
--- a/loader/ne/module.c
+++ b/loader/ne/module.c
@@ -1019,7 +1019,7 @@ HINSTANCE16 WINAPI LoadModule16( LPCSTR name, LPVOID paramBlock )
     LPSTR cmdline;
     WORD cmdShow;
     HANDLE hThread = -1;
-    int socket;
+    int socket = -1;
 
     /* Load module */
 
@@ -1066,7 +1066,11 @@ HINSTANCE16 WINAPI LoadModule16( LPCSTR name, LPVOID paramBlock )
         struct new_thread_request *req = server_alloc_req( sizeof(*req), 0 );
         req->suspend = 0;
         req->inherit = 0;
-        if (!server_call_fd( REQ_NEW_THREAD, -1, &socket )) hThread = req->handle;
+        if (!server_call( REQ_NEW_THREAD ))
+        {
+            hThread = req->handle;
+            socket = wine_server_recv_fd( hThread, 0 );
+        }
     }
     SERVER_END_REQ;
     if (hThread == -1) return 0;
diff --git a/memory/virtual.c b/memory/virtual.c
index e1043366650..a94d3a5891e 100644
--- a/memory/virtual.c
+++ b/memory/virtual.c
@@ -1478,8 +1478,10 @@ LPVOID WINAPI MapViewOfFileEx(
     UINT ptr = (UINT)-1, size = 0;
     int flags = MAP_PRIVATE;
     int unix_handle = -1;
-    int prot;
-    struct get_mapping_info_request *req = get_req_buffer();
+    int prot, anonymous, res;
+    void *base;
+    DWORD size_low, size_high, header_size, shared_size;
+    HANDLE shared_file;
 
     /* Check parameters */
 
@@ -1490,25 +1492,44 @@ LPVOID WINAPI MapViewOfFileEx(
         return NULL;
     }
 
-    req->handle = handle;
-    if (server_call_fd( REQ_GET_MAPPING_INFO, -1, &unix_handle )) goto error;
-    prot = req->protect;
+    SERVER_START_REQ
+    {
+        struct get_mapping_info_request *req = server_alloc_req( sizeof(*req), 0 );
+        req->handle = handle;
+        res = server_call( REQ_GET_MAPPING_INFO );
+        prot        = req->protect;
+        base        = req->base;
+        size_low    = req->size_low;
+        size_high   = req->size_high;
+        header_size = req->header_size;
+        shared_file = req->shared_file;
+        shared_size = req->shared_size;
+        anonymous   = req->anonymous;
+    }
+    SERVER_END_REQ;
+    if (res) goto error;
+
+    if (!anonymous)
+    {
+        if ((unix_handle = FILE_GetUnixHandle( handle, 0 )) == -1) goto error;
+    }
 
     if (prot & VPROT_IMAGE)
-        return map_image( handle, unix_handle, req->base, req->size_low, req->header_size,
-                          req->shared_file, req->shared_size );
+        return map_image( handle, unix_handle, base, size_low, header_size,
+                          shared_file, shared_size );
+
 
-    if (req->size_high || offset_high)
+    if (size_high || offset_high)
         ERR("Offsets larger than 4Gb not supported\n");
 
-    if ((offset_low >= req->size_low) ||
-        (count > req->size_low - offset_low))
+    if ((offset_low >= size_low) ||
+        (count > size_low - offset_low))
     {
         SetLastError( ERROR_INVALID_PARAMETER );
         goto error;
     }
     if (count) size = ROUND_SIZE( offset_low, count );
-    else size = req->size_low - offset_low;
+    else size = size_low - offset_low;
 
     switch(access)
     {
diff --git a/scheduler/client.c b/scheduler/client.c
index a936f73fdd3..607e5661322 100644
--- a/scheduler/client.c
+++ b/scheduler/client.c
@@ -157,7 +157,7 @@ static void send_request( enum request req, struct request_header *header )
     header->req = req;
     NtCurrentTeb()->buffer_info->cur_req = (char *)header - (char *)NtCurrentTeb()->buffer;
     /* write a single byte; the value is ignored anyway */
-    if (write( NtCurrentTeb()->socket, header, 1 ) == -1)
+    if (write( NtCurrentTeb()->request_fd, header, 1 ) == -1)
     {
         if (errno == EPIPE) SYSDEPS_ExitThread(0);
         server_perror( "sendmsg" );
@@ -220,7 +220,7 @@ static void wait_reply(void)
 
     for (;;)
     {
-        if ((ret = read( NtCurrentTeb()->socket, dummy, 1 )) > 0) return;
+        if ((ret = read( NtCurrentTeb()->reply_fd, dummy, 1 )) > 0) return;
         if (!ret) break;
         if (errno == EINTR) continue;
         if (errno == EPIPE) break;
@@ -232,22 +232,90 @@ static void wait_reply(void)
 
 
 /***********************************************************************
- *           wait_reply_fd
+ *           wine_server_call
+ *
+ * Perform a server call.
+ */
+unsigned int wine_server_call( enum request req )
+{
+    void *req_ptr = get_req_buffer();
+    send_request( req, req_ptr );
+    wait_reply();
+    return ((struct request_header *)req_ptr)->error;
+}
+
+
+/***********************************************************************
+ *           server_call_fd
  *
- * Wait for a reply from the server, when a file descriptor is passed.
+ * Perform a server call, passing a file descriptor.
  */
-static void wait_reply_fd( int *fd )
+unsigned int server_call_fd( enum request req, int fd_out )
+{
+    unsigned int res;
+    void *req_ptr = get_req_buffer();
+
+    send_request_fd( req, req_ptr, fd_out );
+    wait_reply();
+
+    if ((res = ((struct request_header *)req_ptr)->error))
+        SetLastError( RtlNtStatusToDosError(res) );
+    return res;  /* error code */
+}
+
+
+/***********************************************************************
+ *           set_handle_fd
+ *
+ * Store the fd for a given handle in the server
+ */
+static int set_handle_fd( int handle, int fd )
+{
+    SERVER_START_REQ
+    {
+        struct set_handle_info_request *req = wine_server_alloc_req( sizeof(*req), 0 );
+        req->handle = handle;
+        req->flags  = 0;
+        req->mask   = 0;
+        req->fd     = fd;
+        if (!server_call( REQ_SET_HANDLE_INFO ))
+        {
+            if (req->cur_fd != fd)
+            {
+                /* someone was here before us */
+                close( fd );
+                fd = req->cur_fd;
+            }
+            else fcntl( fd, F_SETFD, 1 ); /* set close on exec flag */
+        }
+        else
+        {
+            close( fd );
+            fd = -1;
+        }
+    }
+    SERVER_END_REQ;
+    return fd;
+}
+
+
+/***********************************************************************
+ *           wine_server_recv_fd
+ *
+ * Receive a file descriptor passed from the server.
+ * The file descriptor must be closed after use.
+ */
+int wine_server_recv_fd( int handle, int cache )
 {
     struct iovec vec;
-    int ret;
-    char dummy[1];
+    int ret, fd, server_handle;
 
 #ifdef HAVE_MSGHDR_ACCRIGHTS
     struct msghdr msghdr;
 
-    *fd = -1;
-    msghdr.msg_accrights    = (void *)fd;
-    msghdr.msg_accrightslen = sizeof(*fd);
+    fd = -1;
+    msghdr.msg_accrights    = (void *)&fd;
+    msghdr.msg_accrightslen = sizeof(fd);
 #else  /* HAVE_MSGHDR_ACCRIGHTS */
     struct msghdr msghdr;
     struct cmsg_fd cmsg;
@@ -265,17 +333,25 @@ static void wait_reply_fd( int *fd )
     msghdr.msg_namelen = 0;
     msghdr.msg_iov     = &vec;
     msghdr.msg_iovlen  = 1;
-    vec.iov_base = (void *)dummy;
-    vec.iov_len  = 1;
+    vec.iov_base = (void *)&server_handle;
+    vec.iov_len  = sizeof(server_handle);
 
     for (;;)
     {
         if ((ret = recvmsg( NtCurrentTeb()->socket, &msghdr, 0 )) > 0)
         {
 #ifndef HAVE_MSGHDR_ACCRIGHTS
-            *fd = cmsg.fd;
+            fd = cmsg.fd;
 #endif
-            return;
+            if (handle != server_handle)
+                server_protocol_error( "recv_fd: got handle %d, expected %d\n",
+                                       server_handle, handle );
+            if (cache)
+            {
+                fd = set_handle_fd( handle, fd );
+                if (fd != -1) fd = dup(fd);
+            }
+            return fd;
         }
         if (!ret) break;
         if (errno == EINTR) continue;
@@ -287,44 +363,6 @@ static void wait_reply_fd( int *fd )
 }
 
 
-/***********************************************************************
- *           wine_server_call
- *
- * Perform a server call.
- */
-unsigned int wine_server_call( enum request req )
-{
-    void *req_ptr = get_req_buffer();
-    send_request( req, req_ptr );
-    wait_reply();
-    return ((struct request_header *)req_ptr)->error;
-}
-
-
-/***********************************************************************
- *           server_call_fd
- *
- * Perform a server call, passing a file descriptor.
- * If *fd is != -1, it will be passed to the server.
- * If the server passes an fd, it will be stored into *fd.
- */
-unsigned int server_call_fd( enum request req, int fd_out, int *fd_in )
-{
-    unsigned int res;
-    void *req_ptr = get_req_buffer();
-
-    if (fd_out == -1) send_request( req, req_ptr );
-    else send_request_fd( req, req_ptr, fd_out );
-
-    if (fd_in) wait_reply_fd( fd_in );
-    else wait_reply();
-
-    if ((res = ((struct request_header *)req_ptr)->error))
-        SetLastError( RtlNtStatusToDosError(res) );
-    return res;  /* error code */
-}
-
-
 /***********************************************************************
  *           get_config_dir
  *
@@ -564,8 +602,16 @@ int CLIENT_InitThread(void)
     /* ignore SIGPIPE so that we get a EPIPE error instead  */
     signal( SIGPIPE, SIG_IGN );
 
-    wait_reply_fd( &fd );
-    if (fd == -1) server_protocol_error( "no fd passed on first request\n" );
+    teb->request_fd = wine_server_recv_fd( -1, 0 );
+    if (teb->request_fd == -1) server_protocol_error( "no request fd passed on first request\n" );
+    fcntl( teb->request_fd, F_SETFD, 1 ); /* set close on exec flag */
+
+    teb->reply_fd = wine_server_recv_fd( -1, 0 );
+    if (teb->reply_fd == -1) server_protocol_error( "no reply fd passed on first request\n" );
+    fcntl( teb->reply_fd, F_SETFD, 1 ); /* set close on exec flag */
+
+    fd = wine_server_recv_fd( -1, 0 );
+    if (fd == -1) server_protocol_error( "no fd received for thread buffer\n" );
 
     if ((size = lseek( fd, 0, SEEK_END )) == -1) server_perror( "lseek" );
     teb->buffer = mmap( 0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
@@ -573,6 +619,8 @@ int CLIENT_InitThread(void)
     if (teb->buffer == (void*)-1) server_perror( "mmap" );
     teb->buffer_info = (struct server_buffer_info *)((char *)teb->buffer + size) - 1;
 
+    wait_reply();
+
     req = (struct get_thread_buffer_request *)teb->buffer;
     teb->pid = req->pid;
     teb->tid = req->tid;
@@ -597,6 +645,7 @@ int CLIENT_InitThread(void)
     return ret;
 }
 
+
 /***********************************************************************
  *           CLIENT_BootDone
  *
diff --git a/scheduler/handle.c b/scheduler/handle.c
index 21c60213a69..f60d4b851d8 100644
--- a/scheduler/handle.c
+++ b/scheduler/handle.c
@@ -6,6 +6,7 @@
 
 #include <assert.h>
 #include <stdio.h>
+#include <unistd.h>
 #include "winbase.h"
 #include "server.h"
 #include "winerror.h"
@@ -40,10 +41,13 @@ BOOL WINAPI GetHandleInformation( HANDLE handle, LPDWORD flags )
     BOOL ret;
     SERVER_START_REQ
     {
-        struct get_handle_info_request *req = server_alloc_req( sizeof(*req), 0 );
+        struct set_handle_info_request *req = server_alloc_req( sizeof(*req), 0 );
         req->handle = handle;
-        ret = !server_call( REQ_GET_HANDLE_INFO );
-        if (ret && flags) *flags = req->flags;
+        req->flags  = 0;
+        req->mask   = 0;
+        req->fd     = -1;
+        ret = !server_call( REQ_SET_HANDLE_INFO );
+        if (ret && flags) *flags = req->old_flags;
     }
     SERVER_END_REQ;
     return ret;
@@ -62,6 +66,7 @@ BOOL WINAPI SetHandleInformation( HANDLE handle, DWORD mask, DWORD flags )
         req->handle = handle;
         req->flags  = flags;
         req->mask   = mask;
+        req->fd     = -1;
         ret = !server_call( REQ_SET_HANDLE_INFO );
     }
     SERVER_END_REQ;
@@ -89,7 +94,11 @@ BOOL WINAPI DuplicateHandle( HANDLE source_process, HANDLE source,
         req->options     = options;
 
         ret = !server_call( REQ_DUP_HANDLE );
-        if (ret && dest) *dest = req->handle;
+        if (ret)
+        {
+            if (dest) *dest = req->handle;
+            if (req->fd != -1) close( req->fd );
+        }
     }
     SERVER_END_REQ;
     return ret;
diff --git a/scheduler/thread.c b/scheduler/thread.c
index e8942925e20..afc1a117a98 100644
--- a/scheduler/thread.c
+++ b/scheduler/thread.c
@@ -91,7 +91,9 @@ static BOOL THREAD_InitTEB( TEB *teb )
     teb->tls_ptr   = teb->tls_array;
     teb->exit_code = STILL_ACTIVE;
     teb->socket    = -1;
-    teb->stack_top = (void *)~0UL;
+    teb->request_fd = -1;
+    teb->reply_fd   = -1;
+    teb->stack_top  = (void *)~0UL;
     teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer);
     teb->StaticUnicodeString.Buffer = (PWSTR)teb->StaticUnicodeBuffer;
     teb->teb_sel = SELECTOR_AllocBlock( teb, 0x1000, WINE_LDT_FLAGS_DATA|WINE_LDT_FLAGS_32BIT );
@@ -113,6 +115,8 @@ static void CALLBACK THREAD_FreeTEB( TEB *teb )
     /* Free the associated memory */
 
     if (teb->socket != -1) close( teb->socket );
+    close( NtCurrentTeb()->request_fd );
+    close( NtCurrentTeb()->reply_fd );
     if (teb->stack_sel) FreeSelector16( teb->stack_sel );
     FreeSelector16( teb->teb_sel );
     if (teb->buffer) munmap( (void *)teb->buffer,
@@ -280,7 +284,7 @@ HANDLE WINAPI CreateThread( SECURITY_ATTRIBUTES *sa, DWORD stack,
                             LPTHREAD_START_ROUTINE start, LPVOID param,
                             DWORD flags, LPDWORD id )
 {
-    int socket, handle = -1;
+    int socket = -1, handle = -1;
     TEB *teb;
     void *tid = 0;
 
@@ -290,10 +294,11 @@ HANDLE WINAPI CreateThread( SECURITY_ATTRIBUTES *sa, DWORD stack,
 
         req->suspend = ((flags & CREATE_SUSPENDED) != 0);
         req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
-        if (!server_call_fd( REQ_NEW_THREAD, -1, &socket ))
+        if (!server_call( REQ_NEW_THREAD ))
         {
             handle = req->handle;
             tid = req->tid;
+            socket = wine_server_recv_fd( handle, 0 );
         }
     }
     SERVER_END_REQ;
diff --git a/server/console.c b/server/console.c
index 6f72ade2ac6..dcdc28722eb 100644
--- a/server/console.c
+++ b/server/console.c
@@ -419,7 +419,7 @@ DECL_HANDLER(alloc_console)
         if ((out = alloc_handle( current->process, current->process->console_out,
                                  req->access, req->inherit )) != -1)
             goto done;  /* everything is fine */
-        close_handle( current->process, in );
+        close_handle( current->process, in, NULL );
         in = -1;
     }
     free_console( current->process );
diff --git a/server/debugger.c b/server/debugger.c
index c88f82ef27e..7f84410b4e7 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -118,7 +118,7 @@ static int fill_create_process_event( struct debug_event *event, void *arg )
     /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
     if ((handle = alloc_handle( debugger, thread, THREAD_ALL_ACCESS, FALSE )) == -1)
     {
-        close_handle( debugger, event->data.info.create_process.process );
+        close_handle( debugger, event->data.info.create_process.process, NULL );
         return 0;
     }
     event->data.info.create_process.thread = handle;
@@ -128,8 +128,8 @@ static int fill_create_process_event( struct debug_event *event, void *arg )
         /* the doc says write access too, but this doesn't seem a good idea */
         ((handle = alloc_handle( debugger, process->exe.file, GENERIC_READ, FALSE )) == -1))
     {
-        close_handle( debugger, event->data.info.create_process.process );
-        close_handle( debugger, event->data.info.create_process.thread );
+        close_handle( debugger, event->data.info.create_process.process, NULL );
+        close_handle( debugger, event->data.info.create_process.thread, NULL );
         return 0;
     }
     event->data.info.create_process.file       = handle;
@@ -316,17 +316,17 @@ static void debug_event_destroy( struct object *obj )
         switch(event->data.code)
         {
         case CREATE_THREAD_DEBUG_EVENT:
-            close_handle( debugger, event->data.info.create_thread.handle );
+            close_handle( debugger, event->data.info.create_thread.handle, NULL );
             break;
         case CREATE_PROCESS_DEBUG_EVENT:
             if (event->data.info.create_process.file != -1)
-                close_handle( debugger, event->data.info.create_process.file );
-            close_handle( debugger, event->data.info.create_process.thread );
-            close_handle( debugger, event->data.info.create_process.process );
+                close_handle( debugger, event->data.info.create_process.file, NULL );
+            close_handle( debugger, event->data.info.create_process.thread, NULL );
+            close_handle( debugger, event->data.info.create_process.process, NULL );
             break;
         case LOAD_DLL_DEBUG_EVENT:
             if (event->data.info.load_dll.handle != -1)
-                close_handle( debugger, event->data.info.load_dll.handle );
+                close_handle( debugger, event->data.info.load_dll.handle, NULL );
             break;
         }
     }
diff --git a/server/file.c b/server/file.c
index 5c0f59a285e..e0ce8a76285 100644
--- a/server/file.c
+++ b/server/file.c
@@ -325,11 +325,6 @@ struct file *get_file_obj( struct process *process, int handle, unsigned int acc
     return (struct file *)get_handle_obj( process, handle, access, &file_ops );
 }
 
-int file_get_mmap_fd( struct file *file )
-{
-    return dup( file->obj.fd );
-}
-
 static int set_file_pointer( int handle, int *low, int *high, int whence )
 {
     struct file *file;
@@ -479,7 +474,8 @@ DECL_HANDLER(get_handle_fd)
     req->fd = -1;
     if ((obj = get_handle_obj( current->process, req->handle, req->access, NULL )))
     {
-        set_reply_fd( current, obj->ops->get_fd( obj ) );
+        if ((req->fd = get_handle_fd( current->process, req->handle, req->access )) == -1)
+            send_client_fd( current, obj->ops->get_fd( obj ), req->handle );
         release_object( obj );
     }
 }
diff --git a/server/handle.c b/server/handle.c
index 1d1317f7f89..6a3e1f613c3 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -19,8 +19,9 @@
 
 struct handle_entry
 {
-    struct object *ptr;
-    unsigned int   access;
+    struct object *ptr;       /* object */
+    unsigned int   access;    /* access rights */
+    int            fd;        /* file descriptor (in client process) */
 };
 
 struct handle_table
@@ -173,6 +174,7 @@ static int alloc_entry( struct handle_table *table, void *obj, unsigned int acce
     table->free = i + 1;
     entry->ptr    = grab_object( obj );
     entry->access = access;
+    entry->fd     = -1;
     return index_to_handle(i);
 }
 
@@ -269,6 +271,7 @@ struct object *copy_handle_table( struct process *process, struct process *paren
         for (i = 0; i <= table->last; i++, ptr++)
         {
             if (!ptr->ptr) continue;
+            ptr->fd = -1;
             if (ptr->access & RESERVED_INHERIT) grab_object( ptr->ptr );
             else ptr->ptr = NULL; /* don't inherit this entry */
         }
@@ -280,7 +283,7 @@ struct object *copy_handle_table( struct process *process, struct process *paren
 
 /* close a handle and decrement the refcount of the associated object */
 /* return 1 if OK, 0 on error */
-int close_handle( struct process *process, int handle )
+int close_handle( struct process *process, int handle, int *fd )
 {
     struct handle_table *table;
     struct handle_entry *entry;
@@ -294,6 +297,9 @@ int close_handle( struct process *process, int handle )
     }
     obj = entry->ptr;
     entry->ptr = NULL;
+    if (fd) *fd = entry->fd;
+    else if (entry->fd != -1) return 1;  /* silently ignore close attempt if we cannot close the fd */
+    entry->fd = -1;
     table = HANDLE_IS_GLOBAL(handle) ? global_table : (struct handle_table *)process->handles;
     if (entry < table->entries + table->free) table->free = entry - table->entries;
     if (entry == table->entries + table->last) shrink_handle_table( table );
@@ -351,11 +357,27 @@ struct object *get_handle_obj( struct process *process, int handle,
     return grab_object( obj );
 }
 
+/* retrieve the cached fd for a given handle */
+int get_handle_fd( struct process *process, int handle, unsigned int access )
+{
+    struct handle_entry *entry;
+
+    if (HANDLE_IS_GLOBAL(handle)) return -1;  /* no fd cache for global handles */
+    if (!(entry = get_handle( process, handle ))) return -1;
+    if ((entry->access & access) != access)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return -1;
+    }
+    return entry->fd;
+}
+
 /* get/set the handle reserved flags */
-/* return the new flags (or -1 on error) */
-static int set_handle_info( struct process *process, int handle, int mask, int flags )
+/* return the old flags (or -1 on error) */
+static int set_handle_info( struct process *process, int handle, int mask, int flags, int *fd )
 {
     struct handle_entry *entry;
+    unsigned int old_access;
 
     if (get_magic_handle( handle ))
     {
@@ -364,10 +386,14 @@ static int set_handle_info( struct process *process, int handle, int mask, int f
         return 0;
     }
     if (!(entry = get_handle( process, handle ))) return -1;
+    old_access = entry->access;
     mask  = (mask << RESERVED_SHIFT) & RESERVED_ALL;
     flags = (flags << RESERVED_SHIFT) & mask;
     entry->access = (entry->access & ~mask) | flags;
-    return (entry->access & RESERVED_ALL) >> RESERVED_SHIFT;
+    /* if no current fd set it, otherwise return current fd */
+    if (entry->fd == -1) entry->fd = *fd;
+    *fd = entry->fd;
+    return (old_access & RESERVED_ALL) >> RESERVED_SHIFT;
 }
 
 /* duplicate a handle */
@@ -420,19 +446,17 @@ int open_object( const WCHAR *name, size_t len, const struct object_ops *ops,
 /* close a handle */
 DECL_HANDLER(close_handle)
 {
-    close_handle( current->process, req->handle );
-}
-
-/* get information about a handle */
-DECL_HANDLER(get_handle_info)
-{
-    req->flags = set_handle_info( current->process, req->handle, 0, 0 );
+    close_handle( current->process, req->handle, &req->fd );
 }
 
 /* set a handle information */
 DECL_HANDLER(set_handle_info)
 {
-    set_handle_info( current->process, req->handle, req->mask, req->flags );
+    int fd = req->fd;
+
+    if (HANDLE_IS_GLOBAL(req->handle)) fd = -1;  /* no fd cache for global handles */
+    req->old_flags = set_handle_info( current->process, req->handle, req->mask, req->flags, &fd );
+    req->cur_fd = fd;
 }
 
 /* duplicate a handle */
@@ -441,6 +465,7 @@ DECL_HANDLER(dup_handle)
     struct process *src, *dst;
 
     req->handle = -1;
+    req->fd = -1;
     if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE )))
     {
         if (req->options & DUP_HANDLE_MAKE_GLOBAL)
@@ -456,7 +481,10 @@ DECL_HANDLER(dup_handle)
         }
         /* close the handle no matter what happened */
         if (req->options & DUP_HANDLE_CLOSE_SOURCE)
-            close_handle( src, req->src_handle );
+        {
+            if (src == current->process) close_handle( src, req->src_handle, &req->fd );
+            else close_handle( src, req->src_handle, NULL );
+        }
         release_object( src );
     }
 }
diff --git a/server/handle.h b/server/handle.h
index 7bba30b4861..d648dd7de66 100644
--- a/server/handle.h
+++ b/server/handle.h
@@ -23,9 +23,10 @@ struct object_ops;
 /* that the thing pointed to starts with a struct object... */
 extern int alloc_handle( struct process *process, void *obj,
                          unsigned int access, int inherit );
-extern int close_handle( struct process *process, int handle );
+extern int close_handle( struct process *process, int handle, int *fd );
 extern struct object *get_handle_obj( struct process *process, int handle,
                                       unsigned int access, const struct object_ops *ops );
+extern int get_handle_fd( struct process *process, int handle, unsigned int access );
 extern int duplicate_handle( struct process *src, int src_handle, struct process *dst,
                              unsigned int access, int inherit, int options );
 extern int open_object( const WCHAR *name, size_t len, const struct object_ops *ops,
diff --git a/server/mapping.c b/server/mapping.c
index a68e8fb8285..38d1d899bef 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -30,6 +30,7 @@ struct mapping
     int            shared_size;     /* shared mapping total size */
 };
 
+static int mapping_get_fd( struct object *obj );
 static void mapping_dump( struct object *obj, int verbose );
 static void mapping_destroy( struct object *obj );
 
@@ -43,7 +44,7 @@ static const struct object_ops mapping_ops =
     NULL,                        /* satisfied */
     NULL,                        /* get_poll_events */
     NULL,                        /* poll_event */
-    no_get_fd,                   /* get_fd */
+    mapping_get_fd,              /* get_fd */
     no_flush,                    /* flush */
     no_get_file_info,            /* get_file_info */
     mapping_destroy              /* destroy */
@@ -86,6 +87,13 @@ static void init_page_size(void)
 #define ROUND_SIZE(addr,size) \
    (((int)(size) + ((int)(addr) & page_mask) + page_mask) & ~page_mask)
 
+/* get the fd to use for mmaping a file */
+inline static int get_mmap_fd( struct file *file )
+{
+    struct object *obj;
+    if (!(obj = (struct object *)file)) return -1;
+    return obj->ops->get_fd( obj );
+}
 
 /* allocate and fill the temp file for a shared PE image mapping */
 static int build_shared_mapping( struct mapping *mapping, int fd,
@@ -115,7 +123,7 @@ static int build_shared_mapping( struct mapping *mapping, int fd,
 
     if (!(mapping->shared_file = create_temp_file( GENERIC_READ|GENERIC_WRITE ))) goto error;
     if (!grow_file( mapping->shared_file, 0, total_size )) goto error;
-    if ((shared_fd = file_get_mmap_fd( mapping->shared_file )) == -1) goto error;
+    if ((shared_fd = get_mmap_fd( mapping->shared_file )) == -1) goto error;
 
     if (!(buffer = malloc( max_size ))) goto error;
 
@@ -159,7 +167,7 @@ static int get_image_params( struct mapping *mapping )
 
     /* load the headers */
 
-    if ((fd = file_get_mmap_fd( mapping->file )) == -1) return 0;
+    if ((fd = get_mmap_fd( mapping->file )) == -1) return 0;
     filepos = lseek( fd, 0, SEEK_SET );
     if (read( fd, &dos, sizeof(dos) ) != sizeof(dos)) goto error;
     if (dos.e_magic != IMAGE_DOS_SIGNATURE) goto error;
@@ -270,6 +278,13 @@ static void mapping_dump( struct object *obj, int verbose )
     fputc( '\n', stderr );
 }
 
+static int mapping_get_fd( struct object *obj )
+{
+    struct mapping *mapping = (struct mapping *)obj;
+    assert( obj->ops == &mapping_ops );
+    return get_mmap_fd( mapping->file );
+}
+
 static void mapping_destroy( struct object *obj )
 {
     struct mapping *mapping = (struct mapping *)obj;
@@ -323,11 +338,10 @@ DECL_HANDLER(get_mapping_info)
         req->base        = mapping->base;
         req->shared_file = -1;
         req->shared_size = mapping->shared_size;
+        req->anonymous   = !mapping->file;
         if (mapping->shared_file)
             req->shared_file = alloc_handle( current->process, mapping->shared_file,
                                              GENERIC_READ|GENERIC_WRITE, 0 );
-        if (mapping->file) set_reply_fd( current, file_get_mmap_fd( mapping->file ) );
         release_object( mapping );
     }
 }
-
diff --git a/server/object.h b/server/object.h
index 7a7d1a3d013..deec306b32f 100644
--- a/server/object.h
+++ b/server/object.h
@@ -148,7 +148,6 @@ extern void abandon_mutexes( struct thread *thread );
 
 extern struct file *get_file_obj( struct process *process, int handle,
                                   unsigned int access );
-extern int file_get_mmap_fd( struct file *file );
 extern int grow_file( struct file *file, int size_high, int size_low );
 extern int create_anonymous_file(void);
 extern struct file *create_temp_file( int access );
diff --git a/server/pipe.c b/server/pipe.c
index 5c830c0897b..7838c12caf5 100644
--- a/server/pipe.c
+++ b/server/pipe.c
@@ -165,7 +165,7 @@ DECL_HANDLER(create_pipe)
                                    STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|GENERIC_WRITE,
                                    req->inherit );
             if (hwrite == -1)
-                close_handle( current->process, hread );
+                close_handle( current->process, hread, NULL );
         }
         release_object( obj[0] );
         release_object( obj[1] );
diff --git a/server/request.c b/server/request.c
index c3928e5ccb0..fff02dd4531 100644
--- a/server/request.c
+++ b/server/request.c
@@ -71,6 +71,32 @@ static const struct object_ops master_socket_ops =
 };
 
 
+struct request_socket
+{
+    struct object       obj;         /* object header */
+    struct thread      *thread;      /* owning thread */
+};
+
+static void request_socket_dump( struct object *obj, int verbose );
+static void request_socket_poll_event( struct object *obj, int event );
+
+static const struct object_ops request_socket_ops =
+{
+    sizeof(struct request_socket), /* size */
+    request_socket_dump,           /* dump */
+    no_add_queue,                  /* add_queue */
+    NULL,                          /* remove_queue */
+    NULL,                          /* signaled */
+    NULL,                          /* satisfied */
+    NULL,                          /* get_poll_events */
+    request_socket_poll_event,     /* poll_event */
+    no_get_fd,                     /* get_fd */
+    no_flush,                      /* flush */
+    no_get_file_info,              /* get_file_info */
+    no_destroy                     /* destroy */
+};
+
+
 struct thread *current = NULL;  /* thread handling the current request */
 unsigned int global_error = 0;  /* global error code for when no thread is current */
 
@@ -149,13 +175,6 @@ static inline void call_req_handler( struct thread *thread )
     fatal_protocol_error( current, "bad request %d\n", req );
 }
 
-/* set the fd to pass to the thread */
-void set_reply_fd( struct thread *thread, int pass_fd )
-{
-    assert( thread->pass_fd == -1 );
-    thread->pass_fd = pass_fd;
-}
-
 /* send a reply to a thread */
 void send_reply( struct thread *thread )
 {
@@ -213,35 +232,49 @@ int write_request( struct thread *thread )
 
     header->error = thread->error;
 
-    if (thread->pass_fd == -1)
+    assert (thread->pass_fd == -1);
+
+    ret = write( thread->reply_fd, header, 1 );
+    if (ret > 0)
     {
-        /* write a single byte; the value is ignored anyway */
-        ret = write( thread->obj.fd, header, 1 );
+        set_select_events( &thread->obj, POLLIN );
+        return 1;
     }
-    else  /* we have an fd to send */
+    if (errno == EWOULDBLOCK) return 0;  /* not a fatal error */
+    if (errno == EPIPE)
     {
+        kill_thread( thread, 0 );  /* normal death */
+    }
+    else
+    {
+        perror("sendmsg");
+        thread->exit_code = 1;
+        kill_thread( thread, 1 );
+    }
+    return -1;
+}
+
+/* send an fd to a client */
+int send_client_fd( struct thread *thread, int fd, int handle )
+{
+    int ret;
+
 #ifdef HAVE_MSGHDR_ACCRIGHTS
-        msghdr.msg_accrightslen = sizeof(int);
-        msghdr.msg_accrights = (void *)&thread->pass_fd;
+    msghdr.msg_accrightslen = sizeof(fd);
+    msghdr.msg_accrights = (void *)&fd;
 #else  /* HAVE_MSGHDR_ACCRIGHTS */
-        msghdr.msg_control    = &cmsg;
-        msghdr.msg_controllen = sizeof(cmsg);
-        cmsg.fd = thread->pass_fd;
+    msghdr.msg_control    = &cmsg;
+    msghdr.msg_controllen = sizeof(cmsg);
+    cmsg.fd = fd;
 #endif  /* HAVE_MSGHDR_ACCRIGHTS */
 
-        myiovec.iov_base = (void *)header;
-        myiovec.iov_len  = 1;
+    myiovec.iov_base = (void *)&handle;
+    myiovec.iov_len  = sizeof(handle);
 
-        ret = sendmsg( thread->obj.fd, &msghdr, 0 );
-        close( thread->pass_fd );
-        thread->pass_fd = -1;
-    }
-    if (ret > 0)
-    {
-        set_select_events( &thread->obj, POLLIN );
-        return 1;
-    }
-    if (errno == EWOULDBLOCK) return 0;  /* not a fatal error */
+    ret = sendmsg( thread->obj.fd, &msghdr, 0 );
+    close( fd );
+
+    if (ret > 0) return 0;
     if (errno == EPIPE)
     {
         kill_thread( thread, 0 );  /* normal death */
@@ -297,6 +330,61 @@ static void master_socket_destroy( struct object *obj )
     socket_cleanup();
 }
 
+static void request_socket_dump( struct object *obj, int verbose )
+{
+    struct request_socket *sock = (struct request_socket *)obj;
+    assert( obj->ops == &request_socket_ops );
+    fprintf( stderr, "Request socket fd=%d thread=%p\n", sock->obj.fd, sock->thread );
+}
+
+/* handle a request socket event */
+static void request_socket_poll_event( struct object *obj, int event )
+{
+    struct request_socket *sock = (struct request_socket *)obj;
+    assert( obj->ops == &request_socket_ops );
+
+    if (event & (POLLERR | POLLHUP)) kill_thread( sock->thread, 0 );
+    else if (event & POLLIN)
+    {
+        struct thread *thread = sock->thread;
+        int ret;
+        char dummy[1];
+
+        ret = read( sock->obj.fd, &dummy, 1 );
+        if (ret > 0)
+        {
+            call_req_handler( thread );
+            return;
+        }
+        if (!ret)  /* closed pipe */
+        {
+            kill_thread( thread, 0 );
+            return;
+        }
+        perror("read");
+        thread->exit_code = 1;
+        kill_thread( thread, 1 );
+    }
+}
+
+/* create a request socket and send the fd to the client thread */
+struct object *create_request_socket( struct thread *thread )
+{
+    struct request_socket *sock;
+    int fd[2];
+
+    if (pipe( fd )) return NULL;
+    if (!(sock = alloc_object( &request_socket_ops, fd[0] )))
+    {
+        close( fd[1] );
+        return NULL;
+    }
+    sock->thread = thread;
+    send_client_fd( thread, fd[1], -1 );
+    set_select_events( &sock->obj, POLLIN );
+    return &sock->obj;
+}
+
 /* return the configuration directory ($WINEPREFIX or $HOME/.wine) */
 const char *get_config_dir(void)
 {
diff --git a/server/request.h b/server/request.h
index ef856f31d6d..c58d5fbccb2 100644
--- a/server/request.h
+++ b/server/request.h
@@ -33,11 +33,12 @@ extern void fatal_perror( const char *err, ... ) WINE_NORETURN;
 extern const char *get_config_dir(void);
 extern void read_request( struct thread *thread );
 extern int write_request( struct thread *thread );
-extern void set_reply_fd( struct thread *thread, int pass_fd );
+extern int send_client_fd( struct thread *thread, int fd, int handle );
 extern void send_reply( struct thread *thread );
 extern void open_master_socket(void);
 extern void close_master_socket(void);
 extern void lock_master_socket( int locked );
+extern struct object *create_request_socket( struct thread *thread );
 
 extern void trace_request( enum request req );
 extern void trace_reply( struct thread *thread );
@@ -90,7 +91,6 @@ DECL_HANDLER(unload_dll);
 DECL_HANDLER(queue_apc);
 DECL_HANDLER(get_apc);
 DECL_HANDLER(close_handle);
-DECL_HANDLER(get_handle_info);
 DECL_HANDLER(set_handle_info);
 DECL_HANDLER(dup_handle);
 DECL_HANDLER(open_process);
@@ -205,7 +205,6 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_queue_apc,
     (req_handler)req_get_apc,
     (req_handler)req_close_handle,
-    (req_handler)req_get_handle_info,
     (req_handler)req_set_handle_info,
     (req_handler)req_dup_handle,
     (req_handler)req_open_process,
diff --git a/server/thread.c b/server/thread.c
index 7cfacdc2dad..12e937716e1 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -89,26 +89,33 @@ static struct thread *booting_thread;
 static int alloc_client_buffer( struct thread *thread )
 {
     struct get_thread_buffer_request *req;
-    int fd;
+    int fd, fd_pipe[2];
 
-    if ((fd = create_anonymous_file()) == -1) return -1;
+    if (pipe( fd_pipe ) == -1) return -1;
+    if ((fd = create_anonymous_file()) == -1) goto error;
     if (ftruncate( fd, MAX_REQUEST_LENGTH ) == -1) goto error;
     if ((thread->buffer = mmap( 0, MAX_REQUEST_LENGTH, PROT_READ | PROT_WRITE,
                                 MAP_SHARED, fd, 0 )) == (void*)-1) goto error;
     thread->buffer_info = (struct server_buffer_info *)((char *)thread->buffer + MAX_REQUEST_LENGTH) - 1;
+    if (!(thread->request_fd = create_request_socket( thread ))) goto error;
+    thread->reply_fd = fd_pipe[1];
     /* build the first request into the buffer and send it */
     req = thread->buffer;
     req->pid  = get_process_id( thread->process );
     req->tid  = get_thread_id( thread );
     req->boot = (thread == booting_thread);
     req->version = SERVER_PROTOCOL_VERSION;
-    set_reply_fd( thread, fd );
+
+    send_client_fd( thread, fd_pipe[0], -1 );
+    send_client_fd( thread, fd, -1 );
     send_reply( thread );
     return 1;
 
  error:
     file_set_error();
     if (fd != -1) close( fd );
+    close( fd_pipe[0] );
+    close( fd_pipe[1] );
     return 0;
 }
 
@@ -135,6 +142,8 @@ struct thread *create_thread( int fd, struct process *process )
     thread->apc_tail    = NULL;
     thread->error       = 0;
     thread->pass_fd     = -1;
+    thread->request_fd  = NULL;
+    thread->reply_fd    = -1;
     thread->state       = RUNNING;
     thread->attached    = 0;
     thread->exit_code   = 0;
@@ -197,7 +206,9 @@ static void destroy_thread( struct object *obj )
     if (thread->info) release_object( thread->info );
     if (thread->queue) release_object( thread->queue );
     if (thread->buffer != (void *)-1) munmap( thread->buffer, MAX_REQUEST_LENGTH );
+    if (thread->reply_fd != -1) close( thread->reply_fd );
     if (thread->pass_fd != -1) close( thread->pass_fd );
+    if (thread->request_fd) release_object( thread->request_fd );
 }
 
 /* dump a thread on stdout for debugging purposes */
@@ -618,7 +629,11 @@ void kill_thread( struct thread *thread, int violent_death )
     wake_up( &thread->obj, 0 );
     detach_thread( thread, violent_death ? SIGTERM : 0 );
     remove_select_user( &thread->obj );
+    release_object( thread->request_fd );
+    close( thread->reply_fd );
     munmap( thread->buffer, MAX_REQUEST_LENGTH );
+    thread->request_fd = NULL;
+    thread->reply_fd = -1;
     thread->buffer = (void *)-1;
     release_object( thread );
 }
@@ -675,7 +690,7 @@ DECL_HANDLER(new_thread)
             if ((req->handle = alloc_handle( current->process, thread,
                                              THREAD_ALL_ACCESS, req->inherit )) != -1)
             {
-                set_reply_fd( current, sock[1] );
+                send_client_fd( current, sock[1], req->handle );
                 /* thread object will be released when the thread gets killed */
                 add_process_thread( current->process, thread );
                 return;
diff --git a/server/thread.h b/server/thread.h
index c0db4819b46..8e365692ca8 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -48,7 +48,9 @@ struct thread
     struct thread_apc  *apc_head;    /* queue of async procedure calls */
     struct thread_apc  *apc_tail;    /* queue of async procedure calls */
     unsigned int        error;       /* current error code */
-    int                 pass_fd;   /* fd to pass to the client */
+    struct object      *request_fd;  /* fd for receiving client requests */
+    int                 pass_fd;     /* fd to pass to the client */
+    int                 reply_fd;    /* fd to use to wake a client waiting on a reply */
     enum run_state      state;     /* running state */
     int                 attached;  /* is thread attached with ptrace? */
     int                 exit_code; /* thread exit code */
diff --git a/server/trace.c b/server/trace.c
index bad07e832f8..534f4d43e24 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -471,21 +471,23 @@ static void dump_close_handle_request( const struct close_handle_request *req )
     fprintf( stderr, " handle=%d", req->handle );
 }
 
-static void dump_get_handle_info_request( const struct get_handle_info_request *req )
+static void dump_close_handle_reply( const struct close_handle_request *req )
 {
-    fprintf( stderr, " handle=%d", req->handle );
-}
-
-static void dump_get_handle_info_reply( const struct get_handle_info_request *req )
-{
-    fprintf( stderr, " flags=%d", req->flags );
+    fprintf( stderr, " fd=%d", req->fd );
 }
 
 static void dump_set_handle_info_request( const struct set_handle_info_request *req )
 {
     fprintf( stderr, " handle=%d,", req->handle );
     fprintf( stderr, " flags=%d,", req->flags );
-    fprintf( stderr, " mask=%d", req->mask );
+    fprintf( stderr, " mask=%d,", req->mask );
+    fprintf( stderr, " fd=%d", req->fd );
+}
+
+static void dump_set_handle_info_reply( const struct set_handle_info_request *req )
+{
+    fprintf( stderr, " old_flags=%d,", req->old_flags );
+    fprintf( stderr, " cur_fd=%d", req->cur_fd );
 }
 
 static void dump_dup_handle_request( const struct dup_handle_request *req )
@@ -500,7 +502,8 @@ static void dump_dup_handle_request( const struct dup_handle_request *req )
 
 static void dump_dup_handle_reply( const struct dup_handle_request *req )
 {
-    fprintf( stderr, " handle=%d", req->handle );
+    fprintf( stderr, " handle=%d,", req->handle );
+    fprintf( stderr, " fd=%d", req->fd );
 }
 
 static void dump_open_process_request( const struct open_process_request *req )
@@ -957,7 +960,8 @@ static void dump_get_mapping_info_reply( const struct get_mapping_info_request *
     fprintf( stderr, " header_size=%d,", req->header_size );
     fprintf( stderr, " base=%p,", req->base );
     fprintf( stderr, " shared_file=%d,", req->shared_file );
-    fprintf( stderr, " shared_size=%d", req->shared_size );
+    fprintf( stderr, " shared_size=%d,", req->shared_size );
+    fprintf( stderr, " anonymous=%d", req->anonymous );
 }
 
 static void dump_create_device_request( const struct create_device_request *req )
@@ -1471,7 +1475,6 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_queue_apc_request,
     (dump_func)dump_get_apc_request,
     (dump_func)dump_close_handle_request,
-    (dump_func)dump_get_handle_info_request,
     (dump_func)dump_set_handle_info_request,
     (dump_func)dump_dup_handle_request,
     (dump_func)dump_open_process_request,
@@ -1582,9 +1585,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)0,
     (dump_func)0,
     (dump_func)dump_get_apc_reply,
-    (dump_func)0,
-    (dump_func)dump_get_handle_info_reply,
-    (dump_func)0,
+    (dump_func)dump_close_handle_reply,
+    (dump_func)dump_set_handle_info_reply,
     (dump_func)dump_dup_handle_reply,
     (dump_func)dump_open_process_reply,
     (dump_func)dump_select_reply,
@@ -1695,7 +1697,6 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
     "queue_apc",
     "get_apc",
     "close_handle",
-    "get_handle_info",
     "set_handle_info",
     "dup_handle",
     "open_process",
-- 
GitLab