vfs: factor waiting code from read and writes into common function
This commit is contained in:
parent
3de9bd9d04
commit
0f9267d5fc
2 changed files with 39 additions and 56 deletions
71
vfs/read.go
71
vfs/read.go
|
@ -212,6 +212,43 @@ func (fh *ReadFileHandle) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
return fh.readAt(p, off)
|
return fh.readAt(p, off)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This waits for *poff to equal off or aborts after the timeout.
|
||||||
|
//
|
||||||
|
// Waits here potentially affect all seeks so need to keep them short
|
||||||
|
//
|
||||||
|
// Call with fh.mu Locked
|
||||||
|
func waitSequential(what string, remote string, cond *sync.Cond, maxWait time.Duration, poff *int64, off int64) {
|
||||||
|
var (
|
||||||
|
timeout = time.NewTimer(maxWait)
|
||||||
|
done = make(chan struct{})
|
||||||
|
abort = false
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-timeout.C:
|
||||||
|
// take the lock to make sure that cond.Wait() is called before
|
||||||
|
// cond.Broadcast. NB cond.L == mu
|
||||||
|
cond.L.Lock()
|
||||||
|
// set abort flag and give all the waiting goroutines a kick on timeout
|
||||||
|
abort = true
|
||||||
|
fs.Debugf(remote, "aborting in-sequence %s wait, off=%d", what, off)
|
||||||
|
cond.Broadcast()
|
||||||
|
cond.L.Unlock()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for *poff != off && !abort {
|
||||||
|
fs.Debugf(remote, "waiting for in-sequence %s to %d for %v", what, off, maxWait)
|
||||||
|
cond.Wait()
|
||||||
|
}
|
||||||
|
// tidy up end timer
|
||||||
|
close(done)
|
||||||
|
timeout.Stop()
|
||||||
|
if *poff != off {
|
||||||
|
fs.Debugf(remote, "failed to wait for in-sequence %s to %d", what, off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implementation of ReadAt - call with lock held
|
// Implementation of ReadAt - call with lock held
|
||||||
func (fh *ReadFileHandle) readAt(p []byte, off int64) (n int, err error) {
|
func (fh *ReadFileHandle) readAt(p []byte, off int64) (n int, err error) {
|
||||||
// defer log.Trace(fh.remote, "p[%d], off=%d", len(p), off)("n=%d, err=%v", &n, &err)
|
// defer log.Trace(fh.remote, "p[%d], off=%d", len(p), off)("n=%d, err=%v", &n, &err)
|
||||||
|
@ -229,39 +266,7 @@ func (fh *ReadFileHandle) readAt(p []byte, off int64) (n int, err error) {
|
||||||
maxBuf = len(p)
|
maxBuf = len(p)
|
||||||
}
|
}
|
||||||
if gap := off - fh.offset; gap > 0 && gap < int64(8*maxBuf) {
|
if gap := off - fh.offset; gap > 0 && gap < int64(8*maxBuf) {
|
||||||
// Set a background timer so we don't wait for long
|
waitSequential("read", fh.remote, fh.cond, fh.file.VFS().Opt.ReadWait, &fh.offset, off)
|
||||||
// Waits here potentially affect all seeks so need to keep them short
|
|
||||||
// The default time here was made by finding the
|
|
||||||
// smallest when mounting a local backend that didn't
|
|
||||||
// cause seeks.
|
|
||||||
maxWait := fh.file.VFS().Opt.ReadWait
|
|
||||||
timeout := time.NewTimer(maxWait)
|
|
||||||
done := make(chan struct{})
|
|
||||||
abort := false
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-timeout.C:
|
|
||||||
// take the lock to make sure that fh.cond.Wait() is called before
|
|
||||||
// fh.cond.Broadcast. NB fh.cond.L == fh.mu
|
|
||||||
fh.mu.Lock()
|
|
||||||
// set abort flag and give all the waiting goroutines a kick on timeout
|
|
||||||
abort = true
|
|
||||||
fs.Debugf(fh.remote, "aborting in-sequence read wait, off=%d", off)
|
|
||||||
fh.cond.Broadcast()
|
|
||||||
fh.mu.Unlock()
|
|
||||||
case <-done:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for fh.offset != off && !abort {
|
|
||||||
fs.Debugf(fh.remote, "waiting for in-sequence read to %d for %v", off, maxWait)
|
|
||||||
fh.cond.Wait()
|
|
||||||
}
|
|
||||||
// tidy up end timer
|
|
||||||
close(done)
|
|
||||||
timeout.Stop()
|
|
||||||
if fh.offset != off {
|
|
||||||
fs.Debugf(fh.remote, "failed to wait for in-sequence read to %d", off)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
doSeek := off != fh.offset
|
doSeek := off != fh.offset
|
||||||
if doSeek && fh.noSeek {
|
if doSeek && fh.noSeek {
|
||||||
|
|
24
vfs/write.go
24
vfs/write.go
|
@ -5,7 +5,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
@ -131,28 +130,7 @@ func (fh *WriteFileHandle) writeAt(p []byte, off int64) (n int, err error) {
|
||||||
return 0, ECLOSED
|
return 0, ECLOSED
|
||||||
}
|
}
|
||||||
if fh.offset != off {
|
if fh.offset != off {
|
||||||
// Set a background timer so we don't wait forever
|
waitSequential("write", fh.remote, fh.cond, fh.file.VFS().Opt.WriteWait, &fh.offset, off)
|
||||||
maxWait := fh.file.VFS().Opt.WriteWait
|
|
||||||
timeout := time.NewTimer(maxWait)
|
|
||||||
done := make(chan struct{})
|
|
||||||
abort := int32(0)
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-timeout.C:
|
|
||||||
// set abort flag an give all the waiting goroutines a kick on timeout
|
|
||||||
atomic.StoreInt32(&abort, 1)
|
|
||||||
fh.cond.Broadcast()
|
|
||||||
case <-done:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// Wait for an in-sequence write or abort
|
|
||||||
for fh.offset != off && atomic.LoadInt32(&abort) == 0 {
|
|
||||||
// fs.Debugf(fh.remote, "waiting for in-sequence write to %d", off)
|
|
||||||
fh.cond.Wait()
|
|
||||||
}
|
|
||||||
// tidy up end timer
|
|
||||||
close(done)
|
|
||||||
timeout.Stop()
|
|
||||||
}
|
}
|
||||||
if fh.offset != off {
|
if fh.offset != off {
|
||||||
fs.Errorf(fh.remote, "WriteFileHandle.Write: can't seek in file without --vfs-cache-mode >= writes")
|
fs.Errorf(fh.remote, "WriteFileHandle.Write: can't seek in file without --vfs-cache-mode >= writes")
|
||||||
|
|
Loading…
Reference in a new issue