diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2013-03-04 21:27:36 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-04 21:27:36 +0100 |
commit | 04b08da9af0c450d645ab7389d1467308cfc2db8 (patch) | |
tree | db247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/pkg/go/token | |
parent | 917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff) | |
download | golang-upstream/1.1_hg20130304.tar.gz |
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/pkg/go/token')
-rw-r--r-- | src/pkg/go/token/position.go | 22 | ||||
-rw-r--r-- | src/pkg/go/token/position_test.go | 51 |
2 files changed, 63 insertions, 10 deletions
diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go index 647d1b770..f5d999561 100644 --- a/src/pkg/go/token/position.go +++ b/src/pkg/go/token/position.go @@ -76,7 +76,7 @@ type Pos int // associated with it, and NoPos().IsValid() is false. NoPos is always // smaller than any other Pos value. The corresponding Position value // for NoPos is the zero value for Position. -// +// const NoPos Pos = 0 // IsValid returns true if the position is valid. @@ -295,9 +295,9 @@ type FileSet struct { // NewFileSet creates a new file set. func NewFileSet() *FileSet { - s := new(FileSet) - s.base = 1 // 0 == NoPos - return s + return &FileSet{ + base: 1, // 0 == NoPos + } } // Base returns the minimum base offset that must be provided to @@ -347,7 +347,7 @@ func (s *FileSet) AddFile(filename string, base, size int) *File { // Iterate calls f for the files in the file set in the order they were added // until f returns false. -// +// func (s *FileSet) Iterate(f func(*File) bool) { for i := 0; ; i++ { var file *File @@ -367,8 +367,10 @@ func searchFiles(a []*File, x int) int { } func (s *FileSet) file(p Pos) *File { + s.mutex.RLock() // common case: p is in last file if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size { + s.mutex.RUnlock() return f } // p is not in last file - search all files @@ -376,10 +378,14 @@ func (s *FileSet) file(p Pos) *File { f := s.files[i] // f.base <= int(p) by definition of searchFiles if int(p) <= f.base+f.size { - s.last = f + s.mutex.RUnlock() + s.mutex.Lock() + s.last = f // race is ok - s.last is only a cache + s.mutex.Unlock() return f } } + s.mutex.RUnlock() return nil } @@ -389,9 +395,7 @@ func (s *FileSet) file(p Pos) *File { // func (s *FileSet) File(p Pos) (f *File) { if p != NoPos { - s.mutex.RLock() f = s.file(p) - s.mutex.RUnlock() } return } @@ -399,11 +403,9 @@ func (s *FileSet) File(p Pos) (f *File) { // Position converts a Pos in the fileset into a general Position. func (s *FileSet) Position(p Pos) (pos Position) { if p != NoPos { - s.mutex.RLock() if f := s.file(p); f != nil { pos = f.position(p) } - s.mutex.RUnlock() } return } diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go index 160107df4..1d36c2226 100644 --- a/src/pkg/go/token/position_test.go +++ b/src/pkg/go/token/position_test.go @@ -6,6 +6,8 @@ package token import ( "fmt" + "math/rand" + "sync" "testing" ) @@ -179,3 +181,52 @@ func TestFiles(t *testing.T) { } } } + +// FileSet.File should return nil if Pos is past the end of the FileSet. +func TestFileSetPastEnd(t *testing.T) { + fset := NewFileSet() + for _, test := range tests { + fset.AddFile(test.filename, fset.Base(), test.size) + } + if f := fset.File(Pos(fset.Base())); f != nil { + t.Errorf("expected nil, got %v", f) + } +} + +func TestFileSetCacheUnlikely(t *testing.T) { + fset := NewFileSet() + offsets := make(map[string]int) + for _, test := range tests { + offsets[test.filename] = fset.Base() + fset.AddFile(test.filename, fset.Base(), test.size) + } + for file, pos := range offsets { + f := fset.File(Pos(pos)) + if f.Name() != file { + t.Errorf("expecting %q at position %d, got %q", file, pos, f.Name()) + } + } +} + +// issue 4345. Test concurrent use of FileSet.Pos does not trigger a +// race in the FileSet position cache. +func TestFileSetRace(t *testing.T) { + fset := NewFileSet() + for i := 0; i < 100; i++ { + fset.AddFile(fmt.Sprintf("file-%d", i), fset.Base(), 1031) + } + max := int32(fset.Base()) + var stop sync.WaitGroup + r := rand.New(rand.NewSource(7)) + for i := 0; i < 2; i++ { + r := rand.New(rand.NewSource(r.Int63())) + stop.Add(1) + go func() { + for i := 0; i < 1000; i++ { + fset.Position(Pos(r.Int31n(max))) + } + stop.Done() + }() + } + stop.Wait() +} |