]> Cypherpunks repositories - gostls13.git/commitdiff
go/internal/srcimporter: parse files concurrently (fixes TODO)
authorRobert Griesemer <gri@golang.org>
Tue, 28 Feb 2017 21:38:04 +0000 (13:38 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 1 Mar 2017 01:21:00 +0000 (01:21 +0000)
Passes go test -race.

Change-Id: I14b5b1b1a8ad1e43d60013823d71d78a6519581f
Reviewed-on: https://go-review.googlesource.com/37588
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/go/internal/srcimporter/srcimporter.go

index 9e20a23cdb3a7e62fd4766a6ad126037f0c3e1de..62ee7b6bdf5146943e39c30317cb1d6909c7c8e5 100644 (file)
@@ -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) {