Merge topic 'file-LOCK-command'
93017828
Help: Add notes for topic 'file-LOCK-command'e6db4c5a
file: Add LOCK subcommand to do file and directory locking05d6531c
cmSystemTools: Add StringToInt helper
This commit is contained in:
commit
d90e288b7b
|
@ -305,3 +305,33 @@ status messages (subject to the :variable:`CMAKE_INSTALL_MESSAGE` variable),
|
||||||
and ``NO_SOURCE_PERMISSIONS`` is default.
|
and ``NO_SOURCE_PERMISSIONS`` is default.
|
||||||
Installation scripts generated by the :command:`install` command
|
Installation scripts generated by the :command:`install` command
|
||||||
use this signature (with some undocumented options for internal use).
|
use this signature (with some undocumented options for internal use).
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
file(LOCK <path> [DIRECTORY] [RELEASE]
|
||||||
|
[GUARD <FUNCTION|FILE|PROCESS>]
|
||||||
|
[RESULT_VARIABLE <variable>]
|
||||||
|
[TIMEOUT <seconds>])
|
||||||
|
|
||||||
|
Lock a file specified by ``<path>`` if no ``DIRECTORY`` option present and file
|
||||||
|
``<path>/cmake.lock`` otherwise. File will be locked for scope defined by
|
||||||
|
``GUARD`` option (default value is ``PROCESS``). ``RELEASE`` option can be used
|
||||||
|
to unlock file explicitly. If option ``TIMEOUT`` is not specified CMake will
|
||||||
|
wait until lock succeed or until fatal error occurs. If ``TIMEOUT`` is set to
|
||||||
|
``0`` lock will be tried once and result will be reported immediately. If
|
||||||
|
``TIMEOUT`` is not ``0`` CMake will try to lock file for the period specified
|
||||||
|
by ``<seconds>`` value. Any errors will be interpreted as fatal if there is no
|
||||||
|
``RESULT_VARIABLE`` option. Otherwise result will be stored in ``<variable>``
|
||||||
|
and will be ``0`` on success or error message on failure.
|
||||||
|
|
||||||
|
Note that lock is advisory - there is no guarantee that other processes will
|
||||||
|
respect this lock, i.e. lock synchronize two or more CMake instances sharing
|
||||||
|
some modifiable resources. Similar logic applied to ``DIRECTORY`` option -
|
||||||
|
locking parent directory doesn't prevent other ``LOCK`` commands to lock any
|
||||||
|
child directory or file.
|
||||||
|
|
||||||
|
Trying to lock file twice is not allowed. Any intermediate directories and
|
||||||
|
file itself will be created if they not exist. ``GUARD`` and ``TIMEOUT``
|
||||||
|
options ignored on ``RELEASE`` operation.
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
file-LOCK-command
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
* The :command:`file(LOCK)` subcommand was created to allow CMake
|
||||||
|
processes to synchronize through file and directory locks.
|
|
@ -219,6 +219,12 @@ set(SRCS
|
||||||
cmExtraKateGenerator.h
|
cmExtraKateGenerator.h
|
||||||
cmExtraSublimeTextGenerator.cxx
|
cmExtraSublimeTextGenerator.cxx
|
||||||
cmExtraSublimeTextGenerator.h
|
cmExtraSublimeTextGenerator.h
|
||||||
|
cmFileLock.cxx
|
||||||
|
cmFileLock.h
|
||||||
|
cmFileLockPool.cxx
|
||||||
|
cmFileLockPool.h
|
||||||
|
cmFileLockResult.cxx
|
||||||
|
cmFileLockResult.h
|
||||||
cmFileTimeComparison.cxx
|
cmFileTimeComparison.cxx
|
||||||
cmFileTimeComparison.h
|
cmFileTimeComparison.h
|
||||||
cmGeneratedFileStream.cxx
|
cmGeneratedFileStream.cxx
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
#include "cm_curl.h"
|
#include "cm_curl.h"
|
||||||
|
#include "cmFileLockResult.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#undef GetCurrentDirectory
|
#undef GetCurrentDirectory
|
||||||
|
@ -202,6 +203,10 @@ bool cmFileCommand
|
||||||
{
|
{
|
||||||
return this->HandleGenerateCommand(args);
|
return this->HandleGenerateCommand(args);
|
||||||
}
|
}
|
||||||
|
else if ( subCommand == "LOCK" )
|
||||||
|
{
|
||||||
|
return this->HandleLockCommand(args);
|
||||||
|
}
|
||||||
|
|
||||||
std::string e = "does not recognize sub-command "+subCommand;
|
std::string e = "does not recognize sub-command "+subCommand;
|
||||||
this->SetError(e);
|
this->SetError(e);
|
||||||
|
@ -3501,6 +3506,204 @@ bool cmFileCommand::HandleGenerateCommand(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool cmFileCommand::HandleLockCommand(
|
||||||
|
std::vector<std::string> const& args)
|
||||||
|
{
|
||||||
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
|
// Default values
|
||||||
|
bool directory = false;
|
||||||
|
bool release = false;
|
||||||
|
enum Guard {
|
||||||
|
GUARD_FUNCTION,
|
||||||
|
GUARD_FILE,
|
||||||
|
GUARD_PROCESS
|
||||||
|
};
|
||||||
|
Guard guard = GUARD_PROCESS;
|
||||||
|
std::string resultVariable;
|
||||||
|
unsigned timeout = static_cast<unsigned>(-1);
|
||||||
|
|
||||||
|
// Parse arguments
|
||||||
|
if(args.size() < 2)
|
||||||
|
{
|
||||||
|
this->Makefile->IssueMessage(
|
||||||
|
cmake::FATAL_ERROR,
|
||||||
|
"sub-command LOCK requires at least two arguments.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path = args[1];
|
||||||
|
for (unsigned i = 2; i < args.size(); ++i)
|
||||||
|
{
|
||||||
|
if (args[i] == "DIRECTORY")
|
||||||
|
{
|
||||||
|
directory = true;
|
||||||
|
}
|
||||||
|
else if (args[i] == "RELEASE")
|
||||||
|
{
|
||||||
|
release = true;
|
||||||
|
}
|
||||||
|
else if (args[i] == "GUARD")
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
const char* merr = "expected FUNCTION, FILE or PROCESS after GUARD";
|
||||||
|
if (i >= args.size())
|
||||||
|
{
|
||||||
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR, merr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (args[i] == "FUNCTION")
|
||||||
|
{
|
||||||
|
guard = GUARD_FUNCTION;
|
||||||
|
}
|
||||||
|
else if (args[i] == "FILE")
|
||||||
|
{
|
||||||
|
guard = GUARD_FILE;
|
||||||
|
}
|
||||||
|
else if (args[i] == "PROCESS")
|
||||||
|
{
|
||||||
|
guard = GUARD_PROCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << merr << ", but got:\n \"" << args[i] << "\".";
|
||||||
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (args[i] == "RESULT_VARIABLE")
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
if (i >= args.size())
|
||||||
|
{
|
||||||
|
this->Makefile->IssueMessage(
|
||||||
|
cmake::FATAL_ERROR,
|
||||||
|
"expected variable name after RESULT_VARIABLE");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resultVariable = args[i];
|
||||||
|
}
|
||||||
|
else if (args[i] == "TIMEOUT")
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
if (i >= args.size())
|
||||||
|
{
|
||||||
|
this->Makefile->IssueMessage(
|
||||||
|
cmake::FATAL_ERROR,
|
||||||
|
"expected timeout value after TIMEOUT");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int scanned;
|
||||||
|
if(!cmSystemTools::StringToInt(args[i].c_str(), &scanned) || scanned < 0)
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "TIMEOUT value \"" << args[i] << "\" is not an unsigned integer.";
|
||||||
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
timeout = static_cast<unsigned>(scanned);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or TIMEOUT\n";
|
||||||
|
e << "but got: \"" << args[i] << "\".";
|
||||||
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (directory)
|
||||||
|
{
|
||||||
|
path += "/cmake.lock";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cmsys::SystemTools::FileIsFullPath(path))
|
||||||
|
{
|
||||||
|
path = this->Makefile->GetCurrentDirectory() + ("/" + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unify path (remove '//', '/../', ...)
|
||||||
|
path = cmSystemTools::CollapseFullPath(path);
|
||||||
|
|
||||||
|
// Create file and directories if needed
|
||||||
|
std::string parentDir = cmSystemTools::GetParentDirectory(path);
|
||||||
|
if (!cmSystemTools::MakeDirectory(parentDir))
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "directory\n \"" << parentDir << "\"\ncreation failed ";
|
||||||
|
e << "(check permissions).";
|
||||||
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
cmSystemTools::SetFatalErrorOccured();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FILE *file = cmsys::SystemTools::Fopen(path, "w");
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "file\n \"" << path << "\"\ncreation failed (check permissions).";
|
||||||
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
cmSystemTools::SetFatalErrorOccured();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
// Actual lock/unlock
|
||||||
|
cmFileLockPool& lockPool = this->Makefile->GetLocalGenerator()->
|
||||||
|
GetGlobalGenerator()->GetFileLockPool();
|
||||||
|
|
||||||
|
cmFileLockResult fileLockResult(cmFileLockResult::MakeOk());
|
||||||
|
if (release)
|
||||||
|
{
|
||||||
|
fileLockResult = lockPool.Release(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (guard)
|
||||||
|
{
|
||||||
|
case GUARD_FUNCTION:
|
||||||
|
fileLockResult = lockPool.LockFunctionScope(path, timeout);
|
||||||
|
break;
|
||||||
|
case GUARD_FILE:
|
||||||
|
fileLockResult = lockPool.LockFileScope(path, timeout);
|
||||||
|
break;
|
||||||
|
case GUARD_PROCESS:
|
||||||
|
fileLockResult = lockPool.LockProcessScope(path, timeout);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cmSystemTools::SetFatalErrorOccured();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string result = fileLockResult.GetOutputMessage();
|
||||||
|
|
||||||
|
if (resultVariable.empty() && !fileLockResult.IsOk())
|
||||||
|
{
|
||||||
|
cmOStringStream e;
|
||||||
|
e << "error locking file\n \"" << path << "\"\n" << result << ".";
|
||||||
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
|
||||||
|
cmSystemTools::SetFatalErrorOccured();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resultVariable.empty())
|
||||||
|
{
|
||||||
|
this->Makefile->AddDefinition(resultVariable, result.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
static_cast<void>(args);
|
||||||
|
this->SetError("sub-command LOCK not implemented in bootstrap cmake");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool cmFileCommand::HandleTimestampCommand(
|
bool cmFileCommand::HandleTimestampCommand(
|
||||||
std::vector<std::string> const& args)
|
std::vector<std::string> const& args)
|
||||||
|
|
|
@ -75,6 +75,7 @@ protected:
|
||||||
|
|
||||||
bool HandleTimestampCommand(std::vector<std::string> const& args);
|
bool HandleTimestampCommand(std::vector<std::string> const& args);
|
||||||
bool HandleGenerateCommand(std::vector<std::string> const& args);
|
bool HandleGenerateCommand(std::vector<std::string> const& args);
|
||||||
|
bool HandleLockCommand(std::vector<std::string> const& args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddEvaluationFile(const std::string &inputName,
|
void AddEvaluationFile(const std::string &inputName,
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*============================================================================
|
||||||
|
CMake - Cross Platform Makefile Generator
|
||||||
|
Copyright 2014 Ruslan Baratov
|
||||||
|
|
||||||
|
Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
see accompanying file Copyright.txt for details.
|
||||||
|
|
||||||
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the License for more information.
|
||||||
|
============================================================================*/
|
||||||
|
|
||||||
|
#include "cmFileLock.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include "cmFileLockResult.h"
|
||||||
|
|
||||||
|
// Common implementation
|
||||||
|
|
||||||
|
cmFileLock::~cmFileLock()
|
||||||
|
{
|
||||||
|
if (!this->Filename.empty())
|
||||||
|
{
|
||||||
|
const cmFileLockResult result = this->Release();
|
||||||
|
static_cast<void>(result);
|
||||||
|
assert(result.IsOk());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::Lock(
|
||||||
|
const std::string& filename, unsigned timeout)
|
||||||
|
{
|
||||||
|
if (filename.empty())
|
||||||
|
{
|
||||||
|
// Error is internal since all the directories and file must be created
|
||||||
|
// before actual lock called.
|
||||||
|
return cmFileLockResult::MakeInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->Filename.empty())
|
||||||
|
{
|
||||||
|
// Error is internal since double-lock must be checked in class
|
||||||
|
// cmFileLockPool by the cmFileLock::IsLocked method.
|
||||||
|
return cmFileLockResult::MakeInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->Filename = filename;
|
||||||
|
cmFileLockResult result = this->OpenFile();
|
||||||
|
if (result.IsOk())
|
||||||
|
{
|
||||||
|
if (timeout == static_cast<unsigned>(-1))
|
||||||
|
{
|
||||||
|
result = this->LockWithoutTimeout();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = this->LockWithTimeout(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.IsOk())
|
||||||
|
{
|
||||||
|
this->Filename = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmFileLock::IsLocked(const std::string& filename) const
|
||||||
|
{
|
||||||
|
return filename == this->Filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
# include "cmFileLockWin32.cxx"
|
||||||
|
#else
|
||||||
|
# include "cmFileLockUnix.cxx"
|
||||||
|
#endif
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*============================================================================
|
||||||
|
CMake - Cross Platform Makefile Generator
|
||||||
|
Copyright 2014 Ruslan Baratov
|
||||||
|
|
||||||
|
Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
see accompanying file Copyright.txt for details.
|
||||||
|
|
||||||
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the License for more information.
|
||||||
|
============================================================================*/
|
||||||
|
|
||||||
|
#ifndef cmFileLock_h
|
||||||
|
#define cmFileLock_h
|
||||||
|
|
||||||
|
#include "cmStandardIncludes.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
# include <windows.h> // HANDLE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class cmFileLockResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cross-platform file locking.
|
||||||
|
* @details Under the hood this class use 'fcntl' for Unix-like platforms and
|
||||||
|
* 'LockFileEx'/'UnlockFileEx' for Win32 platform. Locks are exclusive and
|
||||||
|
* advisory.
|
||||||
|
*/
|
||||||
|
class cmFileLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cmFileLock();
|
||||||
|
~cmFileLock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lock the file.
|
||||||
|
* @param timeoutSec Lock timeout. If -1 try until success or fatal error.
|
||||||
|
*/
|
||||||
|
cmFileLockResult Lock(const std::string& filename, unsigned timeoutSec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unlock the file.
|
||||||
|
*/
|
||||||
|
cmFileLockResult Release();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check file is locked by this class.
|
||||||
|
* @details This function helps to find double locks (deadlocks) and to do
|
||||||
|
* explicit unlocks.
|
||||||
|
*/
|
||||||
|
bool IsLocked(const std::string& filename) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
cmFileLock(const cmFileLock&);
|
||||||
|
cmFileLock& operator=(const cmFileLock&);
|
||||||
|
|
||||||
|
cmFileLockResult OpenFile();
|
||||||
|
cmFileLockResult LockWithoutTimeout();
|
||||||
|
cmFileLockResult LockWithTimeout(unsigned timeoutSec);
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
typedef HANDLE FileId;
|
||||||
|
BOOL LockFile(DWORD flags);
|
||||||
|
#else
|
||||||
|
typedef int FileId;
|
||||||
|
int LockFile(int cmd, int type);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FileId File;
|
||||||
|
std::string Filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // cmFileLock_h
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*============================================================================
|
||||||
|
CMake - Cross Platform Makefile Generator
|
||||||
|
Copyright 2014 Ruslan Baratov
|
||||||
|
|
||||||
|
Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
see accompanying file Copyright.txt for details.
|
||||||
|
|
||||||
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the License for more information.
|
||||||
|
============================================================================*/
|
||||||
|
|
||||||
|
#include "cmFileLockPool.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "cmFileLock.h"
|
||||||
|
#include "cmFileLockResult.h"
|
||||||
|
|
||||||
|
cmFileLockPool::cmFileLockPool()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockPool::~cmFileLockPool()
|
||||||
|
{
|
||||||
|
for (It i = this->FunctionScopes.begin();
|
||||||
|
i != this->FunctionScopes.end(); ++i)
|
||||||
|
{
|
||||||
|
delete *i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (It i = this->FileScopes.begin(); i != this->FileScopes.end(); ++i)
|
||||||
|
{
|
||||||
|
delete *i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmFileLockPool::PushFunctionScope()
|
||||||
|
{
|
||||||
|
this->FunctionScopes.push_back(new ScopePool());
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmFileLockPool::PopFunctionScope()
|
||||||
|
{
|
||||||
|
assert(!this->FunctionScopes.empty());
|
||||||
|
delete this->FunctionScopes.back();
|
||||||
|
this->FunctionScopes.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmFileLockPool::PushFileScope()
|
||||||
|
{
|
||||||
|
this->FileScopes.push_back(new ScopePool());
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmFileLockPool::PopFileScope()
|
||||||
|
{
|
||||||
|
assert(!this->FileScopes.empty());
|
||||||
|
delete this->FileScopes.back();
|
||||||
|
this->FileScopes.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockPool::LockFunctionScope(
|
||||||
|
const std::string& filename, unsigned timeoutSec)
|
||||||
|
{
|
||||||
|
if (this->IsAlreadyLocked(filename))
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeAlreadyLocked();
|
||||||
|
}
|
||||||
|
if (this->FunctionScopes.empty())
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeNoFunction();
|
||||||
|
}
|
||||||
|
return this->FunctionScopes.back()->Lock(filename, timeoutSec);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockPool::LockFileScope(
|
||||||
|
const std::string& filename, unsigned timeoutSec)
|
||||||
|
{
|
||||||
|
if (this->IsAlreadyLocked(filename))
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeAlreadyLocked();
|
||||||
|
}
|
||||||
|
assert(!this->FileScopes.empty());
|
||||||
|
return this->FileScopes.back()->Lock(filename, timeoutSec);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockPool::LockProcessScope(
|
||||||
|
const std::string& filename, unsigned timeoutSec)
|
||||||
|
{
|
||||||
|
if (this->IsAlreadyLocked(filename))
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeAlreadyLocked();
|
||||||
|
}
|
||||||
|
return this->ProcessScope.Lock(filename, timeoutSec);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockPool::Release(const std::string& filename)
|
||||||
|
{
|
||||||
|
for (It i = this->FunctionScopes.begin();
|
||||||
|
i != this->FunctionScopes.end(); ++i)
|
||||||
|
{
|
||||||
|
const cmFileLockResult result = (*i)->Release(filename);
|
||||||
|
if (!result.IsOk())
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (It i = this->FileScopes.begin(); i != this->FileScopes.end(); ++i)
|
||||||
|
{
|
||||||
|
const cmFileLockResult result = (*i)->Release(filename);
|
||||||
|
if (!result.IsOk())
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->ProcessScope.Release(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmFileLockPool::IsAlreadyLocked(const std::string& filename) const
|
||||||
|
{
|
||||||
|
for (CIt i = this->FunctionScopes.begin();
|
||||||
|
i != this->FunctionScopes.end(); ++i)
|
||||||
|
{
|
||||||
|
const bool result = (*i)->IsAlreadyLocked(filename);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CIt i = this->FileScopes.begin(); i != this->FileScopes.end(); ++i)
|
||||||
|
{
|
||||||
|
const bool result = (*i)->IsAlreadyLocked(filename);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->ProcessScope.IsAlreadyLocked(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockPool::ScopePool::ScopePool()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockPool::ScopePool::~ScopePool()
|
||||||
|
{
|
||||||
|
for (It i = this->Locks.begin(); i != this->Locks.end(); ++i)
|
||||||
|
{
|
||||||
|
delete *i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockPool::ScopePool::Lock(
|
||||||
|
const std::string& filename, unsigned timeoutSec)
|
||||||
|
{
|
||||||
|
cmFileLock *lock = new cmFileLock();
|
||||||
|
const cmFileLockResult result = lock->Lock(filename, timeoutSec);
|
||||||
|
if (result.IsOk())
|
||||||
|
{
|
||||||
|
this->Locks.push_back(lock);
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete lock;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockPool::ScopePool::Release(
|
||||||
|
const std::string& filename)
|
||||||
|
{
|
||||||
|
for (It i = this->Locks.begin(); i != this->Locks.end(); ++i)
|
||||||
|
{
|
||||||
|
if ((*i)->IsLocked(filename))
|
||||||
|
{
|
||||||
|
return (*i)->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmFileLockPool::ScopePool::IsAlreadyLocked(
|
||||||
|
const std::string& filename) const
|
||||||
|
{
|
||||||
|
for (CIt i = this->Locks.begin(); i != this->Locks.end(); ++i)
|
||||||
|
{
|
||||||
|
if ((*i)->IsLocked(filename))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*============================================================================
|
||||||
|
CMake - Cross Platform Makefile Generator
|
||||||
|
Copyright 2014 Ruslan Baratov
|
||||||
|
|
||||||
|
Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
see accompanying file Copyright.txt for details.
|
||||||
|
|
||||||
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the License for more information.
|
||||||
|
============================================================================*/
|
||||||
|
#ifndef cmFileLockPool_h
|
||||||
|
#define cmFileLockPool_h
|
||||||
|
|
||||||
|
#include "cmStandardIncludes.h"
|
||||||
|
|
||||||
|
class cmFileLockResult;
|
||||||
|
class cmFileLock;
|
||||||
|
|
||||||
|
class cmFileLockPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cmFileLockPool();
|
||||||
|
~cmFileLockPool();
|
||||||
|
|
||||||
|
//@{
|
||||||
|
/**
|
||||||
|
* @brief Function scope control.
|
||||||
|
*/
|
||||||
|
void PushFunctionScope();
|
||||||
|
void PopFunctionScope();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//@{
|
||||||
|
/**
|
||||||
|
* @brief File scope control.
|
||||||
|
*/
|
||||||
|
void PushFileScope();
|
||||||
|
void PopFileScope();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//@{
|
||||||
|
/**
|
||||||
|
* @brief Lock the file in given scope.
|
||||||
|
* @param timeoutSec Lock timeout. If -1 try until success or fatal error.
|
||||||
|
*/
|
||||||
|
cmFileLockResult LockFunctionScope(
|
||||||
|
const std::string& filename, unsigned timeoutSec
|
||||||
|
);
|
||||||
|
cmFileLockResult LockFileScope(
|
||||||
|
const std::string& filename, unsigned timeoutSec
|
||||||
|
);
|
||||||
|
cmFileLockResult LockProcessScope(
|
||||||
|
const std::string& filename, unsigned timeoutSec
|
||||||
|
);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unlock the file explicitly.
|
||||||
|
*/
|
||||||
|
cmFileLockResult Release(const std::string& filename);
|
||||||
|
|
||||||
|
private:
|
||||||
|
cmFileLockPool(const cmFileLockPool&);
|
||||||
|
cmFileLockPool& operator=(const cmFileLockPool&);
|
||||||
|
|
||||||
|
bool IsAlreadyLocked(const std::string& filename) const;
|
||||||
|
|
||||||
|
class ScopePool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScopePool();
|
||||||
|
~ScopePool();
|
||||||
|
|
||||||
|
cmFileLockResult Lock(const std::string& filename, unsigned timeoutSec);
|
||||||
|
cmFileLockResult Release(const std::string& filename);
|
||||||
|
bool IsAlreadyLocked(const std::string& filename) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScopePool(const ScopePool&);
|
||||||
|
ScopePool& operator=(const ScopePool&);
|
||||||
|
|
||||||
|
typedef std::list<cmFileLock*> List;
|
||||||
|
typedef List::iterator It;
|
||||||
|
typedef List::const_iterator CIt;
|
||||||
|
|
||||||
|
List Locks;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::list<ScopePool*> List;
|
||||||
|
|
||||||
|
typedef List::iterator It;
|
||||||
|
typedef List::const_iterator CIt;
|
||||||
|
|
||||||
|
List FunctionScopes;
|
||||||
|
List FileScopes;
|
||||||
|
ScopePool ProcessScope;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // cmFileLockPool_h
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*============================================================================
|
||||||
|
CMake - Cross Platform Makefile Generator
|
||||||
|
Copyright 2014 Ruslan Baratov
|
||||||
|
|
||||||
|
Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
see accompanying file Copyright.txt for details.
|
||||||
|
|
||||||
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the License for more information.
|
||||||
|
============================================================================*/
|
||||||
|
|
||||||
|
#include "cmFileLockResult.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockResult::MakeOk()
|
||||||
|
{
|
||||||
|
return cmFileLockResult(OK, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockResult::MakeSystem()
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
const Error lastError = GetLastError();
|
||||||
|
#else
|
||||||
|
const Error lastError = errno;
|
||||||
|
#endif
|
||||||
|
return cmFileLockResult(SYSTEM, lastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockResult::MakeTimeout()
|
||||||
|
{
|
||||||
|
return cmFileLockResult(TIMEOUT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockResult::MakeAlreadyLocked()
|
||||||
|
{
|
||||||
|
return cmFileLockResult(ALREADY_LOCKED, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockResult::MakeInternal()
|
||||||
|
{
|
||||||
|
return cmFileLockResult(INTERNAL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLockResult::MakeNoFunction()
|
||||||
|
{
|
||||||
|
return cmFileLockResult(NO_FUNCTION, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmFileLockResult::IsOk() const
|
||||||
|
{
|
||||||
|
return this->Type == OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cmFileLockResult::GetOutputMessage() const
|
||||||
|
{
|
||||||
|
switch (this->Type)
|
||||||
|
{
|
||||||
|
case OK:
|
||||||
|
return "0";
|
||||||
|
case SYSTEM:
|
||||||
|
#if defined(_WIN32)
|
||||||
|
{
|
||||||
|
char* errorText = NULL;
|
||||||
|
|
||||||
|
// http://stackoverflow.com/a/455533/2288008
|
||||||
|
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||||
|
::FormatMessageA(
|
||||||
|
flags,
|
||||||
|
NULL,
|
||||||
|
this->ErrorValue,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPSTR)&errorText,
|
||||||
|
0,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (errorText != NULL)
|
||||||
|
{
|
||||||
|
const std::string message = errorText;
|
||||||
|
::LocalFree(errorText);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Internal error (FormatMessageA failed)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return strerror(this->ErrorValue);
|
||||||
|
#endif
|
||||||
|
case TIMEOUT:
|
||||||
|
return "Timeout reached";
|
||||||
|
case ALREADY_LOCKED:
|
||||||
|
return "File already locked";
|
||||||
|
case NO_FUNCTION:
|
||||||
|
return "'GUARD FUNCTION' not used in function definition";
|
||||||
|
case INTERNAL:
|
||||||
|
default:
|
||||||
|
return "Internal error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult::cmFileLockResult(ErrorType typeValue, Error errorValue):
|
||||||
|
Type(typeValue), ErrorValue(errorValue)
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*============================================================================
|
||||||
|
CMake - Cross Platform Makefile Generator
|
||||||
|
Copyright 2014 Ruslan Baratov
|
||||||
|
|
||||||
|
Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
see accompanying file Copyright.txt for details.
|
||||||
|
|
||||||
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the License for more information.
|
||||||
|
============================================================================*/
|
||||||
|
|
||||||
|
#ifndef cmFileLockResult_h
|
||||||
|
#define cmFileLockResult_h
|
||||||
|
|
||||||
|
#include "cmStandardIncludes.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
# include <windows.h> // DWORD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Result of the locking/unlocking file.
|
||||||
|
* @note See @c cmFileLock
|
||||||
|
*/
|
||||||
|
class cmFileLockResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#if defined(_WIN32)
|
||||||
|
typedef DWORD Error;
|
||||||
|
#else
|
||||||
|
typedef int Error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Successful lock/unlock.
|
||||||
|
*/
|
||||||
|
static cmFileLockResult MakeOk();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lock/Unlock failed. Read error/GetLastError.
|
||||||
|
*/
|
||||||
|
static cmFileLockResult MakeSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lock/Unlock failed. Timeout reached.
|
||||||
|
*/
|
||||||
|
static cmFileLockResult MakeTimeout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief File already locked.
|
||||||
|
*/
|
||||||
|
static cmFileLockResult MakeAlreadyLocked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal error.
|
||||||
|
*/
|
||||||
|
static cmFileLockResult MakeInternal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Try to lock with function guard outside of the function
|
||||||
|
*/
|
||||||
|
static cmFileLockResult MakeNoFunction();
|
||||||
|
|
||||||
|
bool IsOk() const;
|
||||||
|
std::string GetOutputMessage() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum ErrorType
|
||||||
|
{
|
||||||
|
OK,
|
||||||
|
SYSTEM,
|
||||||
|
TIMEOUT,
|
||||||
|
ALREADY_LOCKED,
|
||||||
|
INTERNAL,
|
||||||
|
NO_FUNCTION
|
||||||
|
};
|
||||||
|
|
||||||
|
cmFileLockResult(ErrorType type, Error errorValue);
|
||||||
|
|
||||||
|
ErrorType Type;
|
||||||
|
Error ErrorValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // cmFileLockResult_h
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*============================================================================
|
||||||
|
CMake - Cross Platform Makefile Generator
|
||||||
|
Copyright 2014 Ruslan Baratov
|
||||||
|
|
||||||
|
Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
see accompanying file Copyright.txt for details.
|
||||||
|
|
||||||
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the License for more information.
|
||||||
|
============================================================================*/
|
||||||
|
|
||||||
|
#include "cmFileLock.h"
|
||||||
|
|
||||||
|
#include <errno.h> // errno
|
||||||
|
#include <stdio.h> // SEEK_SET
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "cmSystemTools.h"
|
||||||
|
|
||||||
|
cmFileLock::cmFileLock(): File(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::Release()
|
||||||
|
{
|
||||||
|
if (this->Filename.empty())
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
const int lockResult = this->LockFile(F_SETLK, F_UNLCK);
|
||||||
|
|
||||||
|
this->Filename = "";
|
||||||
|
|
||||||
|
if (lockResult == 0)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeSystem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::OpenFile()
|
||||||
|
{
|
||||||
|
this->File = ::open(this->Filename.c_str(), O_RDWR);
|
||||||
|
if (this->File == -1)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeSystem();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::LockWithoutTimeout()
|
||||||
|
{
|
||||||
|
if (this->LockFile(F_SETLKW, F_WRLCK) == -1)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeSystem();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::LockWithTimeout(unsigned seconds)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (this->LockFile(F_SETLK, F_WRLCK) == -1)
|
||||||
|
{
|
||||||
|
if (errno != EACCES && errno != EAGAIN)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeSystem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
if (seconds == 0)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeTimeout();
|
||||||
|
}
|
||||||
|
--seconds;
|
||||||
|
cmSystemTools::Delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmFileLock::LockFile(int cmd, int type)
|
||||||
|
{
|
||||||
|
struct ::flock lock;
|
||||||
|
lock.l_start = 0;
|
||||||
|
lock.l_len = 0; // lock all bytes
|
||||||
|
lock.l_pid = 0; // unused (for F_GETLK only)
|
||||||
|
lock.l_type = static_cast<short>(type); // exclusive lock
|
||||||
|
lock.l_whence = SEEK_SET;
|
||||||
|
return ::fcntl(this->File, cmd, &lock);
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*============================================================================
|
||||||
|
CMake - Cross Platform Makefile Generator
|
||||||
|
Copyright 2014 Ruslan Baratov
|
||||||
|
|
||||||
|
Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
see accompanying file Copyright.txt for details.
|
||||||
|
|
||||||
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the License for more information.
|
||||||
|
============================================================================*/
|
||||||
|
|
||||||
|
#include "cmFileLock.h"
|
||||||
|
|
||||||
|
#include <windows.h> // CreateFileW
|
||||||
|
#include "cmSystemTools.h"
|
||||||
|
|
||||||
|
cmFileLock::cmFileLock(): File(INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::Release()
|
||||||
|
{
|
||||||
|
if (this->Filename.empty())
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
const unsigned long len = static_cast<unsigned long>(-1);
|
||||||
|
static OVERLAPPED overlapped;
|
||||||
|
const DWORD reserved = 0;
|
||||||
|
const BOOL unlockResult = UnlockFileEx(
|
||||||
|
File,
|
||||||
|
reserved,
|
||||||
|
len,
|
||||||
|
len,
|
||||||
|
&overlapped
|
||||||
|
);
|
||||||
|
|
||||||
|
this->Filename = "";
|
||||||
|
|
||||||
|
if (unlockResult)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeSystem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::OpenFile()
|
||||||
|
{
|
||||||
|
const DWORD access = GENERIC_READ | GENERIC_WRITE;
|
||||||
|
const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||||
|
const PSECURITY_ATTRIBUTES security = NULL;
|
||||||
|
const DWORD attr = 0;
|
||||||
|
const HANDLE templ = NULL;
|
||||||
|
this->File = CreateFileW(
|
||||||
|
cmSystemTools::ConvertToWindowsExtendedPath(this->Filename).c_str(),
|
||||||
|
access,
|
||||||
|
shareMode,
|
||||||
|
security,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
attr,
|
||||||
|
templ
|
||||||
|
);
|
||||||
|
if (this->File == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeSystem();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::LockWithoutTimeout()
|
||||||
|
{
|
||||||
|
if (!this->LockFile(LOCKFILE_EXCLUSIVE_LOCK))
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeSystem();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileLockResult cmFileLock::LockWithTimeout(unsigned seconds)
|
||||||
|
{
|
||||||
|
const DWORD flags = LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const BOOL result = this->LockFile(flags);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeOk();
|
||||||
|
}
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
if (error != ERROR_LOCK_VIOLATION)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeSystem();
|
||||||
|
}
|
||||||
|
if (seconds == 0)
|
||||||
|
{
|
||||||
|
return cmFileLockResult::MakeTimeout();
|
||||||
|
}
|
||||||
|
--seconds;
|
||||||
|
cmSystemTools::Delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL cmFileLock::LockFile(DWORD flags)
|
||||||
|
{
|
||||||
|
const DWORD reserved = 0;
|
||||||
|
const unsigned long len = static_cast<unsigned long>(-1);
|
||||||
|
static OVERLAPPED overlapped;
|
||||||
|
return LockFileEx(
|
||||||
|
this->File,
|
||||||
|
flags,
|
||||||
|
reserved,
|
||||||
|
len,
|
||||||
|
len,
|
||||||
|
&overlapped
|
||||||
|
);
|
||||||
|
}
|
|
@ -23,6 +23,7 @@
|
||||||
#include "cmGeneratorExpression.h"
|
#include "cmGeneratorExpression.h"
|
||||||
|
|
||||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
|
# include "cmFileLockPool.h"
|
||||||
# include <cmsys/hash_map.hxx>
|
# include <cmsys/hash_map.hxx>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -348,6 +349,10 @@ public:
|
||||||
std::set<cmTarget const*> const&
|
std::set<cmTarget const*> const&
|
||||||
GetFilenameTargetDepends(cmSourceFile* sf) const;
|
GetFilenameTargetDepends(cmSourceFile* sf) const;
|
||||||
|
|
||||||
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
|
cmFileLockPool& GetFileLockPool() { return FileLockPool; }
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Generate();
|
virtual void Generate();
|
||||||
|
|
||||||
|
@ -499,6 +504,11 @@ private:
|
||||||
|
|
||||||
mutable std::map<cmSourceFile*, std::set<cmTarget const*> >
|
mutable std::map<cmSourceFile*, std::set<cmTarget const*> >
|
||||||
FilenameTargetDepends;
|
FilenameTargetDepends;
|
||||||
|
|
||||||
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
|
// Pool of file locks
|
||||||
|
cmFileLockPool FileLockPool;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -79,9 +79,15 @@ public:
|
||||||
this->GG = lg->GetGlobalGenerator();
|
this->GG = lg->GetGlobalGenerator();
|
||||||
this->LG = this->GG->GetCurrentLocalGenerator();
|
this->LG = this->GG->GetCurrentLocalGenerator();
|
||||||
this->GG->SetCurrentLocalGenerator(lg);
|
this->GG->SetCurrentLocalGenerator(lg);
|
||||||
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
|
this->GG->GetFileLockPool().PushFileScope();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
~cmLocalGeneratorCurrent()
|
~cmLocalGeneratorCurrent()
|
||||||
{
|
{
|
||||||
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
|
this->GG->GetFileLockPool().PopFileScope();
|
||||||
|
#endif
|
||||||
this->GG->SetCurrentLocalGenerator(this->LG);
|
this->GG->SetCurrentLocalGenerator(this->LG);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4522,10 +4522,20 @@ void cmMakefile::PushScope()
|
||||||
this->Internal->VarUsageStack.push(usage);
|
this->Internal->VarUsageStack.push(usage);
|
||||||
|
|
||||||
this->PushLoopBlockBarrier();
|
this->PushLoopBlockBarrier();
|
||||||
|
|
||||||
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
|
this->GetLocalGenerator()->GetGlobalGenerator()->
|
||||||
|
GetFileLockPool().PushFunctionScope();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmMakefile::PopScope()
|
void cmMakefile::PopScope()
|
||||||
{
|
{
|
||||||
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||||
|
this->GetLocalGenerator()->GetGlobalGenerator()->
|
||||||
|
GetFileLockPool().PopFunctionScope();
|
||||||
|
#endif
|
||||||
|
|
||||||
this->PopLoopBlockBarrier();
|
this->PopLoopBlockBarrier();
|
||||||
|
|
||||||
cmDefinitions* current = &this->Internal->VarStack.top();
|
cmDefinitions* current = &this->Internal->VarStack.top();
|
||||||
|
|
|
@ -2923,3 +2923,11 @@ std::vector<std::string> cmSystemTools::tokenize(const std::string& str,
|
||||||
}
|
}
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool cmSystemTools::StringToInt(const char* str, int* value)
|
||||||
|
{
|
||||||
|
char *endp;
|
||||||
|
*value = static_cast<int>(strtol(str, &endp, 10));
|
||||||
|
return (*endp == '\0') && (endp != str);
|
||||||
|
}
|
||||||
|
|
|
@ -458,6 +458,9 @@ public:
|
||||||
static std::vector<std::string> tokenize(const std::string& str,
|
static std::vector<std::string> tokenize(const std::string& str,
|
||||||
const std::string& sep);
|
const std::string& sep);
|
||||||
|
|
||||||
|
/** Convert string to int. Expected that the whole string is an integer */
|
||||||
|
static bool StringToInt(const char* str, int* value);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
struct WindowsFileRetry
|
struct WindowsFileRetry
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,8 @@
|
||||||
|
CMake Error at LOCK-error-file-create-fail\.cmake:[0-9]+ \(file\):
|
||||||
|
file
|
||||||
|
|
||||||
|
".*"
|
||||||
|
|
||||||
|
creation failed \(check permissions\)\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1,3 @@
|
||||||
|
set(tmp "${CMAKE_CURRENT_BINARY_DIR}/temp-directory")
|
||||||
|
file(MAKE_DIRECTORY "${tmp}")
|
||||||
|
file(LOCK "${tmp}")
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,6 @@
|
||||||
|
CMake Error at LOCK-error-guard-incorrect\.cmake:[0-9]+ \(file\):
|
||||||
|
expected FUNCTION, FILE or PROCESS after GUARD, but got:
|
||||||
|
|
||||||
|
"FUNCTIO"\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" GUARD FUNCTIO)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
CMake Error at LOCK-error-incorrect-timeout\.cmake:[0-9]+ \(file\):
|
||||||
|
TIMEOUT value "qwerty" is not an unsigned integer\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
CMake Error at LOCK-error-incorrect-timeout-trail\.cmake:[0-9]+ \(file\):
|
||||||
|
TIMEOUT value "123xyz" is not an unsigned integer\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" TIMEOUT 123xyz)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" TIMEOUT qwerty)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,6 @@
|
||||||
|
CMake Error at LOCK-error-lock-fail.cmake:[0-9]+ \(file\):
|
||||||
|
directory
|
||||||
|
|
||||||
|
".*"
|
||||||
|
|
||||||
|
creation failed \(check permissions\)\.
|
|
@ -0,0 +1,6 @@
|
||||||
|
set(lfile "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock")
|
||||||
|
FILE(WRITE "${lfile}" "")
|
||||||
|
|
||||||
|
# Try to lock file '${lfile}/cmake.lock'. Since `lfile` is not a directory
|
||||||
|
# expected that operation will fail.
|
||||||
|
file(LOCK "${lfile}" DIRECTORY)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
CMake Error at LOCK-error-negative-timeout\.cmake:[0-9]+ \(file\):
|
||||||
|
TIMEOUT value "-2" is not an unsigned integer\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" TIMEOUT -2)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,8 @@
|
||||||
|
CMake Error at LOCK-error-no-function\.cmake:[0-9]+ \(file\):
|
||||||
|
error locking file
|
||||||
|
|
||||||
|
".*"
|
||||||
|
|
||||||
|
'GUARD FUNCTION' not used in function definition\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" GUARD FUNCTION)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
CMake Error at LOCK-error-no-guard\.cmake:[0-9]+ \(file\):
|
||||||
|
expected FUNCTION, FILE or PROCESS after GUARD
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" GUARD)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
CMake Error at LOCK-error-no-path.cmake:[0-9]+ \(file\):
|
||||||
|
file must be called with at least two arguments.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
CMake Error at LOCK-error-no-result-variable\.cmake:[0-9]+ \(file\):
|
||||||
|
expected variable name after RESULT_VARIABLE
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" RESULT_VARIABLE)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,4 @@
|
||||||
|
CMake Error at LOCK-error-no-timeout\.cmake:[0-9]+ \(file\):
|
||||||
|
expected timeout value after TIMEOUT
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock" TIMEOUT)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,12 @@
|
||||||
|
Output:[ ]*
|
||||||
|
Error: CMake Error at .*.timeout-script\.cmake:[0-9]+ \(file\):
|
||||||
|
error locking file
|
||||||
|
|
||||||
|
".*"
|
||||||
|
|
||||||
|
Timeout reached\.
|
||||||
|
+
|
||||||
|
CMake Error at LOCK-error-timeout\.cmake:[0-9]+ \(message\):
|
||||||
|
Result: 1
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]+ \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
.*
|
|
@ -0,0 +1,17 @@
|
||||||
|
set(script "${CMAKE_CURRENT_LIST_DIR}/timeout-script.cmake")
|
||||||
|
set(file_to_lock "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock")
|
||||||
|
|
||||||
|
file(LOCK "${file_to_lock}")
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${CMAKE_COMMAND}" "-Dfile_to_lock=${file_to_lock}" -P "${script}"
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
OUTPUT_VARIABLE output
|
||||||
|
ERROR_VARIABLE error
|
||||||
|
)
|
||||||
|
|
||||||
|
message("Output: ${output}")
|
||||||
|
message("Error: ${error}")
|
||||||
|
|
||||||
|
if(NOT result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Result: ${result}")
|
||||||
|
endif()
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,6 @@
|
||||||
|
CMake Error at LOCK-error-unknown-option\.cmake:[0-9]+ \(file\):
|
||||||
|
expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or TIMEOUT
|
||||||
|
|
||||||
|
but got: "UNKNOWN"\.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists\.txt:[0-9]* \(include\)
|
|
@ -0,0 +1 @@
|
||||||
|
file(LOCK "${CMAKE_CURRENT_BINARY_DIR}/temp-file" UNKNOWN)
|
|
@ -0,0 +1,11 @@
|
||||||
|
-- Simple lock
|
||||||
|
-- Directory lock
|
||||||
|
-- Release
|
||||||
|
-- Lock function scope
|
||||||
|
-- Lock file scope
|
||||||
|
-- Lock in subdirectory
|
||||||
|
-- Lock process scope
|
||||||
|
-- Error double lock
|
||||||
|
-- Ok
|
||||||
|
-- Timeout 0
|
||||||
|
-- Timeout not 0
|
|
@ -0,0 +1,40 @@
|
||||||
|
set(lfile "${CMAKE_CURRENT_BINARY_DIR}/file-to-lock")
|
||||||
|
set(ldir "${CMAKE_CURRENT_BINARY_DIR}/dir-to-lock")
|
||||||
|
|
||||||
|
message(STATUS "Simple lock")
|
||||||
|
file(LOCK ${lfile})
|
||||||
|
|
||||||
|
message(STATUS "Directory lock")
|
||||||
|
file(LOCK ${ldir} DIRECTORY)
|
||||||
|
|
||||||
|
message(STATUS "Release")
|
||||||
|
file(LOCK ${lfile} RELEASE)
|
||||||
|
|
||||||
|
function(foo)
|
||||||
|
file(LOCK "${lfile}" GUARD FUNCTION)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
message(STATUS "Lock function scope")
|
||||||
|
foo()
|
||||||
|
|
||||||
|
message(STATUS "Lock file scope")
|
||||||
|
add_subdirectory(subdir_test_unlock)
|
||||||
|
|
||||||
|
message(STATUS "Lock process scope")
|
||||||
|
file(LOCK "${lfile}" GUARD PROCESS)
|
||||||
|
|
||||||
|
message(STATUS "Error double lock")
|
||||||
|
file(LOCK "${lfile}" RESULT_VARIABLE lock_result)
|
||||||
|
if(lock_result STREQUAL "File already locked")
|
||||||
|
message(STATUS "Ok")
|
||||||
|
else()
|
||||||
|
message(STATUS FATAL_ERROR "Expected error message")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Timeout 0")
|
||||||
|
file(LOCK "${lfile}" RELEASE)
|
||||||
|
file(LOCK "${lfile}" TIMEOUT 0)
|
||||||
|
|
||||||
|
message(STATUS "Timeout not 0")
|
||||||
|
file(LOCK "${lfile}" RELEASE)
|
||||||
|
file(LOCK "${lfile}" TIMEOUT 3)
|
|
@ -3,3 +3,17 @@ include(RunCMake)
|
||||||
run_cmake(INSTALL-DIRECTORY)
|
run_cmake(INSTALL-DIRECTORY)
|
||||||
run_cmake(INSTALL-MESSAGE-bad)
|
run_cmake(INSTALL-MESSAGE-bad)
|
||||||
run_cmake(FileOpenFailRead)
|
run_cmake(FileOpenFailRead)
|
||||||
|
run_cmake(LOCK)
|
||||||
|
run_cmake(LOCK-error-file-create-fail)
|
||||||
|
run_cmake(LOCK-error-guard-incorrect)
|
||||||
|
run_cmake(LOCK-error-incorrect-timeout)
|
||||||
|
run_cmake(LOCK-error-incorrect-timeout-trail)
|
||||||
|
run_cmake(LOCK-error-lock-fail)
|
||||||
|
run_cmake(LOCK-error-negative-timeout)
|
||||||
|
run_cmake(LOCK-error-no-function)
|
||||||
|
run_cmake(LOCK-error-no-guard)
|
||||||
|
run_cmake(LOCK-error-no-path)
|
||||||
|
run_cmake(LOCK-error-no-result-variable)
|
||||||
|
run_cmake(LOCK-error-no-timeout)
|
||||||
|
run_cmake(LOCK-error-timeout)
|
||||||
|
run_cmake(LOCK-error-unknown-option)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
message(STATUS "Lock in subdirectory")
|
||||||
|
file(LOCK "${lfile}" GUARD FILE)
|
|
@ -0,0 +1,5 @@
|
||||||
|
if(NOT file_to_lock)
|
||||||
|
message(FATAL_ERROR "file_to_lock is empty")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(LOCK "${file_to_lock}" TIMEOUT 1)
|
Loading…
Reference in New Issue