[llvm] 032ed39 - [Support] Class to facilitate file locking
Serge Pavlov via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 29 23:43:33 PDT 2020
Author: Serge Pavlov
Date: 2020-07-30T13:42:20+07:00
New Revision: 032ed39defc7403b94c367fa43432606c418e946
URL: https://github.com/llvm/llvm-project/commit/032ed39defc7403b94c367fa43432606c418e946
DIFF: https://github.com/llvm/llvm-project/commit/032ed39defc7403b94c367fa43432606c418e946.diff
LOG: [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
Added:
Modified:
llvm/include/llvm/Support/FileSystem.h
llvm/include/llvm/Support/raw_ostream.h
llvm/lib/Support/raw_ostream.cpp
llvm/unittests/Support/Path.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h
index 18d787a75f61..5a8842ee26e2 100644
--- a/llvm/include/llvm/Support/FileSystem.h
+++ b/llvm/include/llvm/Support/FileSystem.h
@@ -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.
diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h
index 8d289f7c765f..e9c710d0f38f 100644
--- a/llvm/include/llvm/Support/raw_ostream.h
+++ b/llvm/include/llvm/Support/raw_ostream.h
@@ -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 @@ class raw_fd_ostream : public raw_pwrite_stream {
/// 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 @@ class raw_fd_ostream : public raw_pwrite_stream {
/// - 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
diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index f2d78d773239..86c48993957a 100644
--- a/llvm/lib/Support/raw_ostream.cpp
+++ b/llvm/lib/Support/raw_ostream.cpp
@@ -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() {}
//===----------------------------------------------------------------------===//
diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp
index 19ff49d25426..9d7ab1a60090 100644
--- a/llvm/unittests/Support/Path.cpp
+++ b/llvm/unittests/Support/Path.cpp
@@ -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
More information about the llvm-commits
mailing list