From b6489ee4bc75d2d28ea096ba96eb5438cf618508 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Fri, 19 Jun 2026 21:07:47 +0100 Subject: [PATCH] docs(lsp): clarify drainStderr cancellation relies on pipe EOF The goroutine parks in the blocking stderr.Read, so closing c.done does not interrupt it; termination relies on the read erroring at pipe EOF when the process is killed. The c.done select is only a best-effort fast path. --- internal/lsp/client.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/lsp/client.go b/internal/lsp/client.go index 5874ac6..21393d3 100644 --- a/internal/lsp/client.go +++ b/internal/lsp/client.go @@ -121,6 +121,12 @@ func NewClient(cmd *exec.Cmd) (*Client, error) { // drainStderr consumes stderr output to prevent the LSP server from blocking. // LSP servers may write diagnostic messages to stderr which we discard. +// +// Cancellation note: the goroutine parks in the blocking stderr.Read below, so +// closing c.done does not interrupt it directly — termination relies on the +// read returning an error when the pipe reaches EOF (the process is killed on +// Close). The c.done check is only a best-effort fast path for the case where +// done is already closed between reads. func (c *Client) drainStderr() { buf := make([]byte, StderrBufferSize) for {