Skip to content

Commit 2f2c5e4

Browse files
committed
cmd/go/internal/par: use generic Cache
Using generics here makes the code easier to understand, as the contract is clearly specified. It also makes the code a little more concise, as it's easy to write a wrapper for the cache that adds an error value, meaning that a bunch of auxilliary types no longer need to be defined for this common case. The load.cachingRepo code has been changed to use a separate cache for each key-value type combination, which seems a bit less sleazy, but might have some knock-on effect on memory usage, and could easily be changed back if desired. Because there's no longer an unambiguous way to find out whether there's an entry in the cache, the Cache.Get method now returns a bool as well as the value itself. Change-Id: I28443125bab0b3720cc95d750e72d28e9b96257d Reviewed-on: https://go-review.googlesource.com/c/go/+/463843 Reviewed-by: Bryan Mills <[email protected]> Run-TryBot: roger peppe <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent 9222a01 commit 2f2c5e4

File tree

17 files changed

+241
-322
lines changed

17 files changed

+241
-322
lines changed

src/cmd/go/internal/load/pkg.go

+32-44
Original file line numberDiff line numberDiff line change
@@ -612,11 +612,11 @@ func ClearPackageCachePartial(args []string) {
612612
delete(packageCache, arg)
613613
}
614614
}
615-
resolvedImportCache.DeleteIf(func(key any) bool {
616-
return shouldDelete[key.(importSpec).path]
615+
resolvedImportCache.DeleteIf(func(key importSpec) bool {
616+
return shouldDelete[key.path]
617617
})
618-
packageDataCache.DeleteIf(func(key any) bool {
619-
return shouldDelete[key.(string)]
618+
packageDataCache.DeleteIf(func(key string) bool {
619+
return shouldDelete[key]
620620
})
621621
}
622622

@@ -628,8 +628,8 @@ func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package {
628628
p := packageCache[arg]
629629
if p != nil {
630630
delete(packageCache, arg)
631-
resolvedImportCache.DeleteIf(func(key any) bool {
632-
return key.(importSpec).path == p.ImportPath
631+
resolvedImportCache.DeleteIf(func(key importSpec) bool {
632+
return key.path == p.ImportPath
633633
})
634634
packageDataCache.Delete(p.ImportPath)
635635
}
@@ -846,7 +846,7 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo
846846
parentIsStd: parentIsStd,
847847
mode: mode,
848848
}
849-
r := resolvedImportCache.Do(importKey, func() any {
849+
r := resolvedImportCache.Do(importKey, func() resolvedImport {
850850
var r resolvedImport
851851
if cfg.ModulesEnabled {
852852
r.dir, r.path, r.err = modload.Lookup(parentPath, parentIsStd, path)
@@ -866,16 +866,19 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo
866866
r.path = path
867867
}
868868
return r
869-
}).(resolvedImport)
869+
})
870870
// Invariant: r.path is set to the resolved import path. If the path cannot
871871
// be resolved, r.path is set to path, the source import path.
872872
// r.path is never empty.
873873

874874
// Load the package from its directory. If we already found the package's
875875
// directory when resolving its import path, use that.
876-
data := packageDataCache.Do(r.path, func() any {
876+
p, err := packageDataCache.Do(r.path, func() (*build.Package, error) {
877877
loaded = true
878-
var data packageData
878+
var data struct {
879+
p *build.Package
880+
err error
881+
}
879882
if r.dir != "" {
880883
var buildMode build.ImportMode
881884
buildContext := cfg.BuildContext
@@ -961,10 +964,10 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo
961964
!strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") {
962965
data.err = fmt.Errorf("code in directory %s expects import %q", data.p.Dir, data.p.ImportComment)
963966
}
964-
return data
965-
}).(packageData)
967+
return data.p, data.err
968+
})
966969

967-
return data.p, loaded, data.err
970+
return p, loaded, err
968971
}
969972

970973
// importSpec describes an import declaration in source code. It is used as a
@@ -984,20 +987,11 @@ type resolvedImport struct {
984987
err error
985988
}
986989

987-
// packageData holds information loaded from a package. It is the value type
988-
// in packageDataCache.
989-
type packageData struct {
990-
p *build.Package
991-
err error
992-
}
993-
994-
// resolvedImportCache maps import strings (importSpec) to canonical package names
995-
// (resolvedImport).
996-
var resolvedImportCache par.Cache
990+
// resolvedImportCache maps import strings to canonical package names.
991+
var resolvedImportCache par.Cache[importSpec, resolvedImport]
997992

998-
// packageDataCache maps canonical package names (string) to package metadata
999-
// (packageData).
1000-
var packageDataCache par.Cache
993+
// packageDataCache maps canonical package names (string) to package metadata.
994+
var packageDataCache par.ErrCache[string, *build.Package]
1001995

1002996
// preloadWorkerCount is the number of concurrent goroutines that can load
1003997
// packages. Experimentally, there are diminishing returns with more than
@@ -1109,13 +1103,13 @@ func cleanImport(path string) string {
11091103
return path
11101104
}
11111105

1112-
var isDirCache par.Cache
1106+
var isDirCache par.Cache[string, bool]
11131107

11141108
func isDir(path string) bool {
1115-
return isDirCache.Do(path, func() any {
1109+
return isDirCache.Do(path, func() bool {
11161110
fi, err := fsys.Stat(path)
11171111
return err == nil && fi.IsDir()
1118-
}).(bool)
1112+
})
11191113
}
11201114

11211115
// ResolveImportPath returns the true meaning of path when it appears in parent.
@@ -1236,12 +1230,12 @@ func vendoredImportPath(path, parentPath, parentDir, parentRoot string) (found s
12361230

12371231
var (
12381232
modulePrefix = []byte("\nmodule ")
1239-
goModPathCache par.Cache
1233+
goModPathCache par.Cache[string, string]
12401234
)
12411235

12421236
// goModPath returns the module path in the go.mod in dir, if any.
12431237
func goModPath(dir string) (path string) {
1244-
return goModPathCache.Do(dir, func() any {
1238+
return goModPathCache.Do(dir, func() string {
12451239
data, err := os.ReadFile(filepath.Join(dir, "go.mod"))
12461240
if err != nil {
12471241
return ""
@@ -1277,7 +1271,7 @@ func goModPath(dir string) (path string) {
12771271
path = s
12781272
}
12791273
return path
1280-
}).(string)
1274+
})
12811275
}
12821276

12831277
// findVersionElement returns the slice indices of the final version element /vN in path.
@@ -2264,8 +2258,8 @@ func (p *Package) collectDeps() {
22642258
}
22652259

22662260
// vcsStatusCache maps repository directories (string)
2267-
// to their VCS information (vcsStatusError).
2268-
var vcsStatusCache par.Cache
2261+
// to their VCS information.
2262+
var vcsStatusCache par.ErrCache[string, vcs.Status]
22692263

22702264
// setBuildInfo gathers build information, formats it as a string to be
22712265
// embedded in the binary, then sets p.Internal.BuildInfo to that string.
@@ -2517,19 +2511,13 @@ func (p *Package) setBuildInfo(autoVCS bool) {
25172511
goto omitVCS
25182512
}
25192513

2520-
type vcsStatusError struct {
2521-
Status vcs.Status
2522-
Err error
2523-
}
2524-
cached := vcsStatusCache.Do(repoDir, func() any {
2525-
st, err := vcsCmd.Status(vcsCmd, repoDir)
2526-
return vcsStatusError{st, err}
2527-
}).(vcsStatusError)
2528-
if err := cached.Err; err != nil {
2514+
st, err := vcsStatusCache.Do(repoDir, func() (vcs.Status, error) {
2515+
return vcsCmd.Status(vcsCmd, repoDir)
2516+
})
2517+
if err != nil {
25292518
setVCSError(err)
25302519
return
25312520
}
2532-
st := cached.Status
25332521

25342522
appendSetting("vcs", vcsCmd.Cmd)
25352523
if st.Revision != "" {

src/cmd/go/internal/modfetch/cache.go

+35-47
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,11 @@ func SideLock() (unlock func(), err error) {
169169
// (so that it can be returned from Lookup multiple times).
170170
// It serializes calls to the underlying Repo.
171171
type cachingRepo struct {
172-
path string
173-
cache par.Cache // cache for all operations
172+
path string
173+
versionsCache par.ErrCache[string, *Versions]
174+
statCache par.ErrCache[string, *RevInfo]
175+
latestCache par.ErrCache[struct{}, *RevInfo]
176+
gomodCache par.ErrCache[string, []byte]
174177

175178
once sync.Once
176179
initRepo func() (Repo, error)
@@ -204,23 +207,17 @@ func (r *cachingRepo) ModulePath() string {
204207
}
205208

206209
func (r *cachingRepo) Versions(prefix string) (*Versions, error) {
207-
type cached struct {
208-
v *Versions
209-
err error
210-
}
211-
c := r.cache.Do("versions:"+prefix, func() any {
212-
v, err := r.repo().Versions(prefix)
213-
return cached{v, err}
214-
}).(cached)
210+
v, err := r.versionsCache.Do(prefix, func() (*Versions, error) {
211+
return r.repo().Versions(prefix)
212+
})
215213

216-
if c.err != nil {
217-
return nil, c.err
218-
}
219-
v := &Versions{
220-
Origin: c.v.Origin,
221-
List: append([]string(nil), c.v.List...),
214+
if err != nil {
215+
return nil, err
222216
}
223-
return v, nil
217+
return &Versions{
218+
Origin: v.Origin,
219+
List: append([]string(nil), v.List...),
220+
}, nil
224221
}
225222

226223
type cachedInfo struct {
@@ -229,10 +226,10 @@ type cachedInfo struct {
229226
}
230227

231228
func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
232-
c := r.cache.Do("stat:"+rev, func() any {
229+
info, err := r.statCache.Do(rev, func() (*RevInfo, error) {
233230
file, info, err := readDiskStat(r.path, rev)
234231
if err == nil {
235-
return cachedInfo{info, nil}
232+
return info, err
236233
}
237234

238235
info, err = r.repo().Stat(rev)
@@ -241,79 +238,70 @@ func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
241238
// then save the information under the proper version, for future use.
242239
if info.Version != rev {
243240
file, _ = CachePath(module.Version{Path: r.path, Version: info.Version}, "info")
244-
r.cache.Do("stat:"+info.Version, func() any {
245-
return cachedInfo{info, err}
241+
r.statCache.Do(info.Version, func() (*RevInfo, error) {
242+
return info, nil
246243
})
247244
}
248245

249246
if err := writeDiskStat(file, info); err != nil {
250247
fmt.Fprintf(os.Stderr, "go: writing stat cache: %v\n", err)
251248
}
252249
}
253-
return cachedInfo{info, err}
254-
}).(cachedInfo)
255-
256-
info := c.info
250+
return info, err
251+
})
257252
if info != nil {
258253
copy := *info
259254
info = &copy
260255
}
261-
return info, c.err
256+
return info, err
262257
}
263258

264259
func (r *cachingRepo) Latest() (*RevInfo, error) {
265-
c := r.cache.Do("latest:", func() any {
260+
info, err := r.latestCache.Do(struct{}{}, func() (*RevInfo, error) {
266261
info, err := r.repo().Latest()
267262

268263
// Save info for likely future Stat call.
269264
if err == nil {
270-
r.cache.Do("stat:"+info.Version, func() any {
271-
return cachedInfo{info, err}
265+
r.statCache.Do(info.Version, func() (*RevInfo, error) {
266+
return info, nil
272267
})
273268
if file, _, err := readDiskStat(r.path, info.Version); err != nil {
274269
writeDiskStat(file, info)
275270
}
276271
}
277272

278-
return cachedInfo{info, err}
279-
}).(cachedInfo)
280-
281-
info := c.info
273+
return info, err
274+
})
282275
if info != nil {
283276
copy := *info
284277
info = &copy
285278
}
286-
return info, c.err
279+
return info, err
287280
}
288281

289282
func (r *cachingRepo) GoMod(version string) ([]byte, error) {
290-
type cached struct {
291-
text []byte
292-
err error
293-
}
294-
c := r.cache.Do("gomod:"+version, func() any {
283+
text, err := r.gomodCache.Do(version, func() ([]byte, error) {
295284
file, text, err := readDiskGoMod(r.path, version)
296285
if err == nil {
297286
// Note: readDiskGoMod already called checkGoMod.
298-
return cached{text, nil}
287+
return text, nil
299288
}
300289

301290
text, err = r.repo().GoMod(version)
302291
if err == nil {
303292
if err := checkGoMod(r.path, version, text); err != nil {
304-
return cached{text, err}
293+
return text, err
305294
}
306295
if err := writeDiskGoMod(file, text); err != nil {
307296
fmt.Fprintf(os.Stderr, "go: writing go.mod cache: %v\n", err)
308297
}
309298
}
310-
return cached{text, err}
311-
}).(cached)
312-
313-
if c.err != nil {
314-
return nil, c.err
299+
return text, err
300+
})
301+
if err != nil {
302+
return nil, err
315303
}
316-
return append([]byte(nil), c.text...), nil
304+
return append([]byte(nil), text...), nil
317305
}
318306

319307
func (r *cachingRepo) Zip(dst io.Writer, version string) error {

src/cmd/go/internal/modfetch/codehost/git.go

+13-26
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,17 @@ func (notExistError) Is(err error) bool { return err == fs.ErrNotExist }
4646

4747
const gitWorkDirType = "git3"
4848

49-
var gitRepoCache par.Cache
49+
var gitRepoCache par.ErrCache[gitCacheKey, Repo]
5050

51-
func newGitRepoCached(remote string, localOK bool) (Repo, error) {
52-
type key struct {
53-
remote string
54-
localOK bool
55-
}
56-
type cached struct {
57-
repo Repo
58-
err error
59-
}
60-
61-
c := gitRepoCache.Do(key{remote, localOK}, func() any {
62-
repo, err := newGitRepo(remote, localOK)
63-
return cached{repo, err}
64-
}).(cached)
51+
type gitCacheKey struct {
52+
remote string
53+
localOK bool
54+
}
6555

66-
return c.repo, c.err
56+
func newGitRepoCached(remote string, localOK bool) (Repo, error) {
57+
return gitRepoCache.Do(gitCacheKey{remote, localOK}, func() (Repo, error) {
58+
return newGitRepo(remote, localOK)
59+
})
6760
}
6861

6962
func newGitRepo(remote string, localOK bool) (Repo, error) {
@@ -132,7 +125,7 @@ type gitRepo struct {
132125

133126
fetchLevel int
134127

135-
statCache par.Cache
128+
statCache par.ErrCache[string, *RevInfo]
136129

137130
refsOnce sync.Once
138131
// refs maps branch and tag refs (e.g., "HEAD", "refs/heads/master")
@@ -637,15 +630,9 @@ func (r *gitRepo) Stat(rev string) (*RevInfo, error) {
637630
if rev == "latest" {
638631
return r.Latest()
639632
}
640-
type cached struct {
641-
info *RevInfo
642-
err error
643-
}
644-
c := r.statCache.Do(rev, func() any {
645-
info, err := r.stat(rev)
646-
return cached{info, err}
647-
}).(cached)
648-
return c.info, c.err
633+
return r.statCache.Do(rev, func() (*RevInfo, error) {
634+
return r.stat(rev)
635+
})
649636
}
650637

651638
func (r *gitRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {

0 commit comments

Comments
 (0)