662a624819
Testing showed that while race condition analysis in #3880 was correct, the way it tries to fix that does not work for the *first* git-lfs process that will actually perform file move. Instead, this commit performs multiple attempts when working with files in LFS storage. Similar logic is already implemented in "cmd/go/internal/robustio" and "cmd/go/internal/renameio" packages. However, they are not public, so we cannot use them.
60 lines
1.1 KiB
Go
60 lines
1.1 KiB
Go
// +build windows
|
|
|
|
package tools
|
|
|
|
import (
|
|
"os"
|
|
"syscall"
|
|
|
|
"github.com/avast/retry-go"
|
|
)
|
|
|
|
const (
|
|
// This is private in [go]/src/internal/syscall/windows/syscall_windows.go :(
|
|
ERROR_SHARING_VIOLATION syscall.Errno = 32
|
|
)
|
|
|
|
func underlyingError(err error) error {
|
|
switch err := err.(type) {
|
|
case *os.PathError:
|
|
return err.Err
|
|
case *os.LinkError:
|
|
return err.Err
|
|
case *os.SyscallError:
|
|
return err.Err
|
|
}
|
|
return err
|
|
}
|
|
|
|
// isEphemeralError returns true if err may be resolved by waiting.
|
|
func isEphemeralError(err error) bool {
|
|
// TODO: Use this instead for Go >= 1.13
|
|
// return errors.Is(err, ERROR_SHARING_VIOLATION)
|
|
|
|
err = underlyingError(err)
|
|
return err == ERROR_SHARING_VIOLATION
|
|
}
|
|
|
|
func RobustRename(oldpath, newpath string) error {
|
|
return retry.Do(
|
|
func() error {
|
|
return os.Rename(oldpath, newpath)
|
|
},
|
|
retry.RetryIf(isEphemeralError),
|
|
retry.LastErrorOnly(true),
|
|
)
|
|
}
|
|
|
|
func RobustOpen(name string) (*os.File, error) {
|
|
var result *os.File
|
|
return result, retry.Do(
|
|
func() error {
|
|
f, err := os.Open(name)
|
|
result = f
|
|
return err
|
|
},
|
|
retry.RetryIf(isEphemeralError),
|
|
retry.LastErrorOnly(true),
|
|
)
|
|
}
|