From cf5ba95194b762765be9bcf54116447032a4d8f0 Mon Sep 17 00:00:00 2001 From: Heschi Kreinick Date: Tue, 4 Feb 2020 00:32:14 -0500 Subject: [PATCH] internal/imports: stop leaking listeners (*dirInfoCache).ScanAndListen saves a listener when it's called. That listener is a chain of callbacks that leads all the way back up to the original caller, i.e. the gopls completion code. It needs to unregister that listener, but in cases where the context was cancelled before it finished its initial walk of the cache, it failed to. In gopls' case, that means retaining the entire state of the workspace as of completion triggering. Return a real stop function on all paths. Change-Id: Iff3046e627b1fa89f576371c4742fee6fdca7589 Reviewed-on: https://go-review.googlesource.com/c/tools/+/217677 Run-TryBot: Heschi Kreinick Reviewed-by: Rebecca Stambler TryBot-Result: Gobot Gobot --- internal/imports/mod_cache.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/internal/imports/mod_cache.go b/internal/imports/mod_cache.go index 6df7d48f90..5b4f03accd 100644 --- a/internal/imports/mod_cache.go +++ b/internal/imports/mod_cache.go @@ -132,20 +132,7 @@ func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener } d.mu.Unlock() - // Process the pre-existing keys. - for _, k := range keys { - select { - case <-ctx.Done(): - cancel() - return func() {} - default: - } - if v, ok := d.Load(k); ok { - listener(v) - } - } - - return func() { + stop := func() { cancel() d.mu.Lock() delete(d.listeners, cookie) @@ -154,6 +141,20 @@ func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener <-sema } } + + // Process the pre-existing keys. + for _, k := range keys { + select { + case <-ctx.Done(): + return stop + default: + } + if v, ok := d.Load(k); ok { + listener(v) + } + } + + return stop } // Store stores the package info for dir.