Skip to content

Commit 212b45a

Browse files
authored
Handle the race in config file registry entry (#1210)
1 parent ba0fb29 commit 212b45a

File tree

1 file changed

+44
-31
lines changed

1 file changed

+44
-31
lines changed

internal/project/configfileregistry.go

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package project
33
import (
44
"context"
55
"fmt"
6+
"maps"
67
"slices"
78
"sync"
89

@@ -14,7 +15,7 @@ import (
1415
)
1516

1617
type ConfigFileEntry struct {
17-
mu sync.Mutex
18+
mu sync.RWMutex
1819
commandLine *tsoptions.ParsedCommandLine
1920
projects collections.Set[*Project]
2021
infos collections.Set[*ScriptInfo]
@@ -114,6 +115,8 @@ func (c *ConfigFileRegistry) acquireConfig(fileName string, path tspath.Path, pr
114115
func (c *ConfigFileRegistry) getConfig(path tspath.Path) *tsoptions.ParsedCommandLine {
115116
entry, ok := c.ConfigFiles.Load(path)
116117
if ok {
118+
entry.mu.RLock()
119+
defer entry.mu.RUnlock()
117120
return entry.commandLine
118121
}
119122
return nil
@@ -199,48 +202,58 @@ func (c *ConfigFileRegistry) onConfigChange(path tspath.Path, changeKind lsproto
199202
return false
200203
}
201204
entry.mu.Lock()
202-
defer entry.mu.Unlock()
203-
if entry.SetPendingReload(PendingReloadFull) {
204-
for info := range entry.infos.Keys() {
205-
delete(c.defaultProjectFinder.configFileForOpenFiles, info.Path())
206-
delete(c.defaultProjectFinder.configFilesAncestorForOpenFiles, info.Path())
207-
}
208-
for project := range entry.projects.Keys() {
209-
if project.configFilePath == path {
210-
switch changeKind {
211-
case lsproto.FileChangeTypeCreated:
212-
fallthrough
213-
case lsproto.FileChangeTypeChanged:
214-
project.deferredClose = false
215-
project.SetPendingReload(PendingReloadFull)
216-
case lsproto.FileChangeTypeDeleted:
217-
project.deferredClose = true
218-
}
219-
} else {
220-
project.markAsDirty()
205+
hasSet := entry.SetPendingReload(PendingReloadFull)
206+
var infos map[*ScriptInfo]struct{}
207+
var projects map[*Project]struct{}
208+
if hasSet {
209+
infos = maps.Clone(entry.infos.Keys())
210+
projects = maps.Clone(entry.projects.Keys())
211+
}
212+
entry.mu.Unlock()
213+
if !hasSet {
214+
return false
215+
}
216+
for info := range infos {
217+
delete(c.defaultProjectFinder.configFileForOpenFiles, info.Path())
218+
delete(c.defaultProjectFinder.configFilesAncestorForOpenFiles, info.Path())
219+
}
220+
for project := range projects {
221+
if project.configFilePath == path {
222+
switch changeKind {
223+
case lsproto.FileChangeTypeCreated:
224+
fallthrough
225+
case lsproto.FileChangeTypeChanged:
226+
project.deferredClose = false
227+
project.SetPendingReload(PendingReloadFull)
228+
case lsproto.FileChangeTypeDeleted:
229+
project.deferredClose = true
221230
}
231+
} else {
232+
project.markAsDirty()
222233
}
223-
return true
224234
}
225-
return false
235+
return true
226236
}
227237

228238
func (c *ConfigFileRegistry) tryInvokeWildCardDirectories(fileName string, path tspath.Path) {
229239
configFiles := c.ConfigFiles.ToMap()
230240
for configPath, entry := range configFiles {
231241
entry.mu.Lock()
232-
if entry.commandLine != nil && entry.commandLine.MatchesFileName(fileName) {
233-
if entry.SetPendingReload(PendingReloadFileNames) {
234-
for project := range entry.projects.Keys() {
235-
if project.configFilePath == configPath {
236-
project.SetPendingReload(PendingReloadFileNames)
237-
} else {
238-
project.markAsDirty()
239-
}
242+
hasSet := entry.commandLine != nil && entry.commandLine.MatchesFileName(fileName) && entry.SetPendingReload(PendingReloadFileNames)
243+
var projects map[*Project]struct{}
244+
if hasSet {
245+
projects = maps.Clone(entry.projects.Keys())
246+
}
247+
entry.mu.Unlock()
248+
if hasSet {
249+
for project := range projects {
250+
if project.configFilePath == configPath {
251+
project.SetPendingReload(PendingReloadFileNames)
252+
} else {
253+
project.markAsDirty()
240254
}
241255
}
242256
}
243-
entry.mu.Unlock()
244257
}
245258
}
246259

0 commit comments

Comments
 (0)