From 7b1f966a6c456270004a2aec2c53057ca8efbac9 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 12 Feb 2014 09:45:56 -0500 Subject: [PATCH] Windows: Make file delete/rename retry configurable Several CMake operations need to replace files in rapid succession. This commonly fails on Windows due to filesystem lock behavior so we have retry loops. No matter how many times we retry or how long we delay there will inevitably be someone with an environment that needs more. Make the retry count and delay configurable in the Windows Registry keys: {HKCU,HKLM}/Software/Kitware/CMake/Config in DWORD values FilesystemRetryCount = Number of tries FilesystemRetryDelay = Delay in milliseconds between tries Leave the feature undocumented for now to see how it goes. --- Source/cmCoreTryCompile.cxx | 13 +++++++---- Source/cmSystemTools.cxx | 46 +++++++++++++++++++++++++++++++++---- Source/cmSystemTools.h | 9 ++++++++ 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index bbdb3a1d8..7b5206945 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -585,15 +585,20 @@ void cmCoreTryCompile::CleanupFiles(const char* binDir) } else { +#ifdef _WIN32 // Sometimes anti-virus software hangs on to new files so we // cannot delete them immediately. Try a few times. - int tries = 5; + cmSystemTools::WindowsFileRetry retry = + cmSystemTools::GetWindowsFileRetry(); while(!cmSystemTools::RemoveFile(fullPath.c_str()) && - --tries && cmSystemTools::FileExists(fullPath.c_str())) + --retry.Count && cmSystemTools::FileExists(fullPath.c_str())) { - cmSystemTools::Delay(500); + cmSystemTools::Delay(retry.Delay); } - if(tries == 0) + if(retry.Count == 0) +#else + if(!cmSystemTools::RemoveFile(fullPath.c_str())) +#endif { std::string m = "Remove failed on file: " + fullPath; cmSystemTools::ReportLastSystemError(m.c_str()); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 41c7509b2..ff0597563 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -863,6 +863,44 @@ bool cmSystemTools::CopyFileIfDifferent(const char* source, return Superclass::CopyFileIfDifferent(source, destination); } +//---------------------------------------------------------------------------- +#ifdef _WIN32 +cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry() +{ + static WindowsFileRetry retry = {0,0}; + if(!retry.Count) + { + unsigned int data[2] = {0,0}; + HKEY const keys[2] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE}; + wchar_t const* const values[2] = {L"FilesystemRetryCount", + L"FilesystemRetryDelay"}; + for(int k=0; k < 2; ++k) + { + HKEY hKey; + if(RegOpenKeyExW(keys[k], L"Software\\Kitware\\CMake\\Config", + 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) + { + for(int v=0; v < 2; ++v) + { + DWORD dwData, dwType, dwSize = 4; + if(!data[v] && + RegQueryValueExW(hKey, values[v], 0, &dwType, (BYTE *)&dwData, + &dwSize) == ERROR_SUCCESS && + dwType == REG_DWORD && dwSize == 4) + { + data[v] = static_cast(dwData); + } + } + RegCloseKey(hKey); + } + } + retry.Count = data[0]? data[0] : 5; + retry.Delay = data[1]? data[1] : 500; + } + return retry; +} +#endif + //---------------------------------------------------------------------------- bool cmSystemTools::RenameFile(const char* oldname, const char* newname) { @@ -874,10 +912,10 @@ bool cmSystemTools::RenameFile(const char* oldname, const char* newname) fails then remove the read-only attribute from any existing destination. Try multiple times since we may be racing against another process creating/opening the destination file just before our MoveFileEx. */ - int tries = 5; + WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry(); while(!MoveFileExW(cmsys::Encoding::ToWide(oldname).c_str(), cmsys::Encoding::ToWide(newname).c_str(), - MOVEFILE_REPLACE_EXISTING) && --tries) + MOVEFILE_REPLACE_EXISTING) && --retry.Count) { // Try again only if failure was due to access permissions. if(GetLastError() != ERROR_ACCESS_DENIED) @@ -896,10 +934,10 @@ bool cmSystemTools::RenameFile(const char* oldname, const char* newname) else { // The file may be temporarily in use so wait a bit. - cmSystemTools::Delay(100); + cmSystemTools::Delay(retry.Delay); } } - return tries > 0; + return retry.Count > 0; #else /* On UNIX we have an OS-provided call to do this atomically. */ return rename(oldname, newname) == 0; diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 4e854c8c5..4a5d2981b 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -460,6 +460,15 @@ public: /** Tokenize a string */ static std::vector tokenize(const std::string& str, const std::string& sep); + +#ifdef _WIN32 + struct WindowsFileRetry + { + unsigned int Count; + unsigned int Delay; + }; + static WindowsFileRetry GetWindowsFileRetry(); +#endif private: static bool s_ForceUnixPaths; static bool s_RunCommandHideConsole;