mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
[Support] Class to facilitate file locking
This change define RAII class `FileLocker` and methods `lock` and `tryLockFor` of the class `raw_fd_stream` to facilitate using file locks. Differential Revision: https://reviews.llvm.org/D79066
This commit is contained in:
parent
27d3d1c9d2
commit
1398f4e3fc
@ -1179,6 +1179,35 @@ std::error_code unlockFile(int FD);
|
||||
/// means that the filesystem may have failed to perform some buffered writes.
|
||||
std::error_code closeFile(file_t &F);
|
||||
|
||||
/// RAII class that facilitates file locking.
|
||||
class FileLocker {
|
||||
int FD; ///< Locked file handle.
|
||||
FileLocker(int FD) : FD(FD) {}
|
||||
friend class llvm::raw_fd_ostream;
|
||||
|
||||
public:
|
||||
FileLocker(const FileLocker &L) = delete;
|
||||
FileLocker(FileLocker &&L) : FD(L.FD) { L.FD = -1; }
|
||||
~FileLocker() {
|
||||
if (FD != -1)
|
||||
unlockFile(FD);
|
||||
}
|
||||
FileLocker &operator=(FileLocker &&L) {
|
||||
FD = L.FD;
|
||||
L.FD = -1;
|
||||
return *this;
|
||||
}
|
||||
FileLocker &operator=(const FileLocker &L) = delete;
|
||||
std::error_code unlock() {
|
||||
if (FD != -1) {
|
||||
std::error_code Result = unlockFile(FD);
|
||||
FD = -1;
|
||||
return Result;
|
||||
}
|
||||
return std::error_code();
|
||||
}
|
||||
};
|
||||
|
||||
std::error_code getUniqueID(const Twine Path, UniqueID &Result);
|
||||
|
||||
/// Get disk space usage information.
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@ -30,12 +31,14 @@ class format_object_base;
|
||||
class FormattedString;
|
||||
class FormattedNumber;
|
||||
class FormattedBytes;
|
||||
template <class T> class LLVM_NODISCARD Expected;
|
||||
|
||||
namespace sys {
|
||||
namespace fs {
|
||||
enum FileAccess : unsigned;
|
||||
enum OpenFlags : unsigned;
|
||||
enum CreationDisposition : unsigned;
|
||||
class FileLocker;
|
||||
} // end namespace fs
|
||||
} // end namespace sys
|
||||
|
||||
@ -468,7 +471,7 @@ public:
|
||||
/// fsync.
|
||||
void close();
|
||||
|
||||
bool supportsSeeking() { return SupportsSeeking; }
|
||||
bool supportsSeeking() const { return SupportsSeeking; }
|
||||
|
||||
/// Flushes the stream and repositions the underlying file descriptor position
|
||||
/// to the offset specified from the beginning of the file.
|
||||
@ -496,6 +499,38 @@ public:
|
||||
/// - from The Zen of Python, by Tim Peters
|
||||
///
|
||||
void clear_error() { EC = std::error_code(); }
|
||||
|
||||
/// Locks the underlying file.
|
||||
///
|
||||
/// @returns RAII object that releases the lock upon leaving the scope, if the
|
||||
/// locking was successful. Otherwise returns corresponding
|
||||
/// error code.
|
||||
///
|
||||
/// The function blocks the current thread until the lock become available or
|
||||
/// error occurs.
|
||||
///
|
||||
/// Possible use of this function may be as follows:
|
||||
///
|
||||
/// @code{.cpp}
|
||||
/// if (auto L = stream.lock()) {
|
||||
/// // ... do action that require file to be locked.
|
||||
/// } else {
|
||||
/// handleAllErrors(std::move(L.takeError()), [&](ErrorInfoBase &EIB) {
|
||||
/// // ... handle lock error.
|
||||
/// });
|
||||
/// }
|
||||
/// @endcode
|
||||
LLVM_NODISCARD Expected<sys::fs::FileLocker> lock();
|
||||
|
||||
/// Tries to lock the underlying file within the specified period.
|
||||
///
|
||||
/// @returns RAII object that releases the lock upon leaving the scope, if the
|
||||
/// locking was successful. Otherwise returns corresponding
|
||||
/// error code.
|
||||
///
|
||||
/// It is used as @ref lock.
|
||||
LLVM_NODISCARD
|
||||
Expected<sys::fs::FileLocker> tryLockFor(std::chrono::milliseconds Timeout);
|
||||
};
|
||||
|
||||
/// This returns a reference to a raw_fd_ostream for standard output. Use it
|
||||
|
@ -861,6 +861,21 @@ bool raw_fd_ostream::has_colors() const {
|
||||
return sys::Process::FileDescriptorHasColors(FD);
|
||||
}
|
||||
|
||||
Expected<sys::fs::FileLocker> raw_fd_ostream::lock() {
|
||||
std::error_code EC = sys::fs::lockFile(FD);
|
||||
if (!EC)
|
||||
return sys::fs::FileLocker(FD);
|
||||
return errorCodeToError(EC);
|
||||
}
|
||||
|
||||
Expected<sys::fs::FileLocker>
|
||||
raw_fd_ostream::tryLockFor(std::chrono::milliseconds Timeout) {
|
||||
std::error_code EC = sys::fs::tryLockFile(FD, Timeout);
|
||||
if (!EC)
|
||||
return sys::fs::FileLocker(FD);
|
||||
return errorCodeToError(EC);
|
||||
}
|
||||
|
||||
void raw_fd_ostream::anchor() {}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -2179,4 +2179,51 @@ TEST_F(FileSystemTest, widenPath) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows refuses lock request if file region is already locked by the same
|
||||
// process. POSIX system in this case updates the existing lock.
|
||||
TEST_F(FileSystemTest, FileLocker) {
|
||||
using namespace std::chrono;
|
||||
int FD;
|
||||
std::error_code EC;
|
||||
SmallString<64> TempPath;
|
||||
EC = fs::createTemporaryFile("test", "temp", FD, TempPath);
|
||||
ASSERT_NO_ERROR(EC);
|
||||
FileRemover Cleanup(TempPath);
|
||||
raw_fd_ostream Stream(TempPath, EC);
|
||||
|
||||
EC = fs::tryLockFile(FD);
|
||||
ASSERT_NO_ERROR(EC);
|
||||
EC = fs::unlockFile(FD);
|
||||
ASSERT_NO_ERROR(EC);
|
||||
|
||||
if (auto L = Stream.lock()) {
|
||||
ASSERT_ERROR(fs::tryLockFile(FD));
|
||||
ASSERT_NO_ERROR(L->unlock());
|
||||
ASSERT_NO_ERROR(fs::tryLockFile(FD));
|
||||
ASSERT_NO_ERROR(fs::unlockFile(FD));
|
||||
} else {
|
||||
ADD_FAILURE();
|
||||
handleAllErrors(L.takeError(), [&](ErrorInfoBase &EIB) {});
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR(fs::tryLockFile(FD));
|
||||
ASSERT_NO_ERROR(fs::unlockFile(FD));
|
||||
|
||||
{
|
||||
Expected<fs::FileLocker> L1 = Stream.lock();
|
||||
ASSERT_THAT_EXPECTED(L1, Succeeded());
|
||||
raw_fd_ostream Stream2(FD, false);
|
||||
Expected<fs::FileLocker> L2 = Stream2.tryLockFor(250ms);
|
||||
ASSERT_THAT_EXPECTED(L2, Failed());
|
||||
ASSERT_NO_ERROR(L1->unlock());
|
||||
Expected<fs::FileLocker> L3 = Stream.tryLockFor(0ms);
|
||||
ASSERT_THAT_EXPECTED(L3, Succeeded());
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR(fs::tryLockFile(FD));
|
||||
ASSERT_NO_ERROR(fs::unlockFile(FD));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // anonymous namespace
|
||||
|
Loading…
Reference in New Issue
Block a user