From 59db9754095662b2d2bd6fe9dbf4e9d494424f0f Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 16 Dec 2013 11:25:50 -0800 Subject: [PATCH] godoc: limit concurrency to local disk filesystem Not for Go 1.2. Still needs a flag. Linux at least (and likely other OSes) don't like you doing a few hundred readdirs at once and spawing as many threads. R=golang-dev, adg, jeremyjackins CC=golang-dev https://golang.org/cl/30620043 --- cmd/godoc/main.go | 8 +++- godoc/vfs/gatefs/gatefs.go | 89 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 godoc/vfs/gatefs/gatefs.go diff --git a/cmd/godoc/main.go b/cmd/godoc/main.go index c9065784..bf35c80f 100644 --- a/cmd/godoc/main.go +++ b/cmd/godoc/main.go @@ -46,6 +46,7 @@ import ( "code.google.com/p/go.tools/godoc" "code.google.com/p/go.tools/godoc/static" "code.google.com/p/go.tools/godoc/vfs" + "code.google.com/p/go.tools/godoc/vfs/gatefs" "code.google.com/p/go.tools/godoc/vfs/mapfs" "code.google.com/p/go.tools/godoc/vfs/zipfs" ) @@ -162,10 +163,13 @@ func main() { usage() } + var fsGate chan bool + fsGate = make(chan bool, 20) + // Determine file system to use. if *zipfile == "" { // use file system of underlying OS - fs.Bind("/", vfs.OS(*goroot), "/", vfs.BindReplace) + fs.Bind("/", gatefs.New(vfs.OS(*goroot), fsGate), "/", vfs.BindReplace) } else { // use file system specified via .zip file (path separator must be '/') rc, err := zip.OpenReader(*zipfile) @@ -183,7 +187,7 @@ func main() { // Bind $GOPATH trees into Go root. for _, p := range filepath.SplitList(build.Default.GOPATH) { - fs.Bind("/src/pkg", vfs.OS(p), "/src", vfs.BindAfter) + fs.Bind("/src/pkg", gatefs.New(vfs.OS(p), fsGate), "/src", vfs.BindAfter) } httpMode := *httpAddr != "" diff --git a/godoc/vfs/gatefs/gatefs.go b/godoc/vfs/gatefs/gatefs.go new file mode 100644 index 00000000..2e6c47f1 --- /dev/null +++ b/godoc/vfs/gatefs/gatefs.go @@ -0,0 +1,89 @@ +// Copyright 2013 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 gatefs provides an implementation of the FileSystem +// interface that wraps another FileSystem and limits its concurrency. +package gatefs + +import ( + "fmt" + "os" + + "code.google.com/p/go.tools/godoc/vfs" +) + +// New returns a new FileSystem that delegates to fs. +// If gateCh is non-nil and buffered, it's used as a gate +// to limit concurrency on calls to fs. +func New(fs vfs.FileSystem, gateCh chan bool) vfs.FileSystem { + if cap(gateCh) == 0 { + return fs + } + return gatefs{fs, gate(gateCh)} +} + +type gate chan bool + +func (g gate) enter() { g <- true } +func (g gate) leave() { <-g } + +type gatefs struct { + fs vfs.FileSystem + gate +} + +func (fs gatefs) String() string { + return fmt.Sprintf("gated(%s, %d)", fs.fs.String(), cap(fs.gate)) +} + +func (fs gatefs) Open(p string) (vfs.ReadSeekCloser, error) { + fs.enter() + defer fs.leave() + rsc, err := fs.fs.Open(p) + if err != nil { + return nil, err + } + return gatef{rsc, fs.gate}, nil +} + +func (fs gatefs) Lstat(p string) (os.FileInfo, error) { + fs.enter() + defer fs.leave() + return fs.fs.Lstat(p) +} + +func (fs gatefs) Stat(p string) (os.FileInfo, error) { + fs.enter() + defer fs.leave() + return fs.fs.Stat(p) +} + +func (fs gatefs) ReadDir(p string) ([]os.FileInfo, error) { + fs.enter() + defer fs.leave() + return fs.fs.ReadDir(p) +} + +type gatef struct { + rsc vfs.ReadSeekCloser + gate +} + +func (f gatef) Read(p []byte) (n int, err error) { + f.enter() + defer f.leave() + return f.rsc.Read(p) +} + +func (f gatef) Seek(offset int64, whence int) (ret int64, err error) { + f.enter() + defer f.leave() + return f.rsc.Seek(offset, whence) +} + +func (f gatef) Close() error { + f.enter() + defer f.leave() + return f.rsc.Close() +}