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.
This commit is contained in:
Brad King 2014-02-12 09:45:56 -05:00
parent 5104f55d3f
commit 7b1f966a6c
3 changed files with 60 additions and 8 deletions

View File

@ -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());

View File

@ -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<unsigned int>(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;

View File

@ -460,6 +460,15 @@ public:
/** Tokenize a string */
static std::vector<std::string> 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;