internal/lsp: fix concurrent map write in file invalidation

The invalidateContent function does not acquire a snapshot's mutex to
avoid blocking other work (even though it probably should since it's
only called after a context is canceled). A case was added to iterate
through files when a file is created, and it did not respect the fact
that the snapshot's mutex was not locked, resulting in a concurrent map
read and write. This change makes sure that the access of the snapshot's
files map is guarded by a mutex.

As a follow-up, we should just acquire snapshot.mu in invalidateContent.

Updates golang/go#36006

Change-Id: Idd904ae582055ce786062df50875ac7f0896fd1c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/210199
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index 0ebfc70..275893b 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -424,6 +424,17 @@
 	return s.ids[uri]
 }
 
+func (s *snapshot) getFileURIs() []span.URI {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	var uris []span.URI
+	for uri := range s.files {
+		uris = append(uris, uri)
+	}
+	return uris
+}
+
 func (s *snapshot) getFile(uri span.URI) source.FileHandle {
 	s.mu.Lock()
 	defer s.mu.Unlock()
@@ -556,7 +567,7 @@
 	// TODO(rstambler): Speed this up by mapping directories to filenames.
 	if action == source.Create {
 		if dirStat, err := os.Stat(dir(f.URI().Filename())); err == nil {
-			for uri := range v.snapshot.files {
+			for _, uri := range v.snapshot.getFileURIs() {
 				if fdirStat, err := os.Stat(dir(uri.Filename())); err == nil {
 					if os.SameFile(dirStat, fdirStat) {
 						for _, id := range v.snapshot.ids[uri] {