blob: d140d476d1af4b3ce55df0a2af2f5f3a9f17a9b6 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package modfetch
import (
"sync"
"cmd/go/internal/par"
)
// A cachingRepo is a cache around an underlying Repo,
// avoiding redundant calls to ModulePath, Versions, Stat, Latest, and GoMod (but not Zip).
// It is also safe for simultaneous use by multiple goroutines
// (so that it can be returned from Lookup multiple times).
// It serializes calls to the underlying Repo.
type cachingRepo struct {
path string
cache par.Cache // cache for all operations
mu sync.Mutex // protects r's methods
r Repo
}
func newCachingRepo(r Repo) *cachingRepo {
return &cachingRepo{
r: r,
path: r.ModulePath(),
}
}
func (r *cachingRepo) ModulePath() string {
return r.path
}
func (r *cachingRepo) Versions(prefix string) ([]string, error) {
type cached struct {
list []string
err error
}
c := r.cache.Do("versions:"+prefix, func() interface{} {
r.mu.Lock()
defer r.mu.Unlock()
list, err := r.r.Versions(prefix)
return cached{list, err}
}).(cached)
if c.err != nil {
return nil, c.err
}
return append([]string(nil), c.list...), nil
}
func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
type cached struct {
info *RevInfo
err error
}
c := r.cache.Do("stat:"+rev, func() interface{} {
r.mu.Lock()
defer r.mu.Unlock()
info, err := r.r.Stat(rev)
return cached{info, err}
}).(cached)
if c.err != nil {
return nil, c.err
}
info := *c.info
return &info, nil
}
func (r *cachingRepo) Latest() (*RevInfo, error) {
type cached struct {
info *RevInfo
err error
}
c := r.cache.Do("latest:", func() interface{} {
r.mu.Lock()
defer r.mu.Unlock()
info, err := r.r.Latest()
return cached{info, err}
}).(cached)
if c.err != nil {
return nil, c.err
}
info := *c.info
return &info, nil
}
func (r *cachingRepo) GoMod(rev string) ([]byte, error) {
type cached struct {
text []byte
err error
}
c := r.cache.Do("gomod:", func() interface{} {
r.mu.Lock()
defer r.mu.Unlock()
text, err := r.r.GoMod(rev)
return cached{text, err}
}).(cached)
if c.err != nil {
return nil, c.err
}
return append([]byte(nil), c.text...), nil
}
func (r *cachingRepo) Zip(version, tmpdir string) (string, error) {
r.mu.Lock()
defer r.mu.Unlock()
return r.r.Zip(version, tmpdir)
}