@@ -16,13 +16,13 @@ import (
16
16
"go/scanner"
17
17
"go/token"
18
18
"go/types"
19
- "io"
20
19
"log"
21
20
"os"
22
21
"path/filepath"
23
22
"runtime"
24
23
"strings"
25
24
"sync"
25
+ "sync/atomic"
26
26
"time"
27
27
28
28
"golang.org/x/sync/errgroup"
@@ -55,7 +55,7 @@ const (
55
55
// NeedName adds Name and PkgPath.
56
56
NeedName LoadMode = 1 << iota
57
57
58
- // NeedFiles adds GoFiles and OtherFiles.
58
+ // NeedFiles adds GoFiles, OtherFiles, and IgnoredFiles
59
59
NeedFiles
60
60
61
61
// NeedCompiledGoFiles adds CompiledGoFiles.
@@ -77,7 +77,7 @@ const (
77
77
// NeedSyntax adds Syntax and Fset.
78
78
NeedSyntax
79
79
80
- // NeedTypesInfo adds TypesInfo.
80
+ // NeedTypesInfo adds TypesInfo and Fset .
81
81
NeedTypesInfo
82
82
83
83
// NeedTypesSizes adds TypesSizes.
@@ -691,18 +691,19 @@ func (p *Package) String() string { return p.ID }
691
691
// loaderPackage augments Package with state used during the loading phase
692
692
type loaderPackage struct {
693
693
* Package
694
- importErrors map [string ]error // maps each bad import to its error
695
- loadOnce sync.Once
696
- color uint8 // for cycle detection
697
- needsrc bool // load from source (Mode >= LoadTypes)
698
- needtypes bool // type information is either requested or depended on
699
- initial bool // package was matched by a pattern
700
- goVersion int // minor version number of go command on PATH
694
+ importErrors map [string ]error // maps each bad import to its error
695
+ preds []* loaderPackage // packages that import this one
696
+ unfinishedSuccs atomic.Int32 // number of direct imports not yet loaded
697
+ color uint8 // for cycle detection
698
+ needsrc bool // load from source (Mode >= LoadTypes)
699
+ needtypes bool // type information is either requested or depended on
700
+ initial bool // package was matched by a pattern
701
+ goVersion int // minor version number of go command on PATH
701
702
}
702
703
703
704
// loader holds the working state of a single call to load.
704
705
type loader struct {
705
- pkgs map [string ]* loaderPackage
706
+ pkgs map [string ]* loaderPackage // keyed by Package.ID
706
707
Config
707
708
sizes types.Sizes // non-nil if needed by mode
708
709
parseCache map [string ]* parseValue
@@ -764,7 +765,7 @@ func newLoader(cfg *Config) *loader {
764
765
ld .requestedMode = ld .Mode
765
766
ld .Mode = impliedLoadMode (ld .Mode )
766
767
767
- if ld .Mode & (NeedTypes | NeedSyntax | NeedTypesInfo ) != 0 {
768
+ if ld .Mode & (NeedSyntax | NeedTypes | NeedTypesInfo ) != 0 {
768
769
if ld .Fset == nil {
769
770
ld .Fset = token .NewFileSet ()
770
771
}
@@ -805,7 +806,7 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
805
806
exportDataInvalid := len (ld .Overlay ) > 0 || pkg .ExportFile == "" && pkg .PkgPath != "unsafe"
806
807
// This package needs type information if the caller requested types and the package is
807
808
// either a root, or it's a non-root and the user requested dependencies ...
808
- needtypes := (ld .Mode & NeedTypes | NeedTypesInfo != 0 && (rootIndex >= 0 || ld .Mode & NeedDeps != 0 ))
809
+ needtypes := (ld .Mode & ( NeedTypes | NeedTypesInfo ) != 0 && (rootIndex >= 0 || ld .Mode & NeedDeps != 0 ))
809
810
// This package needs source if the call requested source (or types info, which implies source)
810
811
// and the package is either a root, or itas a non- root and the user requested dependencies...
811
812
needsrc := ((ld .Mode & (NeedSyntax | NeedTypesInfo ) != 0 && (rootIndex >= 0 || ld .Mode & NeedDeps != 0 )) ||
@@ -830,9 +831,10 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
830
831
}
831
832
}
832
833
833
- if ld .Mode & NeedImports != 0 {
834
- // Materialize the import graph.
835
-
834
+ // Materialize the import graph if it is needed (NeedImports),
835
+ // or if we'll be using loadPackages (Need{Syntax|Types|TypesInfo}).
836
+ var leaves []* loaderPackage // packages with no unfinished successors
837
+ if ld .Mode & (NeedImports | NeedSyntax | NeedTypes | NeedTypesInfo ) != 0 {
836
838
const (
837
839
white = 0 // new
838
840
grey = 1 // in progress
@@ -851,63 +853,76 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
851
853
// dependency on a package that does. These are the only packages
852
854
// for which we load source code.
853
855
var stack []* loaderPackage
854
- var visit func (lpkg * loaderPackage ) bool
855
- visit = func (lpkg * loaderPackage ) bool {
856
- switch lpkg .color {
857
- case black :
858
- return lpkg .needsrc
859
- case grey :
856
+ var visit func (from , lpkg * loaderPackage ) bool
857
+ visit = func (from , lpkg * loaderPackage ) bool {
858
+ if lpkg .color == grey {
860
859
panic ("internal error: grey node" )
861
860
}
862
- lpkg .color = grey
863
- stack = append (stack , lpkg ) // push
864
- stubs := lpkg .Imports // the structure form has only stubs with the ID in the Imports
865
- lpkg .Imports = make (map [string ]* Package , len (stubs ))
866
- for importPath , ipkg := range stubs {
867
- var importErr error
868
- imp := ld .pkgs [ipkg .ID ]
869
- if imp == nil {
870
- // (includes package "C" when DisableCgo)
871
- importErr = fmt .Errorf ("missing package: %q" , ipkg .ID )
872
- } else if imp .color == grey {
873
- importErr = fmt .Errorf ("import cycle: %s" , stack )
861
+ if lpkg .color == white {
862
+ lpkg .color = grey
863
+ stack = append (stack , lpkg ) // push
864
+ stubs := lpkg .Imports // the structure form has only stubs with the ID in the Imports
865
+ lpkg .Imports = make (map [string ]* Package , len (stubs ))
866
+ for importPath , ipkg := range stubs {
867
+ var importErr error
868
+ imp := ld .pkgs [ipkg .ID ]
869
+ if imp == nil {
870
+ // (includes package "C" when DisableCgo)
871
+ importErr = fmt .Errorf ("missing package: %q" , ipkg .ID )
872
+ } else if imp .color == grey {
873
+ importErr = fmt .Errorf ("import cycle: %s" , stack )
874
+ }
875
+ if importErr != nil {
876
+ if lpkg .importErrors == nil {
877
+ lpkg .importErrors = make (map [string ]error )
878
+ }
879
+ lpkg .importErrors [importPath ] = importErr
880
+ continue
881
+ }
882
+
883
+ if visit (lpkg , imp ) {
884
+ lpkg .needsrc = true
885
+ }
886
+ lpkg .Imports [importPath ] = imp .Package
874
887
}
875
- if importErr != nil {
876
- if lpkg .importErrors == nil {
877
- lpkg .importErrors = make (map [string ]error )
888
+
889
+ // -- postorder --
890
+
891
+ // Complete type information is required for the
892
+ // immediate dependencies of each source package.
893
+ if lpkg .needsrc && ld .Mode & NeedTypes != 0 {
894
+ for _ , ipkg := range lpkg .Imports {
895
+ ld .pkgs [ipkg .ID ].needtypes = true
878
896
}
879
- lpkg .importErrors [importPath ] = importErr
880
- continue
881
897
}
882
898
883
- if visit (imp ) {
884
- lpkg .needsrc = true
899
+ // NeedTypeSizes causes TypeSizes to be set even
900
+ // on packages for which types aren't needed.
901
+ if ld .Mode & NeedTypesSizes != 0 {
902
+ lpkg .TypesSizes = ld .sizes
885
903
}
886
- lpkg .Imports [importPath ] = imp .Package
887
- }
888
904
889
- // Complete type information is required for the
890
- // immediate dependencies of each source package.
891
- if lpkg .needsrc && ld .Mode & NeedTypes != 0 {
892
- for _ , ipkg := range lpkg .Imports {
893
- ld .pkgs [ipkg .ID ].needtypes = true
905
+ // Add packages with no imports directly to the queue of leaves.
906
+ if len (lpkg .Imports ) == 0 {
907
+ leaves = append (leaves , lpkg )
894
908
}
909
+
910
+ stack = stack [:len (stack )- 1 ] // pop
911
+ lpkg .color = black
895
912
}
896
913
897
- // NeedTypeSizes causes TypeSizes to be set even
898
- // on packages for which types aren't needed.
899
- if ld . Mode & NeedTypesSizes != 0 {
900
- lpkg .TypesSizes = ld . sizes
914
+ // Add edge from predecessor.
915
+ if from != nil {
916
+ from . unfinishedSuccs . Add ( + 1 ) // incref
917
+ lpkg .preds = append ( lpkg . preds , from )
901
918
}
902
- stack = stack [:len (stack )- 1 ] // pop
903
- lpkg .color = black
904
919
905
920
return lpkg .needsrc
906
921
}
907
922
908
923
// For each initial package, create its import DAG.
909
924
for _ , lpkg := range initial {
910
- visit (lpkg )
925
+ visit (nil , lpkg )
911
926
}
912
927
913
928
} else {
@@ -920,16 +935,45 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
920
935
921
936
// Load type data and syntax if needed, starting at
922
937
// the initial packages (roots of the import DAG).
923
- if ld .Mode & (NeedTypes | NeedSyntax | NeedTypesInfo ) != 0 {
924
- var wg sync.WaitGroup
925
- for _ , lpkg := range initial {
926
- wg .Add (1 )
927
- go func (lpkg * loaderPackage ) {
928
- ld .loadRecursive (lpkg )
929
- wg .Done ()
930
- }(lpkg )
938
+ if ld .Mode & (NeedSyntax | NeedTypes | NeedTypesInfo ) != 0 {
939
+
940
+ // We avoid using g.SetLimit to limit concurrency as
941
+ // it makes g.Go stop accepting work, which prevents
942
+ // workers from enqeuing, and thus finishing, and thus
943
+ // allowing the group to make progress: deadlock.
944
+ //
945
+ // Instead we use the ioLimit and cpuLimit semaphores.
946
+ g , _ := errgroup .WithContext (ld .Context )
947
+
948
+ // enqueues adds a package to the type-checking queue.
949
+ // It must have no unfinished successors.
950
+ var enqueue func (* loaderPackage )
951
+ enqueue = func (lpkg * loaderPackage ) {
952
+ g .Go (func () error {
953
+ // Parse and type-check.
954
+ ld .loadPackage (lpkg )
955
+
956
+ // Notify each waiting predecessor,
957
+ // and enqueue it when it becomes a leaf.
958
+ for _ , pred := range lpkg .preds {
959
+ if pred .unfinishedSuccs .Add (- 1 ) == 0 { // decref
960
+ enqueue (pred )
961
+ }
962
+ }
963
+
964
+ return nil
965
+ })
966
+ }
967
+
968
+ // Load leaves first, adding new packages
969
+ // to the queue as they become leaves.
970
+ for _ , leaf := range leaves {
971
+ enqueue (leaf )
972
+ }
973
+
974
+ if err := g .Wait (); err != nil {
975
+ return nil , err // cancelled
931
976
}
932
- wg .Wait ()
933
977
}
934
978
935
979
// If the context is done, return its error and
@@ -976,7 +1020,7 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
976
1020
if ld .requestedMode & NeedSyntax == 0 {
977
1021
ld .pkgs [i ].Syntax = nil
978
1022
}
979
- if ld .requestedMode & NeedTypes == 0 && ld . requestedMode & NeedSyntax == 0 {
1023
+ if ld .requestedMode & ( NeedSyntax | NeedTypes | NeedTypesInfo ) == 0 {
980
1024
ld .pkgs [i ].Fset = nil
981
1025
}
982
1026
if ld .requestedMode & NeedTypesInfo == 0 {
@@ -993,31 +1037,10 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
993
1037
return result , nil
994
1038
}
995
1039
996
- // loadRecursive loads the specified package and its dependencies,
997
- // recursively, in parallel, in topological order.
998
- // It is atomic and idempotent.
999
- // Precondition: ld.Mode&NeedTypes.
1000
- func (ld * loader ) loadRecursive (lpkg * loaderPackage ) {
1001
- lpkg .loadOnce .Do (func () {
1002
- // Load the direct dependencies, in parallel.
1003
- var wg sync.WaitGroup
1004
- for _ , ipkg := range lpkg .Imports {
1005
- imp := ld .pkgs [ipkg .ID ]
1006
- wg .Add (1 )
1007
- go func (imp * loaderPackage ) {
1008
- ld .loadRecursive (imp )
1009
- wg .Done ()
1010
- }(imp )
1011
- }
1012
- wg .Wait ()
1013
- ld .loadPackage (lpkg )
1014
- })
1015
- }
1016
-
1017
- // loadPackage loads the specified package.
1040
+ // loadPackage loads/parses/typechecks the specified package.
1018
1041
// It must be called only once per Package,
1019
1042
// after immediate dependencies are loaded.
1020
- // Precondition: ld.Mode & NeedTypes.
1043
+ // Precondition: ld.Mode&(NeedSyntax| NeedTypes|NeedTypesInfo) != 0 .
1021
1044
func (ld * loader ) loadPackage (lpkg * loaderPackage ) {
1022
1045
if lpkg .PkgPath == "unsafe" {
1023
1046
// Fill in the blanks to avoid surprises.
@@ -1053,6 +1076,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
1053
1076
if ! lpkg .needtypes && ! lpkg .needsrc {
1054
1077
return
1055
1078
}
1079
+
1080
+ // TODO(adonovan): this condition looks wrong:
1081
+ // I think it should be lpkg.needtypes && !lpg.needsrc,
1082
+ // so that NeedSyntax without NeedTypes can be satisfied by export data.
1056
1083
if ! lpkg .needsrc {
1057
1084
if err := ld .loadFromExportData (lpkg ); err != nil {
1058
1085
lpkg .Errors = append (lpkg .Errors , Error {
@@ -1169,15 +1196,19 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
1169
1196
return
1170
1197
}
1171
1198
1172
- lpkg .TypesInfo = & types.Info {
1173
- Types : make (map [ast.Expr ]types.TypeAndValue ),
1174
- Defs : make (map [* ast.Ident ]types.Object ),
1175
- Uses : make (map [* ast.Ident ]types.Object ),
1176
- Implicits : make (map [ast.Node ]types.Object ),
1177
- Instances : make (map [* ast.Ident ]types.Instance ),
1178
- Scopes : make (map [ast.Node ]* types.Scope ),
1179
- Selections : make (map [* ast.SelectorExpr ]* types.Selection ),
1180
- FileVersions : make (map [* ast.File ]string ),
1199
+ // Populate TypesInfo only if needed, as it
1200
+ // causes the type checker to work much harder.
1201
+ if ld .Config .Mode & NeedTypesInfo != 0 {
1202
+ lpkg .TypesInfo = & types.Info {
1203
+ Types : make (map [ast.Expr ]types.TypeAndValue ),
1204
+ Defs : make (map [* ast.Ident ]types.Object ),
1205
+ Uses : make (map [* ast.Ident ]types.Object ),
1206
+ Implicits : make (map [ast.Node ]types.Object ),
1207
+ Instances : make (map [* ast.Ident ]types.Instance ),
1208
+ Scopes : make (map [ast.Node ]* types.Scope ),
1209
+ Selections : make (map [* ast.SelectorExpr ]* types.Selection ),
1210
+ FileVersions : make (map [* ast.File ]string ),
1211
+ }
1181
1212
}
1182
1213
lpkg .TypesSizes = ld .sizes
1183
1214
@@ -1231,6 +1262,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
1231
1262
}
1232
1263
}
1233
1264
1265
+ // Type-checking is CPU intensive.
1266
+ cpuLimit <- unit {} // acquire a token
1267
+ defer func () { <- cpuLimit }() // release a token
1268
+
1234
1269
typErr := types .NewChecker (tc , ld .Fset , lpkg .Types , lpkg .TypesInfo ).Files (lpkg .Syntax )
1235
1270
lpkg .importErrors = nil // no longer needed
1236
1271
@@ -1295,8 +1330,11 @@ type importerFunc func(path string) (*types.Package, error)
1295
1330
func (f importerFunc ) Import (path string ) (* types.Package , error ) { return f (path ) }
1296
1331
1297
1332
// We use a counting semaphore to limit
1298
- // the number of parallel I/O calls per process.
1299
- var ioLimit = make (chan bool , 20 )
1333
+ // the number of parallel I/O calls or CPU threads per process.
1334
+ var (
1335
+ ioLimit = make (chan unit , 20 )
1336
+ cpuLimit = make (chan unit , runtime .GOMAXPROCS (0 ))
1337
+ )
1300
1338
1301
1339
func (ld * loader ) parseFile (filename string ) (* ast.File , error ) {
1302
1340
ld .parseCacheMu .Lock ()
@@ -1324,14 +1362,17 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) {
1324
1362
}
1325
1363
var err error
1326
1364
if src == nil {
1327
- ioLimit <- true // acquire a token
1365
+ ioLimit <- unit {} // acquire a token
1328
1366
src , err = os .ReadFile (filename )
1329
1367
<- ioLimit // release a token
1330
1368
}
1331
1369
if err != nil {
1332
1370
v .err = err
1333
1371
} else {
1372
+ // Parsing is CPU intensive.
1373
+ cpuLimit <- unit {} // acquire a token
1334
1374
v .f , v .err = ld .ParseFile (ld .Fset , filename , src )
1375
+ <- cpuLimit // release a token
1335
1376
}
1336
1377
1337
1378
close (v .ready )
@@ -1531,4 +1572,4 @@ func usesExportData(cfg *Config) bool {
1531
1572
return cfg .Mode & NeedExportFile != 0 || cfg .Mode & NeedTypes != 0 && cfg .Mode & NeedDeps == 0
1532
1573
}
1533
1574
1534
- var _ interface {} = io . Discard // assert build toolchain is go1.16 or later
1575
+ type unit struct {}
0 commit comments