go/loader: limit concurrency of I/O operations during parsing

This is a putative fix for the file descriptor exhaustion problem
described in https://github.com/golang/go/issues/10306.

Change-Id: If603fb9bbaec1b53f6b44d15b2c202e4670035ce
Reviewed-on: https://go-review.googlesource.com/8421
Reviewed-by: Matt Joiner <anacrolix@gmail.com>
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/go/loader/util.go b/go/loader/util.go
index 1166c92..0404e99 100644
--- a/go/loader/util.go
+++ b/go/loader/util.go
@@ -17,6 +17,10 @@
 	"golang.org/x/tools/go/buildutil"
 )
 
+// We use a counting semaphore to limit
+// the number of parallel I/O calls per process.
+var sema = make(chan bool, 10)
+
 // parseFiles parses the Go source files within directory dir and
 // returns the ASTs of the ones that could be at least partially parsed,
 // along with a list of I/O and parse errors encountered.
@@ -38,7 +42,11 @@
 		}
 		wg.Add(1)
 		go func(i int, file string) {
-			defer wg.Done()
+			sema <- true // wait
+			defer func() {
+				wg.Done()
+				<-sema // signal
+			}()
 			var rd io.ReadCloser
 			var err error
 			if ctxt.OpenFile != nil {