From 18bcf46fad1cbb41d43275a6d7154555492136c2 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Sat, 16 Nov 2024 10:51:03 +0100 Subject: [PATCH] Fix race between stopped thread retrieval and continued event If the debug adapter sends continued right after stopped, before the client retrieved the threads the continued change was lost. Should fix https://github.com/mfussenegger/nvim-dap/issues/1365 --- lua/dap/session.lua | 15 ++++++++++++++- spec/integration_spec.lua | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lua/dap/session.lua b/lua/dap/session.lua index fcfe9861..3b2aca03 100644 --- a/lua/dap/session.lua +++ b/lua/dap/session.lua @@ -657,7 +657,8 @@ function Session:update_threads(cb) threads[thread.id] = thread local old_thread = self.threads[thread.id] if old_thread then - thread.stopped = old_thread.stopped + local stopped = old_thread.stopped == nil and false or old_thread.stopped + thread.stopped = stopped thread.frames = old_thread.frames end end @@ -689,12 +690,24 @@ function Session:event_stopped(stopped) local co = coroutine.running() if self.dirty.threads or (stopped.threadId and self.threads[stopped.threadId] == nil) then + local thread = { + id = stopped.threadId, + name = "Unknown", + stopped = true + } + if thread.id then + self.threads[thread.id] = thread + end self:update_threads(coresume(co)) local err = coroutine.yield() if err then utils.notify('Error retrieving threads: ' .. utils.fmt_error(err), vim.log.levels.ERROR) return end + if thread.stopped == false then + log.debug("Thread resumed during stopped event handling", stopped, thread) + return + end end local should_jump = stopped.reason ~= 'pause' or stopped.allThreadsStopped diff --git a/spec/integration_spec.lua b/spec/integration_spec.lua index 1f0b9cd8..2e83bd76 100644 --- a/spec/integration_spec.lua +++ b/spec/integration_spec.lua @@ -322,6 +322,32 @@ describe('dap with fake server', function() }) end) + it("Doesn't jump on stopped if continue is received before threads response", function() + run_and_wait_until_initialized(config, server) + server.client.threads = function(self, request) + self:send_event("continued", { + threadId = 1, + }) + self:send_response(request, { + threads = { { id = 1, name = 'thread1' }, } + }) + end + local log = require('dap.log').create_logger('dap.log') + local debug = log.debug + local messages = {} + log.debug = function(...) + table.insert(messages, {...}) + debug(...) + end + server.client:send_event('stopped', { + threadId = 1, + reason = 'unknown', + }) + wait_for_response(server, "threads") + wait(function() return #messages >= 5 end) + assert.are.same("Thread resumed during stopped event handling", messages[6][1]) + end) + it("Clears stopped state on continued event", function() local buf = api.nvim_create_buf(true, false) local win = api.nvim_get_current_win()