diff --git a/dlls/kernel32/tests/debugger.c b/dlls/kernel32/tests/debugger.c index 8062c294612..33e18d82e62 100644 --- a/dlls/kernel32/tests/debugger.c +++ b/dlls/kernel32/tests/debugger.c @@ -1278,7 +1278,7 @@ static void test_debugger(const char *argv0) ok(ctx.ev.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT, "dwDebugEventCode = %d\n", ctx.ev.dwDebugEventCode); if ((skip_reply_later = !ContinueDebugEvent(ctx.ev.dwProcessId, ctx.ev.dwThreadId, DBG_REPLY_LATER))) - todo_wine win_skip("Skipping unsupported DBG_REPLY_LATER tests\n"); + win_skip("Skipping unsupported DBG_REPLY_LATER tests\n"); else { DEBUG_EVENT de; @@ -1402,7 +1402,7 @@ static void test_debugger(const char *argv0) else win_skip("call_debug_service_code not supported on this architecture\n"); if (skip_reply_later) - todo_wine win_skip("Skipping unsupported DBG_REPLY_LATER tests\n"); + win_skip("Skipping unsupported DBG_REPLY_LATER tests\n"); else if (sizeof(loop_code) > 1) { HANDLE thread_a, thread_b; diff --git a/server/debugger.c b/server/debugger.c index 5b06b73b4be..5ff63b92b9c 100644 --- a/server/debugger.c +++ b/server/debugger.c @@ -38,7 +38,7 @@ #include "thread.h" #include "request.h" -enum debug_event_state { EVENT_QUEUED, EVENT_SENT, EVENT_CONTINUED }; +enum debug_event_state { EVENT_QUEUED, EVENT_SENT, EVENT_DELAYED, EVENT_CONTINUED }; /* debug event */ struct debug_event @@ -265,6 +265,31 @@ static void link_event( struct debug_event *event ) } } +/* resume a delayed debug event already in the queue */ +static void resume_event( struct debug_event *event ) +{ + struct debug_ctx *debug_ctx = event->debugger->debug_ctx; + + assert( debug_ctx ); + event->state = EVENT_QUEUED; + if (!event->sender->process->debug_event) + { + grab_object( debug_ctx ); + wake_up( &debug_ctx->obj, 0 ); + release_object( debug_ctx ); + } +} + +/* delay a debug event already in the queue to be replayed when thread wakes up */ +static void delay_event( struct debug_event *event ) +{ + struct debug_ctx *debug_ctx = event->debugger->debug_ctx; + + assert( debug_ctx ); + event->state = EVENT_DELAYED; + if (event->sender->process->debug_event == event) event->sender->process->debug_event = NULL; +} + /* find the next event that we can send to the debugger */ static struct debug_event *find_event_to_send( struct debug_ctx *debug_ctx ) { @@ -273,6 +298,7 @@ static struct debug_event *find_event_to_send( struct debug_ctx *debug_ctx ) LIST_FOR_EACH_ENTRY( event, &debug_ctx->event_queue, struct debug_event, entry ) { if (event->state == EVENT_SENT) continue; /* already sent */ + if (event->state == EVENT_DELAYED) continue; /* delayed until thread resumes */ if (event->sender->process->debug_event) continue; /* process busy with another one */ return event; } @@ -365,6 +391,29 @@ static int continue_debug_event( struct process *process, struct thread *thread, { struct debug_event *event; + if (status == DBG_REPLY_LATER) + { + /* if thread is suspended, delay all its events and resume process + * if not, reset the event for immediate replay */ + LIST_FOR_EACH_ENTRY( event, &debug_ctx->event_queue, struct debug_event, entry ) + { + if (event->sender != thread) continue; + if (thread->suspend) + { + delay_event( event ); + resume_process( process ); + } + else if (event->state == EVENT_SENT) + { + assert( event->sender->process->debug_event == event ); + event->sender->process->debug_event = NULL; + resume_event( event ); + return 1; + } + } + return 1; + } + /* find the event in the queue */ LIST_FOR_EACH_ENTRY( event, &debug_ctx->event_queue, struct debug_event, entry ) { @@ -372,7 +421,6 @@ static int continue_debug_event( struct process *process, struct thread *thread, if (event->sender == thread) { assert( event->sender->process->debug_event == event ); - event->status = status; event->state = EVENT_CONTINUED; wake_up( &event->obj, 0 ); @@ -430,6 +478,24 @@ void generate_debug_event( struct thread *thread, int code, const void *arg ) } } +void resume_delayed_debug_events( struct thread *thread ) +{ + struct thread *debugger = thread->process->debugger; + struct debug_event *event; + + if (debugger) + { + assert( debugger->debug_ctx ); + LIST_FOR_EACH_ENTRY( event, &debugger->debug_ctx->event_queue, struct debug_event, entry ) + { + if (event->sender != thread) continue; + if (event->state != EVENT_DELAYED) continue; + resume_event( event ); + suspend_process( thread->process ); + } + } +} + /* attach a process to a debugger thread and suspend it */ static int debugger_attach( struct process *process, struct thread *debugger ) { @@ -615,7 +681,8 @@ DECL_HANDLER(continue_debug_event) struct process *process; if (req->status != DBG_EXCEPTION_NOT_HANDLED && - req->status != DBG_CONTINUE) + req->status != DBG_CONTINUE && + req->status != DBG_REPLY_LATER) { set_error( STATUS_INVALID_PARAMETER ); return; diff --git a/server/object.h b/server/object.h index 8114a8fb6b9..3144eb56678 100644 --- a/server/object.h +++ b/server/object.h @@ -207,6 +207,7 @@ extern void sock_init(void); extern int set_process_debugger( struct process *process, struct thread *debugger ); extern void generate_debug_event( struct thread *thread, int code, const void *arg ); +extern void resume_delayed_debug_events( struct thread *thread ); extern void generate_startup_debug_events( struct process *process, client_ptr_t entry ); extern void debug_exit_thread( struct thread *thread ); diff --git a/server/thread.c b/server/thread.c index edf70c61bde..6e677c8cf29 100644 --- a/server/thread.c +++ b/server/thread.c @@ -612,7 +612,8 @@ int resume_thread( struct thread *thread ) int old_count = thread->suspend; if (thread->suspend > 0) { - if (!(--thread->suspend + thread->process->suspend)) wake_thread( thread ); + if (!(--thread->suspend)) resume_delayed_debug_events( thread ); + if (!(thread->suspend + thread->process->suspend)) wake_thread( thread ); } return old_count; }