Merge pull request #11231 from shuffle2/updater

windows: Rename: use std::filesystem::rename for posix behavior
This commit is contained in:
JMC47 2022-10-30 13:32:10 -04:00 committed by GitHub
commit f277a921a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 12 additions and 49 deletions

View File

@ -33,6 +33,7 @@
#include <Shlwapi.h> #include <Shlwapi.h>
#include <commdlg.h> // for GetSaveFileName #include <commdlg.h> // for GetSaveFileName
#include <direct.h> // getcwd #include <direct.h> // getcwd
#include <filesystem>
#include <io.h> #include <io.h>
#include <objbase.h> // guid stuff #include <objbase.h> // guid stuff
#include <shellapi.h> #include <shellapi.h>
@ -322,65 +323,27 @@ bool DeleteDir(const std::string& filename, IfAbsentBehavior behavior)
return false; return false;
} }
// Repeatedly invokes func until it returns true or max_attempts failures.
// Waits after each failure, with each delay doubling in length.
template <typename FuncType>
static bool AttemptMaxTimesWithExponentialDelay(int max_attempts, std::chrono::milliseconds delay,
std::string_view func_name, const FuncType& func)
{
for (int failed_attempts = 0; failed_attempts < max_attempts; ++failed_attempts)
{
if (func())
{
return true;
}
if (failed_attempts + 1 < max_attempts)
{
INFO_LOG_FMT(COMMON, "{} attempt failed, delaying for {} milliseconds", func_name,
delay.count());
std::this_thread::sleep_for(delay);
delay *= 2;
}
}
return false;
}
// renames file srcFilename to destFilename, returns true on success // renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string& srcFilename, const std::string& destFilename) bool Rename(const std::string& srcFilename, const std::string& destFilename)
{ {
DEBUG_LOG_FMT(COMMON, "Rename: {} --> {}", srcFilename, destFilename); DEBUG_LOG_FMT(COMMON, "Rename: {} --> {}", srcFilename, destFilename);
#ifdef _WIN32 #ifdef _WIN32
const std::wstring source_wstring = UTF8ToTStr(srcFilename); std::error_code error;
const std::wstring destination_wstring = UTF8ToTStr(destFilename); std::filesystem::rename(UTF8ToWString(srcFilename), UTF8ToWString(destFilename), error);
if (error)
// On Windows ReplaceFile can fail spuriously due to antivirus checking or other noise.
// Retry the operation with increasing delays, and if none of them work there's probably a
// persistent problem.
const bool success = AttemptMaxTimesWithExponentialDelay(
3, std::chrono::milliseconds(5), fmt::format("Rename {} --> {}", srcFilename, destFilename),
[&source_wstring, &destination_wstring] {
if (ReplaceFile(destination_wstring.c_str(), source_wstring.c_str(), nullptr,
REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr))
{ {
return true; ERROR_LOG_FMT(COMMON, "Rename failed: {} --> {}: {}", srcFilename, destFilename,
error.message());
} }
// Might have failed because the destination doesn't exist. const bool success = !error;
if (GetLastError() == ERROR_FILE_NOT_FOUND)
{
return MoveFile(source_wstring.c_str(), destination_wstring.c_str()) != 0;
}
return false;
});
constexpr auto error_string_func = GetLastErrorString;
#else #else
const bool success = rename(srcFilename.c_str(), destFilename.c_str()) == 0; const bool success = rename(srcFilename.c_str(), destFilename.c_str()) == 0;
constexpr auto error_string_func = LastStrerrorString;
#endif
if (!success) if (!success)
{ {
ERROR_LOG_FMT(COMMON, "Rename: rename failed on {} --> {}: {}", srcFilename, destFilename, ERROR_LOG_FMT(COMMON, "Rename failed {} --> {}: {}", srcFilename, destFilename,
error_string_func()); LastStrerrorString());
} }
#endif
return success; return success;
} }