|
@@ -34,19 +34,19 @@ type sharedPullerState struct {
|
|
|
created time.Time
|
|
created time.Time
|
|
|
|
|
|
|
|
// Mutable, must be locked for access
|
|
// Mutable, must be locked for access
|
|
|
- err error // The first error we hit
|
|
|
|
|
- fd fs.File // The fd of the temp file
|
|
|
|
|
- copyTotal int // Total number of copy actions for the whole job
|
|
|
|
|
- pullTotal int // Total number of pull actions for the whole job
|
|
|
|
|
- copyOrigin int // Number of blocks copied from the original file
|
|
|
|
|
- copyOriginShifted int // Number of blocks copied from the original file but shifted
|
|
|
|
|
- copyNeeded int // Number of copy actions still pending
|
|
|
|
|
- pullNeeded int // Number of block pulls still pending
|
|
|
|
|
- updated time.Time // Time when any of the counters above were last updated
|
|
|
|
|
- closed bool // True if the file has been finalClosed.
|
|
|
|
|
- available []int32 // Indexes of the blocks that are available in the temporary file
|
|
|
|
|
- availableUpdated time.Time // Time when list of available blocks was last updated
|
|
|
|
|
- mut sync.RWMutex // Protects the above
|
|
|
|
|
|
|
+ err error // The first error we hit
|
|
|
|
|
+ writer *lockedWriterAt // Wraps fd to prevent fd closing at the same time as writing
|
|
|
|
|
+ copyTotal int // Total number of copy actions for the whole job
|
|
|
|
|
+ pullTotal int // Total number of pull actions for the whole job
|
|
|
|
|
+ copyOrigin int // Number of blocks copied from the original file
|
|
|
|
|
+ copyOriginShifted int // Number of blocks copied from the original file but shifted
|
|
|
|
|
+ copyNeeded int // Number of copy actions still pending
|
|
|
|
|
+ pullNeeded int // Number of block pulls still pending
|
|
|
|
|
+ updated time.Time // Time when any of the counters above were last updated
|
|
|
|
|
+ closed bool // True if the file has been finalClosed.
|
|
|
|
|
+ available []int32 // Indexes of the blocks that are available in the temporary file
|
|
|
|
|
+ availableUpdated time.Time // Time when list of available blocks was last updated
|
|
|
|
|
+ mut sync.RWMutex // Protects the above
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// A momentary state representing the progress of the puller
|
|
// A momentary state representing the progress of the puller
|
|
@@ -62,17 +62,32 @@ type pullerProgress struct {
|
|
|
BytesTotal int64 `json:"bytesTotal"`
|
|
BytesTotal int64 `json:"bytesTotal"`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// A lockedWriterAt synchronizes WriteAt calls with an external mutex.
|
|
|
|
|
|
|
+// lockedWriterAt adds a lock to protect from closing the fd at the same time as writing.
|
|
|
// WriteAt() is goroutine safe by itself, but not against for example Close().
|
|
// WriteAt() is goroutine safe by itself, but not against for example Close().
|
|
|
type lockedWriterAt struct {
|
|
type lockedWriterAt struct {
|
|
|
- mut *sync.RWMutex
|
|
|
|
|
- wr io.WriterAt
|
|
|
|
|
|
|
+ mut sync.RWMutex
|
|
|
|
|
+ fd fs.File
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (w lockedWriterAt) WriteAt(p []byte, off int64) (n int, err error) {
|
|
|
|
|
- (*w.mut).Lock()
|
|
|
|
|
- defer (*w.mut).Unlock()
|
|
|
|
|
- return w.wr.WriteAt(p, off)
|
|
|
|
|
|
|
+// WriteAt itself is goroutine safe, thus just needs to acquire a read-lock to
|
|
|
|
|
+// prevent closing concurrently (see SyncClose).
|
|
|
|
|
+func (w *lockedWriterAt) WriteAt(p []byte, off int64) (n int, err error) {
|
|
|
|
|
+ w.mut.RLock()
|
|
|
|
|
+ defer w.mut.RUnlock()
|
|
|
|
|
+ return w.fd.WriteAt(p, off)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SyncClose ensures that no more writes are happening before going ahead and
|
|
|
|
|
+// syncing and closing the fd, thus needs to acquire a write-lock.
|
|
|
|
|
+func (w *lockedWriterAt) SyncClose() error {
|
|
|
|
|
+ w.mut.Lock()
|
|
|
|
|
+ defer w.mut.Unlock()
|
|
|
|
|
+ if err := w.fd.Sync(); err != nil {
|
|
|
|
|
+ // Sync() is nice if it works but not worth failing the
|
|
|
|
|
+ // operation over if it fails.
|
|
|
|
|
+ l.Debugf("fsync failed: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return w.fd.Close()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// tempFile returns the fd for the temporary file, reusing an open fd
|
|
// tempFile returns the fd for the temporary file, reusing an open fd
|
|
@@ -87,8 +102,8 @@ func (s *sharedPullerState) tempFile() (io.WriterAt, error) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// If the temp file is already open, return the file descriptor
|
|
// If the temp file is already open, return the file descriptor
|
|
|
- if s.fd != nil {
|
|
|
|
|
- return lockedWriterAt{&s.mut, s.fd}, nil
|
|
|
|
|
|
|
+ if s.writer != nil {
|
|
|
|
|
+ return s.writer, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if err := inWritableDir(s.tempFileInWritableDir, s.fs, s.tempName, s.ignorePerms); err != nil {
|
|
if err := inWritableDir(s.tempFileInWritableDir, s.fs, s.tempName, s.ignorePerms); err != nil {
|
|
@@ -96,7 +111,7 @@ func (s *sharedPullerState) tempFile() (io.WriterAt, error) {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return lockedWriterAt{&s.mut, s.fd}, nil
|
|
|
|
|
|
|
+ return s.writer, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// tempFileInWritableDir should only be called from tempFile.
|
|
// tempFileInWritableDir should only be called from tempFile.
|
|
@@ -171,7 +186,7 @@ func (s *sharedPullerState) tempFileInWritableDir(_ string) error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Same fd will be used by all writers
|
|
// Same fd will be used by all writers
|
|
|
- s.fd = fd
|
|
|
|
|
|
|
+ s.writer = &lockedWriterAt{sync.NewRWMutex(), fd}
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -265,18 +280,12 @@ func (s *sharedPullerState) finalClose() (bool, error) {
|
|
|
return false, nil
|
|
return false, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if s.fd != nil {
|
|
|
|
|
- if err := s.fd.Sync(); err != nil {
|
|
|
|
|
- // Sync() is nice if it works but not worth failing the
|
|
|
|
|
- // operation over if it fails.
|
|
|
|
|
- l.Debugf("fsync %q failed: %v", s.tempName, err)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if err := s.fd.Close(); err != nil && s.err == nil {
|
|
|
|
|
|
|
+ if s.writer != nil {
|
|
|
|
|
+ if err := s.writer.SyncClose(); err != nil && s.err == nil {
|
|
|
// This is our error as we weren't errored before.
|
|
// This is our error as we weren't errored before.
|
|
|
s.err = err
|
|
s.err = err
|
|
|
}
|
|
}
|
|
|
- s.fd = nil
|
|
|
|
|
|
|
+ s.writer = nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
s.closed = true
|
|
s.closed = true
|