From: Jacek Caban <jacek@codeweavers.com> When running simulated code, instead of suspending a thread immediately, the kernel sets a registered doorbell, expecting the jit to stop execution as soon as it reaches a consistent state that can be represented as an x86_64 context. Based on patch by Billy Laws. --- dlls/ntdll/unix/signal_arm64.c | 38 +++++++++++++++++++++++++++++++++- server/protocol.def | 5 +++-- server/thread.c | 14 +++++++++++-- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index b53af9dd3a0..f6edbba5de2 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -64,6 +64,18 @@ WINE_DEFAULT_DEBUG_CHANNEL(seh); #define NTDLL_DWARF_H_NO_UNWINDER #include "dwarf.h" +struct arm64_thread_data +{ + BOOL suspend_pending; +}; + +C_ASSERT( sizeof(struct arm64_thread_data) <= sizeof(((struct teb_data *)0)->cpu_data) ); + +static inline struct arm64_thread_data *arm64_thread_data( struct thread_data *data ) +{ + return (struct arm64_thread_data *)get_teb_data(data)->cpu_data; +} + /*********************************************************************** * signal context platform-specific definitions */ @@ -320,7 +332,20 @@ NTSTATUS signal_set_full_context( CONTEXT *context ) { struct thread_data *data = get_thread_data(); struct syscall_frame *frame = get_syscall_frame( data ); - NTSTATUS status = NtSetContextThread( GetCurrentThread(), context ); + struct arm64_thread_data *arm64_data = arm64_thread_data( data ); + NTSTATUS status; + + if (arm64_data->suspend_pending) + { + sigset_t old_set; + pthread_sigmask( SIG_BLOCK, &server_block_set, &old_set ); + *data->teb->ChpeV2CpuAreaInfo->SuspendDoorbell = 0; + arm64_data->suspend_pending = FALSE; + wait_suspend( context ); + status = NtSetContextThread( GetCurrentThread(), context ); + pthread_sigmask( SIG_SETMASK, &old_set, NULL ); + } + else status = NtSetContextThread( GetCurrentThread(), context ); if (!status && (context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) frame->restore_flags |= CONTEXT_INTEGER; @@ -1321,12 +1346,23 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *_sigcontext ) { ucontext_t *sigcontext = _sigcontext; struct thread_data *data = get_thread_data(); + CHPE_V2_CPU_AREA_INFO *chpe; CONTEXT context; if (!data->teb) { server_select( NULL, 0, SELECT_INTERRUPTIBLE, 0, NULL, NULL ); } + else if ((chpe = data->teb->ChpeV2CpuAreaInfo) && chpe->InSimulation && chpe->SuspendDoorbell) + { + NTSTATUS status = server_select( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_COOPERATIVE_SUSPEND, + 0, NULL, NULL ); + if (status == STATUS_THREAD_WAS_SUSPENDED) + { + *chpe->SuspendDoorbell = -1; + arm64_thread_data( data )->suspend_pending = TRUE; + } + } else if (is_inside_syscall( data, SP_sig(sigcontext) )) { context.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; diff --git a/server/protocol.def b/server/protocol.def index 2d651e076de..3ee3be605d4 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1448,8 +1448,9 @@ typedef volatile struct VARARG(call,apc_call); /* APC call arguments */ VARARG(contexts,contexts); /* suspend context(s) */ @END -#define SELECT_ALERTABLE 1 -#define SELECT_INTERRUPTIBLE 2 +#define SELECT_ALERTABLE 1 +#define SELECT_INTERRUPTIBLE 2 +#define SELECT_COOPERATIVE_SUSPEND 4 /* Create an event */ diff --git a/server/thread.c b/server/thread.c index e0d04b066da..19c928f7bee 100644 --- a/server/thread.c +++ b/server/thread.c @@ -132,6 +132,7 @@ struct context struct object obj; /* object header */ struct object *sync; /* sync object for wait/signal */ unsigned int status; /* status of the context */ + int cooperative;/* waiting for the cooperative suspend */ struct context_data regs[2]; /* context data */ }; #define CTX_NATIVE 0 /* context for native machine */ @@ -445,6 +446,7 @@ static inline void init_thread_structure( struct thread *thread ) static inline int is_thread_suspended( struct thread *thread ) { + if (thread->context && thread->context->cooperative) return 0; if (thread->suspend) return 1; return !thread->bypass_proc_suspend && thread->process->suspend; } @@ -485,8 +487,9 @@ static struct context *create_thread_context( struct thread *thread ) { struct context *context; if (!(context = alloc_object( &context_ops ))) return NULL; - context->sync = NULL; - context->status = STATUS_PENDING; + context->sync = NULL; + context->status = STATUS_PENDING; + context->cooperative = 0; memset( &context->regs, 0, sizeof(context->regs) ); context->regs[CTX_NATIVE].machine = native_machine; @@ -1167,6 +1170,12 @@ static int check_wait( struct thread *thread ) if ((wait->flags & SELECT_INTERRUPTIBLE) && !list_empty( &thread->system_apc )) return STATUS_KERNEL_APC; + if ((wait->flags & SELECT_COOPERATIVE_SUSPEND) && thread->context) + { + thread->context->cooperative = 1; + return STATUS_THREAD_WAS_SUSPENDED; + } + /* Suspended threads may not acquire locks, but they can run system APCs */ if (is_thread_suspended( thread )) return -1; @@ -1988,6 +1997,7 @@ DECL_HANDLER(select) copy_context( &ctx->regs[CTX_WOW], wow_context, wow_context->flags & ~ctx->regs[CTX_WOW].flags ); } ctx->status = STATUS_SUCCESS; + ctx->cooperative = 0; current->suspend_cookie = req->cookie; signal_sync( ctx->sync ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10903