From 1f101e6fdbeee9e1bbfd1ca0ebc6b45a02bdda70 Mon Sep 17 00:00:00 2001 From: Francis Belanger Date: Thu, 5 Feb 2026 09:45:29 -0500 Subject: [PATCH] fix: improve server process shutdown with SIGKILL fallback and proper cleanup --- lua/opencode/curl.lua | 2 +- lua/opencode/opencode_server.lua | 27 ++++++++++++++++++++------ tests/unit/opencode_server_spec.lua | 30 +++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/lua/opencode/curl.lua b/lua/opencode/curl.lua index e936692e..67e4cc0d 100644 --- a/lua/opencode/curl.lua +++ b/lua/opencode/curl.lua @@ -130,7 +130,7 @@ function M.request(opts) shutdown = function() if job and job.pid then pcall(function() - job:kill('sigterm') + job:kill(15) -- SIGTERM end) end end, diff --git a/lua/opencode/opencode_server.lua b/lua/opencode/opencode_server.lua index 293255d1..ea5f6116 100644 --- a/lua/opencode/opencode_server.lua +++ b/lua/opencode/opencode_server.lua @@ -24,7 +24,9 @@ local function ensure_vim_leave_autocmd() callback = function() local state = require('opencode.state') if state.opencode_server then - state.opencode_server:shutdown():wait(2000) + pcall(function() + state.opencode_server:shutdown():wait(2000) + end) end end, }) @@ -56,15 +58,27 @@ function OpencodeServer:shutdown() end if self.job and self.job.pid then + local job = self.job + + self.job = nil + self.url = nil + self.handle = nil + pcall(function() - self.job:kill('sigterm') + job:kill(15) -- SIGTERM end) + + vim.defer_fn(function() + if job and job.pid then + pcall(function() + job:kill(9) -- SIGKILL + end) + end + end, 500) + else + self.shutdown_promise:resolve(true) end - self.job = nil - self.url = nil - self.handle = nil - self.shutdown_promise:resolve(true) return self.shutdown_promise end @@ -118,6 +132,7 @@ function OpencodeServer:spawn(opts) end end, }, function(exit_opts) + -- Clear fields if not already cleared by shutdown() self.job = nil self.url = nil self.handle = nil diff --git a/tests/unit/opencode_server_spec.lua b/tests/unit/opencode_server_spec.lua index 566855d7..889c4715 100644 --- a/tests/unit/opencode_server_spec.lua +++ b/tests/unit/opencode_server_spec.lua @@ -46,17 +46,39 @@ describe('opencode.opencode_server', function() it('shutdown resolves shutdown_promise and clears fields', function() local server = OpencodeServer.new() - server.job = { pid = 2, kill = function() end } - server.url = 'http://x' - server.handle = 2 + local exit_callback + + -- Mock vim.system to capture the exit callback + vim.system = function(cmd, opts, on_exit) + exit_callback = on_exit + return { pid = 2, kill = function() end } + end + + -- Spawn the server so the exit callback is set up + server:spawn({ + cwd = '.', + on_ready = function() end, + on_error = function() end, + on_exit = function() end, + }) + local resolved = false server:get_shutdown_promise():and_then(function() resolved = true end) + + -- Call shutdown (sends SIGTERM) server:shutdown() - vim.wait(50, function() + + -- Simulate the process exiting by calling the exit callback + vim.schedule(function() + exit_callback({ code = 0, signal = 0 }) + end) + + vim.wait(100, function() return resolved end) + assert.is_true(resolved) assert.is_nil(server.job) assert.is_nil(server.url)