server: Block by waiting on context handle in get_thread_context.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48052
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45546
Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2020-04-22 14:33:23 +02:00 committed by Alexandre Julliard
parent aa0c4bb5e7
commit c4dab9b76e
6 changed files with 77 additions and 57 deletions

View File

@ -807,42 +807,35 @@ NTSTATUS set_thread_context( HANDLE handle, const context_t *context, BOOL *self
NTSTATUS get_thread_context( HANDLE handle, context_t *context, unsigned int flags, BOOL *self )
{
NTSTATUS ret;
DWORD dummy, i;
SERVER_START_REQ( get_thread_context )
{
req->handle = wine_server_obj_handle( handle );
req->flags = flags;
req->suspend = 1;
wine_server_set_reply( req, context, sizeof(*context) );
ret = wine_server_call( req );
*self = reply->self;
handle = wine_server_ptr_handle( reply->handle );
}
SERVER_END_REQ;
if (ret == STATUS_PENDING)
{
for (i = 0; i < 100; i++)
LARGE_INTEGER timeout;
timeout.QuadPart = -1000000;
if (NtWaitForSingleObject( handle, FALSE, &timeout ))
{
SERVER_START_REQ( get_thread_context )
{
req->handle = wine_server_obj_handle( handle );
req->flags = flags;
req->suspend = 0;
wine_server_set_reply( req, context, sizeof(*context) );
ret = wine_server_call( req );
}
SERVER_END_REQ;
if (ret == STATUS_PENDING)
{
LARGE_INTEGER timeout;
timeout.QuadPart = -10000;
NtDelayExecution( FALSE, &timeout );
}
else break;
NtClose( handle );
return STATUS_ACCESS_DENIED;
}
NtResumeThread( handle, &dummy );
if (ret == STATUS_PENDING) ret = STATUS_ACCESS_DENIED;
SERVER_START_REQ( get_thread_context )
{
req->handle = wine_server_obj_handle( handle );
req->flags = flags;
wine_server_set_reply( req, context, sizeof(*context) );
ret = wine_server_call( req );
}
SERVER_END_REQ;
}
return ret;
}

View File

@ -2900,14 +2900,14 @@ struct get_thread_context_request
struct request_header __header;
obj_handle_t handle;
unsigned int flags;
int suspend;
char __pad_20[4];
};
struct get_thread_context_reply
{
struct reply_header __header;
int self;
obj_handle_t handle;
/* VARARG(context,context); */
char __pad_12[4];
};
@ -2916,9 +2916,7 @@ struct set_thread_context_request
{
struct request_header __header;
obj_handle_t handle;
int suspend;
/* VARARG(context,context); */
char __pad_20[4];
};
struct set_thread_context_reply
{
@ -6684,7 +6682,7 @@ union generic_reply
/* ### protocol_version begin ### */
#define SERVER_PROTOCOL_VERSION 601
#define SERVER_PROTOCOL_VERSION 602
/* ### protocol_version end ### */

View File

@ -2157,11 +2157,11 @@ enum char_info_mode
/* Retrieve the current context of a thread */
@REQ(get_thread_context)
obj_handle_t handle; /* thread handle */
obj_handle_t handle; /* thread or context handle */
unsigned int flags; /* context flags */
int suspend; /* suspend the thread if needed */
@REPLY
int self; /* was it a handle to the current thread? */
obj_handle_t handle; /* pending context handle */
VARARG(context,context); /* thread context */
@END
@ -2169,7 +2169,6 @@ enum char_info_mode
/* Set the current context of a thread */
@REQ(set_thread_context)
obj_handle_t handle; /* thread handle */
int suspend; /* suspend the thread if needed */
VARARG(context,context); /* thread context */
@REPLY
int self; /* was it a handle to the current thread? */

View File

@ -1484,13 +1484,12 @@ C_ASSERT( FIELD_OFFSET(struct get_timer_info_reply, signaled) == 16 );
C_ASSERT( sizeof(struct get_timer_info_reply) == 24 );
C_ASSERT( FIELD_OFFSET(struct get_thread_context_request, handle) == 12 );
C_ASSERT( FIELD_OFFSET(struct get_thread_context_request, flags) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_thread_context_request, suspend) == 20 );
C_ASSERT( sizeof(struct get_thread_context_request) == 24 );
C_ASSERT( FIELD_OFFSET(struct get_thread_context_reply, self) == 8 );
C_ASSERT( FIELD_OFFSET(struct get_thread_context_reply, handle) == 12 );
C_ASSERT( sizeof(struct get_thread_context_reply) == 16 );
C_ASSERT( FIELD_OFFSET(struct set_thread_context_request, handle) == 12 );
C_ASSERT( FIELD_OFFSET(struct set_thread_context_request, suspend) == 16 );
C_ASSERT( sizeof(struct set_thread_context_request) == 24 );
C_ASSERT( sizeof(struct set_thread_context_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct set_thread_context_reply, self) == 8 );
C_ASSERT( sizeof(struct set_thread_context_reply) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_selector_entry_request, handle) == 12 );

View File

@ -136,6 +136,7 @@ struct context
};
static void dump_context( struct object *obj, int verbose );
static int context_signaled( struct object *obj, struct wait_queue_entry *entry );
static const struct object_ops context_ops =
{
@ -144,7 +145,7 @@ static const struct object_ops context_ops =
no_get_type, /* get_type */
add_queue, /* add_queue */
remove_queue, /* remove_queue */
NULL, /* signaled */
context_signaled, /* signaled */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@ -267,6 +268,13 @@ static void dump_context( struct object *obj, int verbose )
}
static int context_signaled( struct object *obj, struct wait_queue_entry *entry )
{
struct context *context = (struct context *)obj;
return context->status != STATUS_PENDING;
}
static struct context *create_thread_context( struct thread *thread )
{
struct context *context;
@ -377,6 +385,7 @@ static void cleanup_thread( struct thread *thread )
if (thread->context)
{
thread->context->status = STATUS_ACCESS_DENIED;
wake_up( &thread->context->obj, 0 );
release_object( thread->context );
thread->context = NULL;
}
@ -1619,9 +1628,11 @@ DECL_HANDLER(select)
}
if (!current->context && !(current->context = create_thread_context( current ))) return;
copy_context( &current->context->regs, context, context->flags & ~current->context->regs.flags );
copy_context( &current->context->regs, context,
context->flags & ~(current->context->regs.flags | get_context_system_regs(current->process->cpu)) );
current->context->status = STATUS_SUCCESS;
current->suspend_cookie = req->cookie;
wake_up( &current->context->obj, 0 );
}
if (!req->cookie)
@ -1802,6 +1813,8 @@ DECL_HANDLER(get_apc_result)
/* retrieve the current context of a thread */
DECL_HANDLER(get_thread_context)
{
struct context *thread_context = NULL;
unsigned int system_flags;
struct thread *thread;
context_t *context;
@ -1810,39 +1823,58 @@ DECL_HANDLER(get_thread_context)
set_error( STATUS_INVALID_PARAMETER );
return;
}
if (!(thread = get_thread_from_handle( req->handle, THREAD_GET_CONTEXT ))) return;
reply->self = (thread == current);
if (thread != current && (!thread->context || thread->context->status == STATUS_PENDING))
if ((thread_context = (struct context *)get_handle_obj( current->process, req->handle, 0, &context_ops )))
{
/* thread is not suspended, retry (if it's still running) */
close_handle( current->process, req->handle ); /* avoid extra server call */
system_flags = get_context_system_regs( thread_context->regs.cpu );
}
else if ((thread = get_thread_from_handle( req->handle, THREAD_GET_CONTEXT )))
{
clear_error();
system_flags = get_context_system_regs( thread->process->cpu );
if (thread->state == RUNNING)
{
set_error( STATUS_PENDING );
if (req->suspend)
reply->self = (thread == current);
if (thread != current) stop_thread( thread );
if (thread->context)
{
release_object( thread );
/* make sure we have suspend access */
if (!(thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) return;
suspend_thread( thread );
/* make sure that system regs are valid in thread context */
if (req->flags & system_flags & ~thread->context->regs.flags)
get_thread_context( thread, &thread->context->regs, req->flags & system_flags );
if (!get_error()) thread_context = (struct context *)grab_object( thread->context );
}
else if (!get_error() && (context = set_reply_data_size( sizeof(context_t) )))
{
assert( reply->self );
memset( context, 0, sizeof(context_t) );
context->cpu = thread_context->regs.cpu;
if (req->flags & system_flags)
{
get_thread_context( thread, context, req->flags & system_flags );
context->flags |= req->flags & system_flags;
}
}
}
else set_error( STATUS_UNSUCCESSFUL );
release_object( thread );
}
else if ((context = set_reply_data_size( sizeof(context_t) )))
{
unsigned int flags = get_context_system_regs( thread->process->cpu );
if (get_error() || !thread_context) return;
set_error( thread_context->status );
if (!thread_context->status && (context = set_reply_data_size( sizeof(context_t) )))
{
memset( context, 0, sizeof(context_t) );
context->cpu = thread->process->cpu;
if (thread->context)
{
copy_context( context, &thread->context->regs, req->flags & ~flags );
context->flags |= req->flags & ~flags;
}
if (req->flags & flags) get_thread_context( thread, context, req->flags & flags );
context->cpu = thread_context->regs.cpu;
copy_context( context, &thread_context->regs, req->flags );
context->flags = req->flags;
}
release_object( thread );
else if (thread_context->status == STATUS_PENDING)
{
reply->handle = alloc_handle( current->process, thread_context, SYNCHRONIZE, 0 );
}
release_object( thread_context );
}
/* set the current context of a thread */

View File

@ -2675,19 +2675,18 @@ static void dump_get_thread_context_request( const struct get_thread_context_req
{
fprintf( stderr, " handle=%04x", req->handle );
fprintf( stderr, ", flags=%08x", req->flags );
fprintf( stderr, ", suspend=%d", req->suspend );
}
static void dump_get_thread_context_reply( const struct get_thread_context_reply *req )
{
fprintf( stderr, " self=%d", req->self );
fprintf( stderr, ", handle=%04x", req->handle );
dump_varargs_context( ", context=", cur_size );
}
static void dump_set_thread_context_request( const struct set_thread_context_request *req )
{
fprintf( stderr, " handle=%04x", req->handle );
fprintf( stderr, ", suspend=%d", req->suspend );
dump_varargs_context( ", context=", cur_size );
}