From: Robert Griesemer Date: Tue, 28 Feb 2017 21:38:04 +0000 (-0800) Subject: go/internal/srcimporter: parse files concurrently (fixes TODO) X-Git-Tag: go1.9beta1~1376 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c861a4c78695038832bb4cf92a47c5e09566721b;p=gostls13.git go/internal/srcimporter: parse files concurrently (fixes TODO) Passes go test -race. Change-Id: I14b5b1b1a8ad1e43d60013823d71d78a6519581f Reviewed-on: https://go-review.googlesource.com/37588 Run-TryBot: Robert Griesemer Reviewed-by: Alan Donovan TryBot-Result: Gobot Gobot --- diff --git a/src/go/internal/srcimporter/srcimporter.go b/src/go/internal/srcimporter/srcimporter.go index 9e20a23cdb..62ee7b6bdf 100644 --- a/src/go/internal/srcimporter/srcimporter.go +++ b/src/go/internal/srcimporter/srcimporter.go @@ -14,6 +14,7 @@ import ( "go/token" "go/types" "path/filepath" + "sync" ) // An Importer provides the context for importing packages from source code. @@ -116,31 +117,9 @@ func (p *Importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*type filenames = append(filenames, bp.GoFiles...) filenames = append(filenames, bp.CgoFiles...) - // parse package files - // TODO(gri) do this concurrently - var files []*ast.File - for _, filename := range filenames { - filepath := p.joinPath(bp.Dir, filename) - var file *ast.File - if open := p.ctxt.OpenFile; open != nil { - f, err := open(filepath) - if err != nil { - return nil, fmt.Errorf("opening package file %s failed (%v)", filepath, err) - } - file, err = parser.ParseFile(p.fset, filepath, f, 0) - f.Close() // ignore Close error - import may still succeed - } else { - // Special-case when ctxt doesn't provide a custom OpenFile and use the - // parser's file reading mechanism directly. This appears to be quite a - // bit faster than opening the file and providing an io.ReaderCloser in - // both cases. - // TODO(gri) investigate performance difference (issue #19281) - file, err = parser.ParseFile(p.fset, filepath, nil, 0) - } - if err != nil { - return nil, fmt.Errorf("parsing package file %s failed (%v)", filepath, err) - } - files = append(files, file) + files, err := p.parseFiles(bp.Dir, filenames) + if err != nil { + return nil, err } // type-check package files @@ -159,6 +138,47 @@ func (p *Importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*type return pkg, nil } +func (p *Importer) parseFiles(dir string, filenames []string) ([]*ast.File, error) { + open := p.ctxt.OpenFile // possibly nil + + files := make([]*ast.File, len(filenames)) + errors := make([]error, len(filenames)) + + var wg sync.WaitGroup + wg.Add(len(filenames)) + for i, filename := range filenames { + go func(i int, filepath string) { + defer wg.Done() + if open != nil { + src, err := open(filepath) + if err != nil { + errors[i] = fmt.Errorf("opening package file %s failed (%v)", filepath, err) + return + } + files[i], errors[i] = parser.ParseFile(p.fset, filepath, src, 0) + src.Close() // ignore Close error - parsing may have succeeded which is all we need + } else { + // Special-case when ctxt doesn't provide a custom OpenFile and use the + // parser's file reading mechanism directly. This appears to be quite a + // bit faster than opening the file and providing an io.ReaderCloser in + // both cases. + // TODO(gri) investigate performance difference (issue #19281) + files[i], errors[i] = parser.ParseFile(p.fset, filepath, nil, 0) + } + }(i, p.joinPath(dir, filename)) + } + wg.Wait() + + // if there are errors, return the first one for deterministic results + for _, err := range errors { + if err != nil { + return nil, err + } + } + + return files, nil +} + // context-controlled file system operations func (p *Importer) absPath(path string) (string, error) {