Commit 28418cc9 authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Maintain a file descriptor cache on the client side.

parent 25800801
...@@ -1093,7 +1093,7 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc ...@@ -1093,7 +1093,7 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
{ {
req->handle = handle; req->handle = handle;
io->u.Status = wine_server_call(req); io->u.Status = wine_server_call(req);
if (!io->u.Status && reply->fd != -1) close(reply->fd); if (!io->u.Status) server_remove_fd_from_cache( handle );
} }
SERVER_END_REQ; SERVER_END_REQ;
break; break;
......
...@@ -61,6 +61,7 @@ extern void DECLSPEC_NORETURN server_protocol_error( const char *err, ... ); ...@@ -61,6 +61,7 @@ extern void DECLSPEC_NORETURN server_protocol_error( const char *err, ... );
extern void DECLSPEC_NORETURN server_protocol_perror( const char *err ); extern void DECLSPEC_NORETURN server_protocol_perror( const char *err );
extern void DECLSPEC_NORETURN server_exit_thread( int status ); extern void DECLSPEC_NORETURN server_exit_thread( int status );
extern void DECLSPEC_NORETURN server_abort_thread( int status ); extern void DECLSPEC_NORETURN server_abort_thread( int status );
extern int server_remove_fd_from_cache( obj_handle_t handle );
/* module handling */ /* module handling */
extern NTSTATUS MODULE_DllThreadAttach( LPVOID lpReserved ); extern NTSTATUS MODULE_DllThreadAttach( LPVOID lpReserved );
......
...@@ -312,7 +312,10 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, ...@@ -312,7 +312,10 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source,
if (!(ret = wine_server_call( req ))) if (!(ret = wine_server_call( req )))
{ {
if (dest) *dest = reply->handle; if (dest) *dest = reply->handle;
if (reply->fd != -1) close( reply->fd ); if (reply->closed)
server_remove_fd_from_cache( source );
else if (options & DUPLICATE_CLOSE_SOURCE)
WARN( "failed to close handle %p in process %p\n", source, source_process );
} }
} }
SERVER_END_REQ; SERVER_END_REQ;
...@@ -338,7 +341,7 @@ NTSTATUS WINAPI NtClose( HANDLE Handle ) ...@@ -338,7 +341,7 @@ NTSTATUS WINAPI NtClose( HANDLE Handle )
{ {
req->handle = Handle; req->handle = Handle;
ret = wine_server_call( req ); ret = wine_server_call( req );
if (!ret && reply->fd != -1) close( reply->fd ); if (!ret) server_remove_fd_from_cache( Handle );
} }
SERVER_END_REQ; SERVER_END_REQ;
return ret; return ret;
......
...@@ -438,6 +438,92 @@ static int receive_fd( obj_handle_t *handle ) ...@@ -438,6 +438,92 @@ static int receive_fd( obj_handle_t *handle )
} }
inline static unsigned int handle_to_index( obj_handle_t handle )
{
return ((unsigned long)handle >> 2) - 1;
}
static int *fd_cache;
static unsigned int fd_cache_size;
/***********************************************************************
* add_fd_to_cache
*
* Caller must hold fd_cache_section.
*/
static int add_fd_to_cache( obj_handle_t handle, int fd )
{
unsigned int idx = handle_to_index( handle );
if (idx >= fd_cache_size)
{
unsigned int i, size = max( 32, fd_cache_size * 2 );
int *new_cache;
if (size <= idx) size = idx + 1;
if (fd_cache)
new_cache = RtlReAllocateHeap( GetProcessHeap(), 0, fd_cache, size*sizeof(fd_cache[0]) );
else
new_cache = RtlAllocateHeap( GetProcessHeap(), 0, size*sizeof(fd_cache[0]) );
if (new_cache)
{
for (i = fd_cache_size; i < size; i++) new_cache[i] = -1;
fd_cache = new_cache;
fd_cache_size = size;
}
}
if (idx < fd_cache_size)
{
assert( fd_cache[idx] == -1 );
fd_cache[idx] = fd;
TRACE("added %p (%d) to cache\n", handle, fd );
return 1;
}
return 0;
}
/***********************************************************************
* get_cached_fd
*
* Caller must hold fd_cache_section.
*/
static inline int get_cached_fd( obj_handle_t handle )
{
unsigned int idx = handle_to_index( handle );
int fd = -1;
if (idx < fd_cache_size) fd = fd_cache[idx];
return fd;
}
/***********************************************************************
* server_remove_fd_from_cache
*/
int server_remove_fd_from_cache( obj_handle_t handle )
{
unsigned int idx = handle_to_index( handle );
int fd = -1;
RtlEnterCriticalSection( &fd_cache_section );
if (idx < fd_cache_size)
{
fd = fd_cache[idx];
fd_cache[idx] = -1;
}
RtlLeaveCriticalSection( &fd_cache_section );
if (fd != -1)
{
close( fd );
TRACE("removed %p (%d) from cache\n", handle, fd );
}
return fd;
}
/*********************************************************************** /***********************************************************************
* wine_server_fd_to_handle (NTDLL.@) * wine_server_fd_to_handle (NTDLL.@)
* *
...@@ -488,20 +574,27 @@ int wine_server_fd_to_handle( int fd, unsigned int access, unsigned int attribut ...@@ -488,20 +574,27 @@ int wine_server_fd_to_handle( int fd, unsigned int access, unsigned int attribut
int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *unix_fd, int *flags ) int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *unix_fd, int *flags )
{ {
obj_handle_t fd_handle; obj_handle_t fd_handle;
int ret, removable = -1, fd = -1; int ret = 0, removable = 0, fd = -1;
RtlEnterCriticalSection( &fd_cache_section ); RtlEnterCriticalSection( &fd_cache_section );
*unix_fd = -1; *unix_fd = -1;
fd = get_cached_fd( handle );
if (fd != -1 && !flags)
{
if ((fd = dup(fd)) == -1) ret = FILE_GetNtStatus();
goto done;
}
SERVER_START_REQ( get_handle_fd ) SERVER_START_REQ( get_handle_fd )
{ {
req->handle = handle; req->handle = handle;
req->access = access; req->access = access;
req->cached = (fd != -1);
if (!(ret = wine_server_call( req ))) if (!(ret = wine_server_call( req )))
{ {
fd = reply->fd; removable = reply->flags & FD_FLAG_REMOVABLE;
removable = reply->removable;
if (flags) *flags = reply->flags; if (flags) *flags = reply->flags;
} }
} }
...@@ -525,26 +618,10 @@ int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *uni ...@@ -525,26 +618,10 @@ int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *uni
if (removable) goto done; /* don't cache it */ if (removable) goto done; /* don't cache it */
/* and store it back into the cache */ if (add_fd_to_cache( handle, fd ))
SERVER_START_REQ( set_handle_fd )
{ {
req->handle = fd_handle; if ((fd = dup(fd)) == -1) ret = FILE_GetNtStatus();
req->fd = fd;
if (!(ret = wine_server_call( req )))
{
if (reply->cur_fd != -1) /* it has been cached */
{
if (reply->cur_fd != fd) close( fd ); /* someone was here before us */
if ((fd = dup(reply->cur_fd)) == -1) ret = FILE_GetNtStatus();
}
}
else
{
close( fd );
fd = -1;
}
} }
SERVER_END_REQ;
done: done:
RtlLeaveCriticalSection( &fd_cache_section ); RtlLeaveCriticalSection( &fd_cache_section );
......
...@@ -585,7 +585,7 @@ struct dup_handle_reply ...@@ -585,7 +585,7 @@ struct dup_handle_reply
{ {
struct reply_header __header; struct reply_header __header;
obj_handle_t handle; obj_handle_t handle;
int fd; int closed;
}; };
#define DUP_HANDLE_CLOSE_SOURCE DUPLICATE_CLOSE_SOURCE #define DUP_HANDLE_CLOSE_SOURCE DUPLICATE_CLOSE_SOURCE
#define DUP_HANDLE_SAME_ACCESS DUPLICATE_SAME_ACCESS #define DUP_HANDLE_SAME_ACCESS DUPLICATE_SAME_ACCESS
...@@ -840,12 +840,11 @@ struct get_handle_fd_request ...@@ -840,12 +840,11 @@ struct get_handle_fd_request
struct request_header __header; struct request_header __header;
obj_handle_t handle; obj_handle_t handle;
unsigned int access; unsigned int access;
int cached;
}; };
struct get_handle_fd_reply struct get_handle_fd_reply
{ {
struct reply_header __header; struct reply_header __header;
int fd;
int removable;
int flags; int flags;
}; };
#define FD_FLAG_OVERLAPPED 0x01 #define FD_FLAG_OVERLAPPED 0x01
...@@ -854,6 +853,7 @@ struct get_handle_fd_reply ...@@ -854,6 +853,7 @@ struct get_handle_fd_reply
#define FD_FLAG_SEND_SHUTDOWN 0x08 #define FD_FLAG_SEND_SHUTDOWN 0x08
#define FD_FLAG_AVAILABLE 0x10 /* in overlap read/write operation, #define FD_FLAG_AVAILABLE 0x10 /* in overlap read/write operation,
* only handle available data (don't wait) */ * only handle available data (don't wait) */
#define FD_FLAG_REMOVABLE 0x20
struct set_handle_fd_request struct set_handle_fd_request
...@@ -4425,6 +4425,6 @@ union generic_reply ...@@ -4425,6 +4425,6 @@ union generic_reply
struct query_symlink_reply query_symlink_reply; struct query_symlink_reply query_symlink_reply;
}; };
#define SERVER_PROTOCOL_VERSION 256 #define SERVER_PROTOCOL_VERSION 257
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
...@@ -1944,19 +1944,15 @@ DECL_HANDLER(get_handle_fd) ...@@ -1944,19 +1944,15 @@ DECL_HANDLER(get_handle_fd)
{ {
struct fd *fd; struct fd *fd;
reply->fd = -1;
if ((fd = get_handle_fd_obj( current->process, req->handle, req->access ))) if ((fd = get_handle_fd_obj( current->process, req->handle, req->access )))
{ {
int unix_fd = get_unix_fd( fd ); if (!req->cached)
if (unix_fd != -1)
{ {
int cached_fd = get_handle_unix_fd( current->process, req->handle, req->access ); int unix_fd = get_unix_fd( fd );
if (cached_fd != -1) reply->fd = cached_fd; if (unix_fd != -1) send_client_fd( current->process, unix_fd, req->handle );
else if (!get_error()) send_client_fd( current->process, unix_fd, req->handle );
} }
if (fd->inode) reply->removable = fd->inode->device->removable;
reply->flags = fd->fd_ops->get_file_info( fd ); reply->flags = fd->fd_ops->get_file_info( fd );
if (fd->inode && fd->inode->device->removable) reply->flags |= FD_FLAG_REMOVABLE;
release_object( fd ); release_object( fd );
} }
} }
......
...@@ -562,7 +562,6 @@ DECL_HANDLER(dup_handle) ...@@ -562,7 +562,6 @@ DECL_HANDLER(dup_handle)
struct process *src, *dst; struct process *src, *dst;
reply->handle = 0; reply->handle = 0;
reply->fd = -1;
if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE ))) if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE )))
{ {
if (req->options & DUP_HANDLE_MAKE_GLOBAL) if (req->options & DUP_HANDLE_MAKE_GLOBAL)
...@@ -579,8 +578,9 @@ DECL_HANDLER(dup_handle) ...@@ -579,8 +578,9 @@ DECL_HANDLER(dup_handle)
/* close the handle no matter what happened */ /* close the handle no matter what happened */
if (req->options & DUP_HANDLE_CLOSE_SOURCE) if (req->options & DUP_HANDLE_CLOSE_SOURCE)
{ {
if (src == current->process) close_handle( src, req->src_handle, &reply->fd ); unsigned int err = get_error(); /* don't overwrite error from the above calls */
else close_handle( src, req->src_handle, NULL ); reply->closed = close_handle( src, req->src_handle, NULL );
set_error( err );
} }
release_object( src ); release_object( src );
} }
......
...@@ -485,7 +485,7 @@ enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO }; ...@@ -485,7 +485,7 @@ enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO };
unsigned int options; /* duplicate options (see below) */ unsigned int options; /* duplicate options (see below) */
@REPLY @REPLY
obj_handle_t handle; /* duplicated handle in dst process */ obj_handle_t handle; /* duplicated handle in dst process */
int fd; /* associated fd to close */ int closed; /* whether the source handle has been closed */
@END @END
#define DUP_HANDLE_CLOSE_SOURCE DUPLICATE_CLOSE_SOURCE #define DUP_HANDLE_CLOSE_SOURCE DUPLICATE_CLOSE_SOURCE
#define DUP_HANDLE_SAME_ACCESS DUPLICATE_SAME_ACCESS #define DUP_HANDLE_SAME_ACCESS DUPLICATE_SAME_ACCESS
...@@ -661,9 +661,8 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; ...@@ -661,9 +661,8 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT };
@REQ(get_handle_fd) @REQ(get_handle_fd)
obj_handle_t handle; /* handle to the file */ obj_handle_t handle; /* handle to the file */
unsigned int access; /* wanted access rights */ unsigned int access; /* wanted access rights */
int cached; /* is it cached on the client already? */
@REPLY @REPLY
int fd; /* file descriptor */
int removable; /* is device removable? (-1 if unknown) */
int flags; /* file read/write flags (see below) */ int flags; /* file read/write flags (see below) */
@END @END
#define FD_FLAG_OVERLAPPED 0x01 /* fd opened in overlapped mode */ #define FD_FLAG_OVERLAPPED 0x01 /* fd opened in overlapped mode */
...@@ -672,6 +671,7 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; ...@@ -672,6 +671,7 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT };
#define FD_FLAG_SEND_SHUTDOWN 0x08 #define FD_FLAG_SEND_SHUTDOWN 0x08
#define FD_FLAG_AVAILABLE 0x10 /* in overlap read/write operation, #define FD_FLAG_AVAILABLE 0x10 /* in overlap read/write operation,
* only handle available data (don't wait) */ * only handle available data (don't wait) */
#define FD_FLAG_REMOVABLE 0x20 /* is it on a removable device? */
/* Set the cached file descriptor of a handle */ /* Set the cached file descriptor of a handle */
@REQ(set_handle_fd) @REQ(set_handle_fd)
......
...@@ -901,7 +901,7 @@ static void dump_dup_handle_request( const struct dup_handle_request *req ) ...@@ -901,7 +901,7 @@ static void dump_dup_handle_request( const struct dup_handle_request *req )
static void dump_dup_handle_reply( const struct dup_handle_reply *req ) static void dump_dup_handle_reply( const struct dup_handle_reply *req )
{ {
fprintf( stderr, " handle=%p,", req->handle ); fprintf( stderr, " handle=%p,", req->handle );
fprintf( stderr, " fd=%d", req->fd ); fprintf( stderr, " closed=%d", req->closed );
} }
static void dump_open_process_request( const struct open_process_request *req ) static void dump_open_process_request( const struct open_process_request *req )
...@@ -1103,13 +1103,12 @@ static void dump_alloc_file_handle_reply( const struct alloc_file_handle_reply * ...@@ -1103,13 +1103,12 @@ static void dump_alloc_file_handle_reply( const struct alloc_file_handle_reply *
static void dump_get_handle_fd_request( const struct get_handle_fd_request *req ) static void dump_get_handle_fd_request( const struct get_handle_fd_request *req )
{ {
fprintf( stderr, " handle=%p,", req->handle ); fprintf( stderr, " handle=%p,", req->handle );
fprintf( stderr, " access=%08x", req->access ); fprintf( stderr, " access=%08x,", req->access );
fprintf( stderr, " cached=%d", req->cached );
} }
static void dump_get_handle_fd_reply( const struct get_handle_fd_reply *req ) static void dump_get_handle_fd_reply( const struct get_handle_fd_reply *req )
{ {
fprintf( stderr, " fd=%d,", req->fd );
fprintf( stderr, " removable=%d,", req->removable );
fprintf( stderr, " flags=%d", req->flags ); fprintf( stderr, " flags=%d", req->flags );
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment