mirror of https://github.com/Icinga/icinga2.git
Merge pull request #8691 from Icinga/bugfix/retry-rename-on-windows
Retry file rename operations on Windows for some errors
This commit is contained in:
commit
0ee93754ee
|
@ -742,7 +742,59 @@ void Utility::RenameFile(const String& source, const String& target)
|
||||||
{
|
{
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
fs::rename(fs::path(source.Begin(), source.End()), fs::path(target.Begin(), target.End()));
|
fs::path sourcePath(source.Begin(), source.End()), targetPath(target.Begin(), target.End());
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
fs::rename(sourcePath, targetPath);
|
||||||
|
#else /* _WIN32 */
|
||||||
|
/*
|
||||||
|
* Renaming files can be tricky on Windows, especially if your application is built around POSIX filesystem
|
||||||
|
* semantics. For example, the quite common pattern of replacing a file by writing a new version to a temporary
|
||||||
|
* location and then moving it to the final location can fail if the destination file already exists and any
|
||||||
|
* process has an open file handle to it.
|
||||||
|
*
|
||||||
|
* We try to handle this situation as best as we can by retrying the rename operation a few times hoping the other
|
||||||
|
* process closes its file handle in the meantime. This is similar to what for example Go does internally in some
|
||||||
|
* situations (https://golang.org/pkg/cmd/go/internal/robustio/#Rename):
|
||||||
|
*
|
||||||
|
* robustio.Rename is like os.Rename, but on Windows retries errors that may occur if the file is concurrently
|
||||||
|
* read or overwritten. (See https://golang.org/issue/31247 and https://golang.org/issue/32188)
|
||||||
|
*/
|
||||||
|
|
||||||
|
double sleep = 0.1;
|
||||||
|
int last_error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
for (int retries = 0, remaining = 15;; retries++, remaining--) {
|
||||||
|
try {
|
||||||
|
fs::rename(sourcePath, targetPath);
|
||||||
|
|
||||||
|
if (retries > 0) {
|
||||||
|
Log(LogWarning, "Utility") << "Renaming '" << source << "' to '" << target
|
||||||
|
<< "' succeeded after " << retries << " retries";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} catch (const fs::filesystem_error& ex) {
|
||||||
|
int error = ex.code().value();
|
||||||
|
bool ephemeral = error == ERROR_ACCESS_DENIED ||
|
||||||
|
error == ERROR_FILE_NOT_FOUND ||
|
||||||
|
error == ERROR_SHARING_VIOLATION;
|
||||||
|
|
||||||
|
if (remaining <= 0 || !ephemeral) {
|
||||||
|
throw; // giving up
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != last_error) {
|
||||||
|
Log(LogWarning, "Utility") << "Renaming '" << source << "' to '" << target << "' failed: "
|
||||||
|
<< ex.code().message() << " (trying up to " << remaining << " more times)";
|
||||||
|
last_error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utility::Sleep(sleep);
|
||||||
|
sleep *= 1.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* _WIN32 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue