Module: wine Branch: master Commit: b75ae8c31eba2a59e2b32bf8d456ca5756ac6e0d URL: https://source.winehq.org/git/wine.git/?a=commit;h=b75ae8c31eba2a59e2b32bf8d...
Author: Jacek Caban jacek@codeweavers.com Date: Tue Aug 25 14:52:48 2020 +0200
server: Support blocking console host ioctls.
Signed-off-by: Jacek Caban jacek@codeweavers.com Signed-off-by: Alexandre Julliard julliard@winehq.org
---
include/wine/server_protocol.h | 4 ++- server/console.c | 71 ++++++++++++++++++++++++++++++++++++++---- server/protocol.def | 1 + server/request.h | 5 +-- server/trace.c | 1 + 5 files changed, 73 insertions(+), 9 deletions(-)
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index af3353eae3..76206413d4 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1943,8 +1943,10 @@ struct get_next_console_request_request struct request_header __header; obj_handle_t handle; int signal; + int read; unsigned int status; /* VARARG(out_data,bytes); */ + char __pad_28[4]; }; struct get_next_console_request_reply { @@ -6333,7 +6335,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 638 +#define SERVER_PROTOCOL_VERSION 639
/* ### protocol_version end ### */
diff --git a/server/console.c b/server/console.c index 5cdf3e7822..2c2978fc0c 100644 --- a/server/console.c +++ b/server/console.c @@ -192,6 +192,7 @@ struct console_server struct object obj; /* object header */ struct console_input *console; /* attached console */ struct list queue; /* ioctl queue */ + struct list read_queue; /* blocking read queue */ int busy; /* flag if server processing an ioctl */ };
@@ -577,6 +578,12 @@ static void disconnect_console_server( struct console_server *server ) list_remove( &call->entry ); console_host_ioctl_terminate( call, STATUS_CANCELLED ); } + while (!list_empty( &server->read_queue )) + { + struct console_host_ioctl *call = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry ); + list_remove( &call->entry ); + console_host_ioctl_terminate( call, STATUS_CANCELLED ); + }
if (server->console) { @@ -1638,10 +1645,16 @@ static struct object *create_console_server( void ) server->console = NULL; server->busy = 0; list_init( &server->queue ); + list_init( &server->read_queue );
return &server->obj; }
+static int is_blocking_read_ioctl( unsigned int code ) +{ + return code == IOCTL_CONDRV_READ_INPUT; +} + static int console_input_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) { struct console_input *console = get_fd_user( fd ); @@ -2385,11 +2398,10 @@ DECL_HANDLER(get_console_wait_event) /* retrieve the next pending console ioctl request */ DECL_HANDLER(get_next_console_request) { - struct console_host_ioctl *ioctl = NULL; + struct console_host_ioctl *ioctl = NULL, *next; struct console_server *server; struct iosb *iosb = NULL;
- server = (struct console_server *)get_handle_obj( current->process, req->handle, 0, &console_server_ops ); if (!server) return;
@@ -2403,12 +2415,30 @@ DECL_HANDLER(get_next_console_request) if (req->signal) set_event( server->console->event); else reset_event( server->console->event );
- /* set result of previous ioctl, if any */ - if (server->busy) + if (req->read) { - unsigned int status = req->status; + /* set result of current pending ioctl */ + if (list_empty( &server->read_queue )) + { + set_error( STATUS_INVALID_HANDLE ); + release_object( server ); + return; + } + + ioctl = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry ); + list_remove( &ioctl->entry ); + list_move_tail( &server->queue, &server->read_queue ); + } + else if (server->busy) + { + /* set result of previous ioctl */ ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); list_remove( &ioctl->entry ); + } + + if (ioctl) + { + unsigned int status = req->status; if (ioctl->async) { iosb = async_get_iosb( ioctl->async ); @@ -2430,9 +2460,32 @@ DECL_HANDLER(get_next_console_request) } console_host_ioctl_terminate( ioctl, status ); if (iosb) release_object( iosb ); + + if (req->read) + { + release_object( server ); + return; + } server->busy = 0; }
+ /* if we have a blocking read ioctl in queue head and previous blocking read is still waiting, + * move it to read queue for execution after current read is complete. move all blocking + * ioctl at the same time to preserve their order. */ + if (!list_empty( &server->queue ) && !list_empty( &server->read_queue )) + { + ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry ); + if (is_blocking_read_ioctl( ioctl->code )) + { + LIST_FOR_EACH_ENTRY_SAFE( ioctl, next, &server->queue, struct console_host_ioctl, entry ) + { + if (!is_blocking_read_ioctl( ioctl->code )) continue; + list_remove( &ioctl->entry ); + list_add_tail( &server->read_queue, &ioctl->entry ); + } + } + } + /* return the next ioctl */ if (!list_empty( &server->queue )) { @@ -2450,7 +2503,13 @@ DECL_HANDLER(get_next_console_request) iosb->in_data = NULL; }
- server->busy = 1; + if (is_blocking_read_ioctl( ioctl->code )) + { + list_remove( &ioctl->entry ); + assert( list_empty( &server->read_queue )); + list_add_tail( &server->read_queue, &ioctl->entry ); + } + else server->busy = 1; } else { diff --git a/server/protocol.def b/server/protocol.def index 75888ea9a2..14bf046816 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1526,6 +1526,7 @@ enum server_fd_type @REQ(get_next_console_request) obj_handle_t handle; /* console server handle */ int signal; /* server signal state */ + int read; /* 1 if reporting result of blocked read ioctl */ unsigned int status; /* status of previous ioctl */ VARARG(out_data,bytes); /* out_data of previous ioctl */ @REPLY diff --git a/server/request.h b/server/request.h index ba278122eb..4c16b24685 100644 --- a/server/request.h +++ b/server/request.h @@ -1136,8 +1136,9 @@ C_ASSERT( FIELD_OFFSET(struct send_console_signal_request, group_id) == 16 ); C_ASSERT( sizeof(struct send_console_signal_request) == 24 ); C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, signal) == 16 ); -C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, status) == 20 ); -C_ASSERT( sizeof(struct get_next_console_request_request) == 24 ); +C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, read) == 20 ); +C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, status) == 24 ); +C_ASSERT( sizeof(struct get_next_console_request_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct get_next_console_request_reply, code) == 8 ); C_ASSERT( FIELD_OFFSET(struct get_next_console_request_reply, out_size) == 12 ); C_ASSERT( sizeof(struct get_next_console_request_reply) == 16 ); diff --git a/server/trace.c b/server/trace.c index 42e048998c..dac0a5f6e1 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2098,6 +2098,7 @@ static void dump_get_next_console_request_request( const struct get_next_console { fprintf( stderr, " handle=%04x", req->handle ); fprintf( stderr, ", signal=%d", req->signal ); + fprintf( stderr, ", read=%d", req->read ); fprintf( stderr, ", status=%08x", req->status ); dump_varargs_bytes( ", out_data=", cur_size ); }