1
mirror of https://github.com/rclone/rclone synced 2024-11-10 08:08:35 +01:00

vfs: fix deadlock in mount tests

This was caused by this sequence of calls

1> file.Release
1> file.close  -> takes the file lock
2> vfs.waitforWriters
2> dir.walk -> takes the dir lock
1> file.setObject
1> dir.addObject -> attempts to take the dir lock - BLOCKS
2> file.activeWriters -> tries to take file lock - BLOCKS - DEADLOCK

The fix is to make activeWriters not take the file lock and use atomic
operations to read the number of writers instead.
This commit is contained in:
Nick Craig-Wood 2018-03-09 09:49:41 +00:00
parent 7713acf23d
commit a2336ad774

View File

@ -23,6 +23,7 @@ type File struct {
leaf string // leaf name of the object leaf string // leaf name of the object
rwOpenCount int // number of open files on this handle rwOpenCount int // number of open files on this handle
writers []Handle // writers for this file writers []Handle // writers for this file
nwriters int32 // len(writers) which is read/updated with atomic
readWriters int // how many RWFileHandle are open for writing readWriters int // how many RWFileHandle are open for writing
readWriterClosing bool // is a RWFileHandle currently cosing? readWriterClosing bool // is a RWFileHandle currently cosing?
modified bool // has the cache file be modified by a RWFileHandle? modified bool // has the cache file be modified by a RWFileHandle?
@ -103,6 +104,7 @@ func (f *File) rename(d *Dir, o fs.Object) {
func (f *File) addWriter(h Handle) { func (f *File) addWriter(h Handle) {
f.mu.Lock() f.mu.Lock()
f.writers = append(f.writers, h) f.writers = append(f.writers, h)
atomic.AddInt32(&f.nwriters, 1)
if _, ok := h.(*RWFileHandle); ok { if _, ok := h.(*RWFileHandle); ok {
f.readWriters++ f.readWriters++
} }
@ -122,6 +124,7 @@ func (f *File) delWriter(h Handle, modifiedCacheFile bool) (lastWriterAndModifie
} }
if found >= 0 { if found >= 0 {
f.writers = append(f.writers[:found], f.writers[found+1:]...) f.writers = append(f.writers[:found], f.writers[found+1:]...)
atomic.AddInt32(&f.nwriters, -1)
} else { } else {
fs.Debugf(f.o, "File.delWriter couldn't find handle") fs.Debugf(f.o, "File.delWriter couldn't find handle")
} }
@ -172,10 +175,11 @@ func (f *File) finishWriterClose() {
} }
// activeWriters returns the number of writers on the file // activeWriters returns the number of writers on the file
//
// Note that we don't take the mutex here. If we do then we can get a
// deadlock.
func (f *File) activeWriters() int { func (f *File) activeWriters() int {
f.mu.Lock() return int(atomic.LoadInt32(&f.nwriters))
defer f.mu.Unlock()
return len(f.writers)
} }
// ModTime returns the modified time of the file // ModTime returns the modified time of the file