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
{
req->handle = handle;
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;
break;
......
......@@ -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_exit_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 */
extern NTSTATUS MODULE_DllThreadAttach( LPVOID lpReserved );
......
......@@ -312,7 +312,10 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source,
if (!(ret = wine_server_call( req )))
{
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;
......@@ -338,7 +341,7 @@ NTSTATUS WINAPI NtClose( HANDLE Handle )
{
req->handle = Handle;
ret = wine_server_call( req );
if (!ret && reply->fd != -1) close( reply->fd );
if (!ret) server_remove_fd_from_cache( Handle );
}
SERVER_END_REQ;
return ret;
......
......@@ -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.@)
*
......@@ -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 )
{
obj_handle_t fd_handle;
int ret, removable = -1, fd = -1;
int ret = 0, removable = 0, fd = -1;
RtlEnterCriticalSection( &fd_cache_section );
*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 )
{
req->handle = handle;
req->access = access;
req->cached = (fd != -1);
if (!(ret = wine_server_call( req )))
{
fd = reply->fd;
removable = reply->removable;
removable = reply->flags & FD_FLAG_REMOVABLE;
if (flags) *flags = reply->flags;
}
}
......@@ -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 */
/* and store it back into the cache */
SERVER_START_REQ( set_handle_fd )
{
req->handle = fd_handle;
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
if (add_fd_to_cache( handle, fd ))
{
close( fd );
fd = -1;
}
if ((fd = dup(fd)) == -1) ret = FILE_GetNtStatus();
}
SERVER_END_REQ;
done:
RtlLeaveCriticalSection( &fd_cache_section );
......
......@@ -585,7 +585,7 @@ struct dup_handle_reply
{
struct reply_header __header;
obj_handle_t handle;
int fd;
int closed;
};
#define DUP_HANDLE_CLOSE_SOURCE DUPLICATE_CLOSE_SOURCE
#define DUP_HANDLE_SAME_ACCESS DUPLICATE_SAME_ACCESS
......@@ -840,12 +840,11 @@ struct get_handle_fd_request
struct request_header __header;
obj_handle_t handle;
unsigned int access;
int cached;
};
struct get_handle_fd_reply
{
struct reply_header __header;
int fd;
int removable;
int flags;
};
#define FD_FLAG_OVERLAPPED 0x01
......@@ -854,6 +853,7 @@ struct get_handle_fd_reply
#define FD_FLAG_SEND_SHUTDOWN 0x08
#define FD_FLAG_AVAILABLE 0x10 /* in overlap read/write operation,
* only handle available data (don't wait) */
#define FD_FLAG_REMOVABLE 0x20
struct set_handle_fd_request
......@@ -4425,6 +4425,6 @@ union generic_reply
struct query_symlink_reply query_symlink_reply;
};
#define SERVER_PROTOCOL_VERSION 256
#define SERVER_PROTOCOL_VERSION 257
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
......@@ -1944,19 +1944,15 @@ DECL_HANDLER(get_handle_fd)
{
struct fd *fd;
reply->fd = -1;
if ((fd = get_handle_fd_obj( current->process, req->handle, req->access )))
{
int unix_fd = get_unix_fd( fd );
if (unix_fd != -1)
if (!req->cached)
{
int cached_fd = get_handle_unix_fd( current->process, req->handle, req->access );
if (cached_fd != -1) reply->fd = cached_fd;
else if (!get_error()) send_client_fd( current->process, unix_fd, req->handle );
int unix_fd = get_unix_fd( fd );
if (unix_fd != -1) 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 );
if (fd->inode && fd->inode->device->removable) reply->flags |= FD_FLAG_REMOVABLE;
release_object( fd );
}
}
......
......@@ -562,7 +562,6 @@ DECL_HANDLER(dup_handle)
struct process *src, *dst;
reply->handle = 0;
reply->fd = -1;
if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE )))
{
if (req->options & DUP_HANDLE_MAKE_GLOBAL)
......@@ -579,8 +578,9 @@ DECL_HANDLER(dup_handle)
/* close the handle no matter what happened */
if (req->options & DUP_HANDLE_CLOSE_SOURCE)
{
if (src == current->process) close_handle( src, req->src_handle, &reply->fd );
else close_handle( src, req->src_handle, NULL );
unsigned int err = get_error(); /* don't overwrite error from the above calls */
reply->closed = close_handle( src, req->src_handle, NULL );
set_error( err );
}
release_object( src );
}
......
......@@ -485,7 +485,7 @@ enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO };
unsigned int options; /* duplicate options (see below) */
@REPLY
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
#define DUP_HANDLE_CLOSE_SOURCE DUPLICATE_CLOSE_SOURCE
#define DUP_HANDLE_SAME_ACCESS DUPLICATE_SAME_ACCESS
......@@ -661,9 +661,8 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT };
@REQ(get_handle_fd)
obj_handle_t handle; /* handle to the file */
unsigned int access; /* wanted access rights */
int cached; /* is it cached on the client already? */
@REPLY
int fd; /* file descriptor */
int removable; /* is device removable? (-1 if unknown) */
int flags; /* file read/write flags (see below) */
@END
#define FD_FLAG_OVERLAPPED 0x01 /* fd opened in overlapped mode */
......@@ -672,6 +671,7 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT };
#define FD_FLAG_SEND_SHUTDOWN 0x08
#define FD_FLAG_AVAILABLE 0x10 /* in overlap read/write operation,
* 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 */
@REQ(set_handle_fd)
......
......@@ -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 )
{
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 )
......@@ -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 )
{
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 )
{
fprintf( stderr, " fd=%d,", req->fd );
fprintf( stderr, " removable=%d,", req->removable );
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