mirror of https://github.com/spf13/afero.git
mem/file.go - Fix some races in accessing fields of FileData
* Splitting SetModeTime to avoid double locking * Adding locks all over the place.
This commit is contained in:
parent
9be650865e
commit
d29940f63d
|
@ -16,5 +16,5 @@ matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go test -v ./...
|
- go test -race -v ./...
|
||||||
- go build
|
- go build
|
||||||
|
|
|
@ -12,4 +12,4 @@ build_script:
|
||||||
|
|
||||||
go build github.com/spf13/afero
|
go build github.com/spf13/afero
|
||||||
test_script:
|
test_script:
|
||||||
- cmd: go test -v github.com/spf13/afero
|
- cmd: go test --race -v github.com/spf13/afero
|
||||||
|
|
38
mem/file.go
38
mem/file.go
|
@ -74,14 +74,24 @@ func CreateDir(name string) *FileData {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChangeFileName(f *FileData, newname string) {
|
func ChangeFileName(f *FileData, newname string) {
|
||||||
|
f.Lock()
|
||||||
f.name = newname
|
f.name = newname
|
||||||
|
f.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetMode(f *FileData, mode os.FileMode) {
|
func SetMode(f *FileData, mode os.FileMode) {
|
||||||
|
f.Lock()
|
||||||
f.mode = mode
|
f.mode = mode
|
||||||
|
f.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetModTime(f *FileData, mtime time.Time) {
|
func SetModTime(f *FileData, mtime time.Time) {
|
||||||
|
f.Lock()
|
||||||
|
setModTime(f, mtime)
|
||||||
|
f.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setModTime(f *FileData, mtime time.Time) {
|
||||||
f.modtime = mtime
|
f.modtime = mtime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +112,7 @@ func (f *File) Close() error {
|
||||||
f.fileData.Lock()
|
f.fileData.Lock()
|
||||||
f.closed = true
|
f.closed = true
|
||||||
if !f.readOnly {
|
if !f.readOnly {
|
||||||
SetModTime(f.fileData, time.Now())
|
setModTime(f.fileData, time.Now())
|
||||||
}
|
}
|
||||||
f.fileData.Unlock()
|
f.fileData.Unlock()
|
||||||
return nil
|
return nil
|
||||||
|
@ -197,7 +207,7 @@ func (f *File) Truncate(size int64) error {
|
||||||
} else {
|
} else {
|
||||||
f.fileData.data = f.fileData.data[0:size]
|
f.fileData.data = f.fileData.data[0:size]
|
||||||
}
|
}
|
||||||
SetModTime(f.fileData, time.Now())
|
setModTime(f.fileData, time.Now())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +246,7 @@ func (f *File) Write(b []byte) (n int, err error) {
|
||||||
f.fileData.data = append(f.fileData.data[:cur], b...)
|
f.fileData.data = append(f.fileData.data[:cur], b...)
|
||||||
f.fileData.data = append(f.fileData.data, tail...)
|
f.fileData.data = append(f.fileData.data, tail...)
|
||||||
}
|
}
|
||||||
SetModTime(f.fileData, time.Now())
|
setModTime(f.fileData, time.Now())
|
||||||
|
|
||||||
atomic.StoreInt64(&f.at, int64(len(f.fileData.data)))
|
atomic.StoreInt64(&f.at, int64(len(f.fileData.data)))
|
||||||
return
|
return
|
||||||
|
@ -261,17 +271,33 @@ type FileInfo struct {
|
||||||
|
|
||||||
// Implements os.FileInfo
|
// Implements os.FileInfo
|
||||||
func (s *FileInfo) Name() string {
|
func (s *FileInfo) Name() string {
|
||||||
|
s.Lock()
|
||||||
_, name := filepath.Split(s.name)
|
_, name := filepath.Split(s.name)
|
||||||
|
s.Unlock()
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
func (s *FileInfo) Mode() os.FileMode { return s.mode }
|
func (s *FileInfo) Mode() os.FileMode {
|
||||||
func (s *FileInfo) ModTime() time.Time { return s.modtime }
|
s.Lock()
|
||||||
func (s *FileInfo) IsDir() bool { return s.dir }
|
defer s.Unlock()
|
||||||
|
return s.mode
|
||||||
|
}
|
||||||
|
func (s *FileInfo) ModTime() time.Time {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
return s.modtime
|
||||||
|
}
|
||||||
|
func (s *FileInfo) IsDir() bool {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
return s.dir
|
||||||
|
}
|
||||||
func (s *FileInfo) Sys() interface{} { return nil }
|
func (s *FileInfo) Sys() interface{} { return nil }
|
||||||
func (s *FileInfo) Size() int64 {
|
func (s *FileInfo) Size() int64 {
|
||||||
if s.IsDir() {
|
if s.IsDir() {
|
||||||
return int64(42)
|
return int64(42)
|
||||||
}
|
}
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
return int64(len(s.data))
|
return int64(len(s.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
package mem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_FileData_Name_withConcurrence(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
const someName = "someName"
|
||||||
|
const someOtherName = "someOtherName"
|
||||||
|
d := FileData{
|
||||||
|
name: someName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.Name() != someName {
|
||||||
|
t.Errorf("Failed to read correct Name, was %v", d.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeFileName(&d, someOtherName)
|
||||||
|
if d.Name() != someOtherName {
|
||||||
|
t.Errorf("Failed to set Name, was %v", d.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ChangeFileName(&d, someName)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if d.Name() != someName && d.Name() != someOtherName {
|
||||||
|
t.Errorf("Failed to read either Name, was %v", d.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_FileData_ModTime_withConcurrence(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
someTime := time.Now()
|
||||||
|
someOtherTime := someTime.Add(1 * time.Minute)
|
||||||
|
|
||||||
|
d := FileData{
|
||||||
|
modtime: someTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
s := FileInfo{
|
||||||
|
FileData: &d,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ModTime() != someTime {
|
||||||
|
t.Errorf("Failed to read correct value, was %v", s.ModTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
SetModTime(&d, someOtherTime)
|
||||||
|
if s.ModTime() != someOtherTime {
|
||||||
|
t.Errorf("Failed to set ModTime, was %v", s.ModTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
SetModTime(&d, someTime)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if s.ModTime() != someTime && s.ModTime() != someOtherTime {
|
||||||
|
t.Errorf("Failed to read either modtime, was %v", s.ModTime())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_FileData_Mode_withConcurrence(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
const someMode = 0777
|
||||||
|
const someOtherMode = 0660
|
||||||
|
|
||||||
|
d := FileData{
|
||||||
|
mode: someMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
s := FileInfo{
|
||||||
|
FileData: &d,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Mode() != someMode {
|
||||||
|
t.Errorf("Failed to read correct value, was %v", s.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
SetMode(&d, someOtherMode)
|
||||||
|
if s.Mode() != someOtherMode {
|
||||||
|
t.Errorf("Failed to set Mode, was %v", s.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
SetMode(&d, someMode)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if s.Mode() != someMode && s.Mode() != someOtherMode {
|
||||||
|
t.Errorf("Failed to read either mode, was %v", s.Mode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_FileData_IsDir_withConcurrence(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d := FileData{
|
||||||
|
dir: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
s := FileInfo{
|
||||||
|
FileData: &d,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.IsDir() != true {
|
||||||
|
t.Errorf("Failed to read correct value, was %v", s.IsDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
s.Lock()
|
||||||
|
d.dir = false
|
||||||
|
s.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
//just logging the value to trigger a read:
|
||||||
|
t.Logf("Value is %v", s.IsDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_FileData_Size_withConcurrence(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const someData = "Hello"
|
||||||
|
const someOtherDataSize = "Hello World"
|
||||||
|
|
||||||
|
d := FileData{
|
||||||
|
data: []byte(someData),
|
||||||
|
dir: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
s := FileInfo{
|
||||||
|
FileData: &d,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Size() != int64(len(someData)) {
|
||||||
|
t.Errorf("Failed to read correct value, was %v", s.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
s.Lock()
|
||||||
|
d.data = []byte(someOtherDataSize)
|
||||||
|
s.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
//just logging the value to trigger a read:
|
||||||
|
t.Logf("Value is %v", s.Size())
|
||||||
|
|
||||||
|
//Testing the Dir size case
|
||||||
|
d.dir = true
|
||||||
|
if s.Size() != int64(42) {
|
||||||
|
t.Errorf("Failed to read correct value for dir, was %v", s.Size())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue