1
0
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:
Serge Pavlov 2020-04-29 11:26:19 +07:00
parent 27d3d1c9d2
commit 1398f4e3fc
4 changed files with 127 additions and 1 deletions

View File

@ -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.

View File

@ -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

View File

@ -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() {}
//===----------------------------------------------------------------------===//

View File

@ -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