diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go index 189a8d18..326cf5c7 100644 --- a/internal/lsp/cache/view.go +++ b/internal/lsp/cache/view.go @@ -225,15 +225,17 @@ func (v *view) SetContent(ctx context.Context, uri span.URI, content []byte) err // invalidateContent invalidates the content of a Go file, // including any position and type information that depends on it. func (f *goFile) invalidateContent(ctx context.Context) { - f.handleMu.Lock() - defer f.handleMu.Unlock() - + // Mutex acquisition order here is important. It must match the order + // in loadParseTypecheck to avoid deadlocks. f.view.mcache.mu.Lock() defer f.view.mcache.mu.Unlock() f.view.pcache.mu.Lock() defer f.view.pcache.mu.Unlock() + f.handleMu.Lock() + defer f.handleMu.Unlock() + f.invalidateAST(ctx) f.handle = nil } diff --git a/internal/lsp/cache/watcher.go b/internal/lsp/cache/watcher.go index c8f619b1..f788e88e 100644 --- a/internal/lsp/cache/watcher.go +++ b/internal/lsp/cache/watcher.go @@ -48,9 +48,15 @@ func (w *WatchMap) Watch(key interface{}, callback func()) func() { } func (w *WatchMap) Notify(key interface{}) { + // Make a copy of the watcher callbacks so we don't need to hold + // the mutex during the callbacks (to avoid deadlocks). w.mu.Lock() - defer w.mu.Unlock() - for _, entry := range w.watchers[key] { + entries := w.watchers[key] + entriesCopy := make([]watcher, len(entries)) + copy(entriesCopy, entries) + w.mu.Unlock() + + for _, entry := range entriesCopy { entry.callback() } }