mirror of
https://github.com/golang/go
synced 2024-11-26 09:18:07 -07:00
go/token: add (*FileSet).RemoveFile(*File) method
The design of FileSet encourages it to be used as a global variable. Each call to AddFile consumes about 3KB, that is never returned, even after an application no longer cares about the File. This change adds a RemoveFile method that a long-running application can use to release a File that is no longer needed, saving memory. Fixes golang/go#53200 Change-Id: Ifd34d650fe0d18b1395f922a4cd02a535afbe560 Reviewed-on: https://go-review.googlesource.com/c/go/+/410114 Auto-Submit: Alan Donovan <adonovan@google.com> Run-TryBot: Alan Donovan <adonovan@google.com> Reviewed-by: Robert Griesemer <gri@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
98f3eb2d3e
commit
a45bbeae33
1
api/next/53200.txt
Normal file
1
api/next/53200.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
pkg go/token, method (*FileSet) RemoveFile(*File) #53200
|
@ -366,6 +366,9 @@ func (f *File) Position(p Pos) (pos Position) {
|
|||||||
// recently added file, plus one. Unless there is a need to extend an
|
// recently added file, plus one. Unless there is a need to extend an
|
||||||
// interval later, using the FileSet.Base should be used as argument
|
// interval later, using the FileSet.Base should be used as argument
|
||||||
// for FileSet.AddFile.
|
// for FileSet.AddFile.
|
||||||
|
//
|
||||||
|
// A File may be removed from a FileSet when it is no longer needed.
|
||||||
|
// This may reduce memory usage in a long-running application.
|
||||||
type FileSet struct {
|
type FileSet struct {
|
||||||
mutex sync.RWMutex // protects the file set
|
mutex sync.RWMutex // protects the file set
|
||||||
base int // base offset for the next file
|
base int // base offset for the next file
|
||||||
@ -433,6 +436,25 @@ func (s *FileSet) AddFile(filename string, base, size int) *File {
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveFile removes a file from the FileSet so that subsequent
|
||||||
|
// queries for its Pos interval yield a negative result.
|
||||||
|
// This reduces the memory usage of a long-lived FileSet that
|
||||||
|
// encounters an unbounded stream of files.
|
||||||
|
//
|
||||||
|
// Removing a file that does not belong to the set has no effect.
|
||||||
|
func (s *FileSet) RemoveFile(file *File) {
|
||||||
|
s.last.CompareAndSwap(file, nil) // clear last file cache
|
||||||
|
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
if i := searchFiles(s.files, file.base); i >= 0 && s.files[i] == file {
|
||||||
|
last := &s.files[len(s.files)-1]
|
||||||
|
s.files = append(s.files[:i], s.files[i+1:]...)
|
||||||
|
*last = nil // don't prolong lifetime when popping last element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate calls f for the files in the file set in the order they were added
|
// Iterate calls f for the files in the file set in the order they were added
|
||||||
// until f returns false.
|
// until f returns false.
|
||||||
func (s *FileSet) Iterate(f func(*File) bool) {
|
func (s *FileSet) Iterate(f func(*File) bool) {
|
||||||
|
@ -339,3 +339,44 @@ func TestLineStart(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRemoveFile(t *testing.T) {
|
||||||
|
contentA := []byte("this\nis\nfileA")
|
||||||
|
contentB := []byte("this\nis\nfileB")
|
||||||
|
fset := NewFileSet()
|
||||||
|
a := fset.AddFile("fileA", -1, len(contentA))
|
||||||
|
a.SetLinesForContent(contentA)
|
||||||
|
b := fset.AddFile("fileB", -1, len(contentB))
|
||||||
|
b.SetLinesForContent(contentB)
|
||||||
|
|
||||||
|
checkPos := func(pos Pos, want string) {
|
||||||
|
if got := fset.Position(pos).String(); got != want {
|
||||||
|
t.Errorf("Position(%d) = %s, want %s", pos, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkNumFiles := func(want int) {
|
||||||
|
got := 0
|
||||||
|
fset.Iterate(func(*File) bool { got++; return true })
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("Iterate called %d times, want %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apos3 := a.Pos(3)
|
||||||
|
bpos3 := b.Pos(3)
|
||||||
|
checkPos(apos3, "fileA:1:4")
|
||||||
|
checkPos(bpos3, "fileB:1:4")
|
||||||
|
checkNumFiles(2)
|
||||||
|
|
||||||
|
// After removal, queries on fileA fail.
|
||||||
|
fset.RemoveFile(a)
|
||||||
|
checkPos(apos3, "-")
|
||||||
|
checkPos(bpos3, "fileB:1:4")
|
||||||
|
checkNumFiles(1)
|
||||||
|
|
||||||
|
// idempotent / no effect
|
||||||
|
fset.RemoveFile(a)
|
||||||
|
checkPos(apos3, "-")
|
||||||
|
checkPos(bpos3, "fileB:1:4")
|
||||||
|
checkNumFiles(1)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user